diff options
| author | Gubolin <gubolin@fantasymail.de> | 2015-01-11 10:32:59 +0100 |
|---|---|---|
| committer | Gubolin <gubolin@fantasymail.de> | 2015-01-11 10:32:59 +0100 |
| commit | a94bd16d95f00fe3f8f27b425be3c7fc8cd3cd76 (patch) | |
| tree | bde44a9ec9442947c9b46cb6c0f4377eaadcb44e | |
| parent | 8b7b751c04a0f41129a2d39de93c171e8e516c42 (diff) | |
| parent | ff3abf9d87a502cdf31a5f4abcbbf1826fd64fc9 (diff) | |
| download | snap-a94bd16d95f00fe3f8f27b425be3c7fc8cd3cd76.tar.gz snap-a94bd16d95f00fe3f8f27b425be3c7fc8cd3cd76.zip | |
Merge branch 'development' into gh-pages
| -rwxr-xr-x | binary.js | 1099 | ||||
| -rwxr-xr-x | binary.sh | 92 | ||||
| -rw-r--r-- | config.xml | 15 | ||||
| -rwxr-xr-x | desktop.sh | 34 | ||||
| -rwxr-xr-x | index.html | 10 | ||||
| -rwxr-xr-x[-rw-r--r--] | lang-ko.js | 492 | ||||
| -rwxr-xr-x | mobile.sh | 65 | ||||
| -rw-r--r-- | morphic.js | 5 | ||||
| -rw-r--r-- | objects.js | 52 | ||||
| -rw-r--r-- | package.json | 8 | ||||
| -rw-r--r-- | threads.js | 126 |
11 files changed, 1896 insertions, 102 deletions
diff --git a/binary.js b/binary.js new file mode 100755 index 0000000..d1a9b21 --- /dev/null +++ b/binary.js @@ -0,0 +1,1099 @@ +/* + + gui.js + + a programming environment + based on morphic.js, blocks.js, threads.js and objects.js + inspired by Scratch + + written by Jens Mönig + jens@moenig.org + + Copyright (C) 2014 by Jens Mönig + + This file is part of Snap!. + + Snap! is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as + published by the Free Software Foundation, either version 3 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + + + prerequisites: + -------------- + needs blocks.js, threads.js, objects.js and morphic.js + + + toc + --- + the following list shows the order in which all constructors are + defined. Use this list to locate code in this document: + + IDE_Morph + + + credits + ------- + Nathan Dinsmore contributed saving and loading of projects, + ypr-Snap! project conversion and countless bugfixes + Ian Reynolds contributed handling and visualization of sounds + +*/ + +/*global modules, Morph, SpriteMorph, Color, newCanvas, +VariableFrame, Point, MenuMorph, +morphicVersion, DialogBoxMorph, ToggleButtonMorph, contains, +StageMorph, PushButtonMorph, Process, SnapSerializer, MorphicPreferences, +SymbolMorph, +BlockImportDialogMorph, SnapTranslator, localize, List */ + +// Global stuff //////////////////////////////////////////////////////// + +modules.gui = '2014-July-30'; + +// Declarations + +var IDE_Morph; + +// IDE_Morph /////////////////////////////////////////////////////////// + +// I am SNAP's top-level frame, the Editor window + +// IDE_Morph inherits from Morph: + +IDE_Morph.prototype = new Morph(); +IDE_Morph.prototype.constructor = IDE_Morph; +IDE_Morph.uber = Morph.prototype; + +// IDE_Morph preferences settings and skins + +IDE_Morph.prototype.setDefaultDesign = function () { + MorphicPreferences.isFlat = false; + SpriteMorph.prototype.paletteColor = new Color(55, 55, 55); + SpriteMorph.prototype.paletteTextColor = new Color(230, 230, 230); + StageMorph.prototype.paletteTextColor + = SpriteMorph.prototype.paletteTextColor; + StageMorph.prototype.paletteColor = SpriteMorph.prototype.paletteColor; + SpriteMorph.prototype.sliderColor + = SpriteMorph.prototype.paletteColor.lighter(30); + + IDE_Morph.prototype.buttonContrast = 30; + IDE_Morph.prototype.backgroundColor = new Color(40, 40, 40); + IDE_Morph.prototype.frameColor = SpriteMorph.prototype.paletteColor; + + IDE_Morph.prototype.groupColor + = SpriteMorph.prototype.paletteColor.lighter(8); + IDE_Morph.prototype.sliderColor = SpriteMorph.prototype.sliderColor; + IDE_Morph.prototype.buttonLabelColor = new Color(255, 255, 255); + IDE_Morph.prototype.tabColors = [ + IDE_Morph.prototype.groupColor.darker(40), + IDE_Morph.prototype.groupColor.darker(60), + IDE_Morph.prototype.groupColor + ]; + IDE_Morph.prototype.rotationStyleColors = IDE_Morph.prototype.tabColors; + IDE_Morph.prototype.appModeColor = new Color(); + IDE_Morph.prototype.scriptsPaneTexture = 'scriptsPaneTexture.gif'; + IDE_Morph.prototype.padding = 5; +}; + +IDE_Morph.prototype.setFlatDesign = function () { + MorphicPreferences.isFlat = true; + SpriteMorph.prototype.paletteColor = new Color(255, 255, 255); + SpriteMorph.prototype.paletteTextColor = new Color(70, 70, 70); + StageMorph.prototype.paletteTextColor + = SpriteMorph.prototype.paletteTextColor; + StageMorph.prototype.paletteColor = SpriteMorph.prototype.paletteColor; + SpriteMorph.prototype.sliderColor = SpriteMorph.prototype.paletteColor; + + IDE_Morph.prototype.buttonContrast = 30; + IDE_Morph.prototype.backgroundColor = new Color(200, 200, 200); + IDE_Morph.prototype.frameColor = new Color(255, 255, 255); + + IDE_Morph.prototype.groupColor = new Color(230, 230, 230); + IDE_Morph.prototype.sliderColor = SpriteMorph.prototype.sliderColor; + IDE_Morph.prototype.buttonLabelColor = new Color(70, 70, 70); + IDE_Morph.prototype.tabColors = [ + IDE_Morph.prototype.groupColor.lighter(60), + IDE_Morph.prototype.groupColor.darker(10), + IDE_Morph.prototype.groupColor + ]; + IDE_Morph.prototype.rotationStyleColors = [ + IDE_Morph.prototype.groupColor, + IDE_Morph.prototype.groupColor.darker(10), + IDE_Morph.prototype.groupColor.darker(30) + ]; + IDE_Morph.prototype.appModeColor = IDE_Morph.prototype.frameColor; + IDE_Morph.prototype.scriptsPaneTexture = null; + IDE_Morph.prototype.padding = 1; +}; + +IDE_Morph.prototype.setDefaultDesign(); + +// IDE_Morph instance creation: + +function IDE_Morph(isAutoFill) { + this.init(isAutoFill); +} + +IDE_Morph.prototype.init = function (isAutoFill) { + // global font setting + MorphicPreferences.globalFontFamily = 'Helvetica, Arial'; + + // restore saved user preferences + this.userLanguage = null; + + // additional properties: + this.source = 'local'; + this.serializer = new SnapSerializer(); + + this.globalVariables = new VariableFrame(); + this.currentSprite = new SpriteMorph(this.globalVariables); + this.sprites = new List([this.currentSprite]); + this.currentCategory = 'motion'; + this.currentTab = 'scripts'; + this.projectName = ''; + this.projectNotes = ''; + + this.logo = null; + this.controlBar = null; + this.stage = null; + + this.isAutoFill = isAutoFill || true; + + this.filePicker = null; + this.hasChangedMedia = false; + + this.isAnimating = true; + this.stageRatio = 1; // for IDE animations, e.g. when zooming + + this.loadNewProject = false; // flag when starting up translated + this.shield = null; + + // initialize inherited properties: + IDE_Morph.uber.init.call(this); + + // override inherited properites: + this.color = this.appModeColor; +}; + +IDE_Morph.prototype.openIn = function (world) { + var hash; + + this.buildPanes(); + world.add(this); + + this.reactToWorldResize(world.bounds); + + function getURL(url) { + try { + var request = new XMLHttpRequest(); + request.open('GET', url, false); + request.send(); + if (request.status === 200) { + return request.responseText; + } + throw new Error('unable to retrieve ' + url); + } catch (err) { + return; + } + } + + function interpretUrlAnchors() { + if (location.hash.substr(0, 6) === '#open:') { + hash = location.hash.substr(6); + if (hash.charAt(0) === '%' + || hash.search(/\%(?:[0-9a-f]{2})/i) > -1) { + hash = decodeURIComponent(hash); + } + if (contains( + ['project', 'blocks', 'sprites', 'snapdata'].map( + function (each) { + return hash.substr(0, 8).indexOf(each); + } + ), + 1 + )) { + this.droppedText(hash); + } else { + this.droppedText(getURL(hash)); + } + } + } + + if (this.userLanguage) { + this.setLanguage(this.userLanguage, interpretUrlAnchors); + } else { + interpretUrlAnchors.call(this); + } +}; + +// IDE_Morph construction + +IDE_Morph.prototype.buildPanes = function () { + this.createLogo(); + this.createControlBar(); + this.createStage(); +}; + +IDE_Morph.prototype.createLogo = function () { + var myself = this; + + if (this.logo) { + this.logo.destroy(); + } + + this.logo = new Morph(); + this.logo.texture = 'snap_logo_sm.png'; + this.logo.drawNew = function () { + this.image = newCanvas(this.extent()); + var context = this.image.getContext('2d'); + context.fillStyle = this.color; + context.fillRect(0, 0, this.width(), this.height()); + if (this.texture) { + this.drawTexture(this.texture); + } + }; + + this.logo.drawCachedTexture = function () { + var context = this.image.getContext('2d'); + context.drawImage( + this.cachedTexture, + 5, + Math.round((this.height() - this.cachedTexture.height) / 2) + ); + this.changed(); + }; + + this.logo.mouseClickLeft = function () { + myself.snapMenu(); + }; + + this.logo.color = new Color(); + this.logo.setExtent(new Point(200, 28)); // dimensions are fixed + this.add(this.logo); +}; + +IDE_Morph.prototype.createControlBar = function () { + // assumes the logo has already been created + var padding = 5, + button, + stopButton, + pauseButton, + startButton, + x, + colors = [ + this.groupColor, + this.frameColor.darker(50), + this.frameColor.darker(50) + ], + myself = this; + + if (this.controlBar) { + this.controlBar.destroy(); + } + + this.controlBar = new Morph(); + this.controlBar.color = this.appModeColor; + this.controlBar.setHeight(this.logo.height()); // height is fixed + this.controlBar.mouseClickLeft = function () { + this.world().fillPage(); + }; + this.add(this.controlBar); + + // stopButton + button = new PushButtonMorph( + this, + 'stopAllScripts', + new SymbolMorph('octagon', 14) + ); + button.corner = 12; + button.color = colors[0]; + button.highlightColor = colors[1]; + button.pressColor = colors[2]; + button.labelMinExtent = new Point(36, 18); + button.padding = 0; + button.labelShadowOffset = new Point(-1, -1); + button.labelShadowColor = colors[1]; + button.labelColor = new Color(200, 0, 0); + button.contrast = this.buttonContrast; + button.drawNew(); + // button.hint = 'stop\nevery-\nthing'; + button.fixLayout(); + stopButton = button; + this.controlBar.add(stopButton); + + //pauseButton + button = new ToggleButtonMorph( + null, //colors, + myself, // the IDE is the target + 'togglePauseResume', + [ + new SymbolMorph('pause', 12), + new SymbolMorph('pointRight', 14) + ], + function () { // query + return myself.isPaused(); + } + ); + + button.corner = 12; + button.color = colors[0]; + button.highlightColor = colors[1]; + button.pressColor = colors[2]; + button.labelMinExtent = new Point(36, 18); + button.padding = 0; + button.labelShadowOffset = new Point(-1, -1); + button.labelShadowColor = colors[1]; + button.labelColor = new Color(255, 220, 0); + button.contrast = this.buttonContrast; + button.drawNew(); + // button.hint = 'pause/resume\nall scripts'; + button.fixLayout(); + button.refresh(); + pauseButton = button; + this.controlBar.add(pauseButton); + this.controlBar.pauseButton = pauseButton; // for refreshing + + // startButton + button = new PushButtonMorph( + this, + 'pressStart', + new SymbolMorph('flag', 14) + ); + button.corner = 12; + button.color = colors[0]; + button.highlightColor = colors[1]; + button.pressColor = colors[2]; + button.labelMinExtent = new Point(36, 18); + button.padding = 0; + button.labelShadowOffset = new Point(-1, -1); + button.labelShadowColor = colors[1]; + button.labelColor = new Color(0, 200, 0); + button.contrast = this.buttonContrast; + button.drawNew(); + // button.hint = 'start green\nflag scripts'; + button.fixLayout(); + startButton = button; + this.controlBar.add(startButton); + this.controlBar.startButton = startButton; + + this.controlBar.fixLayout = function () { + x = this.right() - padding; + [stopButton, pauseButton, startButton].forEach( + function (button) { + button.setCenter(myself.controlBar.center()); + button.setRight(x); + x -= button.width(); + x -= padding; + } + ); + + x = myself.right() - StageMorph.prototype.dimensions.x; + + this.updateLabel(); + }; + + this.controlBar.updateLabel = function () { + if (this.label) { + this.label.destroy(); + } + }; +}; + +IDE_Morph.prototype.createStage = function () { + // assumes that the logo pane has already been created + if (this.stage) { + this.stage.destroy(); + } + StageMorph.prototype.frameRate = 0; + this.stage = new StageMorph(this.globalVariables); + this.stage.setExtent(this.stage.dimensions); // dimensions are fixed + if (this.currentSprite instanceof SpriteMorph) { + this.currentSprite.setPosition( + this.stage.center().subtract( + this.currentSprite.extent().divideBy(2) + ) + ); + this.stage.add(this.currentSprite); + } + this.add(this.stage); +}; + +IDE_Morph.prototype.createCorral = function () { +}; + +// IDE_Morph resizing + +IDE_Morph.prototype.setExtent = function (point) { + var minExt, + ext; + + // determine the minimum dimensions making sense for the current mode + minExt = StageMorph.prototype.dimensions.add( + this.controlBar.height() + 10 + ); + ext = point.max(minExt); + IDE_Morph.uber.setExtent.call(this, ext); + this.fixLayout(); +}; + +// IDE_Morph events + +IDE_Morph.prototype.reactToWorldResize = function (rect) { + if (this.isAutoFill) { + this.setPosition(rect.origin); + this.setExtent(rect.extent()); + } + if (this.filePicker) { + document.body.removeChild(this.filePicker); + this.filePicker = null; + } +}; + +// IDE_Morph layout + +IDE_Morph.prototype.fixLayout = function () { + var padding = this.padding; + + Morph.prototype.trackChanges = false; + + // controlBar + this.controlBar.setPosition(this.logo.topRight()); + this.controlBar.setWidth(this.right() - this.controlBar.left()); + this.controlBar.fixLayout(); + + // stage + this.stage.setScale(Math.floor(Math.min( + (this.width() - padding * 2) / this.stage.dimensions.x, + (this.height() - this.controlBar.height() * 2 - padding * 2) + / this.stage.dimensions.y + ) * 10) / 10); + this.stage.setCenter(this.center()); + + Morph.prototype.trackChanges = true; + this.changed(); +}; + +IDE_Morph.prototype.droppedText = function (aString, name) { + var lbl = name ? name.split('.')[0] : ''; + if (aString.indexOf('<project') === 0) { + return this.openProjectString(aString); + } + if (aString.indexOf('<snapdata') === 0) { + return this.openCloudDataString(aString); + } + if (aString.indexOf('<blocks') === 0) { + return this.openBlocksString(aString, lbl, true); + } + if (aString.indexOf('<sprites') === 0) { + return this.openSpritesString(aString); + } + if (aString.indexOf('<media') === 0) { + return this.openMediaString(aString); + } +}; + +// IDE_Morph events + +IDE_Morph.prototype.pressStart = function () { + if (this.world().currentKey === 16) { // shiftClicked + this.toggleFastTracking(); + } else { + this.runScripts(); + } +}; + +IDE_Morph.prototype.toggleFastTracking = function () { + if (this.stage.isFastTracked) { + this.stopFastTracking(); + } else { + this.startFastTracking(); + } +}; + +IDE_Morph.prototype.toggleVariableFrameRate = function () { + if (StageMorph.prototype.frameRate) { + StageMorph.prototype.frameRate = 0; + this.stage.fps = 0; + } else { + StageMorph.prototype.frameRate = 30; + this.stage.fps = 30; + } +}; + +IDE_Morph.prototype.startFastTracking = function () { + this.stage.isFastTracked = true; + this.stage.fps = 0; + this.controlBar.startButton.labelString = new SymbolMorph('flash', 14); + this.controlBar.startButton.drawNew(); + this.controlBar.startButton.fixLayout(); +}; + +IDE_Morph.prototype.stopFastTracking = function () { + this.stage.isFastTracked = false; + this.stage.fps = this.stage.frameRate; + this.controlBar.startButton.labelString = new SymbolMorph('flag', 14); + this.controlBar.startButton.drawNew(); + this.controlBar.startButton.fixLayout(); +}; + +IDE_Morph.prototype.runScripts = function () { + this.stage.fireGreenFlagEvent(); +}; + +IDE_Morph.prototype.togglePauseResume = function () { + if (this.stage.threads.isPaused()) { + this.stage.threads.resumeAll(this.stage); + } else { + this.stage.threads.pauseAll(this.stage); + } + this.controlBar.pauseButton.refresh(); +}; + +IDE_Morph.prototype.isPaused = function () { + if (!this.stage) {return false; } + return this.stage.threads.isPaused(); +}; + +IDE_Morph.prototype.stopAllScripts = function () { + this.stage.fireStopAllEvent(); +}; + +IDE_Morph.prototype.selectSprite = function (sprite) { + this.currentSprite = sprite; + this.currentSprite.scripts.fixMultiArgs(); +}; + +// IDE_Morph menus + +IDE_Morph.prototype.snapMenu = function () { + var menu, + world = this.world(); + + menu = new MenuMorph(this); + menu.addItem('About...', 'aboutSnap'); + menu.addLine(); + menu.addItem( + 'Reference manual', + function () { + window.open('help/SnapManual.pdf', 'SnapReferenceManual'); + } + ); + menu.addItem( + 'Snap! website', + function () { + window.open('http://snap.berkeley.edu/', 'SnapWebsite'); + } + ); + menu.addItem( + 'Download source', + function () { + window.open( + 'http://snap.berkeley.edu/snapsource/snap.zip', + 'SnapSource' + ); + } + ); + if (world.isDevMode) { + menu.addLine(); + menu.addItem( + 'Switch back to user mode', + 'switchToUserMode', + 'disable deep-Morphic\ncontext menus' + + '\nand show user-friendly ones', + new Color(0, 100, 0) + ); + } else if (world.currentKey === 16) { // shift-click + menu.addLine(); + menu.addItem( + 'Switch to dev mode', + 'switchToDevMode', + 'enable Morphic\ncontext menus\nand inspectors,' + + '\nnot user-friendly!', + new Color(100, 0, 0) + ); + } + menu.popup(world, this.logo.bottomLeft()); +}; + +// IDE_Morph menu actions + +IDE_Morph.prototype.aboutSnap = function () { + var dlg, aboutTxt, noticeTxt, creditsTxt, versions = '', translations, + module, btn1, btn2, btn3, btn4, licenseBtn, translatorsBtn, + world = this.world(); + + aboutTxt = 'Snap! 4.0\nBuild Your Own Blocks\n\n--- beta ---\n\n' + + 'Copyright \u24B8 2014 Jens M\u00F6nig and ' + + 'Brian Harvey\n' + + 'jens@moenig.org, bh@cs.berkeley.edu\n\n' + + + 'Snap! is developed by the University of California, Berkeley\n' + + ' with support from the National Science Foundation ' + + 'and MioSoft. \n' + + + 'The design of Snap! is influenced and inspired by Scratch,\n' + + 'from the Lifelong Kindergarten group at the MIT Media Lab\n\n' + + + 'for more information see http://snap.berkeley.edu\n' + + 'and http://scratch.mit.edu'; + + noticeTxt = localize('License') + + '\n\n' + + 'Snap! is free software: you can redistribute it and/or modify\n' + + 'it under the terms of the GNU Affero General Public License as\n' + + 'published by the Free Software Foundation, either version 3 of\n' + + 'the License, or (at your option) any later version.\n\n' + + + 'This program is distributed in the hope that it will be useful,\n' + + 'but WITHOUT ANY WARRANTY; without even the implied warranty of\n' + + 'MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n' + + 'GNU Affero General Public License for more details.\n\n' + + + 'You should have received a copy of the\n' + + 'GNU Affero General Public License along with this program.\n' + + 'If not, see http://www.gnu.org/licenses/'; + + creditsTxt = localize('Contributors') + + '\n\nNathan Dinsmore: Saving/Loading, Snap-Logo Design, ' + + 'countless bugfixes' + + '\nKartik Chandra: Paint Editor' + + '\nMichael Ball: Time/Date UI, many bugfixes' + + '\n"Ava" Yuan Yuan: Graphic Effects' + + '\nKyle Hotchkiss: Block search design' + + '\nIan Reynolds: UI Design, Event Bindings, ' + + 'Sound primitives' + + '\nIvan Motyashov: Initial Squeak Porting' + + '\nDavide Della Casa: Morphic Optimizations' + + '\nAchal Dave: Web Audio' + + '\nJoe Otto: Morphic Testing and Debugging'; + + for (module in modules) { + if (Object.prototype.hasOwnProperty.call(modules, module)) { + versions += ('\n' + module + ' (' + + modules[module] + ')'); + } + } + if (versions !== '') { + versions = localize('current module versions:') + ' \n\n' + + 'morphic (' + morphicVersion + ')' + + versions; + } + translations = localize('Translations') + '\n' + SnapTranslator.credits(); + + dlg = new DialogBoxMorph(); + dlg.inform('About Snap', aboutTxt, world); + btn1 = dlg.buttons.children[0]; + translatorsBtn = dlg.addButton( + function () { + dlg.body.text = translations; + dlg.body.drawNew(); + btn1.show(); + btn2.show(); + btn3.hide(); + btn4.hide(); + licenseBtn.hide(); + translatorsBtn.hide(); + dlg.fixLayout(); + dlg.drawNew(); + dlg.setCenter(world.center()); + }, + 'Translators...' + ); + btn2 = dlg.addButton( + function () { + dlg.body.text = aboutTxt; + dlg.body.drawNew(); + btn1.show(); + btn2.hide(); + btn3.show(); + btn4.show(); + licenseBtn.show(); + translatorsBtn.hide(); + dlg.fixLayout(); + dlg.drawNew(); + dlg.setCenter(world.center()); + }, + 'Back...' + ); + btn2.hide(); + licenseBtn = dlg.addButton( + function () { + dlg.body.text = noticeTxt; + dlg.body.drawNew(); + btn1.show(); + btn2.show(); + btn3.hide(); + btn4.hide(); + licenseBtn.hide(); + translatorsBtn.hide(); + dlg.fixLayout(); + dlg.drawNew(); + dlg.setCenter(world.center()); + }, + 'License...' + ); + btn3 = dlg.addButton( + function () { + dlg.body.text = versions; + dlg.body.drawNew(); + btn1.show(); + btn2.show(); + btn3.hide(); + btn4.hide(); + licenseBtn.hide(); + translatorsBtn.hide(); + dlg.fixLayout(); + dlg.drawNew(); + dlg.setCenter(world.center()); + }, + 'Modules...' + ); + btn4 = dlg.addButton( + function () { + dlg.body.text = creditsTxt; + dlg.body.drawNew(); + btn1.show(); + btn2.show(); + translatorsBtn.show(); + btn3.hide(); + btn4.hide(); + licenseBtn.hide(); + dlg.fixLayout(); + dlg.drawNew(); + dlg.setCenter(world.center()); + }, + 'Credits...' + ); + translatorsBtn.hide(); + dlg.fixLayout(); + dlg.drawNew(); +}; + +IDE_Morph.prototype.openProjectString = function (str) { + var msg, + myself = this; + this.nextSteps([ + function () { + msg = myself.showMessage('Opening project...'); + }, + function () { + myself.rawOpenProjectString(str); + }, + function () { + msg.destroy(); + } + ]); +}; + +IDE_Morph.prototype.rawOpenProjectString = function (str) { + StageMorph.prototype.hiddenPrimitives = {}; + StageMorph.prototype.codeMappings = {}; + StageMorph.prototype.codeHeaders = {}; + StageMorph.prototype.enableCodeMapping = false; + if (Process.prototype.isCatchingErrors) { + try { + this.serializer.openProject(this.serializer.load(str), this); + } catch (err) { + this.showMessage('Load failed: ' + err); + } + } else { + this.serializer.openProject(this.serializer.load(str), this); + } + this.stopFastTracking(); +}; + +IDE_Morph.prototype.openBlocksString = function (str, name, silently) { + var msg, + myself = this; + this.nextSteps([ + function () { + msg = myself.showMessage('Opening blocks...'); + }, + function () { + myself.rawOpenBlocksString(str, name, silently); + }, + function () { + msg.destroy(); + } + ]); +}; + +IDE_Morph.prototype.rawOpenBlocksString = function (str, name, silently) { + // name is optional (string), so is silently (bool) + var blocks, + myself = this; + if (Process.prototype.isCatchingErrors) { + try { + blocks = this.serializer.loadBlocks(str, myself.stage); + } catch (err) { + this.showMessage('Load failed: ' + err); + } + } else { + blocks = this.serializer.loadBlocks(str, myself.stage); + } + if (silently) { + blocks.forEach(function (def) { + def.receiver = myself.stage; + myself.stage.globalBlocks.push(def); + myself.stage.replaceDoubleDefinitionsFor(def); + }); + this.showMessage( + 'Imported Blocks Module' + (name ? ': ' + name : '') + '.', + 2 + ); + } else { + new BlockImportDialogMorph(blocks, this.stage, name).popUp(); + } +}; + +IDE_Morph.prototype.openSpritesString = function (str) { + var msg, + myself = this; + this.nextSteps([ + function () { + msg = myself.showMessage('Opening sprite...'); + }, + function () { + myself.rawOpenSpritesString(str); + }, + function () { + msg.destroy(); + } + ]); +}; + +IDE_Morph.prototype.rawOpenSpritesString = function (str) { + if (Process.prototype.isCatchingErrors) { + try { + this.serializer.loadSprites(str, this); + } catch (err) { + this.showMessage('Load failed: ' + err); + } + } else { + this.serializer.loadSprites(str, this); + } +}; + +IDE_Morph.prototype.openMediaString = function (str) { + if (Process.prototype.isCatchingErrors) { + try { + this.serializer.loadMedia(str); + } catch (err) { + this.showMessage('Load failed: ' + err); + } + } else { + this.serializer.loadMedia(str); + } + this.showMessage('Imported Media Module.', 2); +}; + +IDE_Morph.prototype.openProject = function (name) { + var str; + if (name) { + this.showMessage('opening project\n' + name); + str = localStorage['-snap-project-' + name]; + this.openProjectString(str); + location.hash = '#open:' + str; + } +}; + +IDE_Morph.prototype.switchToUserMode = function () { + var world = this.world(); + + world.isDevMode = false; + Process.prototype.isCatchingErrors = true; + this.controlBar.updateLabel(); + this.isAutoFill = true; + this.isDraggable = false; + this.siblings().forEach(function (morph) { + if (morph instanceof DialogBoxMorph) { + world.add(morph); // bring to front + } else { + morph.destroy(); + } + }); + this.flushBlocksCache(); + // prevent non-DialogBoxMorphs from being dropped + // onto the World in user-mode + world.reactToDropOf = function (morph) { + if (!(morph instanceof DialogBoxMorph)) { + world.hand.grab(morph); + } + }; + this.showMessage('entering user mode', 1); + +}; + +IDE_Morph.prototype.switchToDevMode = function () { + var world = this.world(); + + world.isDevMode = true; + Process.prototype.isCatchingErrors = false; + this.controlBar.updateLabel(); + this.isAutoFill = false; + this.isDraggable = true; + this.setExtent(world.extent().subtract(100)); + this.setPosition(world.position().add(20)); + this.flushBlocksCache(); + // enable non-DialogBoxMorphs to be dropped + // onto the World in dev-mode + delete world.reactToDropOf; + this.showMessage( + 'entering development mode.\n\n' + + 'error catching is turned off,\n' + + 'use the browser\'s web console\n' + + 'to see error messages.' + ); +}; + +IDE_Morph.prototype.flushBlocksCache = function (category) { + // if no category is specified, the whole cache gets flushed + if (category) { + this.stage.blocksCache[category] = null; + this.stage.children.forEach(function (m) { + if (m instanceof SpriteMorph) { + m.blocksCache[category] = null; + } + }); + } else { + this.stage.blocksCache = {}; + this.stage.children.forEach(function (m) { + if (m instanceof SpriteMorph) { + m.blocksCache = {}; + } + }); + } +}; + +// IDE_Morph localization + +IDE_Morph.prototype.setLanguage = function (lang, callback) { + var translation = document.getElementById('language'), + src = 'lang-' + lang + '.js', + myself = this; + SnapTranslator.unload(); + if (translation) { + document.head.removeChild(translation); + } + if (lang === 'en') { + return this.reflectLanguage('en', callback); + } + myself.userLanguage = lang; + translation = document.createElement('script'); + translation.id = 'language'; + translation.onload = function () { + myself.reflectLanguage(lang, callback); + }; + document.head.appendChild(translation); + translation.src = src; +}; + +IDE_Morph.prototype.reflectLanguage = function (lang, callback) { + var projectData; + SnapTranslator.language = lang; + if (!this.loadNewProject) { + if (Process.prototype.isCatchingErrors) { + try { + projectData = this.serializer.serialize(this.stage); + } catch (err) { + this.showMessage('Serialization failed: ' + err); + } + } else { + projectData = this.serializer.serialize(this.stage); + } + } + SpriteMorph.prototype.initBlocks(); + this.fixLayout(); + if (this.loadNewProject) { + this.newProject(); + } else { + this.openProjectString(projectData); + } + this.saveSetting('language', lang); + if (callback) {callback.call(this); } +}; + +// IDE_Morph synchronous Http data fetching + +IDE_Morph.prototype.getURL = function (url) { + var request = new XMLHttpRequest(), + myself = this; + try { + request.open('GET', url, false); + request.send(); + if (request.status === 200) { + return request.responseText; + } + throw new Error('unable to retrieve ' + url); + } catch (err) { + myself.showMessage(err); + return; + } +}; + +IDE_Morph.prototype.getURLsbeOrRelative = function (url) { + var request = new XMLHttpRequest(), + myself = this; + try { + request.open('GET', 'http://snap.berkeley.edu/snapsource/' + + url, false); + request.send(); + if (request.status === 200) { + return request.responseText; + } + return myself.getURL(url); + } catch (err) { + myself.showMessage(err); + return; + } +}; + +// IDE_Morph user dialog shortcuts + +IDE_Morph.prototype.showMessage = function (message, secs) { + var m = new MenuMorph(null, message), + intervalHandle; + m.popUpCenteredInWorld(this.world()); + if (secs) { + intervalHandle = setInterval(function () { + m.destroy(); + clearInterval(intervalHandle); + }, secs * 1000); + } + return m; +}; + +IDE_Morph.prototype.inform = function (title, message) { + new DialogBoxMorph().inform( + title, + localize(message), + this.world() + ); +}; + +IDE_Morph.prototype.confirm = function (message, title, action) { + new DialogBoxMorph(null, action).askYesNo( + title, + localize(message), + this.world() + ); +}; + +IDE_Morph.prototype.prompt = function (message, callback, choices, key) { + (new DialogBoxMorph(null, callback)).withKey(key).prompt( + message, + '', + this.world(), + null, + choices + ); +}; diff --git a/binary.sh b/binary.sh new file mode 100755 index 0000000..212e7cb --- /dev/null +++ b/binary.sh @@ -0,0 +1,92 @@ +#!/bin/bash + +if [[ "$snapsource" == "" ]] +then + export snapsource="https://github.com/Gubolin/snap.git" +fi + +if [[ $# < 2 ]] +then + echo "Usage: binary.sh OPTION PLATFORM [FILE]" + echo "" + echo "OPTIONS:" + echo " -m Mobile" + echo " -d Desktop" + echo "" + echo "PLATFORMS:" + echo " Mobile amazon-fireos android blackberry10 firefoxos ios ubuntu wp8 win8 tizen" + echo " Desktop win osx linux32 linux64" + echo "" + echo "If FILE is given, it will be #open-ed inside Snap\! immediately" + exit 0 +fi + +scriptdir=$(readlink -e ".") + +# Requirements: +# git +# UglifyJS2 (https://github.com/mishoo/UglifyJS2) + +ide=true +platform=$2 + +# presentation mode +if [[ "$3" != "" ]] +then + ide=false +fi + +if [ $ide == false ] +then + if [ -f "$3" ] + then + content=$(cat $3) + else + ide=true + fi +fi + +buildsource=$(mktemp -d) +git clone $snapsource $buildsource +cd "$buildsource" +git checkout mobileapp + +rm -rf .git/ + +if [ $ide == false ] +then + # minimize everything + rm lang* ypr.js paint.js cloud.js gui.js + + sed -i '/paint\.js"/d' snap.html + sed -i '/cloud\.js"/d' snap.html + sed -i 's/gui\.js"/binary\.js"/' snap.html + + # load custom project from file + sed -i '/sha512\.js"/a\ + <script type="text/javascript" src="code.js"></script> ' snap.html + + echo "var code =" > code.js + echo "'$content'" >> code.js + echo ";" >> code.js + + sed -i "/ide\.openIn/a\ + ide.droppedText(code); " snap.html +else + rm binary.js +fi + +# compress all js files +find . -name '*.js' | xargs -I {} uglifyjs {} -o {} -c + +# return to the directory where the script was called from +cd "$scriptdir" + +# run helper scripts for building + +if [[ $1 == "-m" ]] +then + ./mobile.sh "$2" "$buildsource" +else + ./desktop.sh "$2" "$buildsource" +fi diff --git a/config.xml b/config.xml new file mode 100644 index 0000000..36a7bcf --- /dev/null +++ b/config.xml @@ -0,0 +1,15 @@ +<?xml version='4.0' encoding='utf-8'?> +<widget id="edu.berkeley.snap" version="0.0.1" xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0"> + <name>Snap!</name> + <description> + Snap! Build Your Own Blocks + A visual, blocks based programming language inspired by Scratch + </description> + <author email="jens@moenig.org" href="http://snap.berkeley.edu"> + Jens Mönig and Brian Harvey + </author> + <content src="snap.html" /> + <access origin="*" /> + <preference name="Fullscreen" value="true" /> + <icon src="www/snap_logo_sm.png" /> +</widget> diff --git a/desktop.sh b/desktop.sh new file mode 100755 index 0000000..72b0786 --- /dev/null +++ b/desktop.sh @@ -0,0 +1,34 @@ +#!/bin/bash + +if [[ "$snapsource" == "" ]] +then + export snapsource="https://github.com/Gubolin/snap.git" +fi + +if [[ $1 = "" ]] +then + echo "Usage: desktop.sh PLATFORM [BUILDSOURCE]" + exit 0 +fi + +scriptdir=$(readlink -e ".") + +# Requirements: +# git, zip, nodejs +# node-webkit (https://github.com/rogerwang/node-webkit), node-webkit-builder (https://github.com/mllrsohn/node-webkit-builder) + +builddir=$(mktemp -d) + +if [[ $2 == "" ]] +then + git clone "$snapsource" $builddir + cd $builddir/ + git checkout mobileapp +else + mv "$2" $builddir + cd $builddir/ +fi + +nwbuild -p "$1" . + +mv build/* $scriptdir/ @@ -21,16 +21,26 @@ <script type="text/javascript" src="octokit.js/octokit.js"></script> <script type="text/javascript" src="github.js"></script> <script type="text/javascript" src="sha512.js"></script> +<<<<<<< HEAD <script type="text/javascript" src="Snapin8r/snapin8r.min.js"></script> <script type="text/javascript" src="vkBeautify/vkbeautify.js"></script> <script type="text/javascript" src="diff-merge/dist/diff.js"></script> +======= + +>>>>>>> mobileapp <script type="text/javascript"> var world; window.onload = function () { world = new WorldMorph(document.getElementById('world')); world.worldCanvas.focus(); +<<<<<<< HEAD new IDE_Morph().openIn(world); setInterval(loop, 10); +======= + var ide = new IDE_Morph() + ide.openIn(world); + setInterval(loop, 1); +>>>>>>> mobileapp }; function loop() { world.doOneCycle(); diff --git a/lang-ko.js b/lang-ko.js index a1e50eb..3241c7f 100644..100755 --- a/lang-ko.js +++ b/lang-ko.js @@ -179,13 +179,13 @@ SnapTranslator.dict.ko = { // translations meta information 'language_name': - 'Korean', // the name as it should appear in the language menu + '한국어', // the name as it should appear in the language menu 'language_translator': 'Yunjae Jang', // your name for the Translators tab 'translator_e-mail': - 'yunjae.jang@inc.korea.ac.kr', // optional + 'janggoons@gmail.com', // optional 'last_changed': - '2012-11-18', // this, too, will appear in the Translators tab + '2014-11-07', // this, too, will appear in the Translators tab // GUI // control bar: @@ -218,7 +218,7 @@ SnapTranslator.dict.ko = { // editor: 'draggable': - '드래그 가능?', + '마우스로 직접 움직이기', // tabs: 'Scripts': @@ -243,8 +243,16 @@ SnapTranslator.dict.ko = { '왼쪽에서 오른쪽으로만', // new sprite button: - 'add a new sprite': - '새로운 스프라이트 추가', + 'add a new Turtle sprite': + '새로운 스프라이트 추가하기', + + // new paint sprite button: + 'paint a new sprite': + '새로운 스프라이트 그리기', + + // new paint costume button: + 'Paint a new costume': + '새로운 모양 그리기', // tab help 'costumes tab help': @@ -300,19 +308,19 @@ SnapTranslator.dict.ko = { 'point towards %dst': '%dst 쪽 보기', 'go to x: %n y: %n': - 'x: %n 、y: %n 쪽으로 가기', + 'x: %n 、y: %n 쪽으로 이동하기', 'go to %dst': - '%dst 위치로 가기', + '%dst 위치로 이동하기', 'glide %n secs to x: %n y: %n': - '%n 초 동안 x: %n 、y: %n 쪽으로 움직이기', + '%n 초 동안 x: %n 、y: %n 쪽으로 이동하기', 'change x by %n': 'x좌표 %n 만큼 바꾸기', 'set x to %n': - 'x좌표 %n 로 정하기', + 'x좌표 %n (으)로 정하기', 'change y by %n': 'y좌표 %n 만큼 바꾸기', 'set y to %n': - 'y좌표 %n 로 정하기', + 'y좌표 %n (으)로 정하기', 'if on edge, bounce': '벽에 닿으면 튕기기', 'x position': @@ -328,13 +336,13 @@ SnapTranslator.dict.ko = { 'next costume': '다음 모양', 'costume #': - '모양 #', + '모양 번호', 'say %s for %n secs': - '%s %n 초 동안 말하기', + '%s 을(를) %n 초 동안 말하기', 'say %s': '%s 말하기', 'think %s for %n secs': - '%s %n 초간 생각하기', + '%s 을(를) %n 초 동안 생각하기', 'think %s': '%s 생각하기', 'Hello!': @@ -342,15 +350,15 @@ SnapTranslator.dict.ko = { 'Hmm...': '흠…', 'change %eff effect by %n': - '%eff 효과 %n 만큼 바꾸기', + '%eff 효과를 %n 만큼 바꾸기', 'set %eff effect to %n': - '%eff 효과 %n 만큼 주기', + '%eff 효과를 %n 만큼 정하기', 'clear graphic effects': '그래픽 효과 지우기', 'change size by %n': - '크기 %n 만큼 바꾸기', + '크기를 %n 만큼 바꾸기', 'set size to %n %': - '크기 %n % 로 정하기', + '크기를 %n % 로 정하기', 'size': '크기', 'show': @@ -373,82 +381,96 @@ SnapTranslator.dict.ko = { 'play sound %snd': '%snd 소리내기', 'play sound %snd until done': - '끝날때까지 %snd 소리내기', + '%snd 을(를) 끝까지 소리내기', 'stop all sounds': '모든 소리 끄기', 'rest for %n beats': - '%n 비트 동안 쉬기', + '%n 박자 동안 쉬기', 'play note %n for %n beats': - '%n 음을 %n 비트로 연주하기', + '%n 음을 %n 박자로 연주하기', 'change tempo by %n': - '템포를 %n 만큼 바꾸기', + '빠르기를 %n 만큼 바꾸기', 'set tempo to %n bpm': - '템포를 %n bpm으로 맞추기', + '빠르기를 %n bpm으로 정하기', 'tempo': - '템포', + '빠르기', // pen: 'clear': - '지우기', + '펜 자국 지우기', 'pen down': '펜 내리기', 'pen up': '펜 올리기', 'set pen color to %clr': - '펜의 색 %clr 으로 정하기', + '펜 색깔을 %clr 으로 정하기', 'change pen color by %n': - '펜의 색 %n 만큼 바꾸기', + '펜 색깔을 %n 만큼 바꾸기', 'set pen color to %n': - '펜의 색 %n 으로 정하기', + '펜 색깔을 %n (으)로 정하기', 'change pen shade by %n': - '펜의 그림자 %n 만큼 바꾸기', + '펜 음영을 %n 만큼 바꾸기', 'set pen shade to %n': - '펜의 그림자 %n 으로 정하기', + '펜 음영을 %n 으로 정하기', 'change pen size by %n': - '펜의 크기 %n 만큼 바꾸기', + '펜 굵기를 %n 만큼 바꾸기', 'set pen size to %n': - '펜의 크기 %n 으로 정하기', + '펜 굵기를 %n (으)로 정하기', 'stamp': - '스탬프', + '도장찍기', // control: 'when %greenflag clicked': - '%greenflag 클릭되었을 때', + '%greenflag 클릭했을 때', 'when %keyHat key pressed': - '%keyHat 키 눌렀을 때', + '%keyHat 키를 눌렀을 때', 'when I am clicked': - '자신이 클릭되었을 때', + '이 스프라이트를 클릭했을 때', 'when I receive %msgHat': - '%msgHat 받을 때', + '%msgHat 을(를) 받았을 때', 'broadcast %msg': '%msg 방송하기', 'broadcast %msg and wait': '%msg 방송하고 기다리기', 'Message name': - '메세지 이름', + '메시지 이름', + 'message': + '메시지', + 'any message': + '어떤 메시지', 'wait %n secs': '%n 초 기다리기', 'wait until %b': '%b 까지 기다리기', 'forever %c': - '무한반복 %c', + '무한 반복하기 %c', 'repeat %n %c': - '반복 %n 회 %c', + '%n 번 반복하기 %c', 'repeat until %b %c': - '반복 %b 계속 확인 %c', + '%b 까지 반복하기 %c', 'if %b %c': '만약 %b 라면 %c', 'if %b %c else %c': '만약 %b 라면 %c 아니면 %c', 'report %s': '%s 출력하기', - 'stop block': - '블록 멈추기', - 'stop script': - '스크립트 멈추기', - 'stop all %stop': - '모두 멈추기 %stop', + 'stop %stopChoices': + '%stopChoices 멈추기', + 'all': + '모두', + 'this script': + '이 스크립트', + 'this block': + '이 블록', + 'stop %stopOthersChoices': + '%stopOthersChoices 멈추기', + 'all but this script': + '이 스크립트를 제외한 모두', + 'other scripts in sprite': + '이 스프라이트에 있는 다른 스크립트', + 'pause all %pause': + '모두 잠시 멈추기 %pause', 'run %cmdRing %inputs': '%cmdRing 을(를) %inputs 으로 실행하기', 'launch %cmdRing %inputs': @@ -461,14 +483,23 @@ SnapTranslator.dict.ko = { '반복해서 %cmdRing 을(를) 호출하기', 'warp %c': '워프 %c', + 'when I start as a clone': + '복제되었을 때', + 'create a clone of %cln': + '%cln 을(를) 복제하기', + 'myself': + '나 자신', + 'delete this clone': + '이 복제본 삭제하기', + // sensing: 'touching %col ?': - '%col 에 닿기?', + '%col 에 닿았는가?', 'touching %clr ?': - '%clr 색에 닿기?', + '%clr 색에 닿았는가?', 'color %clr is touching %clr ?': - '%clr 색이 %clr 색에 닿기?', + '%clr 색이 %clr 색에 닿았는가?', 'ask %s and wait': '%s 을(를) 묻고 기다리기', 'what\'s your name?': @@ -476,36 +507,59 @@ SnapTranslator.dict.ko = { 'answer': '대답', 'mouse x': - '마우스 x좌표', + '마우스의 x좌표', 'mouse y': - '마우스 y좌표', + '마우스의 y좌표', 'mouse down?': - '마우스 클릭하기?', + '마우스를 클릭했는가?', 'key %key pressed?': - '%key 키 클릭하기?', + '%key 키를 눌렀는가?', 'distance to %dst': '%dst 까지 거리', 'reset timer': '타이머 초기화', 'timer': '타이머', + '%att of %spr': + '%att ( %spr 에 대한)', 'http:// %s': 'http:// %s', - + 'turbo mode?': + '터보 모드인가?', + 'set turbo mode to %b': + '터보 모드 %b 으로 설정하기', 'filtered for %clr': '%clr 색 추출하기', 'stack size': '스택 크기', 'frames': '프레임', + 'current %dates': + '현재 %dates', + 'year': + '연도', + 'month': + '월', + 'date': + '일', + 'day of week': + '요일(1~7)', + 'hour': + '시간', + 'minute': + '분', + 'second': + '초', + 'time in milliseconds': + '밀리세컨드초', // operators: '%n mod %n': - '%n 나누기 %n 의 나머지', + '( %n / %n ) 의 나머지', 'round %n': '%n 반올림', '%fun of %n': - '%n 의 %fun', + '%fun ( %n 에 대한)', 'pick random %n to %n': '%n 부터 %n 사이의 난수', '%b and %b': @@ -520,12 +574,14 @@ SnapTranslator.dict.ko = { '거짓', 'join %words': '%words 결합하기', + 'split %s by %delim': + '%s 를 %delim 기준으로 나누기', 'hello': '안녕', 'world': - '세계', + '세상', 'letter %n of %s': - '%n 의 %s 번째 글자', + '%n 번째 글자 ( %s 에 대한)', 'length of %s': '%s 의 길이', 'unicode of %s': @@ -533,9 +589,11 @@ SnapTranslator.dict.ko = { 'unicode %n as letter': '유니코드 %n 에 대한 문자', 'is %s a %typ ?': - '%s 이(가) %typ 인가요?', + '%s 이(가) %typ 인가?', + 'is %s identical to %s ?': + '%s 와(과) %s 가 동일한가?', 'type of %s': - '%s 타입', + '%s 의 타입', // variables: 'Make a variable': @@ -543,12 +601,12 @@ SnapTranslator.dict.ko = { 'Variable name': '변수 이름', 'Delete a variable': - '변수 삭제', + '변수 삭제하기', 'set %var to %s': - '%var 을(를) %s 로 저장', + '변수 %var 에 %s 저장하기', 'change %var by %n': - '%var 에 %n 씩 누적하기', + '변수 %var 을(를) %n 만큼 바꾸기', 'show variable %var': '변수 %var 보이기', 'hide variable %var': @@ -560,38 +618,78 @@ SnapTranslator.dict.ko = { 'list %exp': '리스트 %exp', '%s in front of %l': - '%s 을(를) %l 처음에 추가하기 ', + '%s 을(를) 리스트 %l 의 맨 앞에 추가하기 ', 'item %idx of %l': - '%idx 항목 %l', + '%idx 번째 항목 (리스트 %l 에 대한)', 'all but first of %l': - '%l 첫번째 아이템 제외한 모든 아이템', + '리스트 %l 에서 첫 번째 항목 제외하기', 'length of %l': - '%l 의 크기', + '리스트 %l 의 항목 갯수', '%l contains %s': - '%l 에 %s 포함?', + '리스트 %l 에 %s 포함되었는가?', 'thing': - '아이템', + '어떤 것', 'add %s to %l': - '%s 을(를) %l 마지막에 추가하기 ', + '%s 을(를) 리스트 %l 의 마지막에 추가하기 ', 'delete %ida of %l': - '%ida 을(를) %l 에서 삭제하기 ', + '%ida 번째 항목 삭제하기 (리스트 %l 에 대한)', 'insert %s at %idx of %l': - '%s 을(를) %idx 위치에 추가하기 %l', + '%s 을(를) %idx 위치에 추가하기 (리스트 %l 에 대한)', 'replace item %idx of %l with %s': - '%idx 항목 %l 에 %s 로 교체하기', + '%idx 번째 (리스트 %l 에 대한) 를 %s (으)로 바꾸기', // other 'Make a block': '블록 만들기', + // Paint Editor + 'Paint Editor': + '그림 편집기', + 'undo': + '되돌리기', + 'grow': + '확대', + 'shrink': + '축소', + 'flip ↔': + '↔ 반전', + 'flip ↕': + '↕ 반전', + 'Brush size': + '펜 크기', + 'Constrain proportions of shapes?\n(you can also hold shift)': + '도형 크기 비율을 고정하는가?\n(shift 키를 눌러서 사용할 수 있습니다.)', + 'Paintbrush tool\n(free draw)': + '붓 도구', + 'Stroked Rectangle\n(shift: square)': + '사각형 그리기 도구\n(shift: 정사각형)', + 'Stroked Ellipse\n(shift: circle)': + '타원 그리기 도구\n(shift: 원)', + 'Eraser tool': + '지우개 도구', + 'Set the rotation center': + '회전축 설정하기', + 'Line tool\n(shift: vertical/horizontal)': + '선 그리기 도구\n(shift: 수평/수직)', + 'Filled Rectangle\n(shift: square)': + '채워진 사각형 그리기 도구\n(shift: 정사각형)', + 'Filled Ellipse\n(shift: circle)': + '채워진 타원 그리기 도구\n(shift: 원)', + 'Fill a region': + '색 채우기', + 'Pipette tool\n(pick a color anywhere)': + '스포이드 도구\n(원하는 색 선택하기)', + // menus // snap menu 'About...': 'Snap! 에 대해서...', + 'Reference manual': + '참고자료 다운로드', 'Snap! website': 'Snap! 웹사이트', 'Download source': - '소스 다운로드', + '소스코드 다운로드', 'Switch back to user mode': '사용자 모드로 전환', 'disable deep-Morphic\ncontext menus\nand show user-friendly ones': @@ -609,15 +707,18 @@ SnapTranslator.dict.ko = { 'Open...': '열기...', 'Save': - '저장', + '저장하기', + 'Save to disk': + '내 컴퓨터에 저장하기', + 'experimental - store this project\nin your downloads folder': + '실험적 - 이 프로젝트를\n 다운로드 폴더에 저장합니다.', 'Save As...': - '다른 이름으로 저장...', + '다른 이름으로 저장하기...', 'Import...': '가져오기...', 'file menu import hint': '내보낸 프로젝트 파일, 블록 라이브러리\n' - + '스프라이트 모양 또는 소리를 가져옵니다.\n\n' - + '일부 웹브라우저에서는 지원되지 않습니다.', + + '스프라이트 모양 또는 소리를 가져옵니다.', 'Export project as plain text...': '프로젝트를 텍스트 파일로 내보내기...', 'Export project...': @@ -628,58 +729,171 @@ SnapTranslator.dict.ko = { '블록 내보내기...', 'show global custom block definitions as XML\nin a new browser window': '새롭게 정의한 전역 블록 데이터를\n새로운 윈도우에 XML 형태로 보여주기', + 'Export all scripts as pic...': + '모든 스크립트를 그림파일로 내보내기', + 'show a picture of all scripts\nand block definitions': + '모든 스크립트와 정의된 블록을 그림파일로 보여줍니다.', + 'Import tools': + '추가 도구 가져오기', + 'load the official library of\npowerful blocks': + '강력한 기능을 제공하는\n 블록들을 가져옵니다.', + 'Libraries...': + '라이브러리...', + 'Select categories of additional blocks to add to this project.': + '추가적인 블록을 선택해서\n 사용할 수 있습니다.', + 'Import library': + '라이브러리 가져오기', + + // cloud menu + 'Login...': + '로그인...', + 'Signup...': + '계정만들기...', + 'Reset Password...': + '비밀번호 재설정...', + 'url...': + 'url...', + 'export project media only...': + 'export project media only...', + 'export project without media...': + 'export project without media...', + 'export project as cloud data...': + 'export project as cloud data...', + 'open shared project from cloud...': + 'open shared project from cloud...', // settings menu 'Language...': '언어선택...', + 'Zoom blocks...': + '블록 크기 설정...', + 'Stage size...': + '무대 크기 설정...', + 'Stage size': + '무대 크기', + 'Stage width': + '가로(너비)', + 'Stage height': + '세로(높이)', + 'Default': + '기본설정', + 'Blurred shadows': '반투명 그림자', 'uncheck to use solid drop\nshadows and highlights': '체크해제하면, 그림자와 하이라이트가\n불투명 상태로 됩니다.', 'check to use blurred drop\nshadows and highlights': '체크하면, 그림자와 하이라이트가\n반투명 상태로 됩니다.', + 'Zebra coloring': '중첩 블록 구분하기', 'check to enable alternating\ncolors for nested blocks': '체크하면, 중첩된 블록을\n다른 색으로 구분할 수 있습니다.', 'uncheck to disable alternating\ncolors for nested block': '체크해제하면, 중첩된 블록을\n다른 색으로 구분할 수 없습니다.', + + 'Dynamic input labels': + 'Dynamic input labels', + 'uncheck to disable dynamic\nlabels for variadic inputs': + 'uncheck to disable dynamic\nlabels for variadic inputs', + 'check to enable dynamic\nlabels for variadic inputs': + 'check to enable dynamic\nlabels for variadic inputs', + 'Prefer empty slot drops': '빈 슬롯에 입력 가능', 'settings menu prefer empty slots hint': '설정 메뉴에 빈 슬롯의\n힌트를 사용할 수 있습니다.', 'uncheck to allow dropped\nreporters to kick out others': '체크해제하면, 기존 리포터 블록에\n새로운 리포터 블록으로 대체할 수 있습니다.', + 'Long form input dialog': '긴 형태의 입력 대화창', 'check to always show slot\ntypes in the input dialog': '체크하면, 입력 대화창에\n항상 슬롯의 형태를 보여줍니다.', 'uncheck to use the input\ndialog in short form': '체크해제하면, 입력 대화창을\n짧은 형태로 사용합니다.', + + 'Plain prototype labels': + '새로 만든 블록 인수 설정', + 'uncheck to always show (+) symbols\nin block prototype labels': + '체크해제하면, 블록 편집기에서\n 블록 인수 추가 버튼(+)을\n 보입니다.', + 'check to hide (+) symbols\nin block prototype labels': + '체크하면, 블록 편집기에서\n 블록 인수 추가 버튼(+)을\n 숨깁니다.', + 'Virtual keyboard': '가상 키보드', 'uncheck to disable\nvirtual keyboard support\nfor mobile devices': '체크해제하면, 모바일 기기에서\n가상 키보드를 사용할 수 없습니다.', 'check to enable\nvirtual keyboard support\nfor mobile devices': '체크하면, 모바일 기기에서\n가상 키보드를 사용할 수 있습니다.', + 'Input sliders': '입력창에서 슬라이더 사용', 'uncheck to disable\ninput sliders for\nentry fields': '체크해제하면, 입력창에서\n슬라이더를 사용할 수 없습니다.', 'check to enable\ninput sliders for\nentry fields': '체크하면, 입력창에서\n슬라이더를 사용할 수 있습니다.', + 'Clicking sound': '블록 클릭시 소리', 'uncheck to turn\nblock clicking\nsound off': '체크해제하면, 블록 클릭시\n소리가 꺼집니다.', 'check to turn\nblock clicking\nsound on': '체크하면, 블록 클릭시\n소리가 켜집니다.', + + 'Animations': + '애니메이션', + 'uncheck to disable\nIDE animations': + '체크해제하면, IDE 애니메이션을\n 사용할 수 없습니다.', + + 'Turbo mode': + '터보 모드', + 'check to prioritize\nscript execution': + '체크하면, 스크립트를\n 빠르게 실행합니다.', + 'uncheck to run scripts\nat normal speed': + '체크해제하면, 스크립트 실행 속도를\n 보통으로 합니다.', + + 'Flat design': + '플랫(Flat) 디자인', + 'uncheck for default\nGUI design': + '체크해제하면,\n 기본 GUI 디자인으로\n 변경합니다.', + 'check for alternative\nGUI design': + '체크하면, 플랫(Flat)\n 디자인으로 변경합니다.', + + 'Sprite Nesting': + 'Sprite Nesting', + 'uncheck to disable\nsprite composition': + 'uncheck to disable\nsprite composition', + 'check to enable\nsprite composition': + 'check to enable\nsprite composition', + 'Thread safe scripts': '스레드 안전 스크립트', - 'uncheck to allow\nscript reentrancy': + 'uncheck to allow\nscript reentrance': '체크해제하면, 스크립트\n재진입성을 허락합니다.', - 'check to disallow\nscript reentrancy': + 'check to disallow\nscript reentrance': '체크하면, 스크립트\n재진입성을 허락하지 않습니다.', + + 'Prefer smooth animations': + '자연스러운 애니메이션', + 'uncheck for greater speed\nat variable frame rates': + '체크해제하면, 프레임\n 전환 비율이 빨라집니다.', + 'check for smooth, predictable\nanimations across computers': + '체크하면, 애니메이션이\n 자연스러워 집니다.', + + 'Flat line ends': + '선 끝을 평평하게 만들기', + 'check for flat ends of lines': + '체크하면, 선 끝을\n 평평하게 만듭니다.', + 'uncheck for round ends of lines': + '체크해제하면, 선 끝을\n 둥글게 만듭니다.', + + 'Codification support': + '체계화 지원', + 'uncheck to disable\nblock to text mapping features': + 'uncheck to disable\nblock to text mapping features', + 'check for block\nto text mapping features': + '체크하면, check for block\nto text mapping features', // inputs 'with inputs': @@ -692,35 +906,55 @@ SnapTranslator.dict.ko = { // context menus: 'help': '도움말', + + // palette: + 'find blocks...': + '블록 찾기...', + 'hide primitives': + '기본 블록 숨기기', + 'show primitives': + '기본 블록 보이기', // blocks: 'help...': '블록 도움말...', + 'relabel...': + '블록 바꾸기...', 'duplicate': - '복사', + '복사하기', 'make a copy\nand pick it up': - '복사해서\n그 블록을 들고 있습니다.', + '복사해서\n들고 있습니다.', 'delete': - '삭제', + '삭제하기', 'script pic...': - '스크립트 그림...', + '이 스크립트를 그림파일로 내보내기...', 'open a new window\nwith a picture of this script': '이 스크립트 그림을\n새로운 윈도우에서 엽니다.', 'ringify': - '형태변환', + '블록형태 변환하기', + 'unringify': + 'unringify', // custom blocks: 'delete block definition...': - '블록 삭제', + '블록 삭제하기', 'edit...': '편집…', // sprites: 'edit': - '편집', + '스크립트 편집하기', 'export...': '내보내기...', + // stage: + 'show all': + '모든 스프라이트 나타내기', + 'pic...': + '그림파일로 내보내기...', + 'open a new window\nwith a picture of the stage': + '새로운 창을 열고\n무대의 화면을\n그림파일로 저장한다.', + // scripting area 'clean up': '스크립트 정리하기', @@ -728,12 +962,20 @@ SnapTranslator.dict.ko = { '스크립트를\n수직으로 정렬한다.', 'add comment': '주석 추가하기', + 'undrop': + '마지막으로 가져온 블록', + 'undo the last\nblock drop\nin this pane': + '마지막으로\n 가져온 블록을\n 확인한다.', + 'scripts pic...': + '모든 스크립트를 그림파일로 내보내기...', + 'open a new window\nwith a picture of all scripts': + '새로운 창을 열어서\n 모든 스크립트를\n 그림으로 저장한다.', 'make a block...': '블록 만들기...', // costumes 'rename': - '이름수정', + '이름 수정하기', 'export': '내보내기', @@ -750,7 +992,9 @@ SnapTranslator.dict.ko = { // dialogs // buttons 'OK': - 'OK', + '확인', + 'Ok': + '확인', 'Cancel': '취소', 'Yes': @@ -762,6 +1006,31 @@ SnapTranslator.dict.ko = { 'Help': '도움말', + // zoom blocks + 'Zoom blocks': + '블록 크기 설정', + 'build': + '만들기', + 'your own': + '나만의', + 'blocks': + '블록', + 'normal (1x)': + '기본 크기 (1x)', + 'demo (1.2x)': + '데모 크기 (1.2x)', + 'presentation (1.4x)': + '발표용 크기 (1.4x)', + 'big (2x)': + '큰 크기 (2x)', + 'huge (4x)': + '매우 큰 크기(4x)', + 'giant (8x)': + '정말 큰 크기 (8x)', + 'monstrous (10x)': + '믿을 수 없는 크기 (10x)', + + // costume editor 'Costume Editor': '모양 편집기', @@ -822,7 +1091,7 @@ SnapTranslator.dict.ko = { // block deletion dialog 'Delete Custom Block': - '블록 삭제', + '블록 삭제하기', 'block deletion dialog text': '이 블록과 모든 인스턴스를\n 삭제해도 괜찮습니까?', @@ -911,6 +1180,8 @@ SnapTranslator.dict.ko = { // coments 'add comment here...': '여기에 주석 추가…', + 'comment pic...': + '주석을 그림파일로 내보내기...', // drow downs // directions @@ -933,11 +1204,22 @@ SnapTranslator.dict.ko = { // costumes 'Turtle': - '터틀', + '화살표', + 'Empty': + 'Leer', - // graphical effects + // graphical effects + 'brightness': + '밝기', 'ghost': '유령', + 'negative': + '반전', + 'comic': + '코믹', + 'confetti': + '색종이', + // keys 'space': @@ -1029,9 +1311,11 @@ SnapTranslator.dict.ko = { // math functions 'abs': - '절대값', + '절대값(abs)', 'sqrt': - '제곱근', + '제곱근(sqrt)', + 'floor': + '바닥(floor)', 'sin': 'sin', 'cos': @@ -1049,11 +1333,23 @@ SnapTranslator.dict.ko = { 'e^': 'e^', + // delimiters + 'letter': + '글자', + 'whitespace': + '빈칸', + 'line': + '줄', + 'tab': + '탭', + 'cr': + '새줄(cr)', + // data types 'number': '숫자', 'text': - '텍스트', + '문자', 'Boolean': '불리언', 'list': @@ -1087,12 +1383,12 @@ SnapTranslator.dict.ko = { 'Are you sure you want to delete': '정말로 삭제합니까?', 'unringify': - '형태변환취소', + '블록형태변환 취소하기', 'rename...': '이름수정...', '(180) down': '(180) 아래', 'Ok': - 'OK' + '확인' }; diff --git a/mobile.sh b/mobile.sh new file mode 100755 index 0000000..6ec8a0e --- /dev/null +++ b/mobile.sh @@ -0,0 +1,65 @@ +#!/bin/bash + +if [[ "$snapsource" == "" ]] +then + export snapsource="https://github.com/Gubolin/snap.git" +fi + +if [[ $1 = "" ]] +then + echo "Usage: mobile.sh PLATFORM [BUILDSOURCE]" + exit 0 +fi + +scriptdir=$(readlink -e ".") + +# Requirements: +# git, nodejs, android SDK / other platform(s) +# cordova (https://cordova.apache.org/) + +if [[ $2 != "" ]] +then + buildsource=$(readlink -e "$2") +fi + +builddir=$(mktemp -d) + +cordova create $builddir edu.berkeley.snap "Snap\!" +cd $builddir +rm -rf www config.xml + +if [[ $2 == "" ]] +then + git clone "$snapsource" www + cd www/ + git checkout mobileapp +else + mv "$buildsource" www + cd www +fi + +# add mobile-specific library; it's made available at runtime +sed -i '/link rel="shortcut icon"/a\ + <script type="text/javascript" src="cordova.js"></script>' snap.html + +# add everything needed and build for $device +cordova platform add "$1" +cordova plugin add org.apache.cordova.plugin.softkeyboard +cordova plugin add org.apache.cordova.vibration +cordova plugin add org.apache.cordova.device-motion +cordova plugin add org.apache.cordova.device-orientation +cordova plugin add org.apache.cordova.geolocation +cordova plugin add de.appplant.cordova.plugin.local-notification + +if [[ $1 == "android" ]] +then + # Remove default icons + cd "$builddir/platforms/android" + find -name '*.png' | xargs rm +fi + +cordova build "$1" + +cd $builddir +# TODO other platforms +find -name '*.apk' | xargs -I {} mv {} $scriptdir @@ -10790,6 +10790,11 @@ WorldMorph.prototype.edit = function (aStringOrTextMorph) { this.virtualKeyboard.style.top = this.cursor.top() + pos.y + "px"; this.virtualKeyboard.style.left = this.cursor.left() + pos.x + "px"; this.virtualKeyboard.focus(); + if (cordova) { + if (cordova.plugins.SoftKeyboard) { // Android only + cordova.plugins.SoftKeyboard.show(); // Issue #81 + } + } } if (MorphicPreferences.useSliderForInput) { @@ -415,6 +415,11 @@ SpriteMorph.prototype.initBlocks = function () { spec: 'go back %n layers', defaults: [1] }, + doNotify: { + type: 'command', + category: 'looks', + spec: 'notification with title %s and content %s' + }, doScreenshot: { type: 'command', category: 'looks', @@ -933,6 +938,31 @@ SpriteMorph.prototype.initBlocks = function () { category: 'sensing', spec: 'current %dates' }, + doVibrate: { + type: 'command', + category: 'sensing', + spec: 'vibrate %n seconds' + }, + reportCompassHeading: { + type: 'reporter', + category: 'sensing', + spec: 'current compass heading' + }, + reportAccelerationX: { + type: 'reporter', + category: 'sensing', + spec: 'current acceleration along the x axes' + }, + reportAccelerationY: { + type: 'reporter', + category: 'sensing', + spec: 'current acceleration along the y axes' + }, + reportAccelerationZ: { + type: 'reporter', + category: 'sensing', + spec: 'current acceleration along the z axes' + }, reportLanguage: { type: 'reporter', category: 'sensing', @@ -1818,6 +1848,8 @@ SpriteMorph.prototype.blockTemplates = function (category) { blocks.push('-'); blocks.push(block('comeToFront')); blocks.push(block('goBack')); + blocks.push('-'); + blocks.push(block('doNotify')); // for debugging: /////////////// @@ -1988,6 +2020,14 @@ SpriteMorph.prototype.blockTemplates = function (category) { blocks.push('-'); blocks.push(block('reportLanguage')); blocks.push(block('reportLocation')); + blocks.push('-'); + blocks.push(block('doVibrate')); + blocks.push('-'); + blocks.push(block('reportCompassHeading')); + blocks.push('-'); + blocks.push(block('reportAccelerationX')); + blocks.push(block('reportAccelerationY')); + blocks.push(block('reportAccelerationZ')); // for debugging: /////////////// @@ -4434,6 +4474,8 @@ StageMorph.prototype.init = function (globals) { this.paletteCache = {}; // not to be serialized (!) this.lastAnswer = ''; // last user input, do not persist this.activeSounds = []; // do not persist + this.acceleration = null; // do not persist + this.compassHeading = null; // do not persist this.streamingCamera = false; this.lastCameraCanvas = null; this.lastCameraMotion = new Point(0, 0); @@ -5082,6 +5124,8 @@ StageMorph.prototype.blockTemplates = function (category) { blocks.push('-'); blocks.push(block('show')); blocks.push(block('hide')); + blocks.push('-'); + blocks.push(block('doNotify')); // for debugging: /////////////// @@ -5230,6 +5274,14 @@ StageMorph.prototype.blockTemplates = function (category) { blocks.push('-'); blocks.push(block('reportLanguage')); blocks.push(block('reportLocation')); + blocks.push('-'); + blocks.push(block('doVibrate')); + blocks.push('-'); + blocks.push(block('reportCompassHeading')); + blocks.push('-'); + blocks.push(block('reportAccelerationX')); + blocks.push(block('reportAccelerationY')); + blocks.push(block('reportAccelerationZ')); // for debugging: /////////////// diff --git a/package.json b/package.json new file mode 100644 index 0000000..678a69c --- /dev/null +++ b/package.json @@ -0,0 +1,8 @@ +{ + "name": "Snap!", + "version": "4.0", + "main": "snap.html", + "window": { + "toolbar": false + } +} @@ -2277,6 +2277,20 @@ Process.prototype.reportTextSplit = function (string, delimiter) { return new List(str.split(del)); }; +// Process notification operations + +Process.prototype.doNotify = function (title, content) { + // TODO webkitNotification + if (window.plugin) { + if (window.plugin.notification) { + window.plugin.notification.local.add({ + message: content, + title: title + }); + } + } +}; + // Process debugging Process.prototype.alert = function (data) { @@ -2797,6 +2811,114 @@ Process.prototype.reportDate = function (datefn) { return result; }; +Process.prototype.getCompassHeading = function () { + var stage = this.homeContext.receiver.parentThatIsA(StageMorph); + + stage.compassHeading = null; + if (navigator.compass) { + navigator.compass.getCurrentHeading( + function (result) { + stage.compassHeading = result; + }, + function () { + stage.compassHeading = + {'magneticHeading': 0, 'trueHeading': 0, + 'headingAccuracy': 0, 'timestamp': 0}; + } + ); + } else { + stage.compassHeading = + {'magneticHeading': 0, 'trueHeading': 0, + 'headingAccuracy': 0, 'timestamp': 0}; + } +}; + +Process.prototype.reportCompassHeading = function () { + var stage = this.homeContext.receiver.parentThatIsA(StageMorph); + + if (this.context.wait) { + if (stage.compassHeading !== null) { + return stage.compassHeading.magneticHeading; + } + } else { + this.context.wait = true; + this.getCompassHeading(); + } + this.pushContext('doYield'); + this.pushContext(); +}; + +Process.prototype.getAcceleration = function () { + var stage = this.homeContext.receiver.parentThatIsA(StageMorph); + + stage.acceleration = null; + if (navigator.accelerometer) { + navigator.accelerometer.getCurrentAcceleration( + function (result) { + stage.acceleration = result; + }, + function () { + stage.acceleration = + {'timestamp': 0, 'z': 0, 'y': 0, 'x': 0}; + } + ); + } else { + stage.acceleration = + {'timestamp': 0, 'z': 0, 'y': 0, 'x': 0}; + } +}; + +Process.prototype.reportAccelerationX = function () { + var stage = this.homeContext.receiver.parentThatIsA(StageMorph); + + if (this.context.wait) { + if (stage.acceleration !== null) { + return stage.acceleration.x; + } + } else { + this.context.wait = true; + this.getAcceleration(); + } + this.pushContext('doYield'); + this.pushContext(); +}; + +Process.prototype.reportAccelerationY = function () { + var stage = this.homeContext.receiver.parentThatIsA(StageMorph); + + if (this.context.wait) { + if (stage.acceleration !== null) { + return stage.acceleration.y; + } + } else { + this.context.wait = true; + this.getAcceleration(); + } + this.pushContext('doYield'); + this.pushContext(); +}; + +Process.prototype.reportAccelerationZ = function () { + var stage = this.homeContext.receiver.parentThatIsA(StageMorph); + + if (this.context.wait) { + if (stage.acceleration !== null) { + return stage.acceleration.z; + } + } else { + this.context.wait = true; + this.getAcceleration(); + } + this.pushContext('doYield'); + this.pushContext(); +}; + +Process.prototype.doVibrate = function (seconds) { + if ("vibrate" in navigator) { + window.navigator.vibrate(seconds * 1000); + } +}; + // Process code mapping /* @@ -3206,8 +3328,6 @@ Process.prototype.reportFrameCount = function () { activeAudio audio buffer for interpolated operations, don't persist activeNote audio oscillator for interpolated ops, don't persist activeStream video element for camera streaming, don't persist - isLambda marker for return ops - isImplicitLambda marker for return ops isCustomBlock marker for return ops emptySlots caches the number of empty slots for reification tag string or number to optionally identify the Context, @@ -3235,8 +3355,6 @@ function Context( this.activeAudio = null; this.activeNote = null; this.activeStream = null; - this.isLambda = false; // marks the end of a lambda - this.isImplicitLambda = false; // marks the end of a C-shaped slot this.isCustomBlock = false; // marks the end of a custom block's stack this.emptySlots = 0; // used for block reification this.tag = null; // lexical catch-tag for custom blocks |
