summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGubolin <gubolin@fantasymail.de>2015-07-23 14:28:31 +0200
committerGubolin <gubolin@fantasymail.de>2015-07-23 14:28:31 +0200
commit29b5dba84e8f3aa95a3b9296d5a01a2609f4590d (patch)
tree4ec090b68a695c6ba6d7bc26b6362519b485a0ba
parent526a595c5e00f171eb814f063e58a6c18c5784d4 (diff)
parent5aec52889d79a8257971095719195fabcbc33559 (diff)
downloadsnap-gh-pages.tar.gz
snap-gh-pages.zip
merge developmentgh-pages
-rwxr-xr-xbinary.js1099
-rwxr-xr-xbinary.sh27
-rw-r--r--blocks.js204
-rw-r--r--byob.js18
-rw-r--r--gui.js138
-rw-r--r--help/createClone.pngbin0 -> 187161 bytes
-rw-r--r--help/doCallCC.pngbin0 -> 130086 bytes
-rw-r--r--help/doPauseAll.pngbin0 -> 142103 bytes
-rw-r--r--help/doSetFastTracking.pngbin0 -> 180730 bytes
-rw-r--r--help/doStopOthers.pngbin0 -> 141638 bytes
-rw-r--r--help/doStopThis.pngbin0 -> 171046 bytes
-rw-r--r--help/getLastMessage.pngbin0 -> 84422 bytes
-rw-r--r--help/manual/.gitignore7
-rw-r--r--help/manual/common/defs.tex39
-rw-r--r--help/manual/en/move-10-steps.pngbin0 -> 2595 bytes
-rw-r--r--help/manual/en/snap-manual.tex159
-rw-r--r--help/manual/en/snapping-blocks.pngbin0 -> 12054 bytes
-rw-r--r--help/manual/en/typical-script-inner.pngbin0 -> 4610 bytes
-rw-r--r--help/manual/en/typical-script.pngbin0 -> 11063 bytes
-rw-r--r--help/manual/en/window-regions.pngbin0 -> 142052 bytes
-rw-r--r--help/manual/pl/laczenie-blokow.pngbin0 -> 5025 bytes
-rw-r--r--help/manual/pl/obszary-okna.pdfbin0 -> 65091 bytes
-rw-r--r--help/manual/pl/obszary-okna.pdf_tex59
-rw-r--r--help/manual/pl/obszary-okna.svg1388
-rw-r--r--help/manual/pl/przesun-o-10-krokow.pngbin0 -> 3149 bytes
-rw-r--r--help/manual/pl/snap-podrecznik.tex167
-rw-r--r--help/manual/pl/typowy-skrypt-wnetrze.pngbin0 -> 5211 bytes
-rw-r--r--help/manual/pl/typowy-skrypt.pngbin0 -> 12764 bytes
-rw-r--r--help/receiveInteraction.pngbin49453 -> 113207 bytes
-rw-r--r--help/receiveOnClone.pngbin0 -> 148344 bytes
-rw-r--r--help/removeClone.pngbin0 -> 152507 bytes
-rw-r--r--help/reportAttributeOf.pngbin0 -> 151943 bytes
-rw-r--r--help/reportCallCC.pngbin0 -> 127777 bytes
-rw-r--r--help/reportDate.pngbin0 -> 396763 bytes
-rw-r--r--help/reportIsFastTracking.pngbin0 -> 180193 bytes
-rw-r--r--help/reportJSFunction.pngbin0 -> 135342 bytes
-rw-r--r--help/reportTextSplit.pngbin0 -> 211050 bytes
-rw-r--r--help/reportURL.pngbin0 -> 154595 bytes
-rwxr-xr-xhistory.txt82
-rw-r--r--lang-fr.js154
-rwxr-xr-xlang-pt.js28
-rw-r--r--lists.js5
-rw-r--r--locale.js6
-rw-r--r--morphic.js410
-rwxr-xr-xmorphic.txt37
-rw-r--r--objects.js392
-rw-r--r--paint.js4
-rwxr-xr-xsnap.html (renamed from index.html)2
-rw-r--r--store.js102
-rw-r--r--threads.js232
-rw-r--r--tools.xml2
-rw-r--r--widgets.js11
-rwxr-xr-x[-rw-r--r--]xml.js129
53 files changed, 3023 insertions, 1878 deletions
diff --git a/binary.js b/binary.js
deleted file mode 100755
index d1a9b21..0000000
--- a/binary.js
+++ /dev/null
@@ -1,1099 +0,0 @@
-/*
-
- 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
index 9de5fa0..809d1fe 100755
--- a/binary.sh
+++ b/binary.sh
@@ -18,6 +18,8 @@ then
echo " Desktop win32 win64 osx linux32 linux64"
echo ""
echo "If FILE/URL is given, it will be #open-ed inside Snap! immediately. URL will be loaded at runtime."
+ echo "If URL starts with a hash ('#'), it will be used as initial base url suffix"
+ echo " (for example '#present:UserName=foo\&ProjectName=bar', escape the '&')."
echo ""
echo ""
echo "The following environment variables will be used, if available:"
@@ -49,18 +51,12 @@ 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 *.sh *.pdf *.txt
+ rm *.sh *.pdf *.txt
rm -r help/ Costumes/ Backgrounds/ Sounds/
- sed -i '/paint\.js"/d' snap.html
- sed -i '/cloud\.js"/d' snap.html
- sed -i 's/gui\.js"/binary\.js"/' snap.html
-
# if a file was given, move it to "project.xml"
# it will be loaded like an URL then
if [ -f "$3" ]
@@ -71,12 +67,17 @@ then
url=$3
fi
- # load custom project from url
- sed -i "/ide\.openIn/a\
- ide.droppedText(ide.getURL('$url')); " snap.html
-
-else
- rm binary.js
+ if [[ $url == \#* ]]
+ then
+ # load custom project from cloud or string
+ sed -i "s/snap.html/snap.html${url}/g" config.xml package.json
+ else
+ # load custom project from url
+ sed -i "/ide\.openIn/a\
+ ide.droppedText(ide.getURL('$url'));" snap.html
+ sed -i "/this.toggleAppMode(false)/d" gui.js
+ sed -i "s/snap.html/snap.html#run:/g" config.xml package.json
+ fi
fi
# compress all js files
diff --git a/blocks.js b/blocks.js
index 470ccd5..ac8d27b 100644
--- a/blocks.js
+++ b/blocks.js
@@ -155,8 +155,7 @@ DialogBoxMorph, BlockInputFragmentMorph, PrototypeHatBlockMorph, Costume*/
// Global stuff ////////////////////////////////////////////////////////
-modules.blocks = '2015-March-06';
-
+modules.blocks = '2015-June-25';
var SyntaxElementMorph;
var BlockMorph;
@@ -340,7 +339,7 @@ SyntaxElementMorph.prototype.setScale = function (num) {
};
SyntaxElementMorph.prototype.setScale(1);
-SyntaxElementMorph.prototype.isCachingInputs = true;
+SyntaxElementMorph.prototype.isCachingInputs = false;
// SyntaxElementMorph instance creation:
@@ -360,6 +359,10 @@ SyntaxElementMorph.prototype.init = function () {
this.cachedInputs = null;
};
+// SyntaxElementMorph stepping:
+
+SyntaxElementMorph.prototype.step = null;
+
// SyntaxElementMorph accessing:
SyntaxElementMorph.prototype.parts = function () {
@@ -400,10 +403,14 @@ SyntaxElementMorph.prototype.debugCachedInputs = function () {
}
for (i = 0; i < realInputs.length; i += 1) {
if (this.cachedInputs[i] !== realInputs[i]) {
- throw new Error('cached input does not match ' +
+ throw new Error('cached input does not match: ' +
this.constructor.name +
+ ' #' +
+ i +
' ' +
- i);
+ this.cachedInputs[i].constructor.name +
+ ' != ' +
+ realInputs[i].constructor.name);
}
}
};
@@ -910,25 +917,6 @@ SyntaxElementMorph.prototype.labelPart = function (spec) {
);
part.setContents(['date']);
break;
- case '%locations':
- part = new InputSlotMorph(
- null, // text
- false, // non-numeric
- {
- 'all' : ['all'],
- 'country' : ['country'],
- 'state' : ['state'],
- 'state district' : ['state district'],
- 'suburb' : ['suburb'],
- 'city' : ['city'],
- 'road' : ['road'],
- 'house number' : ['house number'],
- 'licence': ['licence']
- },
- true // read-only
- );
- part.setContents(['all']);
- break;
case '%delim':
part = new InputSlotMorph(
null, // text
@@ -1128,9 +1116,9 @@ SyntaxElementMorph.prototype.labelPart = function (spec) {
acos : ['acos'],
atan : ['atan'],
ln : ['ln'],
- // log : 'log',
- 'e^' : ['e^']
- // '10^' : '10^'
+ log : ['log'],
+ 'e^' : ['e^'],
+ '10^' : ['10^']
},
true
);
@@ -1208,6 +1196,15 @@ SyntaxElementMorph.prototype.labelPart = function (spec) {
);
part.isStatic = true;
break;
+ case '%shd':
+ part = new InputSlotMorph(
+ null,
+ false,
+ 'shadowedVariablesMenu',
+ true
+ );
+ part.isStatic = true;
+ break;
case '%lst':
part = new InputSlotMorph(
null,
@@ -1434,16 +1431,19 @@ SyntaxElementMorph.prototype.labelPart = function (spec) {
new Point() : this.embossing;
part.drawNew();
} else {
- part = new StringMorph(spec);
- part.fontName = this.labelFontName;
- part.fontStyle = this.labelFontStyle;
- part.fontSize = this.fontSize;
- part.color = new Color(255, 255, 255);
- part.isBold = true;
- part.shadowColor = this.color.darker(this.labelContrast);
- part.shadowOffset = MorphicPreferences.isFlat ?
- new Point() : this.embossing;
- part.drawNew();
+ part = new StringMorph(
+ spec, // text
+ this.fontSize, // fontSize
+ this.labelFontStyle, // fontStyle
+ true, // bold
+ false, // italic
+ false, // isNumeric
+ MorphicPreferences.isFlat ?
+ new Point() : this.embossing, // shadowOffset
+ this.color.darker(this.labelContrast), // shadowColor
+ new Color(255, 255, 255), // color
+ this.labelFontName // fontName
+ );
}
return part;
};
@@ -1906,7 +1906,8 @@ SyntaxElementMorph.prototype.endLayout = function () {
%att - chameleon colored rectangular drop-down for attributes
%fun - chameleon colored rectangular drop-down for math functions
%typ - chameleon colored rectangular drop-down for data types
- %var - chameleon colored rectangular drop-down for variable names
+ %var - chameleon colored rectangular drop-down for variable names
+ %shd - Chameleon colored rectuangular drop-down for shadowed var names
%lst - chameleon colored rectangular drop-down for list names
%b - chameleon colored hexagonal slot (for predicates)
%l - list icon
@@ -1960,6 +1961,7 @@ BlockMorph.uber = SyntaxElementMorph.prototype;
// BlockMorph preferences settings:
+BlockMorph.prototype.isCachingInputs = true;
BlockMorph.prototype.zebraContrast = 40; // alternating color brightness
// BlockMorph sound feedback:
@@ -2074,7 +2076,8 @@ BlockMorph.prototype.setSpec = function (spec) {
}
part = myself.labelPart(word);
myself.add(part);
- if (!(part instanceof CommandSlotMorph)) {
+ if (!(part instanceof CommandSlotMorph ||
+ part instanceof StringMorph)) {
part.drawNew();
}
if (part instanceof RingMorph) {
@@ -2241,7 +2244,7 @@ BlockMorph.prototype.userMenu = function () {
);
if (this instanceof CommandBlockMorph && this.nextBlock()) {
menu.addItem(
- this.thumbnail(0.5, 60, false),
+ this.thumbnail(0.5, 60),
function () {
var cpy = this.fullCopy(),
nb = cpy.nextBlock(),
@@ -3032,6 +3035,12 @@ BlockMorph.prototype.alternateBlockColor = function () {
this.fixChildrensBlockColor(true); // has issues if not forced
};
+BlockMorph.prototype.ghost = function () {
+ this.setColor(
+ SpriteMorph.prototype.blockColor[this.category].lighter(35)
+ );
+};
+
BlockMorph.prototype.fixLabelColor = function () {
if (this.zebraContrast > 0 && this.category) {
var clr = SpriteMorph.prototype.blockColor[this.category];
@@ -3080,6 +3089,14 @@ BlockMorph.prototype.fullCopy = function () {
ans.setSpec(this.instantiationSpec);
}
ans.allChildren().filter(function (block) {
+ if (block instanceof SyntaxElementMorph) {
+ block.cachedInputs = null;
+ if (block instanceof InputSlotMorph) {
+ block.contents().clearSelection();
+ }
+ } else if (block instanceof CursorMorph) {
+ block.destroy();
+ }
return !isNil(block.comment);
}).forEach(function (block) {
var cmnt = block.comment.fullCopy();
@@ -3109,40 +3126,40 @@ BlockMorph.prototype.mouseClickLeft = function () {
}
};
+BlockMorph.prototype.reactToTemplateCopy = function () {
+ this.forceNormalColoring();
+};
+
// BlockMorph thumbnail
-BlockMorph.prototype.thumbnail = function (scale, clipWidth, noShadow) {
- var block = this.fullCopy(),
- nb = block.nextBlock(),
+BlockMorph.prototype.thumbnail = function (scale, clipWidth) {
+ var nb = this.nextBlock(),
fadeout = 12,
ext,
trgt,
ctx,
gradient;
- if (nb) {nb.destroy(); }
- if (!noShadow) {block.addShadow(); }
- ext = block.fullBounds().extent();
- if (!noShadow) {
- ext = ext.subtract(this.shadowBlur *
- (useBlurredShadows && !MorphicPreferences.isFlat ? 1 : 2));
- }
+
+ if (nb) {nb.isVisible = false; }
+ ext = this.fullBounds().extent();
trgt = newCanvas(new Point(
- Math.min(ext.x * scale, clipWidth || ext.x),
+ clipWidth ? Math.min(ext.x * scale, clipWidth) : ext.x * scale,
ext.y * scale
));
ctx = trgt.getContext('2d');
ctx.scale(scale, scale);
- ctx.drawImage(block.fullImage(), 0, 0);
+ ctx.drawImage(this.fullImage(), 0, 0);
// draw fade-out
- if (trgt.width === clipWidth) {
+ if (clipWidth && ext.x * scale > clipWidth) {
gradient = ctx.createLinearGradient(
trgt.width / scale - fadeout,
0,
trgt.width / scale,
0
);
- gradient.addColorStop(0, new Color(255, 255, 255, 0).toString());
- gradient.addColorStop(1, 'white');
+ gradient.addColorStop(0, 'transparent');
+ gradient.addColorStop(1, 'black');
+ ctx.globalCompositeOperation = 'destination-out';
ctx.fillStyle = gradient;
ctx.fillRect(
trgt.width / scale - fadeout,
@@ -3151,6 +3168,7 @@ BlockMorph.prototype.thumbnail = function (scale, clipWidth, noShadow) {
trgt.height / scale
);
}
+ if (nb) {nb.isVisible = true; }
return trgt;
};
@@ -6905,6 +6923,21 @@ InputSlotMorph.prototype.getVarNamesDict = function () {
return {};
};
+InputSlotMorph.prototype.shadowedVariablesMenu = function () {
+ var block = this.parentThatIsA(BlockMorph),
+ rcvr,
+ dict = {};
+
+ if (!block) {return dict; }
+ rcvr = block.receiver();
+ if (rcvr) {
+ rcvr.inheritedVariableNames(true).forEach(function (name) {
+ dict[name] = name;
+ });
+ }
+ return dict;
+};
+
InputSlotMorph.prototype.setChoices = function (dict, readonly) {
// externally specify choices and read-only status,
// used for custom blocks
@@ -6925,7 +6958,8 @@ InputSlotMorph.prototype.setChoices = function (dict, readonly) {
// InputSlotMorph layout:
InputSlotMorph.prototype.fixLayout = function () {
- var contents = this.contents(),
+ var width, height, arrowWidth,
+ contents = this.contents(),
arrow = this.arrow();
contents.isNumeric = this.isNumeric;
@@ -6942,35 +6976,32 @@ InputSlotMorph.prototype.fixLayout = function () {
arrow.setSize(this.fontSize);
arrow.show();
} else {
- arrow.setSize(0);
arrow.hide();
}
- this.setHeight(
- contents.height()
- + this.edge * 2
- // + this.typeInPadding * 2
- );
+ arrowWidth = arrow.isVisible ? arrow.width() : 0;
+
+ height = contents.height() + this.edge * 2; // + this.typeInPadding * 2
if (this.isNumeric) {
- this.setWidth(contents.width()
- + Math.floor(arrow.width() * 0.5)
- + this.height()
- + this.typeInPadding * 2
- );
+ width = contents.width()
+ + Math.floor(arrowWidth * 0.5)
+ + height
+ + this.typeInPadding * 2;
} else {
- this.setWidth(Math.max(
+ width = Math.max(
contents.width()
- + arrow.width()
+ + arrowWidth
+ this.edge * 2
+ this.typeInPadding * 2,
contents.rawHeight ? // single vs. multi-line contents
- contents.rawHeight() + arrow.width()
- : contents.height() / 1.2 + arrow.width(),
+ contents.rawHeight() + arrowWidth
+ : contents.height() / 1.2 + arrowWidth,
this.minWidth // for text-type slots
- ));
+ );
}
+ this.setExtent(new Point(width, height));
if (this.isNumeric) {
contents.setPosition(new Point(
- Math.floor(this.height() / 2),
+ Math.floor(height / 2),
this.edge
).add(new Point(this.typeInPadding, 0)).add(this.position()));
} else {
@@ -6980,10 +7011,12 @@ InputSlotMorph.prototype.fixLayout = function () {
).add(new Point(this.typeInPadding, 0)).add(this.position()));
}
- arrow.setPosition(new Point(
- this.right() - arrow.width() - this.edge,
- contents.top()
- ));
+ if (arrow.isVisible) {
+ arrow.setPosition(new Point(
+ this.right() - arrowWidth - this.edge,
+ contents.top()
+ ));
+ }
if (this.parent) {
if (this.parent.fixLayout) {
@@ -7000,6 +7033,15 @@ InputSlotMorph.prototype.fixLayout = function () {
// InputSlotMorph events:
+InputSlotMorph.prototype.mouseDownLeft = function (pos) {
+ if (this.isReadOnly || this.arrow().bounds.containsPoint(pos)) {
+ this.escalateEvent('mouseDownLeft', pos);
+ } else {
+ this.contents().edit();
+ this.contents().selectAll();
+ }
+};
+
InputSlotMorph.prototype.mouseClickLeft = function (pos) {
if (this.arrow().bounds.containsPoint(pos)) {
this.dropDownMenu();
@@ -9276,10 +9318,6 @@ MultiArgMorph.prototype = new ArgMorph();
MultiArgMorph.prototype.constructor = MultiArgMorph;
MultiArgMorph.uber = ArgMorph.prototype;
-// MultiArgMorph preferences settings
-
-MultiArgMorph.prototype.isCachingInputs = false;
-
// MultiArgMorph instance creation:
function MultiArgMorph(
@@ -9710,10 +9748,6 @@ ArgLabelMorph.prototype = new ArgMorph();
ArgLabelMorph.prototype.constructor = ArgLabelMorph;
ArgLabelMorph.uber = ArgMorph.prototype;
-// ArgLabelMorph preferences settings
-
-ArgLabelMorph.prototype.isCachingInputs = false;
-
// MultiArgMorph instance creation:
function ArgLabelMorph(argMorph, labelTxt) {
diff --git a/byob.js b/byob.js
index c9f3daa..4c39872 100644
--- a/byob.js
+++ b/byob.js
@@ -106,7 +106,7 @@ SymbolMorph, isNil*/
// Global stuff ////////////////////////////////////////////////////////
-modules.byob = '2015-March-02';
+modules.byob = '2015-June-25';
// Declarations
@@ -2049,7 +2049,13 @@ BlockLabelFragment.prototype.defTemplateSpecFragment = function () {
)) {
suff = ' \u03BB';
} else if (this.defaultValue) {
- suff = ' = ' + this.defaultValue.toString();
+ if (this.type === '%n') {
+ suff = ' # = ' + this.defaultValue.toString();
+ } else { // 'any' or 'text'
+ suff = ' = ' + this.defaultValue.toString();
+ }
+ } else if (this.type === '%n') {
+ suff = ' #';
}
return this.labelString + suff;
};
@@ -3291,15 +3297,17 @@ BlockExportDialogMorph.prototype.selectNone = function () {
// BlockExportDialogMorph ops
BlockExportDialogMorph.prototype.exportBlocks = function () {
- var str = this.serializer.serialize(this.blocks);
+ var str = encodeURIComponent(
+ this.serializer.serialize(this.blocks)
+ );
if (this.blocks.length > 0) {
- window.open(encodeURI('data:text/xml,<blocks app="'
+ window.open('data:text/xml,<blocks app="'
+ this.serializer.app
+ '" version="'
+ this.serializer.version
+ '">'
+ str
- + '</blocks>'));
+ + '</blocks>');
} else {
new DialogBoxMorph().inform(
'Export blocks',
diff --git a/gui.js b/gui.js
index 79f72ad..479941b 100644
--- a/gui.js
+++ b/gui.js
@@ -69,7 +69,7 @@ SpeechBubbleMorph*/
// Global stuff ////////////////////////////////////////////////////////
-modules.gui = '2015-February-28';
+modules.gui = '2015-June-25';
// Declarations
@@ -82,6 +82,11 @@ var WardrobeMorph;
var SoundIconMorph;
var JukeboxMorph;
+// Get the full url without "snap.html"
+var baseUrl = document.URL.split('/');
+baseUrl.pop(baseUrl.length - 1);
+baseUrl = baseUrl.join('/') + '/';
+
// IDE_Morph ///////////////////////////////////////////////////////////
// I am SNAP's top-level frame, the Editor window
@@ -251,6 +256,36 @@ IDE_Morph.prototype.init = function (isAutoFill) {
setInterval(this.save, 1000 * 60 * 60 * 5); // every 5 minutes
window.peers = [];
+ this.createPeer();
+};
+
+IDE_Morph.prototype.createPeer = function() {
+ var myself = this;
+ this.peer = new Peer(this.peerId, {
+ host: 'snapmesh.herokuapp.com',
+ port: 443,
+ secure: true,
+ path: '/'
+ });
+
+ this.peer.on('open', function (id) {
+ myself.peerId = id;
+ });
+ this.peer.on('disconnected', function () {
+ myself.peer.destroy();
+ myself.createPeer();
+ });
+ this.peer.on('error', function (err) {
+ console.log(err); // DEBUG
+ });
+
+ this.peer.on('connection', function (connection) {
+ connection.on('open', function () {
+ connection.on('data', function (data) {
+ myself.stage.newPeerMessage(data, connection.peer);
+ });
+ });
+ });
};
IDE_Morph.prototype.openIn = function (world) {
@@ -409,8 +444,21 @@ IDE_Morph.prototype.openIn = function (world) {
myself.shield.destroy();
myself.shield = null;
msg.destroy();
- myself.toggleAppMode(true);
- myself.runScripts();
+
+ if (dict.editMode) {
+ myself.toggleAppMode(false);
+ } else {
+ myself.toggleAppMode(true);
+ }
+
+ if (!dict.noRun) {
+ myself.runScripts();
+ }
+
+ if (dict.hideControls) {
+ myself.controlBar.hide();
+ window.onbeforeunload = function () {nop(); };
+ }
}
]);
},
@@ -2379,10 +2427,10 @@ IDE_Morph.prototype.settingsMenu = function () {
addPreference(
'Cache Inputs',
function () {
- SyntaxElementMorph.prototype.isCachingInputs =
- !SyntaxElementMorph.prototype.isCachingInputs;
+ BlockMorph.prototype.isCachingInputs =
+ !BlockMorph.prototype.isCachingInputs;
},
- SyntaxElementMorph.prototype.isCachingInputs,
+ BlockMorph.prototype.isCachingInputs,
'uncheck to stop caching\ninputs (for debugging the evaluator)',
'check to cache inputs\nboosts recursion',
true
@@ -2497,12 +2545,15 @@ IDE_Morph.prototype.projectMenu = function () {
if (GitHub.username) {
menu.addItem('Save with commit message', 'commitProjectToGitHub');
}
- menu.addItem(
- 'Save to disk',
- 'saveProjectToDisk',
- 'store this project\nin the downloads folder\n'
- + '(in supporting browsers)'
- );
+ if (shiftClicked) {
+ menu.addItem(
+ 'Save to disk',
+ 'saveProjectToDisk',
+ 'store this project\nin the downloads folder\n'
+ + '(in supporting browsers)',
+ new Color(100, 0, 0)
+ );
+ }
menu.addItem('Save As...', 'saveProjectsBrowser');
menu.addLine();
menu.addItem(
@@ -2588,13 +2639,10 @@ IDE_Morph.prototype.projectMenu = function () {
function () {
// read a list of libraries from an external file,
var libMenu = new MenuMorph(this, 'Import library'),
- libUrl = 'http://snap.berkeley.edu/snapsource/libraries/' +
- 'LIBRARIES';
+ libUrl = baseUrl + 'libraries/' + 'LIBRARIES';
function loadLib(name) {
- var url = 'http://snap.berkeley.edu/snapsource/libraries/'
- + name
- + '.xml';
+ var url = baseUrl + 'libraries/' + name + '.xml';
myself.droppedText(myself.getURL(url), name);
}
@@ -2711,7 +2759,7 @@ IDE_Morph.prototype.aboutSnap = function () {
module, btn1, btn2, btn3, btn4, licenseBtn, translatorsBtn,
world = this.world();
- aboutTxt = 'Snap! 4.0\nBuild Your Own Blocks\n\n--- beta ---\n\n'
+ aboutTxt = 'Snap! 4.1 (OOP)\nBuild Your Own Blocks\n\n--- dev ---\n\n'
+ 'Copyright \u24B8 2015 Jens M\u00F6nig and '
+ 'Brian Harvey\n'
+ 'jens@moenig.org, bh@cs.berkeley.edu\n\n'
@@ -2745,7 +2793,7 @@ IDE_Morph.prototype.aboutSnap = function () {
creditsTxt = localize('Contributors')
+ '\n\nNathan Dinsmore: Saving/Loading, Snap-Logo Design, '
- + 'countless bugfixes'
+ + '\ncountless bugfixes and optimizations'
+ '\nKartik Chandra: Paint Editor'
+ '\nMichael Ball: Time/Date UI, many bugfixes'
+ '\n"Ava" Yuan Yuan: Graphic Effects'
@@ -3063,7 +3111,9 @@ IDE_Morph.prototype.exportGlobalBlocks = function () {
};
IDE_Morph.prototype.exportSprite = function (sprite) {
- var str = this.serializer.serialize(sprite.allParts());
+ var str = encodeURIComponent(
+ this.serializer.serialize(sprite.allParts())
+ );
window.open('data:text/xml,<sprites app="'
+ this.serializer.app
+ '" version="'
@@ -3693,7 +3743,6 @@ IDE_Morph.prototype.setLanguage = function (lang, callback) {
if (lang === 'en') {
return this.reflectLanguage('en', callback);
}
- myself.userLanguage = lang;
translation = document.createElement('script');
translation.id = 'language';
translation.onload = function () {
@@ -4516,8 +4565,7 @@ IDE_Morph.prototype.getURLsbeOrRelative = function (url) {
var request = new XMLHttpRequest(),
myself = this;
try {
- request.open('GET', 'http://snap.berkeley.edu/snapsource/' +
- url, false);
+ request.open('GET', baseUrl + url, false);
request.send();
if (request.status === 200) {
return request.responseText;
@@ -4973,8 +5021,7 @@ ProjectDialogMorph.prototype.setSource = function (source) {
myself.nameField.setContents(item.name || '');
}
src = myself.ide.getURL(
- 'http://snap.berkeley.edu/snapsource/Examples/' +
- item.name + '.xml'
+ baseUrl + 'Examples/' + item.name + '.xml'
);
xml = myself.ide.serializer.parse(src);
@@ -5028,8 +5075,9 @@ ProjectDialogMorph.prototype.getLocalProjectList = function () {
ProjectDialogMorph.prototype.getExamplesProjectList = function () {
var dir,
projects = [];
+ //alert(baseUrl);
- dir = this.ide.getURL('http://snap.berkeley.edu/snapsource/Examples/');
+ dir = this.ide.getURL(baseUrl + 'Examples/');
dir.split('\n').forEach(
function (line) {
var startIdx = line.search(new RegExp('href=".*xml"')),
@@ -5048,8 +5096,8 @@ ProjectDialogMorph.prototype.getExamplesProjectList = function () {
}
}
);
- projects.sort(function (x, y) {
- return x.name < y.name ? -1 : 1;
+ projects = projects.sort(function (x, y) {
+ return x.name.toLowerCase() < y.name.toLowerCase() ? -1 : 1;
});
return projects;
};
@@ -5213,10 +5261,7 @@ ProjectDialogMorph.prototype.openProject = function () {
} else if (this.source === 'github') {
this.openGitHubProject(proj);
} else if (this.source === 'examples') {
- src = this.ide.getURL(
- 'http://snap.berkeley.edu/snapsource/Examples/' +
- proj.name + '.xml'
- );
+ src = this.ide.getURL(baseUrl + 'Examples/' + proj.name + '.xml');
this.ide.openProjectString(src);
this.destroy();
} else { // 'local'
@@ -5456,6 +5501,7 @@ ProjectDialogMorph.prototype.deleteProject = function () {
ProjectDialogMorph.prototype.shareProject = function () {
var myself = this,
+ ide = this.ide,
proj = this.listField.selected,
entry = this.listField.active;
@@ -5486,6 +5532,15 @@ ProjectDialogMorph.prototype.shareProject = function () {
myself.ide.cloudError(),
[proj.ProjectName]
);
+ // Set the Shared URL if the project is currently open
+ if (proj.ProjectName === ide.projectName) {
+ var usr = SnapCloud.username,
+ projectId = 'Username=' +
+ encodeURIComponent(usr.toLowerCase()) +
+ '&ProjectName=' +
+ encodeURIComponent(proj.projectName);
+ location.hash = projectId;
+ }
},
myself.ide.cloudError()
);
@@ -5496,6 +5551,7 @@ ProjectDialogMorph.prototype.shareProject = function () {
ProjectDialogMorph.prototype.unshareProject = function () {
var myself = this,
+ ide = this.ide,
proj = this.listField.selected,
entry = this.listField.active;
@@ -5527,6 +5583,10 @@ ProjectDialogMorph.prototype.unshareProject = function () {
myself.ide.cloudError(),
[proj.ProjectName]
);
+ // Remove the shared URL if the project is open.
+ if (proj.ProjectName === ide.projectName) {
+ location.hash = '';
+ }
},
myself.ide.cloudError()
);
@@ -5650,7 +5710,7 @@ function SpriteIconMorph(aSprite, aTemplate) {
}
SpriteIconMorph.prototype.init = function (aSprite, aTemplate) {
- var colors, action, query, myself = this;
+ var colors, action, query, hover, myself = this;
if (!aTemplate) {
colors = [
@@ -5680,6 +5740,11 @@ SpriteIconMorph.prototype.init = function (aSprite, aTemplate) {
return false;
};
+ hover = function () {
+ if (!aSprite.exemplar) {return null; }
+ return (localize('parent' + ':\n' + aSprite.exemplar.name));
+ };
+
// additional properties:
this.object = aSprite || new SpriteMorph(); // mandatory, actually
this.version = this.object.version;
@@ -5695,7 +5760,7 @@ SpriteIconMorph.prototype.init = function (aSprite, aTemplate) {
this.object.name, // label string
query, // predicate/selector
null, // environment
- null, // hint
+ hover, // hint
aTemplate // optional, for cached background images
);
@@ -5866,6 +5931,7 @@ SpriteIconMorph.prototype.userMenu = function () {
menu.addItem("duplicate", 'duplicateSprite');
menu.addItem("delete", 'removeSprite');
menu.addLine();
+ menu.addItem("parent...", 'chooseExemplar');
if (this.object.anchor) {
menu.addItem(
localize('detach from') + ' ' + this.object.anchor.name,
@@ -5900,6 +5966,10 @@ SpriteIconMorph.prototype.exportSprite = function () {
this.object.exportSprite();
};
+SpriteIconMorph.prototype.chooseExemplar = function () {
+ this.object.chooseExemplar();
+};
+
SpriteIconMorph.prototype.showSpriteOnStage = function () {
this.object.showOnStage();
};
diff --git a/help/createClone.png b/help/createClone.png
new file mode 100644
index 0000000..b00e867
--- /dev/null
+++ b/help/createClone.png
Binary files differ
diff --git a/help/doCallCC.png b/help/doCallCC.png
new file mode 100644
index 0000000..d286ad4
--- /dev/null
+++ b/help/doCallCC.png
Binary files differ
diff --git a/help/doPauseAll.png b/help/doPauseAll.png
new file mode 100644
index 0000000..975fa73
--- /dev/null
+++ b/help/doPauseAll.png
Binary files differ
diff --git a/help/doSetFastTracking.png b/help/doSetFastTracking.png
new file mode 100644
index 0000000..7ee62a9
--- /dev/null
+++ b/help/doSetFastTracking.png
Binary files differ
diff --git a/help/doStopOthers.png b/help/doStopOthers.png
new file mode 100644
index 0000000..b90dff9
--- /dev/null
+++ b/help/doStopOthers.png
Binary files differ
diff --git a/help/doStopThis.png b/help/doStopThis.png
new file mode 100644
index 0000000..cc072ac
--- /dev/null
+++ b/help/doStopThis.png
Binary files differ
diff --git a/help/getLastMessage.png b/help/getLastMessage.png
new file mode 100644
index 0000000..4484652
--- /dev/null
+++ b/help/getLastMessage.png
Binary files differ
diff --git a/help/manual/.gitignore b/help/manual/.gitignore
new file mode 100644
index 0000000..cb5f3c2
--- /dev/null
+++ b/help/manual/.gitignore
@@ -0,0 +1,7 @@
+*.aux
+*.log
+*.out
+*.toc
+en/snap-manual.pdf
+pl/snap-podrecznik.pdf
+*.synctex.gz
diff --git a/help/manual/common/defs.tex b/help/manual/common/defs.tex
new file mode 100644
index 0000000..bb4d785
--- /dev/null
+++ b/help/manual/common/defs.tex
@@ -0,0 +1,39 @@
+\usepackage[T1]{fontenc}
+\usepackage[utf8]{inputenc}
+\usepackage{baskervald} % Default font
+\usepackage{setspace} \onehalfspacing
+\usepackage{graphicx}
+\usepackage{color}
+\usepackage{textcomp}
+\usepackage{hyperref}
+
+% This macro produces a "Snap!" logo.
+%
+% Note that in Polish (and other languages), nouns are inflected. The form "Snap!" with suffix looks plain stupid, so the macro takes a suffix as argument. If the suffix is non-empty, no exclamation mark is generated, and the suffix is used instead.
+\newcommand{\Snap}[1]{%
+ \textsf{%
+ Snap%
+ \ifx\\#1\\\textit{!\@}%
+ \else #1%
+ \fi%
+ }%
+}
+
+\newcommand{\code}[1]{\textsf{#1}}
+\newcommand{\defaultGraphicsScale}{0.6}
+
+\renewcommand{\thechapter}{\Roman{chapter}}
+\renewcommand{\thesection}{\Alph{section}}
+\renewcommand{\thesubsection}{}
+
+\newcommand{\inlinepic}[1]{%
+ \raisebox{-4pt}{%
+ \includegraphics[scale=\defaultGraphicsScale]{#1}%
+ }%
+}
+
+\newcommand{\bigpic}[1]{
+ \begin{center}
+ \includegraphics[scale=\defaultGraphicsScale]{#1}
+ \end{center}
+}
diff --git a/help/manual/en/move-10-steps.png b/help/manual/en/move-10-steps.png
new file mode 100644
index 0000000..3d276da
--- /dev/null
+++ b/help/manual/en/move-10-steps.png
Binary files differ
diff --git a/help/manual/en/snap-manual.tex b/help/manual/en/snap-manual.tex
new file mode 100644
index 0000000..036ad1e
--- /dev/null
+++ b/help/manual/en/snap-manual.tex
@@ -0,0 +1,159 @@
+\documentclass{report}
+
+\input{../common/defs.tex}
+
+\begin{document}
+
+\title{\Snap{} Reference Manual}
+\author{Brian Harvey \and Jens M\"{o}nig}
+\date{}
+
+\maketitle
+
+\tableofcontents
+
+\chapter*{}
+\section*{Acknowledgements}
+
+We have been extremely lucky in our mentors. Jens cut his teeth in the company of the Smalltalk pioneers: Alan Kay, Dan Ingalls, and the rest of the gang who invented personal computing and object oriented programming in the great days of Xerox PARC. He worked with John Maloney, of the MIT Scratch Team, who developed the Morphic graphics framework that's still at the heart of \Snap{}. The brilliant design of Scratch, from the Lifelong Kindergarten Group at the MIT Media Lab, is crucial to \Snap{}.
+
+\textbf{\emph{Our earlier version, BYOB, was a direct modification of the Scratch source code. \Snap{} is a complete rewrite, but its code structure and its user interface remain deeply indebted to Scratch. And the Scratch Team, who could have seen us as rivals, have been entirely supportive and welcoming to us.}}
+
+Brian grew up at the MIT and Stanford Artificial Intelligence Labs, learning from Lisp inventor John McCarthy, Scheme inventors Gerald~J. Sussman and Guy Steele, and the authors of the world's best computer science book, \textit{Structure and Interpretation of Computer Programs}, Hal Abelson and Gerald~J. Sussman with Julie Sussman, among many other heroes of computer science.
+
+\textbf{\emph{In the glory days of the MIT Logo Lab, we used to say, ``Logo is Lisp disguised as BASIC.'' Now, with its first class procedures, lexical scope, and first class continuations, \Snap{} is Scheme disguised as Scratch.}}
+
+We have been fortunate to get to know an amazing group of brilliant middle school (!\@) and high school students through the Scratch Advanced Topics forum, several of whom have contributed code to \Snap{}: Kartik Chandra, Nathan Dinsmore, Connor Hudson, and Ian Reynolds. Many more have contributed ideas and alpha-testing bug reports. UC Berkeley students who've contributed code include Michael Ball, Achal Dave, Kyle Hotchkiss, Ivan Motyashov, and Yuan Yuan. Contributors of translations are too numerous to list here, but they're in the ``About...'' box in \Snap{} itself. This work was supported in part by the National Science Foundation grant 1143566, and in part by MioSoft.
+
+\clearpage
+
+\begin{center}
+\bf \Huge \Snap{} Reference Manual \\
+\huge Version 4.0
+\vspace{40pt}
+\end{center}
+
+\Snap{} (formerly BYOB) is an extended reimplementation of Scratch (\url{http://scratch.mit.edu}) that allows you to Build Your Own Blocks. It also features first class lists, first class procedures, and continuations. These added capabilities make it suitable for a serious introduction to computer science for high school or college students. To run \Snap{}, open a browser window and connect to either \url{http://snap.berkeley.edu/run} to start with a minimal set of blocks or \url{http://snap.berkeley.edu/init} to load a small set of additional blocks (a little slower startup, but recommended for convenience and assumed in this manual).
+
+\clearpage
+
+\chapter{Blocks, Scripts, and Sprites}
+
+This chapter describes the \Snap{} features inherited from Scratch; experienced Scratch users can skip to section~\ref{sec:nesting-sprites}.
+
+\Snap{} is a programming language---a notation in which you can tell a computer what you want it to do. Unlike most programming languages, though, \Snap{} is a visual language; instead of writing a program using the keyboard, the \Snap{} programmer uses the same drag-and-drop interface familiar to computer users.
+
+Start \Snap{}. You should see the following arrangement of regions in the window:
+
+\begin{center}
+\includegraphics[width=\textwidth]{window-regions}
+\end{center}
+
+(The proportions of these areas may be different, depending on the size and shape of your browser window.)
+
+A \Snap{} program consists of one or more \emph{scripts}, each of which is made of \emph{blocks}. Here's a typical script:
+
+\label{fig:typical-script}
+\bigpic{typical-script}
+
+The five blocks that make up this script have three different colors, corresponding to three of the eight \emph{palettes} in which blocks can be found. The palette area at the left edge of the window shows one palette at a time, chosen with the eight buttons just above the palette area. In this script, the gold blocks are from the Control palette; the green block is from the Pen palette; and the blue blocks are from the Motion palette. A script is assembled by dragging blocks from a palette into the \emph{scripting area} in the middle part of the window. Blocks snap together (hence the name \Snap{} for the language) when you drag a block so that its indentation is near the tab of the one above it:
+
+\bigpic{snapping-blocks}
+
+The white horizontal line is a signal that if you let go of the green block it will snap into the tab of the gold one.
+
+\subsection{Hat Blocks and Command Blocks}
+
+At the top of the script is a \emph{hat} block, which indicates when the script should be carried out. Hat block names typically start with the word ``\code{when}''; in this example, the script should be run when the green flag near the right end of the \Snap{} tool bar is clicked. (The \Snap{} tool bar is part of the \Snap{} window, not the same as the browser's or operating system's menu bar.) A script isn't required to have a hat block, but if not, then the script will be run only if the user clicks on the script itself. A script can't have more than one hat block, and the hat block can be used only at the top of the script; its distinctive shape is meant to remind you of that.
+
+The other blocks in this script are \emph{command} blocks. Each command block corresponds to an action that \Snap{} already knows how to carry out. For example, the block \inlinepic{move-10-steps} tells the sprite (the arrowhead shape on the \emph{stage} at the right end of the window) to move ten steps (a step is a very small unit of distance) in the direction in which the arrowhead is pointing. We'll see shortly that there can be more than one sprite, and that each sprite has its own scripts. Also, a sprite doesn't have to look like an arrowhead, but can have any picture as a costume. The shape of the \code{move} block is meant to remind you of a Lego\texttrademark{} brick; a script is a stack of blocks. (The word ``block'' denotes both the graphical shape on the screen and the procedure, the action, that the block carries out.)
+
+The number~10 in the \code{move} block above is called an \emph{input} to the block. By clicking on the white oval, you can type any number in place of the 10. The sample script on page~\pageref{fig:typical-script} uses 100 as the input value. We'll see later that inputs can have non-oval shapes that accept values other than numbers. We'll also see that you can compute input values, instead of typing a particular value into the oval. A block can have more than one input slot. For example, the \code{glide} block located about halfway down the Motion palette has three inputs.
+
+Most command blocks have that brick shape, but some, like the \code{repeat} block in the sample script, are \emph{C-shaped}. Most C-shaped blocks are found in the Control palette. The slot inside the C shape is a special kind of input slot that accepts a \emph{script} as the input. In the sample script, the \code{repeat} block has two inputs: the number 4 and the script
+
+\bigpic{typical-script-inner}
+
+
+
+\section{Sprites and Parallelism}
+\subsection{Costumes and Sounds}
+\subsection{Inter-Sprite Communication with Broadcast}
+\section{Nesting Sprites: Anchors and Parts}
+\label{sec:nesting-sprites}
+\section{Reporter Blocks and Expressions}
+\section{Predicates and Conditional Evaluation}
+\section{Variables}
+\subsection{Global Variables}
+\subsection{Script Variables}
+\section{Etcetera}
+\chapter{Saving and Loading Projects and Media}
+\section{Local Storage}
+\subsection{Localstore}
+\subsection{XML Export}
+\section{Cloud Storage}
+\section{Loading Saved Projects}
+\chapter{Building a Block}
+\section{Simple Blocks}
+\subsection{Custom Blocks with Inputs}
+\section{Recursion}
+\section{Block Libraries}
+\chapter{First Class Lists}
+\section{The list Block}
+\section{Lists of Lists}
+\section{Functional and Imperative List Programming}
+\section{Higher Order List Operations and Rings}
+\chapter{Typed Inputs}
+\section{Scratch's Type Notation}
+\section{The \Snap{} Input Type Dialog}
+\subsection{Procedure Types}
+\subsection{Pulldown inputs}
+\subsection{Input variants}
+\subsection{Prototype Hints}
+\subsection{Title Text and Symbols}
+\chapter{Procedures as Data}
+\section{Call and Run}
+\subsection{Call/Run with inputs}
+\subsection{Variables in Ring Slots}
+\section{Writing Higher Order Procedures}
+\subsection{Recursive Calls to Multiple-Input Blocks}
+\section{Formal Parameters}
+\section{Procedures as Data}
+\section{Special Forms}
+\subsection{Special Forms in Scratch}
+\chapter{Object Oriented Programming}
+\section{Local State with Script Variables}
+\section{Messages and Dispatch Procedures}
+\section{Inheritance via Delegation}
+\section{An Implementation of Prototyping OOP}
+\chapter{The Outside World}
+\section{The World Wide Web}
+\section{Hardware Devices}
+\section{Date and Time}
+\chapter{Continuations}
+\section{Continuation Passing Style}
+\section{Call/Run w/Continuation}
+\subsection{Nonlocal exit}
+\chapter{User Interface Elements}
+\section{Tool Bar Features}
+\subsection{The \Snap{} Logo Menu}
+\subsection{The File Menu}
+\subsection{The Cloud Menu}
+\subsection{The Settings Menu}
+\subsection{Stage Resizing Buttons}
+\subsection{Project Control Buttons}
+\section{The Palette Area}
+\subsection{Context Menus for Palette Blocks}
+\subsection{Context Menu for the Palette Background}
+\section{The Scripting Area}
+\subsection{Sprite Appearance and Behavior Controls}
+\subsection{Scripting Area Tabs}
+\subsection{Scripts and Blocks Within Scripts}
+\subsection{Scripting Area Background Context Menu}
+\subsection{Controls in the Costumes Tab}
+\subsection{The Paint Editor}
+\subsection{Controls in the Sounds Tab}
+\section{Controls on the Stage}
+\section{The Sprite Corral and Sprite Creation Buttons}
+
+\end{document}
diff --git a/help/manual/en/snapping-blocks.png b/help/manual/en/snapping-blocks.png
new file mode 100644
index 0000000..0dc5922
--- /dev/null
+++ b/help/manual/en/snapping-blocks.png
Binary files differ
diff --git a/help/manual/en/typical-script-inner.png b/help/manual/en/typical-script-inner.png
new file mode 100644
index 0000000..b5503f7
--- /dev/null
+++ b/help/manual/en/typical-script-inner.png
Binary files differ
diff --git a/help/manual/en/typical-script.png b/help/manual/en/typical-script.png
new file mode 100644
index 0000000..6763aad
--- /dev/null
+++ b/help/manual/en/typical-script.png
Binary files differ
diff --git a/help/manual/en/window-regions.png b/help/manual/en/window-regions.png
new file mode 100644
index 0000000..7fe4e98
--- /dev/null
+++ b/help/manual/en/window-regions.png
Binary files differ
diff --git a/help/manual/pl/laczenie-blokow.png b/help/manual/pl/laczenie-blokow.png
new file mode 100644
index 0000000..b640b5f
--- /dev/null
+++ b/help/manual/pl/laczenie-blokow.png
Binary files differ
diff --git a/help/manual/pl/obszary-okna.pdf b/help/manual/pl/obszary-okna.pdf
new file mode 100644
index 0000000..d4e5800
--- /dev/null
+++ b/help/manual/pl/obszary-okna.pdf
Binary files differ
diff --git a/help/manual/pl/obszary-okna.pdf_tex b/help/manual/pl/obszary-okna.pdf_tex
new file mode 100644
index 0000000..6fc4bdf
--- /dev/null
+++ b/help/manual/pl/obszary-okna.pdf_tex
@@ -0,0 +1,59 @@
+%% Creator: Inkscape inkscape 0.48.4, www.inkscape.org
+%% PDF/EPS/PS + LaTeX output extension by Johan Engelen, 2010
+%% Accompanies image file 'obszary-okna.pdf' (pdf, eps, ps)
+%%
+%% To include the image in your LaTeX document, write
+%% \input{<filename>.pdf_tex}
+%% instead of
+%% \includegraphics{<filename>.pdf}
+%% To scale the image, write
+%% \def\svgwidth{<desired width>}
+%% \input{<filename>.pdf_tex}
+%% instead of
+%% \includegraphics[width=<desired width>]{<filename>.pdf}
+%%
+%% Images with a different path to the parent latex file can
+%% be accessed with the `import' package (which may need to be
+%% installed) using
+%% \usepackage{import}
+%% in the preamble, and then including the image with
+%% \import{<path to file>}{<filename>.pdf_tex}
+%% Alternatively, one can specify
+%% \graphicspath{{<path to file>/}}
+%%
+%% For more information, please see info/svg-inkscape on CTAN:
+%% http://tug.ctan.org/tex-archive/info/svg-inkscape
+%%
+\begingroup%
+ \makeatletter%
+ \providecommand\color[2][]{%
+ \errmessage{(Inkscape) Color is used for the text in Inkscape, but the package 'color.sty' is not loaded}%
+ \renewcommand\color[2][]{}%
+ }%
+ \providecommand\transparent[1]{%
+ \errmessage{(Inkscape) Transparency is used (non-zero) for the text in Inkscape, but the package 'transparent.sty' is not loaded}%
+ \renewcommand\transparent[1]{}%
+ }%
+ \providecommand\rotatebox[2]{#2}%
+ \ifx\svgwidth\undefined%
+ \setlength{\unitlength}{855.2bp}%
+ \ifx\svgscale\undefined%
+ \relax%
+ \else%
+ \setlength{\unitlength}{\unitlength * \real{\svgscale}}%
+ \fi%
+ \else%
+ \setlength{\unitlength}{\svgwidth}%
+ \fi%
+ \global\let\svgwidth\undefined%
+ \global\let\svgscale\undefined%
+ \makeatother%
+ \begin{picture}(1,0.60243218)%
+ \put(0,0){\includegraphics[width=\unitlength]{obszary-okna.pdf}}%
+ \put(0.37031041,0.03597468){\color[rgb]{1,0,0}\makebox(0,0)[b]{\smash{\emph{\Large Obszar skryptów}}}}%
+ \put(0.42733084,0.57941921){\color[rgb]{1,0,0}\makebox(0,0)[b]{\smash{\emph{\small Pasek narzędzi}}}}%
+ \put(0.77489765,0.03597473){\color[rgb]{1,0,0}\makebox(0,0)[b]{\smash{\emph{\Large Zagroda duszków}}}}%
+ \put(0.09676227,0.03597473){\color[rgb]{1,0,0}\makebox(0,0)[b]{\smash{\emph{\Large Paleta}}}}%
+ \put(0.77708296,0.27919271){\color[rgb]{1,0,0}\makebox(0,0)[b]{\smash{\emph{\Large Scena}}}}%
+ \end{picture}%
+\endgroup%
diff --git a/help/manual/pl/obszary-okna.svg b/help/manual/pl/obszary-okna.svg
new file mode 100644
index 0000000..f593e27
--- /dev/null
+++ b/help/manual/pl/obszary-okna.svg
@@ -0,0 +1,1388 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="1069"
+ height="644"
+ id="svg2"
+ version="1.1"
+ inkscape:version="0.48.4 r9939"
+ sodipodi:docname="obszary-okna.svg">
+ <defs
+ id="defs4" />
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="0.7"
+ inkscape:cx="571.7409"
+ inkscape:cy="290.47367"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ inkscape:window-width="1362"
+ inkscape:window-height="729"
+ inkscape:window-x="0"
+ inkscape:window-y="18"
+ inkscape:window-maximized="0"
+ fit-margin-top="0"
+ fit-margin-left="0"
+ fit-margin-right="0"
+ fit-margin-bottom="0"
+ inkscape:object-paths="false" />
+ <metadata
+ id="metadata7">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(-14.071424,-18.933605)">
+ <image
+ y="18.933605"
+ x="14.071424"
+ id="image3131"
+ xlink:href="data:image/png;base64,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
+adK999778ccf79ixw263T5o0iWGYb7755rvvvgMAaLXauXPnJiYmUhR15cqVlStXFhUV1brBJUuW
+0DT92GOPAQBWrVo1aNCgH3744ZNPPsEw7M8//ywoKNDr9YMHD543b96ZM2cAAE888cTcuXO/+eab
+zz//3JMCQRCkVk28ejdas1wwXfE8X1ZWNnHixFWrVsFvfv755/Hjxzc6GAS5mzTXmc514LgON5+G
+MgP3eUizn+lN0cRgRFFcotOp3V0rNJL03IULK0aMaGgw1YemxfGbEw4+MVwZ004ml2H+3hgAgCBw
+B11y9c/XC9N3+Yff03P0hriRnym8o3iHgecsVSWnS7N+ZOgiuzmv6OomgWdklI/VkEFXprH2Sp6p
+0hccEXkHqfBjHUZT6RlTyRmeqeKspfkpH9utpf4dxsoVflbDNdZWBmprl4CxuqYfrt940poBXNIY
+0KgWlZqlKQgCSZLOqV4aKiIiovQWk8n06KOPerIWx3GCIMAH+xoRDMuyoii+8MIL9S927733jh8/
+3m3jYxODAQCkp6enp6czDON8HKumt99+u9poYw0Kphpvb+8BAwYAADIzMwEAkiStWbNm8ODBX331
+1erVq++7777XXnuNpumioqIvvvhizZo169at43leFMXc3Fyj0chxN69EsMvK8OHDIyIitmzZ4u3t
+PWfOHF9fX4ZhFi1adP/99+/Zs+fzzz8fMGDAe++9R9N0QUFBzQ0mJyeHh4dLkmQ2m+Pj42majouL
+MxqNoaGhKpXq0qVL33//PQCgf//+cNcw8n379hmNRrcFgiBIXZp49W6cpl8wq3E4HCRJxsXFwZss
+AICVK1euX79+6dKljQ4GQe4md8eZfseDcZuEQEp315Nag/lHewgAAEgAAKx/DDluoOzPi4y3Wgrz
+xw0WEUiiJGEMXfrX6WVF13aGRk+Iin9G4RWS9uc8zm4kCAW4VbkXRV7k7aLAAABCuk7WaKMBhim8
+onCC4lmzKLGEXCMBAEfLElizrSpDrg4VRM6Qv19gLRjWZtpDAABWq7XR1/HBgwcPHjzY9ZsNGzZ4
+smJde3QbjMlkKikpiYiI8HbXcBYZGTlv3jwAwJIlS86ePdsSwThVG0aspsTEROcIWvUvWc8elUol
+TDygc+fOwf/cgwYN6tChw7Fjx7777jtRFPv06XP//ff7+PjcuHFj27ZtgiAsWLCAJMmvvvoqKyur
+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
+W70Qx3F2u/2+++7TaDQBAQEFBQXp6emex+k5HMftdrvD4RD+FIlESqXyRkmIQPjvvLy8PDc396uv
+vhoyZAhCiOd5juPuu+++X3/9NSQkpMUTSZKcOnVqXl5e898g5HJ543t0lrxRDBiGURTlDBsAAAAA
+d4TW+0MsFosoNlR43+Jv/04vvfSSTqebMGHCqlWr/vzzz7Vr137//fc2m81gMPA8LxaLfXx8MAzT
+6XQURREE4ePj4xzYzXFcdXW1TCZz7r3dJaRSqcViaXUzEIvFkhyqQMiMPGgThmHuvvvujz76aMGC
+BadPn7ZYLAghlUqlUqkMBgNN0wRB2Gw2uVzu4+ODEDIYDFarFcdxLy8vuVze3rfYZh62SXuRy+Ux
+MTHuxzXRND1kyJAnn3zyvffey8/Pf+SRR5YvX/7rr78Kn5aVlTEMExYWVlBQoNFo9Hq9sGxueHg4
+y7IXL14MDQ21Wq1Wq1UkElmtVi8vL39//ytXrgwbNmz69Olr1qxxKeyms05YPMput5Mk6e3t7eEo
+MpvNFhMTs3Hjxh9//PHzzz+nadpms/E8T5KksMOjzWYTFqsVi8UymcxsNkskklWrVp05c6a0tDQt
+La22trahoUH4x9LQ0CDMMhc2iLTb7cLqXkJUCoXCbDZjGKZQKDiOMxqNcrncTXeKs1dEIpF4fkfL
+li1jGGbmzJmbNm2aOXPmmTNnSJL09fW9++67hTn9crncbDazLOvl5SXcr1wuz8jImDRpkl6vt1qt
+DMMICxw7U3qO4xoaGoR/AkJJ9zE4HA4cx2FJXwAAAOBO0Xp/CEVRrPe1p+Eb/fYvOHfu3JEjRz76
+6COCIKKjo5csWfLBBx8YDIZ77rnnxIkT58+ff//996VSqbe397fffpuXl7dr167k5GThXIfD4e3t
+nZGRsXTp0va6t7ZybhPRakmKooKuD1Ny3ya5ubl//vnn4sWLDQbDfffdx/P8v//979zc3MzMzAce
+eAAhtGrVqm+//XbTpk3Z2dnPPfeczWazWq1vvvlmdnb2r7/+OnLkyPa7v5vheZu0l759+xYXF7sv
+o1QqhV/Zjx07dvLkybfeeuvhhx8WUkFhyvWWLVs++ugjPz+/nJyctWvXZmdnf/rppy+//LLZbNZo
+NMeOHZs0adKLL774v//975tvvsnMzHz22WcHDBjwww8/+Pv7Hzt2zKWw+2DEYrFEIgkICLh8+bKH
+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
+AAAAAAAQaMghAAAAAAAQaMghAAAAAAAQaMghAAAAAAAQaMghAAAAAAAQaMghAAAAAAAQaMghAAAA
+AAAQaMghAAAAAAAQaMghAAAAAAAQaMghAAAAAAAQaMghAAAAAAAQaMghAAAAAAAQaMghAAAAAAAQ
+aMghAAAAAAAQaMghAAAAAAAQaMghAAAAAAAQaMghAAAAAAAQaMghAAAAAAAQaMghAAAAAAAQaMgh
+AAAAAAAQaMghAAAAAAAQaMghAAAAAAAQaMghAAAAAAAQaMghAAAAAAAQaMghAAAAAAAQaMghAAAA
+AAAQaMghAAAAAAAQaMghAAAAAAAQaMghAAAAAAAQaMghAAAAAAAQaMghAAAAAAAQaMghAAAAAAAQ
+aMghAAAAAAAQaMghAAAAAAAQaMghAAAAAAAQaMghAAAAAAAQaMghAAAAAAAQaMghAAAAAAAQaMgh
+AAAAAAAQaMghAAAAAAAQaMghAAAAAAAQaMghAAAAAAAQaMghAAAAAAAQaMghAAAAAAAQaMghAAAA
+AAAQaMghAAAAAAAQaMghAAAAAAAQaMghAAAAAAAQaMghAAAAAAAQaMghAAAAAAAQaMghAAAAAAAQ
+aMghAAAAAAAQaMghAAAAAAAQaMghAAAAAAAQaMghAAAAAAAQaMghAAAAAAAQaMghAAAAAAAQaMgh
+AAAAAAAQaMghAAAAAAAQaMghAAAAAAAQaMghAAAAAAAQaMghAAAAAAAQaMghAAAAAAAQaMghAAAA
+AAAQaMghAAAAAAAQaMghAAAAAAAQaP8PJBrbZr1ee/wAAAAASUVORK5CYII=
+"
+ height="644"
+ width="1069" />
+ <rect
+ style="fill:none;stroke:#ff0000;stroke-width:3;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+ id="rect3134"
+ width="375.76852"
+ height="530.05426"
+ x="222.83002"
+ y="130.90649"
+ rx="6"
+ ry="6" />
+ <text
+ xml:space="preserve"
+ style="font-size:26.8358593px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#ff0000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
+ x="481.10455"
+ y="532.09589"
+ id="text5151"
+ sodipodi:linespacing="125%"
+ transform="scale(0.85206687,1.1736168)"><tspan
+ sodipodi:role="line"
+ id="tspan5153"
+ x="481.10455"
+ y="532.09589">\emph{\Large Obszar skryptów}</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:19.71675301px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#ff0000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
+ x="523.03198"
+ y="39.194279"
+ id="text5151-9"
+ sodipodi:linespacing="125%"
+ transform="scale(0.90030458,1.1107352)"><tspan
+ sodipodi:role="line"
+ id="tspan5153-5"
+ x="523.03198"
+ y="39.194279">\emph{\small Pasek narzędzi}</tspan></text>
+ <rect
+ style="fill:none;stroke:#ff0000;stroke-width:3;stroke-miterlimit:4;stroke-dasharray:none"
+ id="rect5347"
+ width="1065.7158"
+ height="27.649349"
+ x="15.662526"
+ y="20.626785"
+ rx="5.9998355"
+ ry="6.0881457" />
+ <rect
+ style="fill:none;stroke:#ff0000;stroke-width:3;stroke-miterlimit:4;stroke-dasharray:none"
+ id="rect5412"
+ width="203.04066"
+ height="612.15247"
+ x="2.0203052"
+ y="30.837404"
+ transform="translate(14.071424,18.933605)"
+ rx="6"
+ ry="6" />
+ <rect
+ style="fill:none;stroke:#ff0000;stroke-width:3;stroke-miterlimit:4;stroke-dasharray:none"
+ id="rect5417"
+ width="475.71429"
+ height="357.14285"
+ x="591.42859"
+ y="34"
+ transform="translate(14.071424,18.933605)"
+ rx="5.9998355"
+ ry="6.0881457" />
+ <rect
+ style="fill:none;stroke:#ff0000;stroke-width:3;stroke-miterlimit:4;stroke-dasharray:none"
+ id="rect5419"
+ width="480"
+ height="247.14285"
+ x="588.57141"
+ y="395.42856"
+ transform="translate(14.071424,18.933605)"
+ rx="5.9998355"
+ ry="6.0881457" />
+ <text
+ transform="scale(0.95324637,1.0490467)"
+ sodipodi:linespacing="125%"
+ id="text5421"
+ y="595.28009"
+ x="883.7558"
+ style="font-size:30.02250862px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#ff0000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
+ xml:space="preserve"><tspan
+ y="595.28009"
+ x="883.7558"
+ id="tspan5423"
+ sodipodi:role="line">\emph{\Large Zagroda duszków}</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:24.07473755px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#ff0000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
+ x="153.72911"
+ y="477.34894"
+ id="text5425"
+ sodipodi:linespacing="125%"
+ transform="scale(0.7643984,1.3082183)"><tspan
+ sodipodi:role="line"
+ id="tspan5427"
+ x="153.72911"
+ y="477.34894">\emph{\Large Paleta}</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:36.83586121px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#ff0000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
+ x="722.289"
+ y="426.28369"
+ id="text5429"
+ sodipodi:linespacing="125%"
+ transform="scale(1.1695777,0.85500949)"><tspan
+ sodipodi:role="line"
+ id="tspan5431"
+ x="722.289"
+ y="426.28369">\emph{\Large Scena}</tspan></text>
+ </g>
+</svg>
diff --git a/help/manual/pl/przesun-o-10-krokow.png b/help/manual/pl/przesun-o-10-krokow.png
new file mode 100644
index 0000000..ece840c
--- /dev/null
+++ b/help/manual/pl/przesun-o-10-krokow.png
Binary files differ
diff --git a/help/manual/pl/snap-podrecznik.tex b/help/manual/pl/snap-podrecznik.tex
new file mode 100644
index 0000000..f041a17
--- /dev/null
+++ b/help/manual/pl/snap-podrecznik.tex
@@ -0,0 +1,167 @@
+% !TeX spellcheck = pl
+
+\documentclass{report}
+
+\input{../common/defs.tex}
+
+\usepackage[polish]{babel}
+\usepackage{polski}
+\frenchspacing
+\usepackage{indentfirst}
+
+\begin{document}
+
+\title{\Snap{} \\ Podręcznik użytkownika}
+\author{Brian Harvey \and Jens M\"{o}nig}
+\date{}
+
+\maketitle
+
+\tableofcontents
+
+\chapter*{}
+\section*{Podziękowania}
+
+Mieliśmy ogromne szczęście do mentorów. Jens zdobył dużo doświadczenia pracując wśród pionierów Smalltalka: Alana Kaya, Dana Ingallsa i~reszty ekipy, która wynalazła komputery osobiste i~programowanie obiektowe w~najlepszych dniach firmy Xerox PARC. Pracował z~Johnem Maloneyem z~zespołu Scratcha w~MIT\footnote{Massachusetts Institute of Technology, amerykańska uczelnia techniczna --- przyp. tłum.}, autorem platformy graficznej Morphic, wciąż stanowiącej fundament \Snap{a}. Znakomity projekt języka Scratch, autorstwa Lifelong Kindergarten Group z~MIT Media Lab, odgrywa w~\Snap{ie} kluczową rolę.
+
+\textbf{\emph{Nasza poprzednia wersja, BYOB, była bezpośrednią modyfikacją kodu źródłowego Scratcha. \Snap{} został napisany od zera, lecz struktura jego kodu oraz interfejs użytkownika pozostają mocno zakorzenione w~Scratchu. Z~kolei zespół Scratcha, który mógłby widzieć w~nas rywali, przyjął nas ciepło i~okazał nam całkowite wsparcie.}}
+
+Brian zdobywał szlify w~MIT oraz Stanford Artificial Intelligence Labs\footnote{Laboratorium sztucznej inteligencji na Uniwersytecie Stanforda --- przyp. tłum.}, gdzie uczył się pod okiem Johna McCarthy'ego, twórcy Lispa, oraz Geralda~J. Suss\-mana i~Guya Steele'a, twórców języka Scheme. Zdobywał również wiedzę od wielu innych wybitnych informatyków, w~tym autorów najlepszej książki z zakresu informatyki --- \emph{Struktury i~interpretacji programów komputerowych}: Hala Abelsona, Geralda~J. Suss\-mana i~Julie Suss\-man.
+
+\textbf{\emph{Za starych dobrych czasów mawialiśmy w~MIT Logo Lab: ,,Język Logo to Lisp w przebraniu BASIC-a''. Dziś, ze swoimi pierwszoklasowymi procedurami, zasięgami leksykalnymi~i pierwszoklasowymi kontynuacjami, \Snap{} jest jak Scheme w~przebraniu Scratcha.}}
+
+Szczęśliwym zrządzeniem losu, poprzez forum Scratch Advanced Topics, poznaliśmy wspaniałą grupę błyskotliwych uczniów gimnazjów~(!\@) i liceów. Kilku z nich wniosło swój wkład w~kod \Snap{a}: Kartik Chandra, Nathan Dinsmore, Connor Hudson i~Ian Reynolds. Ponadto wielu zgłosiło pomysły i~raporty błędów podczas testowania wersji alfa. Wśród studentów Uniwersytetu Kalifornijskiego w~Berkeley, którzy przyczynili się do rozwoju kodu, znajdują się Michael Ball, Achal Dave, Kyle Hotchkiss, Ivan Motyashov i~Yuan Yuan. Wymienianie wszystkich tłumaczy zajęłoby zbyt wiele miejsca, ale można ich odnaleźć w~okienku ,,O Snap!...'' dostępnym w~programie. Niniejsze dzieło powstało częściowo w~ramach grantu nr~1143566 udzielonego przez National Science Foundation, a częściowo przy wsparciu firmy MioSoft.
+
+\clearpage
+
+\begin{center}
+\bf \Huge \Snap{} \\
+Podręcznik użytkownika \\
+\huge Wersja 4.0 \vspace{40pt}
+\end{center}
+
+\Snap{} to rozszerzona reimplementacja języka Scratch (\url{http://scratch.mit.edu}), która pozwala na tworzenie własnych bloków (ang.\ \textit{Build Your Own Blocks}; stąd dawna nazwa \Snap{a} --- BYOB). Opisywany tu język obsługuje pierwszoklasowe listy, procedury i~kontynuacje. Te dodatkowe możliwości sprawiają, że nadaje się on do przeprowadzenia poważnego wstępu do informatyki dla uczniów liceów i szkół wyższych. Aby uruchomić środowisko \Snap{}, wystarczy otworzyć przeglądarkę internetową i~wpisać adres \url{http://snap.berkeley.edu/run}, aby zacząć pracę z~minimalnym zestawem bloków. Można też użyć adresu \url{http://snap.berkeley.edu/init}, aby załadować niewielki zestaw dodatkowych bloków. Wiąże się to z~nieco wolniejszym ładowaniem, ale jest zalecane dla wygody użytkowników (w~dalszej części podręcznika będziemy zakładali korzystanie z~tej właśnie metody).
+
+\clearpage
+
+\chapter{Bloki, skrypty i duszki}
+
+W~tym rozdziale poznamy kilka cech języka \Snap{} odziedziczonych po Scratchu; doświadczeni użytkownicy Scratcha mogą przejść od razu do sekcji~\ref{sec:zagnieżdżanie-duszków}.
+
+\Snap{} jest językiem programowania --- notacją, przy pomocy której możemy powiedzieć komputerowi, co ma zrobić. Jednak w~odróżnieniu od większości innych, \Snap{} jest językiem wizualnym; programując w~nim, zamiast posługiwać się klawiaturą, używamy metody ,,przeciągnij i~upuść'', dobrze znanej użytkownikom komputerów.
+
+Uruchom teraz środowisko \Snap{}. Powinieneś zobaczyć ekran podzielony na kilka obszarów:
+
+\begin{center}
+\def\svgwidth{\textwidth}
+\input{obszary-okna.pdf_tex}
+\end{center}
+
+(Proporcje tych stref mogą się różnić, w~zależności od rozmiaru i~kształtu okna przeglądarki.)
+
+Program w~języku \Snap{} składa się z~jednego lub więcej \emph{skryptów}, te zaś z~kolei --- z~\emph{bloków}. Oto przykładowy skrypt:
+
+\label{fig:typowy-skrypt}
+\bigpic{typowy-skrypt}
+
+Na powyższy skrypt składa się pięć bloków w~trzech różnych kolorach, odpowiadających trzem z~ośmiu \emph{palet} z~blokami. Obszar palet, znajdujący się po lewej stronie okna, pokazuje jedną paletę na raz. Do zmiany widocznej palety służy osiem przycisków znajdujących się tuż nad tym obszarem. Bloki ciemnożółte, widoczne w~naszym skrypcie, pochodzą z~palety ,,Kontrola''; zielone z~palety ,,Pisak'', a~niebieskie --- z~palety ,,Ruch''. Aby złożyć taki skrypt, należy poprzeciągać odpowiednie bloki z~palet do \emph{obszaru skryptów}, umiejscowionego na środku okna. Kiedy układamy jeden blok pod drugim w~taki sposób, aby wcięcie dolnego bloku znalazło się w~pobliżu wypustki tego powyżej, bloki łączą się ze sobą (ang. \textit{snap together}; stąd nazwa języka \Snap{}):
+
+\bigpic{laczenie-blokow}
+
+Pozioma biała linia sygnalizuje, że jeśli puścimy zielony blok, połączy się on z~wypustką ciemnożółtego.
+
+\subsection{Bloki-czapki i bloki poleceń}
+
+Na górze skryptu znajduje się \emph{blok-czapka}, który określa, kiedy skrypt ma zostać wykonany. Nazwy bloków-czapek zazwyczaj zaczynają się słowem ,,\code{kiedy}''; nasz przykładowy skrypt powinien zostać uruchomiony w~momencie kliknięcia zielonej flagi, znajdującej się w pobliżu prawej strony paska narzędzi \Snap{a}. (Pasek ten jest częścią okna programu \Snap{}; nie chodzi tutaj o pasek menu przeglądarki lub systemu operacyjnego.) Skrypt nie musi posiadać czapki, jednak w~takim przypadku zostanie wykonany tylko wtedy, gdy użytkownik sam go kliknie. Skrypt nie może mieć więcej niż jednej czapki; jej charakterystyczny kształt służy łatwiejszemu zapamiętaniu tej szczególnej własności.
+
+Pozostałe bloki w naszym skrypcie to \emph{bloki poleceń}. Każdy z~nich oznacza jakąś akcję, którą \Snap{} potrafi wykonać. Na przykład blok \inlinepic{przesun-o-10-krokow} nakazuje duszkowi\footnote{W grafice komputerowej słowem ,,duszek'' (ang. \textit{sprite}) nazywa się ruchomy obiekt na ekranie --- przyp. tłum.}, czyli strzałce na \emph{scenie} po prawej stronie okna, aby przesunął się o~dziesięć kroków do przodu w~kierunku, w~którym jest zwrócony. Każdy krok to niewielka odległość na ekranie. Wkrótce przekonamy się, że na scenie może być więcej duszków, a~każdy z nich może mieć własne skrypty. Ponadto duszki nie muszą wyglądać jak strzałki; ich kostiumy mogą być dowolnymi obrazkami. Kształt bloku \code{przesuń} ma za zadanie przypominać klocek, skrypt zaś jest jak wieża z klocków. Słowa ,,blok'' będziemy używać dla oznaczenia zarówno graficznego symbolu na ekranie, jak i~procedury (akcji) jaką ten blok wykonuje.
+
+Liczbę 10 w powyższym bloku \code{przesuń} nazywamy jego \emph{parametrem}. Kliknąwszy na białym, zaokrąglonym polu, możemy wpisać w~jej miejsce dowolną inną. W przykładowym skrypcie ze strony \pageref{fig:typowy-skrypt} parametrem jest liczba 100. Jak się później okaże, pola parametrów mogą mieć kształty inne od zaokrąglonych; oznacza to wtedy, że akceptują one wartości inne niż liczby. Zobaczymy również, że zamiast wpisywać konkretne wartości w~pola, możemy nakazać komputerowi je obliczać. Ponadto blok może mieć więcej niż jeden parametr. Na przykład blok \code{leć}, znajdujący się mniej więcej w~połowie palety ,,Ruch'', przyjmuje trzy parametry.
+
+Większość bloków poleceń ma kształt klocków, lecz niektóre, jak \code{powtórz} z~tego samego przykładu, wyglądają jak \emph{klamry}. Większość bloków klamrowych można znaleźć na palecie ,,Kontrola'. Wnętrze klamry jest szczególnym rodzajem pola parametru, który przyjmuje \emph{skrypt} jako parametr. W~przykładowym skrypcie blok \code{powtórz} ma dwa parametry: liczbę 4 oraz skrypt
+
+\bigpic{typowy-skrypt-wnetrze}
+
+
+
+\section{Sprites and Parallelism}
+\subsection{Costumes and Sounds}
+\subsection{Inter-Sprite Communication with Broadcast}
+\section{Zagnieżdżanie duszków: kotwice i części}
+\label{sec:zagnieżdżanie-duszków}
+\section{Reporter Blocks and Expressions}
+\section{Predicates and Conditional Evaluation}
+\section{Variables}
+\subsection{Global Variables}
+\subsection{Script Variables}
+\section{Etcetera}
+\chapter{Saving and Loading Projects and Media}
+\section{Local Storage}
+\subsection{Localstore}
+\subsection{XML Export}
+\section{Cloud Storage}
+\section{Loading Saved Projects}
+\chapter{Building a Block}
+\section{Simple Blocks}
+\subsection{Custom Blocks with Inputs}
+\section{Recursion}
+\section{Block Libraries}
+\chapter{First Class Lists}
+\section{The list Block}
+\section{Lists of Lists}
+\section{Functional and Imperative List Programming}
+\section{Higher Order List Operations and Rings}
+\chapter{Typed Inputs}
+\section{Scratch's Type Notation}
+\section{The \Snap{} Input Type Dialog}
+\subsection{Procedure Types}
+\subsection{Pulldown inputs}
+\subsection{Input variants}
+\subsection{Prototype Hints}
+\subsection{Title Text and Symbols}
+\chapter{Procedures as Data}
+\section{Call and Run}
+\subsection{Call/Run with inputs}
+\subsection{Variables in Ring Slots}
+\section{Writing Higher Order Procedures}
+\subsection{Recursive Calls to Multiple-Input Blocks}
+\section{Formal Parameters}
+\section{Procedures as Data}
+\section{Special Forms}
+\subsection{Special Forms in Scratch}
+\chapter{Object Oriented Programming}
+\section{Local State with Script Variables}
+\section{Messages and Dispatch Procedures}
+\section{Inheritance via Delegation}
+\section{An Implementation of Prototyping OOP}
+\chapter{The Outside World}
+\section{The World Wide Web}
+\section{Hardware Devices}
+\section{Date and Time}
+\chapter{Continuations}
+\section{Continuation Passing Style}
+\section{Call/Run w/Continuation}
+\subsection{Nonlocal exit}
+\chapter{User Interface Elements}
+\section{Tool Bar Features}
+\subsection{The \Snap{} Logo Menu}
+\subsection{The File Menu}
+\subsection{The Cloud Menu}
+\subsection{The Settings Menu}
+\subsection{Stage Resizing Buttons}
+\subsection{Project Control Buttons}
+\section{The Palette Area}
+\subsection{Context Menus for Palette Blocks}
+\subsection{Context Menu for the Palette Background}
+\section{The Scripting Area}
+\subsection{Sprite Appearance and Behavior Controls}
+\subsection{Scripting Area Tabs}
+\subsection{Scripts and Blocks Within Scripts}
+\subsection{Scripting Area Background Context Menu}
+\subsection{Controls in the Costumes Tab}
+\subsection{The Paint Editor}
+\subsection{Controls in the Sounds Tab}
+\section{Controls on the Stage}
+\section{The Sprite Corral and Sprite Creation Buttons}
+
+\end{document}
diff --git a/help/manual/pl/typowy-skrypt-wnetrze.png b/help/manual/pl/typowy-skrypt-wnetrze.png
new file mode 100644
index 0000000..c3618ca
--- /dev/null
+++ b/help/manual/pl/typowy-skrypt-wnetrze.png
Binary files differ
diff --git a/help/manual/pl/typowy-skrypt.png b/help/manual/pl/typowy-skrypt.png
new file mode 100644
index 0000000..6e2efe0
--- /dev/null
+++ b/help/manual/pl/typowy-skrypt.png
Binary files differ
diff --git a/help/receiveInteraction.png b/help/receiveInteraction.png
index 6ace2e3..76b0715 100644
--- a/help/receiveInteraction.png
+++ b/help/receiveInteraction.png
Binary files differ
diff --git a/help/receiveOnClone.png b/help/receiveOnClone.png
new file mode 100644
index 0000000..19cdf78
--- /dev/null
+++ b/help/receiveOnClone.png
Binary files differ
diff --git a/help/removeClone.png b/help/removeClone.png
new file mode 100644
index 0000000..3afc24a
--- /dev/null
+++ b/help/removeClone.png
Binary files differ
diff --git a/help/reportAttributeOf.png b/help/reportAttributeOf.png
new file mode 100644
index 0000000..ab4aff8
--- /dev/null
+++ b/help/reportAttributeOf.png
Binary files differ
diff --git a/help/reportCallCC.png b/help/reportCallCC.png
new file mode 100644
index 0000000..8c911c6
--- /dev/null
+++ b/help/reportCallCC.png
Binary files differ
diff --git a/help/reportDate.png b/help/reportDate.png
new file mode 100644
index 0000000..f13cf38
--- /dev/null
+++ b/help/reportDate.png
Binary files differ
diff --git a/help/reportIsFastTracking.png b/help/reportIsFastTracking.png
new file mode 100644
index 0000000..2929a79
--- /dev/null
+++ b/help/reportIsFastTracking.png
Binary files differ
diff --git a/help/reportJSFunction.png b/help/reportJSFunction.png
new file mode 100644
index 0000000..ecb2f0c
--- /dev/null
+++ b/help/reportJSFunction.png
Binary files differ
diff --git a/help/reportTextSplit.png b/help/reportTextSplit.png
new file mode 100644
index 0000000..18d1422
--- /dev/null
+++ b/help/reportTextSplit.png
Binary files differ
diff --git a/help/reportURL.png b/help/reportURL.png
new file mode 100644
index 0000000..e7c24c5
--- /dev/null
+++ b/help/reportURL.png
Binary files differ
diff --git a/history.txt b/history.txt
index d042af4..c4ceb6a 100755
--- a/history.txt
+++ b/history.txt
@@ -2466,3 +2466,85 @@ ______
150306
------
* Blocks: fixed #736
+
+150309
+------
+* Blocks: fixed #738
+* GUI, Blocks: Only enable input caching for blocks
+
+150315
+------
+* Store: fixed #743
+* GUI, html: switch from beta to release candidate
+
+150321
+------
+* OOP: Prototypal inheritance of sprite-local variables
+
+150323
+------
+* OOP: Objects, Blocks, Threads - tweaks
+
+150323
+------
+* OOP: Morphic, Objects, Store - tweak inherited variable watcher slider deserialization
+
+150325
+------
+* Threads: fixed #752
+* OOP: integrated and adjusted fix for #752
+
+150415
+------
+* Threads: flush Stage>>keysPressed when prompting the user
+* Objects: fixed #770
+
+150415
+------
+* OOP: Objects, Prototypal inheritance of sprite-local custom blocks (palette only)
+
+150426
+------
+* Store: fixed #784
+
+150501
+------
+* Morphic, Blocks: select all text when first clicking an input slot
+* BYOB: indicate numeric inputs in the block prototype with the # sign
+* Threads: return empty string when querying first letter of a list
+* GUI: hide “save to disk” option behind shift-click again (has issues in Chrome)
+* GUI: parameters for embedding projects in iFrames, thanks, Bernat!
+
+150518
+------
+* Objects, GUI: Fix encoding for exported sprites (esp. comments)
+
+=== Release v4.0 ===
+
+150518
+------
+* Objects, GUI: Fix encoding for exported sprites (esp. comments)
+* Portuguese translation update, thanks, Manuel!!
+
+150521
+------
+* BYOB: Fix encoding for exported libraries of global blocks
+
+150521
+------
+* BYOB: Fix encoding glitch
+
+150608
+------
+* Blocks: Fixed #820
+
+150625
+------
+* Morphic, Objects, Blocks, XML: Optimizations and dramatic speed-up. Thanks, Nathan!!
+* Objects: push maximum clone count up to 1000, tweak Note::play
+
+=== Start with v4.0.1 ===
+
+150626
+------
+* Morphic: Fix Inspector duplication, update documentation
diff --git a/lang-fr.js b/lang-fr.js
index 4198bf7..20da480 100644
--- a/lang-fr.js
+++ b/lang-fr.js
@@ -185,7 +185,7 @@ SnapTranslator.dict.fr = {
'translator_e-mail':
'i.scool@mac.com', // optional
'last_changed':
- '2014-02-04', // this, too, will appear in the Translators tab
+ '2015-06-25', // this, too, will appear in the Translators tab
// GUI
// control bar:
@@ -392,7 +392,7 @@ SnapTranslator.dict.fr = {
'clear':
'effacer tout',
'pen down':
- 'stylo en position d\u0027\u00EAcriture',
+ 'stylo en position d\u0027\u00E9criture',
'pen up':
'relever le stylo',
'set pen color to %clr':
@@ -419,6 +419,8 @@ SnapTranslator.dict.fr = {
'Quand %keyHat est press\u00E9',
'when I am clicked':
'Quand je suis press\u00E9 ',
+ 'when I am %interaction':
+ 'Quand je suis %interaction',
'when I receive %msgHat':
'Quand je re\u00E7ois %msgHat',
'broadcast %msg':
@@ -447,6 +449,10 @@ SnapTranslator.dict.fr = {
'arr\u00EAter le bloc',
'stop script':
'arr\u00EAter le script',
+ 'stop %stopOthersChoices':
+ 'arr\u00EAter %stopOthersChoices',
+ 'stop %stopChoices':
+ 'arr\u00EAter %stopChoices',
'stop all %stop':
'arr\u00EAter tout %stop',
'run %cmdRing %inputs':
@@ -469,8 +475,16 @@ SnapTranslator.dict.fr = {
'moi-m\u00EAme',
'delete this clone':
'supprime ce clone',
- 'pause all':
- 'mettre en pause',
+ 'pause all %pause':
+ 'mettre en pause %pause',
+ 'all but this script':
+ 'tout sauf ce lutin',
+ 'other scripts in sprite':
+ 'les autres scripts de ce lutin',
+ 'this script':
+ 'ce script',
+ 'this block':
+ 'ce bloc',
// sensing:
'touching %col ?':
@@ -958,6 +972,8 @@ SnapTranslator.dict.fr = {
'tabulations',
'cr':
'retours de ligne',
+ 'letter':
+ 'lettres',
// About Snap
'About Snap':
@@ -1162,5 +1178,133 @@ SnapTranslator.dict.fr = {
'last':
'dernier',
'any':
- 'n\u0027importe quel'
+ 'n\u0027importe quel',
+
+ // miscellaneous
+ 'find blocks...':
+ 'chercher des blocs...',
+ 'hide primitives':
+ 'cacher les primitives',
+ 'show primitives':
+ 'montrer les primitives',
+ 'Login...':
+ 'Connexion...',
+ 'Signup...':
+ 'S\u0027enregistrer...',
+ 'Reset Password...':
+ 'Remise \u00E0 z\u00E9ro du mot de passe',
+ 'show all':
+ 'tout montrer',
+ 'pic...':
+ 'image...',
+ 'open a new window\nwith a picture of the stage':
+ 'ouvre une nouvelle fen\u00EAtre\navec une image de la sc\u00E8ne',
+ 'scripts pic...':
+ 'image des scripts...',
+ 'open a new window\nwith a picture of all scripts':
+ 'ouvre une nouvelle fen\u00EAtre\navec une image de tous les scripts',
+ 'Stage size...':
+ 'Taille de la sc\u00E8ne...',
+ 'Zoom blocks...':
+ 'Agrandir les blocs...',
+
+ 'Plain prototype labels':
+ '\u00C9tiquettes simples de d\u00E9finition',
+ 'uncheck to always show (+) symbols\nin block prototype labels':
+ 'd\u00E9cocher pour montrer en permance le symbole (+)\ndans les \u00e9tiquettes de d\u00E9finition de bloc',
+ 'check to hide (+) symbols\nin block prototype labels':
+ 'cocher pour cacher le symbole (+)\ndans les \u00e9tiquettes de d\u00E9finition de bloc',
+
+ 'check for flat ends of lines':
+ 'cocher pour dessiner des fins de ligne plates',
+ 'uncheck for round ends of lines':
+ 'd\u00E9cocher pour dessiner des fins de lignes arrondies',
+ 'Flat line ends':
+ 'Fins de ligne plates',
+
+ 'Codification support':
+ 'Support de la \u00AB codification \u00BB',
+ 'uncheck to disable\nblock to text mapping features':
+ 'd\u00E9cocher pour d\u00E9activer\nla fonction de transformation :\nbloc vers texte',
+ 'check for block\nto text mapping features':
+ 'cocher pour activer\nla fonction de transformation :\nbloc vers texte',
+
+ 'current %dates':
+ 'date courante %dates',
+ 'year':'ann\u00E9e',
+ 'month':'mois',
+ 'date':'jour',
+ 'hour':'heure',
+ 'minute':'minute',
+ 'second':'seconde',
+ 'time in milliseconds':
+ 'heure en millisecondes',
+ 'day of week':
+ 'jour de la semaine',
+
+ 'brightness':
+ 'luminosit\u00E9',
+ 'transparence':
+ 'transparence',
+ 'negative':
+ 'n\u00E9gatif',
+ 'comic':
+ 'bande dessin\u00E9e',
+
+ 'clicked':
+ 'cliqu\u00E9',
+ 'pressed':
+ 'press\u00E9',
+ 'dropped':
+ 'd\u00E9pos\u00E9',
+ 'mouse-entered':
+ 'survol\u00E9',
+ 'mouse-departed':
+ 'quitt\u00E9',
+
+ 'JavaScript function ( %mult%s ) { %code }':
+ 'fonction JavaScript ( %mult%s ) { %code }',
+
+
+ // Copy / Paste
+ 'Press CTRL+C one more time to effectively copy to clipboard.':
+ 'Taper une nouvelle fois sur CTRL+C pour copier effectivement vers le presse-papier.',
+ 'Press CTRL+V one more time to effectively paste from clipboard.':
+ 'Taper une nouvelle fois sur CTRL+V pour coller effectivement depuis le presse-papier.',
+ 'Press CTRL+X one more time to effectively cut to clipboard.':
+ 'Taper une nouvelle fois sur CTRL+X pour couper effectivement vers le presse-papier.',
+
+ // Paint.js
+ 'undo':'d\u00E9faire',
+ 'Paintbrush tool\n(free draw)':
+ 'Pinceau\n(dessin \u00E0 main lev\u00E9)',
+ 'Stroked Rectangle\n(shift: square)':
+ 'Rectangle\n(Maj: carr\u00E9)',
+ 'Stroked Ellipse\n(shift: circle)':
+ 'Ellipse\n(Maj: cercle)',
+ 'Eraser tool':
+ 'Gomme',
+ 'Set the rotation center':
+ 'Fixe le centre de rotation',
+ 'Line tool\n(shift: vertical/horizontal)':
+ 'Ligne\n(Maj: verticale/horizontale)',
+ 'Filled Rectangle\n(shift: square)':
+ 'Rectangle plein\n(Maj: carr\u00E9)',
+ 'Filled Ellipse\n(shift: circle)':
+ 'Ellipse pleine\n(Maj: cercle)',
+ 'Fill a region':
+ 'Remplir une r\u00E9gion',
+ 'Pipette tool\n(pick a color anywhere)':
+ 'Pipette\n(s\u00E9lectionnez une couleur n\u0027importe o\u00F9',
+ 'grow':'agrandir',
+ 'shrink':'r\u00E9duire',
+ 'flip \u2194':
+ 'miroir \u2194',
+ 'flip \u2195':
+ 'miroir \u2195',
+ 'Brush size':
+ 'Taille de pinceau',
+ 'Constrain proportions of shapes?\n(you can also hold shift)':
+ 'Contrainte sur les proportions de la forme ?\n(vous pouvez aussi maintenir appuy\u00E9 Maj)'
+
};
diff --git a/lang-pt.js b/lang-pt.js
index 2d3804a..e9f309f 100755
--- a/lang-pt.js
+++ b/lang-pt.js
@@ -185,7 +185,7 @@ SnapTranslator.dict.pt = {
'translator_e-mail':
'mmsequeira@gmail.com',
'last_changed':
- '2014-10-01',
+ '2015-05-04',
// GUI
// control bar:
@@ -417,8 +417,18 @@ SnapTranslator.dict.pt = {
'Quando alguém clicar em %greenflag',
'when %keyHat key pressed':
'Quando alguém pressionar a tecla %keyHat',
- 'when I am clicked':
- 'Quando alguém clicar em ti',
+ 'when I am %interaction':
+ 'Quando o rato %interaction',
+ 'clicked':
+ 'clicar em ti',
+ 'pressed':
+ 'pressionar em ti',
+ 'dropped':
+ 'te largar',
+ 'mouse-entered':
+ 'entrar em ti',
+ 'mouse-departed':
+ 'sair de ti',
'when I receive %msgHat':
'Quando receberes a mensagem %msgHat',
'broadcast %msg':
@@ -647,13 +657,17 @@ SnapTranslator.dict.pt = {
'Abrir um projecto…',
'Save':
'Guardar este projecto',
+ 'Save to disk':
+ 'Guardar no disco',
+ 'store this project\nin the downloads folder\n(in supporting browsers)':
+ 'Guardar este projecto\nna sua pasta de descargas\n'
+ + '(em navegadores que o suportem).',
'Save As...':
'Guardar este projecto como…',
'Import...':
'Importar…',
'file menu import hint':
- 'Abrir um projecto exportado,\n'
- + 'substitundo o projecto corrente, ou\n'
+ 'Abrir um projecto exportado,\nsubstitundo o projecto corrente, ou\n'
+ 'importar uma biblioteca de blocos, um\n'
+ 'traje ou um som para o projecto corrente.',
'Export project as plain text...':
@@ -1443,10 +1457,6 @@ SnapTranslator.dict.pt = {
'Suportar produção de código',
'check for block\nto text mapping features':
'Assinalar para funcionalidades\nde mapeamento entre blocos e texto.',
- 'Save to disk':
- 'Guardar no disco',
- 'experimental - store this project\nin your downloads folder':
- 'Experimental - Guardar este projecto\nna sua pasta de descargas.',
'saved.':
'guardado.',
'options...':
diff --git a/lists.js b/lists.js
index bd856fe..6fc9545 100644
--- a/lists.js
+++ b/lists.js
@@ -7,7 +7,7 @@
written by Jens Mönig and Brian Harvey
jens@moenig.org, bh@cs.berkeley.edu
- Copyright (C) 2014 by Jens Mönig and Brian Harvey
+ Copyright (C) 2015 by Jens Mönig and Brian Harvey
This file is part of Snap!.
@@ -61,7 +61,7 @@ PushButtonMorph, SyntaxElementMorph, Color, Point, WatcherMorph,
StringMorph, SpriteMorph, ScrollFrameMorph, CellMorph, ArrowMorph,
MenuMorph, snapEquals, Morph, isNil, localize, MorphicPreferences*/
-modules.lists = '2014-November-20';
+modules.lists = '2015-June-25';
var List;
var ListWatcherMorph;
@@ -634,6 +634,7 @@ ListWatcherMorph.prototype.setStartIndex = function (index) {
};
ListWatcherMorph.prototype.fixLayout = function () {
+ if (!this.label) {return; }
Morph.prototype.trackChanges = false;
if (this.frame) {
this.arrangeCells();
diff --git a/locale.js b/locale.js
index f6059d6..af1e0f4 100644
--- a/locale.js
+++ b/locale.js
@@ -42,7 +42,7 @@
/*global modules, contains*/
-modules.locale = '2015-February-23';
+modules.locale = '2015-June-25';
// Global stuff
@@ -209,7 +209,7 @@ SnapTranslator.dict.pt = {
'translator_e-mail':
'mmsequeira@gmail.com',
'last_changed':
- '2014-10-01'
+ '2015-05-04'
};
SnapTranslator.dict.cs = {
@@ -257,7 +257,7 @@ SnapTranslator.dict.fr = {
'translator_e-mail':
'i.scool@mac.com',
'last_changed':
- '2014-02-04'
+ '2015-06-25'
};
SnapTranslator.dict.si = {
diff --git a/morphic.js b/morphic.js
index 4a16597..f359bb3 100644
--- a/morphic.js
+++ b/morphic.js
@@ -8,7 +8,7 @@
written by Jens Mönig
jens@moenig.org
- Copyright (C) 2014 by Jens Mönig
+ Copyright (C) 2015 by Jens Mönig
This file is part of Snap!.
@@ -527,6 +527,12 @@
a duplicate of the template whose "isDraggable" flag is true and
whose "isTemplate" flag is false, in other words: a non-template.
+ When creating a copy from a template, the copy's
+
+ reactToTemplateCopy
+
+ is invoked, if it is present.
+
Dragging is indicated by adding a drop shadow to the morph in hand.
If a morph follows the hand without displaying a drop shadow it is
merely being moved about without changing its parent (owner morph),
@@ -822,13 +828,13 @@
// use context to paint stuff here
};
- If your new morph stores or references other morphs outside of the
- submorph tree in other properties, be sure to also override the
+ If your new morph stores or references to other morphs outside of
+ the submorph tree in other properties, be sure to also override the
default
- copyRecordingReferences()
+ updateReferences()
- method accordingly if you want it to support duplication.
+ method if you want it to support duplication.
(6) development and user modes
@@ -1020,16 +1026,17 @@
programming hero.
I have originally written morphic.js in Florian Balmer's Notepad2
- editor for Windows and later switched to Apple's Dashcode. I've also
- come to depend on both Douglas Crockford's JSLint, Mozilla's Firebug
- and Google's Chrome to get it right.
+ editor for Windows, later switched to Apple's Dashcode and later
+ still to Apple's Xcode. I've also come to depend on both Douglas
+ Crockford's JSLint, Mozilla's Firebug and Google's Chrome to get
+ it right.
IX. contributors
----------------------
Joe Otto found and fixed many early bugs and taught me some tricks.
Nathan Dinsmore contributed mouse wheel scrolling, cached
- background texture handling and countless bug fixes.
+ background texture handling, countless bug fixes and optimizations.
Ian Reynolds contributed backspace key handling for Chrome.
Davide Della Casa contributed performance optimizations for Firefox.
@@ -1041,7 +1048,7 @@
/*global window, HTMLCanvasElement, getMinimumFontHeight, FileReader, Audio,
FileList, getBlurredShadowSupport*/
-var morphicVersion = '2014-December-05';
+var morphicVersion = '2015-June-26';
var modules = {}; // keep track of additional loaded modules
var useBlurredShadows = getBlurredShadowSupport(); // check for Chrome-bug
@@ -1236,19 +1243,9 @@ function getDocumentPositionOf(aDOMelement) {
return pos;
}
-function clone(target) {
- // answer a new instance of target's type
- if (typeof target === 'object') {
- var Clone = function () {nop(); };
- Clone.prototype = target;
- return new Clone();
- }
- return target;
-}
-
function copy(target) {
// answer a shallow copy of target
- var value, c, property;
+ var value, c, property, keys, l, i;
if (typeof target !== 'object') {
return target;
@@ -1259,18 +1256,16 @@ function copy(target) {
}
if (target instanceof target.constructor &&
target.constructor !== Object) {
- c = clone(target.constructor.prototype);
- for (property in target) {
- if (Object.prototype.hasOwnProperty.call(target, property)) {
- c[property] = target[property];
- }
+ c = Object.create(target.constructor.prototype);
+ keys = Object.keys(target);
+ for (l = keys.length, i = 0; i < l; i += 1) {
+ property = keys[i];
+ c[property] = target[property];
}
} else {
c = {};
for (property in target) {
- if (!c[property]) {
- c[property] = target[property];
- }
+ c[property] = target[property];
}
}
return c;
@@ -2205,7 +2200,7 @@ function Morph() {
// Morph initialization:
-Morph.prototype.init = function () {
+Morph.prototype.init = function (noDraw) {
Morph.uber.init.call(this);
this.isMorph = true;
this.bounds = new Rectangle(0, 0, 50, 40);
@@ -2218,7 +2213,7 @@ Morph.prototype.init = function () {
this.isTemplate = false;
this.acceptsDrops = false;
this.noticesTransparentClick = false;
- this.drawNew();
+ if (!noDraw) {this.drawNew(); }
this.fps = 0;
this.customContextMenu = null;
this.lastTime = Date.now();
@@ -2404,19 +2399,19 @@ Morph.prototype.visibleBounds = function () {
// Morph accessing - simple changes:
Morph.prototype.moveBy = function (delta) {
- this.changed();
- this.bounds = this.bounds.translateBy(delta);
- this.children.forEach(function (child) {
- child.moveBy(delta);
- });
- this.changed();
+ this.fullChanged();
+ this.silentMoveBy(delta);
+ this.fullChanged();
};
Morph.prototype.silentMoveBy = function (delta) {
+ var children = this.children,
+ i = children.length;
this.bounds = this.bounds.translateBy(delta);
- this.children.forEach(function (child) {
- child.silentMoveBy(delta);
- });
+ // ugly optimization avoiding forEach()
+ for (i; i > 0; i -= 1) {
+ children[i - 1].silentMoveBy(delta);
+ }
};
Morph.prototype.setPosition = function (aPoint) {
@@ -2892,7 +2887,7 @@ Morph.prototype.fullChanged = function () {
};
Morph.prototype.childChanged = function () {
- // react to a change in one of my children,
+ // react to a change in one of my children,
// default is to just pass this message on upwards
// override this method for Morphs that need to adjust accordingly
if (this.parent) {
@@ -2929,6 +2924,18 @@ Morph.prototype.addBack = function (aMorph) {
this.addChildFirst(aMorph);
};
+Morph.prototype.topMorphAt = function (point) {
+ var i, result;
+ if (!this.isVisible) {return null; }
+ for (i = this.children.length - 1; i >= 0; i -= 1) {
+ result = this.children[i].topMorphAt(point);
+ if (result) {return result; }
+ }
+ return this.bounds.containsPoint(point) &&
+ (this.noticesTransparentClick || !this.isTransparentAt(point)) ? this
+ : null;
+};
+
Morph.prototype.topMorphSuchThat = function (predicate) {
var next;
if (predicate.call(null, this)) {
@@ -2944,30 +2951,6 @@ Morph.prototype.topMorphSuchThat = function (predicate) {
return null;
};
-Morph.prototype.morphAt = function (aPoint) {
- var morphs = this.allChildren().slice(0).reverse(),
- result = null;
- morphs.forEach(function (m) {
- if (m.fullBounds().containsPoint(aPoint) &&
- (result === null)) {
- result = m;
- }
- });
- return result;
-};
-
-/*
- alternative - more elegant and possibly more
- performant - solution for morphAt.
- Has some issues, commented out for now
-
-Morph.prototype.morphAt = function (aPoint) {
- return this.topMorphSuchThat(function (m) {
- return m.fullBounds().containsPoint(aPoint);
- });
-};
-*/
-
Morph.prototype.overlappedMorphs = function () {
//exclude the World
var world = this.world(),
@@ -3039,45 +3022,54 @@ Morph.prototype.fullCopy = function () {
Other properties are also *shallow* copied, so you must override
to deep copy Arrays and (complex) Objects
*/
- var dict = {}, c;
- c = this.copyRecordingReferences(dict);
+ var map = new Map(), c;
+ c = this.copyRecordingReferences(map);
c.forAllChildren(function (m) {
- m.updateReferences(dict);
+ m.updateReferences(map);
});
return c;
};
-Morph.prototype.copyRecordingReferences = function (dict) {
+Morph.prototype.copyRecordingReferences = function (map) {
/*
Recursively copy this entire composite morph, recording the
correspondence between old and new morphs in the given dictionary.
This dictionary will be used to update intra-composite references
in the copy. See updateReferences().
- Note: This default implementation copies ONLY morphs in the
- submorph hierarchy. If a morph stores morphs in other properties
- that it wants to copy, then it should override this method to do so.
- The same goes for morphs that contain other complex data that
- should be copied when the morph is duplicated.
+
+ Note: This default implementation copies ONLY morphs. If a morph
+ stores morphs in other properties that it wants to copy, then it
+ should override this method to do so. The same goes for morphs that
+ contain other complex data that should be copied when the morph is
+ duplicated.
*/
var c = this.copy();
- dict[this] = c;
+ map.set(this, c);
this.children.forEach(function (m) {
- c.add(m.copyRecordingReferences(dict));
+ c.add(m.copyRecordingReferences(map));
});
return c;
};
-Morph.prototype.updateReferences = function (dict) {
+Morph.prototype.updateReferences = function (map) {
/*
Update intra-morph references within a composite morph that has
been copied. For example, if a button refers to morph X in the
orginal composite then the copy of that button in the new composite
should refer to the copy of X in new composite, not the original X.
*/
- var property;
- for (property in this) {
- if (this[property] && this[property].isMorph && dict[property]) {
- this[property] = dict[property];
+ var properties = Object.keys(this),
+ l = properties.length,
+ property,
+ value,
+ reference,
+ i;
+ for (i = 0; i < l; i += 1) {
+ property = properties[i];
+ value = this[property];
+ if (value && value.isMorph) {
+ reference = map.get(value);
+ if (reference) { this[property] = reference; }
}
}
};
@@ -3148,9 +3140,7 @@ Morph.prototype.slideBackTo = function (situation, inSteps) {
this.fps = 0;
this.step = function () {
- myself.fullChanged();
- myself.silentMoveBy(new Point(xStep, yStep));
- myself.fullChanged();
+ myself.moveBy(new Point(xStep, yStep));
stepCount += 1;
if (stepCount === steps) {
situation.origin.add(myself);
@@ -3699,6 +3689,10 @@ function ShadowMorph() {
this.init();
}
+ShadowMorph.prototype.topMorphAt = function () {
+ return null;
+};
+
// HandleMorph ////////////////////////////////////////////////////////
// I am a resize / move handle that can be attached to any Morph
@@ -3916,20 +3910,6 @@ HandleMorph.prototype.mouseLeave = function () {
this.changed();
};
-// HandleMorph duplicating:
-
-HandleMorph.prototype.copyRecordingReferences = function (dict) {
- // inherited, see comment in Morph
- var c = HandleMorph.uber.copyRecordingReferences.call(
- this,
- dict
- );
- if (c.target && dict[this.target]) {
- c.target = (dict[this.target]);
- }
- return c;
-};
-
// HandleMorph menu:
HandleMorph.prototype.attach = function () {
@@ -4253,20 +4233,6 @@ ColorPaletteMorph.prototype.updateTarget = function () {
}
};
-// ColorPaletteMorph duplicating:
-
-ColorPaletteMorph.prototype.copyRecordingReferences = function (dict) {
- // inherited, see comment in Morph
- var c = ColorPaletteMorph.uber.copyRecordingReferences.call(
- this,
- dict
- );
- if (c.target && dict[this.target]) {
- c.target = (dict[this.target]);
- }
- return c;
-};
-
// ColorPaletteMorph menu:
ColorPaletteMorph.prototype.developersMenu = function () {
@@ -5833,23 +5799,6 @@ SliderMorph.prototype.updateTarget = function () {
}
};
-// SliderMorph duplicating:
-
-SliderMorph.prototype.copyRecordingReferences = function (dict) {
- // inherited, see comment in Morph
- var c = SliderMorph.uber.copyRecordingReferences.call(
- this,
- dict
- );
- if (c.target && dict[this.target]) {
- c.target = (dict[this.target]);
- }
- if (c.button && dict[this.button]) {
- c.button = (dict[this.button]);
- }
- return c;
-};
-
// SliderMorph menu:
SliderMorph.prototype.developersMenu = function () {
@@ -5926,7 +5875,7 @@ SliderMorph.prototype.userSetStart = function (num) {
this.start = Math.max(num, this.stop);
};
-SliderMorph.prototype.setStart = function (num) {
+SliderMorph.prototype.setStart = function (num, noUpdate) {
// for context menu demo purposes
var newStart;
if (typeof num === 'number') {
@@ -5944,12 +5893,12 @@ SliderMorph.prototype.setStart = function (num) {
}
}
this.value = Math.max(this.value, this.start);
- this.updateTarget();
+ if (!noUpdate) {this.updateTarget(); }
this.drawNew();
this.changed();
};
-SliderMorph.prototype.setStop = function (num) {
+SliderMorph.prototype.setStop = function (num, noUpdate) {
// for context menu demo purposes
var newStop;
if (typeof num === 'number') {
@@ -5961,12 +5910,12 @@ SliderMorph.prototype.setStop = function (num) {
}
}
this.value = Math.min(this.value, this.stop);
- this.updateTarget();
+ if (!noUpdate) {this.updateTarget(); }
this.drawNew();
this.changed();
};
-SliderMorph.prototype.setSize = function (num) {
+SliderMorph.prototype.setSize = function (num, noUpdate) {
// for context menu demo purposes
var newSize;
if (typeof num === 'number') {
@@ -5984,7 +5933,7 @@ SliderMorph.prototype.setSize = function (num) {
}
}
this.value = Math.min(this.value, this.stop - this.size);
- this.updateTarget();
+ if (!noUpdate) {this.updateTarget(); }
this.drawNew();
this.changed();
};
@@ -6556,7 +6505,7 @@ InspectorMorph.prototype.setExtent = function (aPoint) {
this.fixLayout();
};
-//InspectorMorph editing ops:
+// InspectorMorph editing ops:
InspectorMorph.prototype.save = function () {
var txt = this.detail.contents.children[0].text.toString(),
@@ -6646,6 +6595,15 @@ InspectorMorph.prototype.step = function () {
this.fixLayout();
};
+// InspectorMorph duplicating:
+
+InspectorMorph.prototype.updateReferences = function (map) {
+ var active = this.list.activeIndex();
+ InspectorMorph.uber.updateReferences.call(this, map);
+ this.buildPanes();
+ this.list.activateIndex(active);
+};
+
// MenuMorph ///////////////////////////////////////////////////////////
// MenuMorph: referenced constructors
@@ -7017,7 +6975,7 @@ StringMorph.prototype.init = function (
this.markedBackgoundColor = new Color(60, 60, 120);
// initialize inherited properties:
- StringMorph.uber.init.call(this);
+ StringMorph.uber.init.call(this, true);
// override inherited properites:
this.color = color || new Color(0, 0, 0);
@@ -7395,6 +7353,11 @@ StringMorph.prototype.selectionStartSlot = function () {
};
StringMorph.prototype.clearSelection = function () {
+ if (!this.currentlySelecting &&
+ this.startMark === 0 &&
+ this.endMark === 0) {
+ return;
+ }
this.currentlySelecting = false;
this.startMark = 0;
this.endMark = 0;
@@ -7447,6 +7410,8 @@ StringMorph.prototype.mouseClickLeft = function (pos) {
StringMorph.prototype.enableSelecting = function () {
this.mouseDownLeft = function (pos) {
+ var crs = this.root().cursor,
+ already = crs ? crs.target === this : false;
this.clearSelection();
if (this.isEditable && (!this.isDraggable)) {
this.edit();
@@ -7454,6 +7419,7 @@ StringMorph.prototype.enableSelecting = function () {
this.startMark = this.slotAt(pos);
this.endMark = this.startMark;
this.currentlySelecting = true;
+ if (!already) {this.escalateEvent('mouseDownLeft', pos); }
}
};
this.mouseMove = function (pos) {
@@ -8060,7 +8026,7 @@ TriggerMorph.prototype.init = function (
this.environment = environment || null;
this.labelString = labelString || null;
this.label = null;
- this.hint = hint || null;
+ this.hint = hint || null; // null, String, or Function
this.fontSize = fontSize || MorphicPreferences.menuFontSize;
this.fontStyle = fontStyle || 'sans-serif';
this.highlightColor = new Color(192, 192, 192);
@@ -8131,20 +8097,6 @@ TriggerMorph.prototype.createLabel = function () {
this.add(this.label);
};
-// TriggerMorph duplicating:
-
-TriggerMorph.prototype.copyRecordingReferences = function (dict) {
- // inherited, see comment in Morph
- var c = TriggerMorph.uber.copyRecordingReferences.call(
- this,
- dict
- );
- if (c.label && dict[this.label]) {
- c.label = (dict[this.label]);
- }
- return c;
-};
-
// TriggerMorph action:
TriggerMorph.prototype.trigger = function () {
@@ -8209,10 +8161,11 @@ TriggerMorph.prototype.triggerDoubleClick = function () {
// TriggerMorph events:
TriggerMorph.prototype.mouseEnter = function () {
+ var contents = this.hint instanceof Function ? this.hint() : this.hint;
this.image = this.highlightImage;
this.changed();
- if (this.hint) {
- this.bubbleHelp(this.hint);
+ if (contents) {
+ this.bubbleHelp(contents);
}
};
@@ -8489,15 +8442,19 @@ FrameMorph.prototype.fullDrawOn = function (aCanvas, aRect) {
});
};
-// FrameMorph scrolling optimization:
+// FrameMorph navigation:
-FrameMorph.prototype.moveBy = function (delta) {
- this.changed();
- this.bounds = this.bounds.translateBy(delta);
- this.children.forEach(function (child) {
- child.silentMoveBy(delta);
- });
- this.changed();
+FrameMorph.prototype.topMorphAt = function (point) {
+ var i, result;
+ if (!(this.isVisible && this.bounds.containsPoint(point))) {
+ return null;
+ }
+ for (i = this.children.length - 1; i >= 0; i -= 1) {
+ result = this.children[i].topMorphAt(point);
+ if (result) {return result; }
+ }
+ return this.noticesTransparentClick ||
+ !this.isTransparentAt(point) ? this : null;
};
// FrameMorph scrolling support:
@@ -8589,20 +8546,6 @@ FrameMorph.prototype.reactToGrabOf = function () {
this.adjustBounds();
};
-// FrameMorph duplicating:
-
-FrameMorph.prototype.copyRecordingReferences = function (dict) {
- // inherited, see comment in Morph
- var c = FrameMorph.uber.copyRecordingReferences.call(
- this,
- dict
- );
- if (c.frame && dict[this.scrollFrame]) {
- c.frame = (dict[this.scrollFrame]);
- }
- return c;
-};
-
// FrameMorph menus:
FrameMorph.prototype.developersMenu = function () {
@@ -8947,32 +8890,23 @@ ScrollFrameMorph.prototype.mouseScroll = function (y, x) {
// ScrollFrameMorph duplicating:
-ScrollFrameMorph.prototype.copyRecordingReferences = function (dict) {
- // inherited, see comment in Morph
- var c = ScrollFrameMorph.uber.copyRecordingReferences.call(
- this,
- dict
- );
- if (c.contents && dict[this.contents]) {
- c.contents = (dict[this.contents]);
- }
- if (c.hBar && dict[this.hBar]) {
- c.hBar = (dict[this.hBar]);
- c.hBar.action = function (num) {
- c.contents.setPosition(
- new Point(c.left() - num, c.contents.position().y)
+ScrollFrameMorph.prototype.updateReferences = function (map) {
+ var myself = this;
+ ScrollFrameMorph.uber.updateReferences.call(this, map);
+ if (this.hBar) {
+ this.hBar.action = function (num) {
+ myself.contents.setPosition(
+ new Point(myself.left() - num, myself.contents.position().y)
);
};
}
- if (c.vBar && dict[this.vBar]) {
- c.vBar = (dict[this.vBar]);
- c.vBar.action = function (num) {
- c.contents.setPosition(
- new Point(c.contents.position().x, c.top() - num)
+ if (this.vBar) {
+ this.vBar.action = function (num) {
+ myself.contents.setPosition(
+ new Point(myself.contents.position().x, myself.top() - num)
);
};
}
- return c;
};
// ScrollFrameMorph menu:
@@ -9136,6 +9070,18 @@ ListMorph.prototype.setExtent = function (aPoint) {
ListMorph.uber.setExtent.call(this, aPoint);
};
+ListMorph.prototype.activeIndex = function () {
+ return this.listContents.children.indexOf(this.active);
+};
+
+ListMorph.prototype.activateIndex = function (idx) {
+ var item = this.listContents.children[idx];
+ if (!item) {return; }
+ item.image = item.pressImage;
+ item.changed();
+ item.trigger();
+};
+
// StringFieldMorph ////////////////////////////////////////////////////
// StringFieldMorph inherit from FrameMorph:
@@ -9232,20 +9178,6 @@ StringFieldMorph.prototype.mouseClickLeft = function (pos) {
}
};
-// StringFieldMorph duplicating:
-
-StringFieldMorph.prototype.copyRecordingReferences = function (dict) {
- // inherited, see comment in Morph
- var c = StringFieldMorph.uber.copyRecordingReferences.call(
- this,
- dict
- );
- if (c.text && dict[this.text]) {
- c.text = (dict[this.text]);
- }
- return c;
-};
-
// BouncerMorph ////////////////////////////////////////////////////////
// I am a Demo of a stepping custom Morph
@@ -9373,54 +9305,18 @@ HandMorph.prototype.changed = function () {
if (this.world !== null) {
b = this.fullBounds();
if (!b.extent().eq(new Point())) {
- this.world.broken.push(this.fullBounds().spread());
+ this.world.broken.push(b.spread());
}
}
-
};
-// HandMorph navigation:
-
-HandMorph.prototype.morphAtPointer = function () {
- var morphs = this.world.allChildren().slice(0).reverse(),
- myself = this,
- result = null;
-
- morphs.forEach(function (m) {
- if (m.visibleBounds().containsPoint(myself.bounds.origin) &&
- result === null &&
- m.isVisible &&
- (m.noticesTransparentClick ||
- (!m.isTransparentAt(myself.bounds.origin))) &&
- (!(m instanceof ShadowMorph)) &&
- m.allParents().every(function (each) {
- return each.isVisible;
- })) {
- result = m;
- }
- });
- if (result !== null) {
- return result;
- }
- return this.world;
-};
+HandMorph.prototype.fullChanged = HandMorph.prototype.changed;
-/*
- alternative - more elegant and possibly more
- performant - solution for morphAtPointer.
- Has some issues, commented out for now
+// HandMorph navigation:
HandMorph.prototype.morphAtPointer = function () {
- var myself = this;
- return this.world.topMorphSuchThat(function (m) {
- return m.visibleBounds().containsPoint(myself.bounds.origin) &&
- m.isVisible &&
- (m.noticesTransparentClick ||
- (! m.isTransparentAt(myself.bounds.origin))) &&
- (! (m instanceof ShadowMorph));
- });
+ return this.world.topMorphAt(this.bounds.origin) || this.world;
};
-*/
HandMorph.prototype.allMorphsAtPointer = function () {
var morphs = this.world.allChildren(),
@@ -9683,6 +9579,9 @@ HandMorph.prototype.processMouseMove = function (event) {
morph = this.morphToGrab.fullCopy();
morph.isTemplate = false;
morph.isDraggable = true;
+ if (morph.reactToTemplateCopy) {
+ morph.reactToTemplateCopy();
+ }
this.grab(morph);
this.grabOrigin = this.morphToGrab.situation();
}
@@ -9957,16 +9856,6 @@ HandMorph.prototype.destroyTemporaries = function () {
});
};
-// HandMorph dragging optimization
-
-HandMorph.prototype.moveBy = function (delta) {
- Morph.prototype.trackChanges = false;
- HandMorph.uber.moveBy.call(this, delta);
- Morph.prototype.trackChanges = true;
- this.fullChanged();
-};
-
-
// WorldMorph //////////////////////////////////////////////////////////
// I represent the <canvas> element
@@ -9995,6 +9884,7 @@ WorldMorph.prototype.init = function (aCanvas, fillPage) {
this.isDraggable = false;
this.currentKey = null; // currently pressed key code
this.worldCanvas = aCanvas;
+ this.noticesTransparentClick = true;
// additional properties:
this.stamp = Date.now(); // reference in multi-world setups
diff --git a/morphic.txt b/morphic.txt
index 042b7e5..11a323d 100755
--- a/morphic.txt
+++ b/morphic.txt
@@ -7,9 +7,9 @@
written by Jens Mönig
jens@moenig.org
- Copyright (C) 2012 by Jens Mönig
+ Copyright (C) 2015 by Jens Mönig
- this documentation last changed: April 07, 2013
+ this documentation last changed: June 26, 2015
This file is part of Snap!.
@@ -465,9 +465,15 @@
MyMorph.prototype.mouseMove = function(pos) {};
- The only optional parameter of such a method is a Point object
+ All of these methods have as optional parameter a Point object
indicating the current position of the Hand inside the World's
- coordinate system.
+ coordinate system. The
+
+ mouseMove(pos, button)
+
+ event method has an additional optional parameter indicating the
+ currently pressed mouse button, which is either 'left' or 'right'.
+ You can use this to let users interact with 3D environments.
Events may be "bubbled" up a morph's owner chain by calling
@@ -522,6 +528,12 @@
a duplicate of the template whose "isDraggable" flag is true and
whose "isTemplate" flag is false, in other words: a non-template.
+ When creating a copy from a template, the copy's
+
+ reactToTemplateCopy
+
+ is invoked, if it is present.
+
Dragging is indicated by adding a drop shadow to the morph in hand.
If a morph follows the hand without displaying a drop shadow it is
merely being moved about without changing its parent (owner morph),
@@ -817,13 +829,13 @@
// use context to paint stuff here
};
- If your new morph stores or references other morphs outside of the
- submorph tree in other properties, be sure to also override the
+ If your new morph stores or references to other morphs outside of
+ the submorph tree in other properties, be sure to also override the
default
- copyRecordingReferences()
+ updateReferences()
- method accordingly if you want it to support duplication.
+ method if you want it to support duplication.
(6) development and user modes
@@ -1015,16 +1027,17 @@
programming hero.
I have originally written morphic.js in Florian Balmer's Notepad2
- editor for Windows and later switched to Apple's Dashcode. I've also
- come to depend on both Douglas Crockford's JSLint, Mozilla's Firebug
- and Google's Chrome to get it right.
+ editor for Windows, later switched to Apple's Dashcode and later
+ still to Apple's Xcode. I've also come to depend on both Douglas
+ Crockford's JSLint, Mozilla's Firebug and Google's Chrome to get
+ it right.
IX. contributors
----------------------
Joe Otto found and fixed many early bugs and taught me some tricks.
Nathan Dinsmore contributed mouse wheel scrolling, cached
- background texture handling and countless bug fixes.
+ background texture handling, countless bug fixes and optimizations.
Ian Reynolds contributed backspace key handling for Chrome.
Davide Della Casa contributed performance optimizations for Firefox.
diff --git a/objects.js b/objects.js
index 14e68da..bf6b3b7 100644
--- a/objects.js
+++ b/objects.js
@@ -105,7 +105,7 @@ InspectorMorph, ListMorph, Math, MenuItemMorph, MenuMorph, Morph,
MorphicPreferences, MouseSensorMorph, Node, Object, PenMorph, Point,
Rectangle, ScrollFrameMorph, ShadowMorph, SliderButtonMorph,
SliderMorph, String, StringFieldMorph, StringMorph, TextMorph,
-TriggerMorph, WorldMorph, clone, contains, copy, degrees, detect,
+TriggerMorph, WorldMorph, contains, copy, degrees, detect,
document, getDocumentPositionOf, isNaN, isObject, isString, newCanvas,
nop, parseFloat, radians, standardSettings, touchScreenSettings,
useBlurredShadows, version, window, modules, IDE_Morph, VariableDialogMorph,
@@ -125,7 +125,7 @@ PrototypeHatBlockMorph*/
// Global stuff ////////////////////////////////////////////////////////
-modules.objects = '2015-February-28';
+modules.objects = '2015-June-25';
var SpriteMorph;
var StageMorph;
@@ -615,7 +615,7 @@ SpriteMorph.prototype.initBlocks = function () {
},
/* migrated to a newer block version:
-
+
receiveClick: {
type: 'hat',
category: 'control',
@@ -974,16 +974,6 @@ SpriteMorph.prototype.initBlocks = function () {
category: 'sensing',
spec: 'current acceleration along the z axes'
},
- reportLanguage: {
- type: 'reporter',
- category: 'sensing',
- spec: 'language'
- },
- reportLocation: {
- type: 'reporter',
- category: 'sensing',
- spec: 'location %locations'
- },
// Operators
reifyScript: {
@@ -1192,6 +1182,13 @@ SpriteMorph.prototype.initBlocks = function () {
spec: 'script variables %scriptVars'
},
+ // inheritance - experimental
+ doDeleteAttr: {
+ type: 'command',
+ category: 'variables',
+ spec: 'delete %shd'
+ },
+
// Lists
reportNewList: {
type: 'reporter',
@@ -1462,11 +1459,13 @@ SpriteMorph.prototype.init = function (globals) {
'confetti': 0
};
+ // sprite inheritance
+ this.exemplar = null;
+
SpriteMorph.uber.init.call(this);
this.isDraggable = true;
this.isDown = false;
-
this.heading = 90;
this.changed();
this.drawNew();
@@ -1759,7 +1758,8 @@ SpriteMorph.prototype.variableBlock = function (varName) {
SpriteMorph.prototype.blockTemplates = function (category) {
var blocks = [], myself = this, varNames, button,
- cat = category || 'motion', txt;
+ cat = category || 'motion', txt,
+ inheritedVars = this.inheritedVariableNames();
function block(selector) {
if (StageMorph.prototype.hiddenPrimitives[selector]) {
@@ -1774,6 +1774,9 @@ SpriteMorph.prototype.blockTemplates = function (category) {
var newBlock = SpriteMorph.prototype.variableBlock(varName);
newBlock.isDraggable = false;
newBlock.isTemplate = true;
+ if (contains(inheritedVars, varName)) {
+ newBlock.ghost();
+ }
return newBlock;
}
@@ -1822,15 +1825,18 @@ SpriteMorph.prototype.blockTemplates = function (category) {
}
function addVar(pair) {
+ var ide;
if (pair) {
- if (myself.variables.silentFind(pair[0])) {
+ if (myself.isVariableNameInUse(pair[0], pair[1])) {
myself.inform('that name is already in use');
} else {
+ ide = myself.parentThatIsA(IDE_Morph);
myself.addVariable(pair[0], pair[1]);
- myself.toggleVariableWatcher(pair[0], pair[1]);
- myself.blocksCache[cat] = null;
- myself.paletteCache[cat] = null;
- myself.parentThatIsA(IDE_Morph).refreshPalette();
+ if (!myself.showingVariableWatcher(pair[0])) {
+ myself.toggleVariableWatcher(pair[0], pair[1]);
+ }
+ ide.flushBlocksCache('variables'); // b/c of inheritance
+ ide.refreshPalette();
}
}
}
@@ -2058,9 +2064,6 @@ SpriteMorph.prototype.blockTemplates = function (category) {
blocks.push('-');
blocks.push(block('reportDate'));
blocks.push('-');
- blocks.push(block('reportLanguage'));
- blocks.push(block('reportLocation'));
- blocks.push('-');
blocks.push(block('doVibrate'));
blocks.push('-');
blocks.push(block('reportCompassHeading'));
@@ -2168,7 +2171,7 @@ SpriteMorph.prototype.blockTemplates = function (category) {
button.showHelp = BlockMorph.prototype.showHelp;
blocks.push(button);
- if (this.variables.allNames().length > 0) {
+ if (this.deletableVariableNames().length > 0) {
button = new PushButtonMorph(
null,
function () {
@@ -2177,7 +2180,7 @@ SpriteMorph.prototype.blockTemplates = function (category) {
null,
myself
);
- myself.variables.allNames().forEach(function (name) {
+ myself.deletableVariableNames().forEach(function (name) {
menu.addItem(name, name);
});
menu.popUpAtHand(myself.world());
@@ -2207,6 +2210,13 @@ SpriteMorph.prototype.blockTemplates = function (category) {
blocks.push(block('doHideVar'));
blocks.push(block('doDeclareVariables'));
+ // inheritance:
+
+ blocks.push('-');
+ blocks.push(block('doDeleteAttr'));
+
+ ///////////////////////////////
+
blocks.push('=');
blocks.push(block('reportNewList'));
@@ -2493,6 +2503,29 @@ SpriteMorph.prototype.freshPalette = function (category) {
}
});
+ // inherited custom blocks: (under construction...)
+
+ // y += unit * 1.6;
+ if (this.exemplar) {
+ this.inheritedBlocks(true).forEach(function (definition) {
+ var block;
+ if (definition.category === category ||
+ (category === 'variables'
+ && contains(
+ ['lists', 'other'],
+ definition.category
+ ))) {
+ block = definition.templateInstance();
+ y += unit * 0.3;
+ block.setPosition(new Point(x, y));
+ palette.addContents(block);
+ block.ghost();
+ x = 0;
+ y += block.height();
+ }
+ });
+ }
+
//layout
palette.scrollX(palette.padding);
@@ -2638,7 +2671,7 @@ SpriteMorph.prototype.searchBlocks = function () {
SpriteMorph.prototype.addVariable = function (name, isGlobal) {
var ide = this.parentThatIsA(IDE_Morph);
if (isGlobal) {
- this.variables.parentFrame.addVar(name);
+ this.globalVariables().addVar(name);
if (ide) {
ide.flushBlocksCache('variables');
}
@@ -2650,7 +2683,10 @@ SpriteMorph.prototype.addVariable = function (name, isGlobal) {
SpriteMorph.prototype.deleteVariable = function (varName) {
var ide = this.parentThatIsA(IDE_Morph);
- this.deleteVariableWatcher(varName);
+ if (!contains(this.inheritedVariableNames(true), varName)) {
+ // check only shadowed variables
+ this.deleteVariableWatcher(varName);
+ }
this.variables.deleteVar(varName);
if (ide) {
ide.flushBlocksCache('variables'); // b/c the var could be global
@@ -2864,7 +2900,7 @@ SpriteMorph.prototype.userMenu = function () {
};
SpriteMorph.prototype.exportSprite = function () {
- if (this.isCoone) {return; }
+ if (this.isClone) {return; }
var ide = this.parentThatIsA(IDE_Morph);
if (ide) {
ide.exportSprite(this);
@@ -2905,7 +2941,7 @@ SpriteMorph.prototype.remove = function () {
SpriteMorph.prototype.createClone = function () {
var stage = this.parentThatIsA(StageMorph);
- if (stage && stage.cloneCount <= 300) {
+ if (stage && stage.cloneCount <= 1000) {
this.fullCopy().clonify(stage);
}
};
@@ -3195,7 +3231,7 @@ SpriteMorph.prototype.applyGraphicsEffects = function (canvas) {
var i;
if (value !== 0) {
for (i = 0; i < p.length; i += 4) {
- p[i] += value; //255 = 100% of this color
+ p[i] += value; //255 = 100% of this color
p[i + 1] += value;
p[i + 2] += value;
}
@@ -3268,7 +3304,7 @@ SpriteMorph.prototype.setEffect = function (effect, value) {
if (eff === 'ghost') {
this.alpha = 1 - Math.min(Math.max(+value || 0, 0), 100) / 100;
} else {
- this.graphicsValues[eff] = value;
+ this.graphicsValues[eff] = +value;
}
this.drawNew();
this.changed();
@@ -3283,7 +3319,7 @@ SpriteMorph.prototype.changeEffect = function (effect, value) {
if (eff === 'ghost') {
this.setEffect(effect, this.getGhostEffect() + (+value || 0));
} else {
- this.setEffect(effect, this.graphicsValues[eff] + value);
+ this.setEffect(effect, +this.graphicsValues[eff] + (+value));
}
};
@@ -3477,7 +3513,7 @@ SpriteMorph.prototype.nestingBounds = function () {
// SpriteMorph motion primitives
-Morph.prototype.setPosition = function (aPoint, justMe) {
+SpriteMorph.prototype.setPosition = function (aPoint, justMe) {
// override the inherited default to make sure my parts follow
// unless it's justMe
var delta = aPoint.subtract(this.topLeft());
@@ -3694,7 +3730,10 @@ SpriteMorph.prototype.allHatBlocksFor = function (message) {
if (morph.selector) {
if (morph.selector === 'receiveMessage') {
event = morph.inputs()[0].evaluate();
- return event === message || (event instanceof Array);
+ return event === message
+ || (event instanceof Array
+ && message !== '__shout__go__'
+ && message !== '__clone__init__');
}
if (morph.selector === 'receiveGo') {
return message === '__shout__go__';
@@ -3853,6 +3892,7 @@ SpriteMorph.prototype.reportThreadCount = function () {
SpriteMorph.prototype.findVariableWatcher = function (varName) {
var stage = this.parentThatIsA(StageMorph),
+ globals = this.globalVariables(),
myself = this;
if (stage === null) {
return null;
@@ -3862,7 +3902,7 @@ SpriteMorph.prototype.findVariableWatcher = function (varName) {
function (morph) {
return morph instanceof WatcherMorph
&& (morph.target === myself.variables
- || morph.target === myself.variables.parentFrame)
+ || morph.target === globals)
&& morph.getter === varName;
}
);
@@ -3870,6 +3910,7 @@ SpriteMorph.prototype.findVariableWatcher = function (varName) {
SpriteMorph.prototype.toggleVariableWatcher = function (varName, isGlobal) {
var stage = this.parentThatIsA(StageMorph),
+ globals = this.globalVariables(),
watcher,
others;
if (stage === null) {
@@ -3889,12 +3930,12 @@ SpriteMorph.prototype.toggleVariableWatcher = function (varName, isGlobal) {
// if no watcher exists, create a new one
if (isNil(isGlobal)) {
- isGlobal = contains(this.variables.parentFrame.names(), varName);
+ isGlobal = contains(globals.names(), varName);
}
watcher = new WatcherMorph(
varName,
this.blockColor.variables,
- isGlobal ? this.variables.parentFrame : this.variables,
+ isGlobal ? globals : this.variables,
varName
);
watcher.setPosition(stage.position().add(10));
@@ -4320,6 +4361,171 @@ SpriteMorph.prototype.restoreLayers = function () {
this.layers = null;
};
+// SpriteMorph inheritance - general
+
+SpriteMorph.prototype.chooseExemplar = function () {
+ var stage = this.parentThatIsA(StageMorph),
+ myself = this,
+ other = stage.children.filter(function (m) {
+ return m instanceof SpriteMorph &&
+ (!contains(m.allExemplars(), myself));
+ }),
+ menu;
+ menu = new MenuMorph(
+ function (aSprite) {myself.setExemplar(aSprite); },
+ localize('current parent') +
+ ':\n' +
+ (this.exemplar ? this.exemplar.name : localize('none'))
+ );
+ other.forEach(function (eachSprite) {
+ menu.addItem(eachSprite.name, eachSprite);
+ });
+ menu.addLine();
+ menu.addItem(localize('none'), null);
+ menu.popUpAtHand(this.world());
+};
+
+SpriteMorph.prototype.setExemplar = function (another) {
+ var ide = this.parentThatIsA(IDE_Morph);
+ this.exemplar = another;
+ if (isNil(another)) {
+ this.variables.parentFrame = (this.globalVariables());
+ } else {
+ this.variables.parentFrame = (another.variables);
+ }
+ if (ide) {
+ ide.flushBlocksCache('variables');
+ ide.refreshPalette();
+ }
+};
+
+SpriteMorph.prototype.allExemplars = function () {
+ // including myself
+ var all = [],
+ current = this;
+ while (!isNil(current)) {
+ all.push(current);
+ current = current.exemplar;
+ }
+ return all;
+};
+
+SpriteMorph.prototype.specimens = function () {
+ // without myself
+ var myself = this;
+ return this.siblings().filter(function (m) {
+ return m instanceof SpriteMorph && (m.exemplar === myself);
+ });
+};
+
+SpriteMorph.prototype.allSpecimens = function () {
+ // without myself
+ var myself = this;
+ return this.siblings().filter(function (m) {
+ return m instanceof SpriteMorph && contains(m.allExemplars(), myself);
+ });
+};
+
+// SpriteMorph inheritance - variables
+
+SpriteMorph.prototype.isVariableNameInUse = function (vName, isGlobal) {
+ if (isGlobal) {
+ return contains(this.variables.allNames(), vName);
+ }
+ if (contains(this.variables.names(), vName)) {return true; }
+ return contains(this.globalVariables().names(), vName);
+};
+
+SpriteMorph.prototype.globalVariables = function () {
+ var current = this.variables.parentFrame;
+ while (current.owner) {
+ current = current.parentFrame;
+ }
+ return current;
+};
+
+SpriteMorph.prototype.shadowVar = function (name, value) {
+ var ide = this.parentThatIsA(IDE_Morph);
+ this.variables.addVar(name, value);
+ if (ide) {
+ ide.flushBlocksCache('variables');
+ ide.refreshPalette();
+ }
+};
+
+SpriteMorph.prototype.inheritedVariableNames = function (shadowedOnly) {
+ var names = [],
+ own = this.variables.names(),
+ current = this.variables.parentFrame;
+
+ function test(each) {
+ return shadowedOnly ? contains(own, each) : !contains(own, each);
+ }
+
+ while (current.owner instanceof SpriteMorph) {
+ names.push.apply(
+ names,
+ current.names().filter(test)
+ );
+ current = current.parentFrame;
+ }
+ return names;
+};
+
+SpriteMorph.prototype.deletableVariableNames = function () {
+ var locals = this.variables.names(),
+ inherited = this.inheritedVariableNames();
+ return locals.concat(
+ this.globalVariables().names().filter(
+ function (each) {
+ return !contains(locals, each) && !contains(inherited, each);
+ }
+ )
+ );
+};
+
+// SpriteMorph inheritance - custom blocks
+
+SpriteMorph.prototype.ownBlocks = function () {
+ var dict = {};
+ this.customBlocks.forEach(function (def) {
+ dict[def.blockSpec()] = def;
+ });
+ return dict;
+};
+
+SpriteMorph.prototype.allBlocks = function (valuesOnly) {
+ var dict = {};
+ this.allExemplars().reverse().forEach(function (sprite) {
+ sprite.customBlocks.forEach(function (def) {
+ dict[def.blockSpec()] = def;
+ });
+ });
+ if (valuesOnly) {
+ return Object.keys(dict).map(function (key) {return dict[key]; });
+ }
+ return dict;
+};
+
+SpriteMorph.prototype.inheritedBlocks = function (valuesOnly) {
+ var dict = {},
+ own = Object.keys(this.ownBlocks()),
+ others = this.allExemplars().reverse();
+ others.pop();
+ others.forEach(function (sprite) {
+ sprite.customBlocks.forEach(function (def) {
+ var spec = def.blockSpec();
+ if (!contains(own, spec)) {
+ dict[spec] = def;
+ }
+ });
+ });
+ if (valuesOnly) {
+ return Object.keys(dict).map(function (key) {return dict[key]; });
+ }
+ return dict;
+};
+
// SpriteMorph highlighting
SpriteMorph.prototype.addHighlight = function (oldHighlight) {
@@ -4603,55 +4809,11 @@ StageMorph.prototype.init = function (globals) {
this.acceptsDrops = false;
this.setColor(new Color(255, 255, 255));
this.fps = this.frameRate;
-
- this.initPeering();
-};
-
-StageMorph.prototype.initPeering = function (id) {
- var myself = this;
-
- if (window.peers) {
- // I don't know why, but it works.
- window.peers.forEach(function (oldpeer) {
- if (id != oldpeer.id) {
- oldpeer.destroy()
- }
- });
- }
-
- this.peer = new Peer(id, {
- host: 'snapmesh.herokuapp.com',
- port: 443,
- secure: true,
- path: '/'
- });
-
- this.peer.on('open', function (id) {
- myself.peerId = id;
- });
- this.peer.on('disconnected', function () {
- // peer.reconnect does not work (?) because 'id' is undefined
- if (!myself.peer.destroyed) {
- myself.initPeering(myself.peerId);
- }
- });
- this.peer.on('error', function (err) {
- console.log(err); // DEBUG
- });
-
- this.peer.on('connection', function (connection) {
- connection.on('open', function () {
- connection.on('data', function (data) {
- myself.newPeerMessage(data, connection.peer);
- });
- });
- });
-
- window.peers.push(this.peer);
};
StageMorph.prototype.newPeerMessage = function (data, peer) {
var ide = this.parentThatIsA(IDE_Morph);
+ if (!ide || !peer) return;
var myself = this;
var hats = [], model, message;
@@ -5282,7 +5444,7 @@ StageMorph.prototype.blockTemplates = function (category) {
function addVar(pair) {
if (pair) {
- if (myself.variables.silentFind(pair[0])) {
+ if (myself.isVariableNameInUse(pair[0])) {
myself.inform('that name is already in use');
} else {
myself.addVariable(pair[0], pair[1]);
@@ -5464,9 +5626,6 @@ StageMorph.prototype.blockTemplates = function (category) {
blocks.push('-');
blocks.push(block('reportDate'));
blocks.push('-');
- blocks.push(block('reportLanguage'));
- blocks.push(block('reportLocation'));
- blocks.push('-');
blocks.push(block('doVibrate'));
blocks.push('-');
blocks.push(block('reportCompassHeading'));
@@ -5608,9 +5767,7 @@ StageMorph.prototype.blockTemplates = function (category) {
blocks.push(block('doShowVar'));
blocks.push(block('doHideVar'));
blocks.push(block('doDeclareVariables'));
-
blocks.push('=');
-
blocks.push(block('reportNewList'));
blocks.push('-');
blocks.push(block('reportCONS'));
@@ -6033,6 +6190,18 @@ StageMorph.prototype.doubleDefinitionsFor
StageMorph.prototype.replaceDoubleDefinitionsFor
= SpriteMorph.prototype.replaceDoubleDefinitionsFor;
+// StageMorph inheritance support - variables
+
+StageMorph.prototype.isVariableNameInUse
+ = SpriteMorph.prototype.isVariableNameInUse;
+
+StageMorph.prototype.globalVariables
+ = SpriteMorph.prototype.globalVariables;
+
+StageMorph.prototype.inheritedVariableNames = function () {
+ return [];
+};
+
// SpriteBubbleMorph ////////////////////////////////////////////////////////
/*
@@ -6807,7 +6976,7 @@ Note.prototype.play = function () {
if (!this.oscillator.stop) {
this.oscillator.stop = this.oscillator.noteOff;
}
- this.oscillator.type = 0;
+ this.oscillator.type = 'sine';
this.oscillator.frequency.value =
Math.pow(2, (this.pitch - 69) / 12) * 440;
this.oscillator.connect(this.gainNode);
@@ -7257,32 +7426,51 @@ WatcherMorph.prototype.isGlobal = function (selector) {
// WatcherMorph slider accessing:
-WatcherMorph.prototype.setSliderMin = function (num) {
+WatcherMorph.prototype.setSliderMin = function (num, noUpdate) {
if (this.target instanceof VariableFrame) {
- this.sliderMorph.setSize(1);
- this.sliderMorph.setStart(num);
- this.sliderMorph.setSize(this.sliderMorph.rangeSize() / 5);
+ this.sliderMorph.setSize(1, noUpdate);
+ this.sliderMorph.setStart(num, noUpdate);
+ this.sliderMorph.setSize(this.sliderMorph.rangeSize() / 5, noUpdate);
}
};
-WatcherMorph.prototype.setSliderMax = function (num) {
+WatcherMorph.prototype.setSliderMax = function (num, noUpdate) {
if (this.target instanceof VariableFrame) {
- this.sliderMorph.setSize(1);
- this.sliderMorph.setStop(num);
- this.sliderMorph.setSize(this.sliderMorph.rangeSize() / 5);
+ this.sliderMorph.setSize(1, noUpdate);
+ this.sliderMorph.setStop(num, noUpdate);
+ this.sliderMorph.setSize(this.sliderMorph.rangeSize() / 5, noUpdate);
}
};
// WatcherMorph updating:
WatcherMorph.prototype.update = function () {
- var newValue,
- num;
+ var newValue, sprite, num;
+
if (this.target && this.getter) {
this.updateLabel();
if (this.target instanceof VariableFrame) {
newValue = this.target.vars[this.getter] ?
this.target.vars[this.getter].value : undefined;
+ if (newValue === undefined && this.target.owner) {
+ sprite = this.target.owner;
+ if (contains(sprite.inheritedVariableNames(), this.getter)) {
+ newValue = this.target.getVar(this.getter);
+ // ghost cell color
+ this.cellMorph.setColor(
+ SpriteMorph.prototype.blockColor.variables
+ .lighter(35)
+ );
+ } else {
+ this.destroy();
+ return;
+ }
+ } else {
+ // un-ghost the cell color
+ this.cellMorph.setColor(
+ SpriteMorph.prototype.blockColor.variables
+ );
+ }
} else {
newValue = this.target[this.getter]();
}
@@ -7367,7 +7555,11 @@ WatcherMorph.prototype.fixLayout = function () {
this.sliderMorph.button.pressColor.b += 100;
this.sliderMorph.setHeight(fontSize);
this.sliderMorph.action = function (num) {
- myself.target.vars[myself.getter].value = Math.round(num);
+ myself.target.setVar(
+ myself.getter,
+ Math.round(num),
+ myself.target.owner
+ );
};
this.add(this.sliderMorph);
}
diff --git a/paint.js b/paint.js
index 7e39671..af5d423 100644
--- a/paint.js
+++ b/paint.js
@@ -5,7 +5,7 @@
inspired by the Scratch paint editor.
written by Kartik Chandra
- Copyright (C) 2014 by Kartik Chandra
+ Copyright (C) 2015 by Kartik Chandra
This file is part of Snap!.
@@ -69,7 +69,7 @@
// Global stuff ////////////////////////////////////////////////////////
-modules.paint = '2014-September-29';
+modules.paint = '2015-June-25';
// Declarations
diff --git a/index.html b/snap.html
index d874cfb..9178719 100755
--- a/index.html
+++ b/snap.html
@@ -2,7 +2,7 @@
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
- <title>Snap! Build Your Own Blocks. Beta</title>
+ <title>Snap! Build Your Own Blocks. Development (OOP)</title>
<link rel="shortcut icon" href="favicon.ico">
<script type="text/javascript" src="morphic.js"></script>
<script type="text/javascript" src="widgets.js"></script>
diff --git a/store.js b/store.js
index b872d0b..49f25c5 100644
--- a/store.js
+++ b/store.js
@@ -61,7 +61,7 @@ SyntaxElementMorph, Variable*/
// Global stuff ////////////////////////////////////////////////////////
-modules.store = '2015-February-28';
+modules.store = '2015-June-25';
// XML_Serializer ///////////////////////////////////////////////////////
@@ -459,9 +459,17 @@ SnapSerializer.prototype.rawLoadProjectModel = function (xmlNode) {
myself.loadValue(model);
});
- // restore nesting associations
+ // restore inheritance and nesting associations
myself.project.stage.children.forEach(function (sprite) {
- var anchor;
+ var exemplar, anchor;
+ if (sprite.inheritanceInfo) { // only sprites can inherit
+ exemplar = myself.project.sprites[
+ sprite.inheritanceInfo.exemplar
+ ];
+ if (exemplar) {
+ sprite.setExemplar(exemplar);
+ }
+ }
if (sprite.nestingInfo) { // only sprites may have nesting info
anchor = myself.project.sprites[sprite.nestingInfo.anchor];
if (anchor) {
@@ -471,6 +479,7 @@ SnapSerializer.prototype.rawLoadProjectModel = function (xmlNode) {
}
});
myself.project.stage.children.forEach(function (sprite) {
+ delete sprite.inheritanceInfo;
if (sprite.nestingInfo) { // only sprites may have nesting info
sprite.nestingScale = +(sprite.nestingInfo.scale || sprite.scale);
delete sprite.nestingInfo;
@@ -491,7 +500,7 @@ SnapSerializer.prototype.rawLoadProjectModel = function (xmlNode) {
/* Watchers */
model.sprites.childrenNamed('watcher').forEach(function (model) {
- var watcher, color, target, hidden, extX, extY;
+ var watcher, color, target, hidden, extX, extY, vFrame;
color = myself.loadColor(model.attributes.color);
target = Object.prototype.hasOwnProperty.call(
@@ -512,14 +521,20 @@ SnapSerializer.prototype.rawLoadProjectModel = function (xmlNode) {
model.attributes,
'var'
)) {
- watcher = new WatcherMorph(
- model.attributes['var'],
- color,
- isNil(target) ? project.globalVariables
- : target.variables,
- model.attributes['var'],
- hidden
- );
+ vFrame = isNil(target) ? project.globalVariables
+ : target.variables;
+ if (Object.prototype.hasOwnProperty.call(
+ vFrame.vars,
+ model.attributes['var']
+ )) {
+ watcher = new WatcherMorph(
+ model.attributes['var'],
+ color,
+ vFrame,
+ model.attributes['var'],
+ hidden
+ );
+ }
} else {
watcher = new WatcherMorph(
localize(myself.watcherLabels[model.attributes.s]),
@@ -531,8 +546,8 @@ SnapSerializer.prototype.rawLoadProjectModel = function (xmlNode) {
}
watcher.setStyle(model.attributes.style || 'normal');
if (watcher.style === 'slider') {
- watcher.setSliderMin(model.attributes.min || '1');
- watcher.setSliderMax(model.attributes.max || '100');
+ watcher.setSliderMin(model.attributes.min || '1', true);
+ watcher.setSliderMax(model.attributes.max || '100', true);
}
watcher.setPosition(
project.stage.topLeft().add(new Point(
@@ -550,12 +565,29 @@ SnapSerializer.prototype.rawLoadProjectModel = function (xmlNode) {
if (extX) {
watcher.cellMorph.contentsMorph.setWidth(+extX);
}
- extY = model.attributes.extY;
- if (extY) {
- watcher.cellMorph.contentsMorph.setHeight(+extY);
+ watcher.setPosition(
+ project.stage.topLeft().add(new Point(
+ +model.attributes.x || 0,
+ +model.attributes.y || 0
+ ))
+ );
+ project.stage.add(watcher);
+ watcher.onNextStep = function () {this.currentValue = null; };
+
+ // set watcher's contentsMorph's extent if it is showing a list
+ // and if its monitor dimensions are given
+ if (watcher.currentValue instanceof List) {
+ extX = model.attributes.extX;
+ if (extX) {
+ watcher.cellMorph.contentsMorph.setWidth(+extX);
+ }
+ extY = model.attributes.extY;
+ if (extY) {
+ watcher.cellMorph.contentsMorph.setHeight(+extY);
+ }
+ // adjust my contentsMorph's handle position
+ watcher.cellMorph.contentsMorph.handle.drawNew();
}
- // adjust my contentsMorph's handle position
- watcher.cellMorph.contentsMorph.handle.drawNew();
}
});
this.objects = {};
@@ -639,9 +671,17 @@ SnapSerializer.prototype.loadSprites = function (xmlString, ide) {
myself.loadObject(sprite, model);
});
- // restore nesting associations
+ // restore inheritance and nesting associations
project.stage.children.forEach(function (sprite) {
- var anchor;
+ var exemplar, anchor;
+ if (sprite.inheritanceInfo) { // only sprites can inherit
+ exemplar = project.sprites[
+ sprite.inheritanceInfo.exemplar
+ ];
+ if (exemplar) {
+ sprite.setExemplar(exemplar);
+ }
+ }
if (sprite.nestingInfo) { // only sprites may have nesting info
anchor = project.sprites[sprite.nestingInfo.anchor];
if (anchor) {
@@ -651,6 +691,7 @@ SnapSerializer.prototype.loadSprites = function (xmlString, ide) {
}
});
project.stage.children.forEach(function (sprite) {
+ delete sprite.inheritanceInfo;
if (sprite.nestingInfo) { // only sprites may have nesting info
sprite.nestingScale = +(sprite.nestingInfo.scale || sprite.scale);
delete sprite.nestingInfo;
@@ -690,6 +731,7 @@ SnapSerializer.prototype.loadMediaModel = function (xmlNode) {
SnapSerializer.prototype.loadObject = function (object, model) {
// private
var blocks = model.require('blocks');
+ this.loadInheritanceInfo(object, model);
this.loadNestingInfo(object, model);
this.loadCostumes(object, model);
this.loadSounds(object, model);
@@ -699,6 +741,14 @@ SnapSerializer.prototype.loadObject = function (object, model) {
this.loadScripts(object.scripts, model.require('scripts'));
};
+SnapSerializer.prototype.loadInheritanceInfo = function (object, model) {
+ // private
+ var info = model.childNamed('inherit');
+ if (info) {
+ object.inheritanceInfo = info.attributes;
+ }
+};
+
SnapSerializer.prototype.loadNestingInfo = function (object, model) {
// private
var info = model.childNamed('nest');
@@ -782,7 +832,7 @@ SnapSerializer.prototype.loadCustomBlocks = function (
names = definition.parseSpec(definition.spec).filter(
function (str) {
- return str.charAt(0) === '%';
+ return str.charAt(0) === '%' && str.length > 1;
}
).map(function (str) {
return str.substr(1);
@@ -1467,6 +1517,7 @@ SpriteMorph.prototype.toXML = function (serializer) {
' draggable="@"' +
'%' +
' costume="@" color="@,@,@" pen="@" ~>' +
+ '%' + // inheritance info
'%' + // nesting info
'<costumes>%</costumes>' +
'<sounds>%</sounds>' +
@@ -1489,6 +1540,13 @@ SpriteMorph.prototype.toXML = function (serializer) {
this.color.b,
this.penPoint,
+ // inheritance info
+ this.exemplar
+ ? '<inherit exemplar="' +
+ this.exemplar.name
+ + '"/>'
+ : '',
+
// nesting info
this.anchor
? '<nest anchor="' +
diff --git a/threads.js b/threads.js
index 0e3df5c..feaf357 100644
--- a/threads.js
+++ b/threads.js
@@ -83,7 +83,7 @@ ArgLabelMorph, localize, XML_Element, hex_sha512*/
// Global stuff ////////////////////////////////////////////////////////
-modules.threads = '2015-February-28';
+modules.threads = '2015-June-25';
var ThreadManager;
var Process;
@@ -348,7 +348,7 @@ ThreadManager.prototype.findProcess = function (block) {
*/
Process.prototype = {};
-Process.prototype.contructor = Process;
+Process.prototype.constructor = Process;
Process.prototype.timeout = 500; // msecs after which to force yield
Process.prototype.isCatchingErrors = true;
@@ -1178,13 +1178,17 @@ Process.prototype.doDeclareVariables = function (varNames) {
Process.prototype.doSetVar = function (varName, value) {
var varFrame = this.context.variables,
name = varName;
-
if (name instanceof Context) {
if (name.expression.selector === 'reportGetVar') {
- name = name.expression.blockSpec;
+ name.variables.setVar(
+ name.expression.blockSpec,
+ value,
+ this.blockReceiver()
+ );
+ return;
}
}
- varFrame.setVar(name, value);
+ varFrame.setVar(name, value, this.blockReceiver());
};
Process.prototype.doChangeVar = function (varName, value) {
@@ -1193,10 +1197,15 @@ Process.prototype.doChangeVar = function (varName, value) {
if (name instanceof Context) {
if (name.expression.selector === 'reportGetVar') {
- name = name.expression.blockSpec;
+ name.variables.changeVar(
+ name.expression.blockSpec,
+ value,
+ this.blockReceiver()
+ );
+ return;
}
}
- varFrame.changeVar(name, value);
+ varFrame.changeVar(name, value, this.blockReceiver());
};
Process.prototype.reportGetVar = function () {
@@ -1241,7 +1250,7 @@ Process.prototype.doShowVar = function (varName) {
}
// if no watcher exists, create a new one
isGlobal = contains(
- this.homeContext.receiver.variables.parentFrame.names(),
+ this.homeContext.receiver.globalVariables().names(),
varName
);
if (isGlobal || target.owner) {
@@ -1324,6 +1333,8 @@ Process.prototype.doRemoveTemporaries = function () {
Process.prototype.sendPeerMessage = function (message, peer) {
var myself = this;
+ var stage = this.homeContext.receiver.parentThatIsA(StageMorph),
+ ide = this.homeContext.receiver.parentThatIsA(IDE_Morph);
if (peer instanceof List) {
peer.asArray().forEach(function (singlePeer) {
@@ -1331,10 +1342,12 @@ Process.prototype.sendPeerMessage = function (message, peer) {
});
return;
}
+ if (peer == ide.peer.id) {
+ stage.newPeerMessage(message, peer);
+ return;
+ }
- var stage = this.homeContext.receiver.parentThatIsA(StageMorph),
- ide = this.homeContext.receiver.parentThatIsA(IDE_Morph);
- var connection = stage.peer.connect(peer, {reliable: true});
+ var connection = ide.peer.connect(peer, {reliable: true});
connection.on('open', function () {
var data;
if (typeof message == "string") {
@@ -1352,11 +1365,11 @@ Process.prototype.reportPeerList = function () {
var myself = this;
if (!this.context.wait) {
- var stage = this.homeContext.receiver.parentThatIsA(StageMorph);
- stage.peer.listAllPeers(function (peers) {
+ this.context.wait = true;
+ var ide = this.homeContext.receiver.parentThatIsA(IDE_Morph);
+ ide.peer.listAllPeers(function (peers) {
myself.context.result = new List(peers);
});
- this.context.wait = true;
} else if (this.context.result) {
return this.context.result;
}
@@ -1366,8 +1379,25 @@ Process.prototype.reportPeerList = function () {
};
Process.prototype.reportPeerId = function () {
- var stage = this.homeContext.receiver.parentThatIsA(StageMorph);
- return stage.peerId;
+ var ide = this.homeContext.receiver.parentThatIsA(IDE_Morph);
+ return ide.peer.id;
+};
+
+// Process sprite inheritance primitives
+
+Process.prototype.doDeleteAttr = function (attrName) {
+ // currently only variables are deletable
+ var name = attrName,
+ rcvr = this.blockReceiver();
+
+ if (name instanceof Context) {
+ if (name.expression.selector === 'reportGetVar') {
+ name = name.expression.blockSpec;
+ }
+ }
+ if (contains(rcvr.inheritedVariableNames(true), name)) {
+ rcvr.deleteVariable(name);
+ }
};
// Process lists primitives
@@ -1402,6 +1432,8 @@ Process.prototype.doDeleteFromList = function (index, list) {
}
if (this.inputOption(index) === 'last') {
idx = list.length();
+ } else if (isNaN(+this.inputOption(index))) {
+ return null;
}
list.remove(idx);
};
@@ -1899,6 +1931,7 @@ Process.prototype.doAsk = function (data) {
isStage = this.blockReceiver() instanceof StageMorph,
activePrompter;
+ stage.keysPressed = {};
if (!this.prompter) {
activePrompter = detect(
stage.children,
@@ -2205,14 +2238,14 @@ Process.prototype.reportMonadic = function (fname, n) {
case 'ln':
result = Math.log(x);
break;
- case 'log':
- result = 0;
+ case 'log': // base 10
+ result = Math.log(x) / Math.LN10;
break;
case 'e^':
result = Math.exp(x);
break;
case '10^':
- result = 0;
+ result = Math.pow(10, x);
break;
default:
nop();
@@ -2268,6 +2301,9 @@ Process.prototype.reportJoinWords = function (aList) {
// Process string ops
Process.prototype.reportLetter = function (idx, string) {
+ if (string instanceof List) { // catch a common user error
+ return '';
+ }
var i = +(idx || 0),
str = (string || '').toString();
return str[i - 1] || '';
@@ -2762,104 +2798,31 @@ Process.prototype.reportTimer = function () {
return 0;
};
-Process.prototype.reportLanguage = function () {
- var ide = this.homeContext.receiver.parentThatIsA(IDE_Morph);
- if (ide) {
- var lang = ide.userLanguage;
- if (lang) {
- return lang;
- }
- }
- return 'en';
-};
-
-Process.prototype.reportLocation = function (name) {
- var myself = this;
-
- if (!myself.context.jsonpScript) {
- if (!navigator.geolocation) {
- myself.handleError({name: 'Location',
- message: 'navigator.geolocation is not supported'});
- }
- window.OSM_JSONP_Callback = function (data) {
- myself.context.json = data;
- window.OSM_JSONP_Callback = undefined;
- };
- myself.context.jsonpScript = document.createElement('script');
-
- navigator.geolocation.getCurrentPosition(
- function (pos) {
- var crd = pos.coords;
-
- myself.context.jsonpScript.src =
- 'https://nominatim.openstreetmap.org/reverse'
- + '?format=json&json_callback=OSM_JSONP_Callback'
- + '&zoom=18&addressdetails=1'
- + '&lat='
- + crd.latitude
- + '&lon='
- + crd.longitude;
- document.getElementsByTagName('HEAD')[0]
- .appendChild(myself.context.jsonpScript);
- },
- function (err) {
- console.log(err);
- myself.handleError({name: 'Location', message: err});
- },
- { enableHightAccuracy: true, timeout: 30000, maximumAge: 0}
- );
- } else {
- if (myself.context.json) {
- switch (myself.inputOption(name)) {
- case 'all':
- return myself.context.json.display_name;
- case 'state district':
- return myself.context.json.address.state_district;
- case 'house number':
- return myself.context.json.address.house_number;
- case 'licence':
- return myself.context.json.licence;
- case 'country':
- case 'state':
- case 'suburb':
- case 'city':
- case 'road':
- return myself.context.
- json.address[myself.inputOption(name)];
- }
- }
- }
-
- this.pushContext('doYield');
- this.pushContext();
-};
-
// Process Dates and times in Snap
-// Map block options to built-in functions
-var dateMap = {
- 'year' : 'getFullYear',
- 'month' : 'getMonth',
- 'date': 'getDate',
- 'day of week' : 'getDay',
- 'hour' : 'getHours',
- 'minute' : 'getMinutes',
- 'second' : 'getSeconds',
- 'time in milliseconds' : 'getTime'
-};
-
Process.prototype.reportDate = function (datefn) {
- var inputFn = this.inputOption(datefn),
- currDate = new Date(),
- func = dateMap[inputFn],
- result = currDate[func]();
+ var currDate, func, result,
+ inputFn = this.inputOption(datefn),
+ // Map block options to built-in functions
+ dateMap = {
+ 'year' : 'getFullYear',
+ 'month' : 'getMonth',
+ 'date': 'getDate',
+ 'day of week' : 'getDay',
+ 'hour' : 'getHours',
+ 'minute' : 'getMinutes',
+ 'second' : 'getSeconds',
+ 'time in milliseconds' : 'getTime'
+ };
if (!dateMap[inputFn]) { return ''; }
+ currDate = new Date();
+ func = dateMap[inputFn];
+ result = currDate[func]();
// Show months as 1-12 and days as 1-7
if (inputFn === 'month' || inputFn === 'day of week') {
result += 1;
}
-
return result;
};
@@ -3627,35 +3590,50 @@ VariableFrame.prototype.silentFind = function (name) {
return null;
};
-VariableFrame.prototype.setVar = function (name, value) {
-/*
- change the specified variable if it exists
- else throw an error, because variables need to be
- declared explicitly (e.g. through a "script variables" block),
- before they can be accessed.
-*/
+VariableFrame.prototype.setVar = function (name, value, sender) {
+ // change the specified variable if it exists
+ // else throw an error, because variables need to be
+ // declared explicitly (e.g. through a "script variables" block),
+ // before they can be accessed.
+ // if the found frame is inherited by the sender sprite
+ // shadow it (create an explicit one for the sender)
+ // before setting the value ("create-on-write")
+
var frame = this.find(name);
if (frame) {
- frame.vars[name].value = value;
+ if (sender instanceof SpriteMorph &&
+ (frame.owner instanceof SpriteMorph) &&
+ (sender !== frame.owner)) {
+ sender.shadowVar(name, value);
+ } else {
+ frame.vars[name].value = value;
+ }
}
};
-VariableFrame.prototype.changeVar = function (name, delta) {
-/*
- change the specified variable if it exists
- else throw an error, because variables need to be
- declared explicitly (e.g. through a "script variables" block,
- before they can be accessed.
-*/
+VariableFrame.prototype.changeVar = function (name, delta, sender) {
+ // change the specified variable if it exists
+ // else throw an error, because variables need to be
+ // declared explicitly (e.g. through a "script variables" block,
+ // before they can be accessed.
+ // if the found frame is inherited by the sender sprite
+ // shadow it (create an explicit one for the sender)
+ // before changing the value ("create-on-write")
+
var frame = this.find(name),
- value;
+ value,
+ newValue;
if (frame) {
value = parseFloat(frame.vars[name].value);
- if (isNaN(value)) {
- frame.vars[name].value = delta;
+ newValue = isNaN(value) ? delta : value + parseFloat(delta);
+ if (sender instanceof SpriteMorph &&
+ (frame.owner instanceof SpriteMorph) &&
+ (sender !== frame.owner)) {
+ sender.shadowVar(name, newValue);
} else {
- frame.vars[name].value = value + parseFloat(delta);
+ frame.vars[name].value = newValue;
}
+
}
};
diff --git a/tools.xml b/tools.xml
index 1f93675..a5d3794 100644
--- a/tools.xml
+++ b/tools.xml
@@ -1 +1 @@
-<blocks app="Snap! 4.0, http://snap.berkeley.edu" version="1"><block-definition s="map %&apos;function&apos; over %&apos;lists&apos;" type="reporter" category="lists"><header></header><code></code><inputs><input type="%repRing"></input><input type="%mult%l"></input></inputs><script><block s="doWarp"><script><block s="doDeclareVariables"><list><l>mapone</l><l>mapmany</l></list></block><block s="doSetVar"><l>mapone</l><block s="reifyScript"><script><block s="doIf"><custom-block s="empty? %l"><block var="data"/></custom-block><script><block s="doReport"><block s="reportNewList"><list></list></block></block></script></block><block s="doReport"><block s="reportCONS"><block s="evaluate"><block var="function"/><list><block s="reportListItem"><l>1</l><block var="data"/></block></list></block><block s="evaluate"><block var="mapone"/><list><block s="reportCDR"><block var="data"/></block></list></block></block></block></script><list><l>data</l></list></block></block><block s="doSetVar"><l>mapmany</l><block s="reifyScript"><script><block s="doIf"><custom-block s="empty? %l"><block s="reportListItem"><l>1</l><block var="data lists"/></block></custom-block><script><block s="doReport"><block s="reportNewList"><list></list></block></block></script></block><block s="doReport"><block s="reportCONS"><block s="evaluate"><block var="function"/><custom-block s="map %repRing over %mult%l"><block s="reifyReporter"><autolambda><block s="reportListItem"><l>1</l><l/></block></autolambda><list></list></block><list><block var="data lists"/></list></custom-block></block><block s="evaluate"><block var="mapmany"/><list><custom-block s="map %repRing over %mult%l"><block s="reifyReporter"><autolambda><block s="reportCDR"><l/></block></autolambda><list></list></block><list><block var="data lists"/></list></custom-block></list></block></block></block></script><list><l>data lists</l></list></block></block><block s="doIfElse"><custom-block s="empty? %l"><block s="reportCDR"><block var="lists"/></block></custom-block><script><block s="doReport"><block s="evaluate"><block var="mapone"/><list><block s="reportListItem"><l>1</l><block var="lists"/></block></list></block></block></script><script><block s="doReport"><block s="evaluate"><block var="mapmany"/><list><block var="lists"/></list></block></block></script></block></script></block></script></block-definition><block-definition s="empty? %&apos;data&apos;" type="predicate" category="lists"><header></header><code></code><inputs><input type="%l"></input></inputs><script><block s="doReport"><block s="reportEquals"><block var="data"/><block s="reportNewList"><list></list></block></block></block></script></block-definition><block-definition s="keep items such that %&apos;pred&apos; from %&apos;data&apos;" type="reporter" category="lists"><header></header><code></code><inputs><input type="%predRing"></input><input type="%l"></input></inputs><script><block s="doWarp"><script><block s="doIf"><custom-block s="empty? %l"><block var="data"/></custom-block><script><block s="doReport"><block s="reportNewList"><list></list></block></block></script></block><block s="doIfElse"><block s="evaluate"><block var="pred"/><list><block s="reportListItem"><l>1</l><block var="data"/></block></list></block><script><block s="doReport"><block s="reportCONS"><block s="reportListItem"><l>1</l><block var="data"/></block><custom-block s="keep items such that %predRing from %l"><block var="pred"/><block s="reportCDR"><block var="data"/></block></custom-block></block></block></script><script><block s="doReport"><custom-block s="keep items such that %predRing from %l"><block var="pred"/><block s="reportCDR"><block var="data"/></block></custom-block></block></script></block></script></block></script></block-definition><block-definition s="combine with %&apos;function&apos; items of %&apos;data&apos;" type="reporter" category="lists"><header></header><code></code><inputs><input type="%repRing"></input><input type="%l"></input></inputs><script><block s="doWarp"><script><block s="doIf"><custom-block s="empty? %l"><block s="reportCDR"><block var="data"/></block></custom-block><script><block s="doReport"><block s="reportListItem"><l>1</l><block var="data"/></block></block></script></block><block s="doReport"><block s="evaluate"><block var="function"/><list><block s="reportListItem"><l>1</l><block var="data"/></block><custom-block s="combine with %repRing items of %l"><block var="function"/><block s="reportCDR"><block var="data"/></block></custom-block></list></block></block></script></block></script></block-definition><block-definition s="if %&apos;test&apos; then %&apos;true&apos; else %&apos;false&apos;" type="reporter" category="control"><header></header><code></code><inputs><input type="%b"></input><input type="%anyUE"></input><input type="%anyUE"></input></inputs><script><block s="doIfElse"><block var="test"/><script><block s="doReport"><block s="evaluate"><block var="true"/><list></list></block></block></script><script><block s="doReport"><block s="evaluate"><block var="false"/><list></list></block></block></script></block></script></block-definition><block-definition s="for %&apos;i&apos; = %&apos;start&apos; to %&apos;end&apos; %&apos;action&apos;" type="command" category="control"><header></header><code></code><inputs><input type="%upvar"></input><input type="%n">1</input><input type="%n">10</input><input type="%cs"></input></inputs><script><block s="doDeclareVariables"><list><l>step</l><l>tester</l></list></block><block s="doIfElse"><block s="reportGreaterThan"><block var="start"/><block var="end"/></block><script><block s="doSetVar"><l>step</l><l>-1</l></block><block s="doSetVar"><l>tester</l><block s="reifyReporter"><autolambda><block s="reportLessThan"><block var="i"/><block var="end"/></block></autolambda><list></list></block></block></script><script><block s="doSetVar"><l>step</l><l>1</l></block><block s="doSetVar"><l>tester</l><block s="reifyReporter"><autolambda><block s="reportGreaterThan"><block var="i"/><block var="end"/></block></autolambda><list></list></block></block></script></block><block s="doSetVar"><l>i</l><block var="start"/></block><block s="doUntil"><block s="evaluate"><block var="tester"/><list></list></block><script><block s="doRun"><block var="action"/><list></list></block><block s="doChangeVar"><l>i</l><block var="step"/></block></script></block></script></block-definition><block-definition s="join words %&apos;words&apos;" type="reporter" category="operators"><header></header><code></code><inputs><input type="%mult%txt"></input></inputs><script><block s="doWarp"><script><block s="doIf"><custom-block s="empty? %l"><block s="reportCDR"><block var="words"/></block></custom-block><script><block s="doReport"><block s="reportListItem"><l>1</l><block var="words"/></block></block></script></block><block s="doReport"><block s="reportJoinWords"><list><block s="reportListItem"><l>1</l><block var="words"/></block><block s="reportJoinWords"><list><l> </l><block s="evaluate"><block s="reifyReporter"><autolambda><custom-block s="join words %mult%txt"><block s="reportCDR"><block var="words"/></block></custom-block></autolambda><list></list></block><list></list></block></list></block></list></block></block></script></block></script></block-definition><block-definition s="list $arrowRight sentence %&apos;data&apos;" type="reporter" category="operators"><header></header><code></code><inputs><input type="%l"></input></inputs><script><block s="doWarp"><script><block s="doReport"><custom-block s="combine with %repRing items of %l"><block s="reifyReporter"><autolambda><custom-block s="join words %mult%txt"><list><l></l><l></l></list></custom-block></autolambda><list></list></block><block var="data"/></custom-block></block></script></block></script></block-definition><block-definition s="sentence $arrowRight list %&apos;text&apos;" type="reporter" category="operators"><header></header><code></code><inputs><input type="%txt"></input></inputs><script><block s="doWarp"><script><block s="doReport"><block s="reportTextSplit"><block var="text"/><l><option>whitespace</option></l></block></block></script></block></script></block-definition><block-definition s="catch %&apos;tag&apos; %&apos;action&apos;" type="command" category="control"><header></header><code></code><inputs><input type="%upvar"></input><input type="%cs"></input></inputs><script><block s="doCallCC"><block s="reifyScript"><script><block s="doSetVar"><l>tag</l><block var="cont"/></block><block s="doRun"><block var="action"/><list></list></block></script><list><l>cont</l></list></block></block></script></block-definition><block-definition s="throw %&apos;cont&apos;" type="command" category="control"><header></header><code></code><inputs><input type="%s">catchtag</input></inputs><script><block s="doRun"><block var="cont"/><list></list></block></script></block-definition><block-definition s="catch %&apos;tag&apos; %&apos;value&apos;" type="reporter" category="control"><header></header><code></code><inputs><input type="%upvar"></input><input type="%anyUE"></input></inputs><script><block s="doCallCC"><block s="reifyScript"><script><block s="doSetVar"><l>tag</l><block var="cont"/></block><block s="doReport"><block s="evaluate"><block var="value"/><list></list></block></block></script><list><l>cont</l></list></block></block></script></block-definition><block-definition s="throw %&apos;tag&apos; %&apos;value&apos;" type="command" category="control"><header></header><code></code><inputs><input type="%s">catchtag</input><input type="%s"></input></inputs><script><block s="doRun"><block var="tag"/><list><block var="value"/></list></block></script></block-definition><block-definition s="for each %&apos;item&apos; of %&apos;data&apos; %&apos;action&apos;" type="command" category="lists"><header></header><code></code><inputs><input type="%upvar"></input><input type="%l"></input><input type="%cs"></input></inputs><script><block s="doUntil"><custom-block s="empty? %l"><block var="data"/></custom-block><script><block s="doSetVar"><l>item</l><block s="reportListItem"><l>1</l><block var="data"/></block></block><block s="doRun"><block var="action"/><list><block s="reportListItem"><l>1</l><block var="data"/></block></list></block><block s="doSetVar"><l>data</l><block s="reportCDR"><block var="data"/></block></block></script></block></script></block-definition><block-definition s="if %&apos;test&apos; do %&apos;action&apos; and pause all $pause-1-255-220-0" type="command" category="control"><header></header><code></code><inputs><input type="%boolUE"></input><input type="%cs"></input></inputs><script><block s="doDeclareVariables"><list><l>breakpoint</l></list></block><block s="doIf"><block s="evaluate"><block var="test"/><list></list></block><script><block s="doSetVar"><l>breakpoint</l><block var="test"/></block><block s="doShowVar"><l>breakpoint</l></block><block s="doRun"><block var="action"/><list></list></block><block s="doPauseAll"></block><block s="doHideVar"><l></l></block></script></block></script></block-definition><block-definition s="word $arrowRight list %&apos;word&apos;" type="reporter" category="operators"><header></header><code></code><inputs><input type="%txt"></input></inputs><script><block s="doWarp"><script><block s="doReport"><block s="reportTextSplit"><block var="word"/><l><option>letter</option></l></block></block></script></block></script></block-definition><block-definition s="ignore %&apos;x&apos;" type="command" category="control"><header></header><code></code><inputs><input type="%s"></input></inputs></block-definition><block-definition s="ask for %&apos;reporter&apos; from %&apos;sprite&apos;" type="reporter" category="sensing"><header></header><code></code><inputs><input type="%repRing"></input><input type="%txt"></input></inputs><script><block s="doReport"><block s="evaluate"><block s="reportAttributeOf"><block var="reporter"/><block var="sprite"/></block><list></list></block></block></script></block-definition><block-definition s="tell %&apos;sprite&apos; to %&apos;commands&apos;" type="command" category="sensing"><header></header><code></code><inputs><input type="%txt"></input><input type="%cs"></input></inputs><script><block s="doRun"><block s="reportAttributeOf"><block var="commands"/><block var="sprite"/></block><list></list></block></script></block-definition></blocks> \ No newline at end of file
+<blocks app="Snap! 4.0, http://snap.berkeley.edu" version="1"><block-definition s="map %&apos;function&apos; over %&apos;lists&apos;" type="reporter" category="lists"><header></header><code></code><inputs><input type="%repRing"></input><input type="%mult%l"></input></inputs><script><block s="doWarp"><script><block s="doDeclareVariables"><list><l>mapone</l><l>mapmany</l></list></block><block s="doSetVar"><l>mapone</l><block s="reifyScript"><script><block s="doIf"><custom-block s="empty? %l"><block var="data"/></custom-block><script><block s="doReport"><block s="reportNewList"><list></list></block></block></script></block><block s="doReport"><block s="reportCONS"><block s="evaluate"><block var="function"/><list><block s="reportListItem"><l>1</l><block var="data"/></block></list></block><block s="evaluate"><block var="mapone"/><list><block s="reportCDR"><block var="data"/></block></list></block></block></block></script><list><l>data</l></list></block></block><block s="doSetVar"><l>mapmany</l><block s="reifyScript"><script><block s="doIf"><custom-block s="empty? %l"><block s="reportListItem"><l>1</l><block var="data lists"/></block></custom-block><script><block s="doReport"><block s="reportNewList"><list></list></block></block></script></block><block s="doReport"><block s="reportCONS"><block s="evaluate"><block var="function"/><custom-block s="map %repRing over %mult%l"><block s="reifyReporter"><autolambda><block s="reportListItem"><l>1</l><l/></block></autolambda><list></list></block><list><block var="data lists"/></list></custom-block></block><block s="evaluate"><block var="mapmany"/><list><custom-block s="map %repRing over %mult%l"><block s="reifyReporter"><autolambda><block s="reportCDR"><l/></block></autolambda><list></list></block><list><block var="data lists"/></list></custom-block></list></block></block></block></script><list><l>data lists</l></list></block></block><block s="doIfElse"><custom-block s="empty? %l"><block s="reportCDR"><block var="lists"/></block></custom-block><script><block s="doReport"><block s="evaluate"><block var="mapone"/><list><block s="reportListItem"><l>1</l><block var="lists"/></block></list></block></block></script><script><block s="doReport"><block s="evaluate"><block var="mapmany"/><list><block var="lists"/></list></block></block></script></block></script></block></script></block-definition><block-definition s="empty? %&apos;data&apos;" type="predicate" category="lists"><header></header><code></code><inputs><input type="%l"></input></inputs><script><block s="doReport"><block s="reportEquals"><block var="data"/><block s="reportNewList"><list></list></block></block></block></script></block-definition><block-definition s="keep items such that %&apos;pred&apos; from %&apos;data&apos;" type="reporter" category="lists"><header></header><code></code><inputs><input type="%predRing"></input><input type="%l"></input></inputs><script><block s="doWarp"><script><block s="doIf"><custom-block s="empty? %l"><block var="data"/></custom-block><script><block s="doReport"><block s="reportNewList"><list></list></block></block></script></block><block s="doIfElse"><block s="evaluate"><block var="pred"/><list><block s="reportListItem"><l>1</l><block var="data"/></block></list></block><script><block s="doReport"><block s="reportCONS"><block s="reportListItem"><l>1</l><block var="data"/></block><custom-block s="keep items such that %predRing from %l"><block var="pred"/><block s="reportCDR"><block var="data"/></block></custom-block></block></block></script><script><block s="doReport"><custom-block s="keep items such that %predRing from %l"><block var="pred"/><block s="reportCDR"><block var="data"/></block></custom-block></block></script></block></script></block></script></block-definition><block-definition s="combine with %&apos;function&apos; items of %&apos;data&apos;" type="reporter" category="lists"><header></header><code></code><inputs><input type="%repRing"></input><input type="%l"></input></inputs><script><block s="doWarp"><script><block s="doIf"><custom-block s="empty? %l"><block s="reportCDR"><block var="data"/></block></custom-block><script><block s="doReport"><block s="reportListItem"><l>1</l><block var="data"/></block></block></script></block><block s="doReport"><block s="evaluate"><block var="function"/><list><block s="reportListItem"><l>1</l><block var="data"/></block><custom-block s="combine with %repRing items of %l"><block var="function"/><block s="reportCDR"><block var="data"/></block></custom-block></list></block></block></script></block></script></block-definition><block-definition s="if %&apos;test&apos; then %&apos;true&apos; else %&apos;false&apos;" type="reporter" category="control"><header></header><code></code><inputs><input type="%b"></input><input type="%anyUE"></input><input type="%anyUE"></input></inputs><script><block s="doIfElse"><block var="test"/><script><block s="doReport"><block s="evaluate"><block var="true"/><list></list></block></block></script><script><block s="doReport"><block s="evaluate"><block var="false"/><list></list></block></block></script></block></script></block-definition><block-definition s="for %&apos;i&apos; = %&apos;start&apos; to %&apos;end&apos; %&apos;action&apos;" type="command" category="control"><header></header><code></code><inputs><input type="%upvar"></input><input type="%n">1</input><input type="%n">10</input><input type="%cs"></input></inputs><script><block s="doDeclareVariables"><list><l>step</l><l>tester</l></list></block><block s="doIfElse"><block s="reportGreaterThan"><block var="start"/><block var="end"/></block><script><block s="doSetVar"><l>step</l><l>-1</l></block><block s="doSetVar"><l>tester</l><block s="reifyReporter"><autolambda><block s="reportLessThan"><block var="i"/><block var="end"/></block></autolambda><list></list></block></block></script><script><block s="doSetVar"><l>step</l><l>1</l></block><block s="doSetVar"><l>tester</l><block s="reifyReporter"><autolambda><block s="reportGreaterThan"><block var="i"/><block var="end"/></block></autolambda><list></list></block></block></script></block><block s="doSetVar"><l>i</l><block var="start"/></block><block s="doUntil"><block s="evaluate"><block var="tester"/><list></list></block><script><block s="doRun"><block var="action"/><list></list></block><block s="doChangeVar"><l>i</l><block var="step"/></block></script></block></script></block-definition><block-definition s="join words %&apos;words&apos;" type="reporter" category="operators"><header></header><code></code><inputs><input type="%mult%txt"></input></inputs><script><block s="doWarp"><script><block s="doIf"><custom-block s="empty? %l"><block s="reportCDR"><block var="words"/></block></custom-block><script><block s="doReport"><block s="reportListItem"><l>1</l><block var="words"/></block></block></script></block><block s="doReport"><block s="reportJoinWords"><list><block s="reportListItem"><l>1</l><block var="words"/></block><l> </l><custom-block s="join words %mult%txt"><block s="reportCDR"><block var="words"/></block></custom-block></list></block></block></script></block></script></block-definition><block-definition s="list $arrowRight sentence %&apos;data&apos;" type="reporter" category="operators"><header></header><code></code><inputs><input type="%l"></input></inputs><script><block s="doWarp"><script><block s="doReport"><custom-block s="join words %mult%txt"><block var="data"/></custom-block></block></script></block></script></block-definition><block-definition s="sentence $arrowRight list %&apos;text&apos;" type="reporter" category="operators"><header></header><code></code><inputs><input type="%txt"></input></inputs><script><block s="doWarp"><script><block s="doReport"><block s="reportTextSplit"><block var="text"/><l><option>whitespace</option></l></block></block></script></block></script></block-definition><block-definition s="catch %&apos;tag&apos; %&apos;action&apos;" type="command" category="control"><header></header><code></code><inputs><input type="%upvar"></input><input type="%cs"></input></inputs><script><block s="doCallCC"><block s="reifyScript"><script><block s="doSetVar"><l>tag</l><block var="cont"/></block><block s="doRun"><block var="action"/><list></list></block></script><list><l>cont</l></list></block></block></script></block-definition><block-definition s="throw %&apos;cont&apos;" type="command" category="control"><header></header><code></code><inputs><input type="%s">catchtag</input></inputs><script><block s="doRun"><block var="cont"/><list></list></block></script></block-definition><block-definition s="catch %&apos;tag&apos; %&apos;value&apos;" type="reporter" category="control"><header></header><code></code><inputs><input type="%upvar"></input><input type="%anyUE"></input></inputs><script><block s="doCallCC"><block s="reifyScript"><script><block s="doSetVar"><l>tag</l><block var="cont"/></block><block s="doReport"><block s="evaluate"><block var="value"/><list></list></block></block></script><list><l>cont</l></list></block></block></script></block-definition><block-definition s="throw %&apos;tag&apos; %&apos;value&apos;" type="command" category="control"><header></header><code></code><inputs><input type="%s">catchtag</input><input type="%s"></input></inputs><script><block s="doRun"><block var="tag"/><list><block var="value"/></list></block></script></block-definition><block-definition s="for each %&apos;item&apos; of %&apos;data&apos; %&apos;action&apos;" type="command" category="lists"><header></header><code></code><inputs><input type="%upvar"></input><input type="%l"></input><input type="%cs"></input></inputs><script><block s="doUntil"><custom-block s="empty? %l"><block var="data"/></custom-block><script><block s="doSetVar"><l>item</l><block s="reportListItem"><l>1</l><block var="data"/></block></block><block s="doRun"><block var="action"/><list><block s="reportListItem"><l>1</l><block var="data"/></block></list></block><block s="doSetVar"><l>data</l><block s="reportCDR"><block var="data"/></block></block></script></block></script></block-definition><block-definition s="if %&apos;test&apos; do %&apos;action&apos; and pause all $pause-1-255-220-0" type="command" category="control"><header></header><code></code><inputs><input type="%boolUE"></input><input type="%cs"></input></inputs><script><block s="doDeclareVariables"><list><l>breakpoint</l></list></block><block s="doIf"><block s="evaluate"><block var="test"/><list></list></block><script><block s="doSetVar"><l>breakpoint</l><block var="test"/></block><block s="doShowVar"><l>breakpoint</l></block><block s="doRun"><block var="action"/><list></list></block><block s="doPauseAll"></block><block s="doHideVar"><l></l></block></script></block></script></block-definition><block-definition s="word $arrowRight list %&apos;word&apos;" type="reporter" category="operators"><header></header><code></code><inputs><input type="%txt"></input></inputs><script><block s="doWarp"><script><block s="doReport"><block s="reportTextSplit"><block var="word"/><l><option>letter</option></l></block></block></script></block></script></block-definition><block-definition s="ignore %&apos;x&apos;" type="command" category="control"><header></header><code></code><inputs><input type="%s"></input></inputs></block-definition></blocks>
diff --git a/widgets.js b/widgets.js
index bb250a8..57e0697 100644
--- a/widgets.js
+++ b/widgets.js
@@ -7,7 +7,7 @@
written by Jens Mönig
jens@moenig.org
- Copyright (C) 2014 by Jens Mönig
+ Copyright (C) 2015 by Jens Mönig
This file is part of Snap!.
@@ -74,7 +74,7 @@ HTMLCanvasElement, fontHeight, SymbolMorph, localize, SpeechBubbleMorph,
ArrowMorph, MenuMorph, isString, isNil, SliderMorph, MorphicPreferences,
ScrollFrameMorph*/
-modules.widgets = '2014-February-13';
+modules.widgets = '2015-June-25';
var PushButtonMorph;
var ToggleButtonMorph;
@@ -493,7 +493,7 @@ function ToggleButtonMorph(
labelString,
query, // predicate/selector
environment,
- hint,
+ hint, // optional, String or Function
template, // optional, for cached background images
minWidth, // <num> optional, if specified label will left-align
hasPreview, // <bool> show press color on left edge (e.g. category)
@@ -560,12 +560,13 @@ ToggleButtonMorph.prototype.init = function (
// ToggleButtonMorph events
ToggleButtonMorph.prototype.mouseEnter = function () {
+ var contents = this.hint instanceof Function ? this.hint() : this.hint;
if (!this.state) {
this.image = this.highlightImage;
this.changed();
}
- if (this.hint) {
- this.bubbleHelp(this.hint);
+ if (contents) {
+ this.bubbleHelp(contents);
}
};
diff --git a/xml.js b/xml.js
index fb612ce..b80f520 100644..100755
--- a/xml.js
+++ b/xml.js
@@ -7,7 +7,7 @@
written by Jens Mönig
jens@moenig.org
- Copyright (C) 2014 by Jens Mönig
+ Copyright (C) 2015 by Jens Mönig
This file is part of Snap!.
@@ -57,15 +57,17 @@
Nathan Dinsmore contributed to the design and implemented a first
working version of a complete XMLSerializer. I have taken much of the
overall design and many of the functions and methods in this file from
- Nathan's fine original prototype.
+ Nathan's fine original prototype. Recently Nathan has once again
+ worked his magic on the parser and optimized it by an order of
+ magnitude.
*/
-/*global modules, isString, detect, Node, isNil*/
+/*global modules, detect, Node, isNil*/
// Global stuff ////////////////////////////////////////////////////////
-modules.xml = '2014-January-09';
+modules.xml = '2015-June-25';
// Declarations
@@ -85,7 +87,8 @@ function ReadStream(arrayOrString) {
// ReadStream constants:
-ReadStream.prototype.space = /[\s]/;
+ReadStream.prototype.nonSpace = /\S|$/g;
+ReadStream.prototype.nonWord = /[\s\>\/\=]|$/g;
// ReadStream accessing:
@@ -115,46 +118,26 @@ ReadStream.prototype.atEnd = function () {
// ReadStream accessing String contents:
-ReadStream.prototype.upTo = function (regex) {
- var i, start;
- if (!isString(this.contents)) {return ''; }
- i = this.contents.substr(this.index).search(regex);
- if (i === -1) {
- return '';
- }
- start = this.index;
- this.index += i;
- return this.contents.substring(start, this.index);
+ReadStream.prototype.upTo = function (str) {
+ var i = this.contents.indexOf(str, this.index);
+ return i === -1 ? '' : this.contents.slice(this.index, this.index = i);
};
-ReadStream.prototype.peekUpTo = function (regex) {
- if (!isString(this.contents)) {return ''; }
- var i = this.contents.substr(this.index).search(regex);
- if (i === -1) {
- return '';
- }
- return this.contents.substring(this.index, this.index + i);
+ReadStream.prototype.peekUpTo = function (str) {
+ var i = this.contents.indexOf(str, this.index);
+ return i === -1 ? '' : this.contents.slice(this.index, i);
};
ReadStream.prototype.skipSpace = function () {
- if (!isString(this.contents)) {return ''; }
- var ch = this.peek();
- while (this.space.test(ch) && ch !== '') {
- this.skip();
- ch = this.peek();
- }
+ this.nonSpace.lastIndex = this.index;
+ var result = this.nonSpace.exec(this.contents);
+ if (result) this.index = result.index;
};
ReadStream.prototype.word = function () {
- var i, start;
- if (!isString(this.contents)) {return ''; }
- i = this.contents.substr(this.index).search(/[\s\>\/\=]|$/);
- if (i === -1) {
- return '';
- }
- start = this.index;
- this.index += i;
- return this.contents.substring(start, this.index);
+ this.nonWord.lastIndex = this.index;
+ var result = this.nonWord.exec(this.contents);
+ return result ? this.contents.slice(this.index, this.index = result.index) : '';
};
// XML_Element ///////////////////////////////////////////////////////////
@@ -166,7 +149,7 @@ ReadStream.prototype.word = function () {
// XML_Element inherits from Node:
-XML_Element.prototype = new Node();
+XML_Element.prototype = Object.create(Node.prototype);
XML_Element.prototype.constructor = XML_Element;
XML_Element.uber = Node.prototype;
@@ -190,9 +173,7 @@ XML_Element.prototype.init = function (tag, contents, parent) {
XML_Element.uber.init.call(this);
// override inherited properties
- if (parent instanceof XML_Element) {
- parent.addChild(this);
- }
+ if (parent) parent.addChild(this);
};
// XML_Element DOM navigation: (aside from what's inherited from Node)
@@ -318,51 +299,18 @@ XML_Element.prototype.escape = function (string, ignoreQuotes) {
};
XML_Element.prototype.unescape = function (string) {
- var stream = new ReadStream(string),
- result = '',
- ch,
- esc;
-
- function nextPut(str) {
- result += str;
- stream.upTo(';');
- stream.skip();
- }
-
- while (!stream.atEnd()) {
- ch = stream.next();
- if (ch === '&') {
- esc = stream.peekUpTo(';');
- switch (esc) {
- case 'apos':
- nextPut('\'');
- break;
- case 'quot':
- nextPut('\"');
- break;
- case 'lt':
- nextPut('<');
- break;
- case 'gt':
- nextPut('>');
- break;
- case 'amp':
- nextPut('&');
- break;
- case '#xD':
- nextPut('\n');
- break;
- case '#126':
- nextPut('~');
- break;
- default:
- result += ch;
- }
- } else {
- result += ch;
+ return string.replace(/&(amp|apos|quot|lt|gt|#xD|#126);/g, function(_, name) {
+ switch (name) {
+ case 'amp': return '&';
+ case 'apos': return '\'';
+ case 'quot': return '"';
+ case 'lt': return '<';
+ case 'gt': return '>';
+ case '#xD': return '\n';
+ case '#126': return '~';
+ default: console.warn('unreachable');
}
- }
- return result;
+ });
};
// XML_Element parsing:
@@ -375,10 +323,7 @@ XML_Element.prototype.parseString = function (string) {
};
XML_Element.prototype.parseStream = function (stream) {
- var key,
- value,
- ch,
- child;
+ var key, value, ch, child;
// tag:
this.tag = stream.word();
@@ -395,9 +340,7 @@ XML_Element.prototype.parseStream = function (stream) {
stream.skipSpace();
ch = stream.next();
if (ch !== '"' && ch !== "'") {
- throw new Error(
- 'Expected single- or double-quoted attribute value'
- );
+ throw new Error('Expected single- or double-quoted attribute value');
}
value = stream.upTo(ch);
stream.skip(1);
@@ -407,7 +350,7 @@ XML_Element.prototype.parseStream = function (stream) {
}
// empty tag:
- if (stream.peek() === '/') {
+ if (ch === '/') {
stream.skip();
if (stream.next() !== '>') {
throw new Error('Expected ">" after "/" in empty tag');