Merge remote-tracking branch 'jmoenig/master'

dev
Manuel Menezes de Sequeira 2016-10-29 19:30:15 +01:00
commit 0492d299b4
13 zmienionych plików z 981 dodań i 278 usunięć

Wyświetl plik

@ -23,3 +23,6 @@ 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/>.
Want to use Snap! but scared by the open-source license? Get in touch with us,
we'll make it work.

146
blocks.js
Wyświetl plik

@ -145,11 +145,11 @@ radians, useBlurredShadows, SpeechBubbleMorph, modules, StageMorph,
fontHeight, TableFrameMorph, SpriteMorph, Context, ListWatcherMorph,
CellMorph, DialogBoxMorph, BlockInputFragmentMorph, PrototypeHatBlockMorph,
Costume, IDE_Morph, BlockDialogMorph, BlockEditorMorph, localize, isNil,
isSnapObject, copy, PushButtonMorph, SpriteIconMorph*/
isSnapObject, copy, PushButtonMorph, SpriteIconMorph, Process*/
// Global stuff ////////////////////////////////////////////////////////
modules.blocks = '2016-July-15';
modules.blocks = '2016-October-27';
var SyntaxElementMorph;
var BlockMorph;
@ -186,7 +186,7 @@ WorldMorph.prototype.customMorphs = function () {
/*
return [
new SymbolMorph(
'pipette',
'stepForward',
50,
new Color(250, 250, 250),
new Point(-1, -1),
@ -346,6 +346,7 @@ SyntaxElementMorph.prototype.init = function (silently) {
this.cachedClr = null;
this.cachedClrBright = null;
this.cachedClrDark = null;
this.cachedNormalColor = null; // for single-stepping
this.isStatic = false; // if true, I cannot be exchanged
SyntaxElementMorph.uber.init.call(this, silently);
@ -731,6 +732,20 @@ SyntaxElementMorph.prototype.setLabelColor = function (
});
};
SyntaxElementMorph.prototype.flash = function () {
if (!this.cachedNormalColor) {
this.cachedNormalColor = this.color;
this.setColor(this.activeHighlight);
}
};
SyntaxElementMorph.prototype.unflash = function () {
if (this.cachedNormalColor) {
var clr = this.cachedNormalColor;
this.cachedNormalColor = null;
this.setColor(clr);
}
};
// SyntaxElementMorph zebra coloring
@ -2332,7 +2347,7 @@ BlockMorph.prototype.userMenu = function () {
function () {
new DialogBoxMorph(
myself,
myself.setSpec,
myself.userSetSpec,
myself
).prompt(
"Variable name",
@ -2430,7 +2445,7 @@ BlockMorph.prototype.userMenu = function () {
}
return menu;
}
if (this.parentThatIsA(RingMorph)) {
if (this.parent.parentThatIsA(RingMorph)) {
menu.addLine();
menu.addItem("unringify", 'unringify');
menu.addItem("ringify", 'ringify');
@ -2527,10 +2542,10 @@ BlockMorph.prototype.deleteBlock = function () {
}
});
}
if (this instanceof ReporterBlockMorph) {
if (this.parent instanceof BlockMorph) {
this.parent.revertToDefaultInput(this);
}
if ((this.parent instanceof BlockMorph)
|| (this.parent instanceof MultiArgMorph)
|| (this.parent instanceof ReporterSlotMorph)) {
this.parent.revertToDefaultInput(this);
} else { // CommandBlockMorph
if (this.parent) {
if (this.parent.fixLayout) {
@ -2583,7 +2598,7 @@ BlockMorph.prototype.ringify = function () {
BlockMorph.prototype.unringify = function () {
// remove a Ring around me, if any
var ring = this.parentThatIsA(RingMorph),
var ring = this.parent.parentThatIsA(RingMorph),
top = this.topBlock(),
scripts = this.parentThatIsA(ScriptsMorph),
block,
@ -5835,7 +5850,8 @@ ArgMorph.prototype.reactToSliderEdit = function () {
}
if (receiver) {
stage = receiver.parentThatIsA(StageMorph);
if (stage && stage.isThreadSafe) {
if (stage && (stage.isThreadSafe ||
Process.prototype.enableSingleStepping)) {
stage.threads.startProcess(top, stage.isThreadSafe);
} else {
top.mouseClickLeft();
@ -7529,7 +7545,6 @@ InputSlotMorph.prototype.mouseDownLeft = function (pos) {
this.escalateEvent('mouseDownLeft', pos);
} else {
this.contents().edit();
this.contents().selectAll();
}
};
@ -7540,7 +7555,6 @@ InputSlotMorph.prototype.mouseClickLeft = function (pos) {
this.dropDownMenu();
} else {
this.contents().edit();
this.contents().selectAll();
}
};
@ -7558,6 +7572,12 @@ InputSlotMorph.prototype.reactToEdit = function () {
this.contents().clearSelection();
};
InputSlotMorph.prototype.freshTextEdit = function (aStringOrTextMorph) {
this.onNextStep = function () {
aStringOrTextMorph.selectAll();
};
};
// InputSlotMorph menu:
InputSlotMorph.prototype.userMenu = function () {
@ -7639,6 +7659,29 @@ InputSlotMorph.prototype.isEmptySlot = function () {
return this.contents().text === '';
};
// InputSlotMorph single-stepping:
InputSlotMorph.prototype.flash = function () {
// don't redraw the label b/c zebra coloring
if (!this.cachedNormalColor) {
this.cachedNormalColor = this.color;
this.color = this.activeHighlight;
this.drawNew();
this.changed();
}
};
InputSlotMorph.prototype.unflash = function () {
// don't redraw the label b/c zebra coloring
if (this.cachedNormalColor) {
var clr = this.cachedNormalColor;
this.cachedNormalColor = null;
this.color = clr;
this.drawNew();
this.changed();
}
};
// InputSlotMorph drawing:
InputSlotMorph.prototype.drawNew = function () {
@ -7647,13 +7690,15 @@ InputSlotMorph.prototype.drawNew = function () {
// initialize my surface property
this.image = newCanvas(this.extent());
context = this.image.getContext('2d');
if (this.parent) {
if (this.cachedNormalColor) { // if flashing
borderColor = this.color;
} else if (this.parent) {
borderColor = this.parent.color;
} else {
borderColor = new Color(120, 120, 120);
}
context.fillStyle = this.color.toString();
if (this.isReadOnly) {
if (this.isReadOnly && !this.cachedNormalColor) { // unless flashing
context.fillStyle = borderColor.darker().toString();
}
@ -7994,6 +8039,16 @@ TemplateSlotMorph.prototype.drawNew = function () {
TemplateSlotMorph.prototype.drawRounded = ReporterBlockMorph
.prototype.drawRounded;
// TemplateSlotMorph single-stepping
TemplateSlotMorph.prototype.flash = function () {
this.template().flash();
};
TemplateSlotMorph.prototype.unflash = function () {
this.template().unflash();
};
// BooleanSlotMorph ////////////////////////////////////////////////////
/*
@ -8142,7 +8197,10 @@ BooleanSlotMorph.prototype.drawNew = function (progress) {
this.fontSize + this.edge * 2
));
}
this.color = this.parent ? this.parent.color : new Color(200, 200, 200);
if (!(this.cachedNormalColor)) { // unless flashing
this.color = this.parent ?
this.parent.color : new Color(200, 200, 200);
}
this.cachedClr = this.color.toString();
this.cachedClrBright = this.bright();
this.cachedClrDark = this.dark();
@ -8162,15 +8220,19 @@ BooleanSlotMorph.prototype.drawDiamond = function (context, progress) {
gradient;
// draw the 'flat' shape:
switch (this.value) {
case true:
context.fillStyle = 'rgb(0, 200, 0)';
break;
case false:
context.fillStyle = 'rgb(200, 0, 0)';
break;
default:
context.fillStyle = this.color.darker(25).toString();
if (this.cachedNormalColor) { // if flashing
context.fillStyle = this.color.toString();
} else {
switch (this.value) {
case true:
context.fillStyle = 'rgb(0, 200, 0)';
break;
case false:
context.fillStyle = 'rgb(200, 0, 0)';
break;
default:
context.fillStyle = this.color.darker(25).toString();
}
}
if (progress && !this.isEmptySlot()) {
@ -8680,6 +8742,7 @@ SymbolMorph.uber = Morph.prototype;
SymbolMorph.prototype.names = [
'square',
'pointRight',
'stepForward',
'gears',
'file',
'fullScreen',
@ -8802,6 +8865,8 @@ SymbolMorph.prototype.symbolCanvasColored = function (aColor) {
return this.drawSymbolStop(canvas, aColor);
case 'pointRight':
return this.drawSymbolPointRight(canvas, aColor);
case 'stepForward':
return this.drawSymbolStepForward(canvas, aColor);
case 'gears':
return this.drawSymbolGears(canvas, aColor);
case 'file':
@ -8945,6 +9010,28 @@ SymbolMorph.prototype.drawSymbolPointRight = function (canvas, color) {
return canvas;
};
SymbolMorph.prototype.drawSymbolStepForward = function (canvas, color) {
// answer a canvas showing a right-pointing triangle
// followed by a vertical bar
var ctx = canvas.getContext('2d');
ctx.fillStyle = color.toString();
ctx.beginPath();
ctx.moveTo(0, 0);
ctx.lineTo(canvas.width * 0.75, Math.round(canvas.height / 2));
ctx.lineTo(0, canvas.height);
ctx.lineTo(0, 0);
ctx.closePath();
ctx.fill();
ctx.fillRect(
canvas.width * 0.75,
0,
canvas.width * 0.25,
canvas.height
);
return canvas;
};
SymbolMorph.prototype.drawSymbolGears = function (canvas, color) {
// answer a canvas showing gears
var ctx = canvas.getContext('2d'),
@ -10371,7 +10458,7 @@ MultiArgMorph.prototype.mouseClickLeft = function (pos) {
this.escalateEvent('mouseClickLeft', pos);
return;
}
// if the <shift> key is pressed, repeat action 5 times
// if the <shift> key is pressed, repeat action 3 times
var arrows = this.arrows(),
leftArrow = arrows.children[0],
rightArrow = arrows.children[1],
@ -12188,6 +12275,13 @@ ScriptFocusMorph.prototype.insertBlock = function (block) {
}
}
}
// experimental: if the inserted block has inputs, go to the first one
if (block.inputs && block.inputs().length) {
this.element = block;
this.atEnd = false;
this.nextElement();
}
};
ScriptFocusMorph.prototype.insertVariableGetter = function () {

48
byob.js
Wyświetl plik

@ -108,7 +108,7 @@ WatcherMorph, Variable*/
// Global stuff ////////////////////////////////////////////////////////
modules.byob = '2016-July-14';
modules.byob = '2016-September-27';
// Declarations
@ -150,6 +150,7 @@ function CustomBlockDefinition(spec, receiver) {
// don't serialize (not needed for functionality):
this.receiver = receiver || null; // for serialization only (pointer)
this.editorDimensions = null; // a rectangle, last bounds of the editor
this.cachedIsRecursive = null; // for automatic yielding
}
// CustomBlockDefinition instantiating blocks
@ -343,6 +344,26 @@ CustomBlockDefinition.prototype.parseSpec = function (spec) {
return parts;
};
CustomBlockDefinition.prototype.isDirectlyRecursive = function () {
var myspec;
if (this.cachedIsRecursive !== null) {
return this.cachedIsRecursive;
}
if (!this.body) {
this.cachedIsRecursive = false;
} else {
myspec = this.spec;
this.cachedIsRecursive = this.body.expression.anyChild(
function (morph) {
return morph instanceof BlockMorph &&
morph.definition &&
morph.definition.spec === myspec;
}
);
}
return this.cachedIsRecursive;
};
// CustomBlockDefinition picturing
CustomBlockDefinition.prototype.scriptsPicture = function () {
@ -769,7 +790,7 @@ CustomCommandBlockMorph.prototype.attachTargets = function () {
};
CustomCommandBlockMorph.prototype.isInUse = function () {
// anser true if an instance of my definition is found
// answer true if an instance of my definition is found
// in any of my receiver's scripts or block definitions
var def = this.definition,
ide = this.receiver().parentThatIsA(IDE_Morph);
@ -1558,7 +1579,7 @@ BlockDialogMorph.prototype.createScopeButtons = function () {
var myself = this;
this.addScopeButton(
function () {myself.setScope('gobal'); },
function () {myself.setScope('global'); },
"for all sprites",
function () {return myself.isGlobal; }
);
@ -1591,7 +1612,7 @@ BlockDialogMorph.prototype.addScopeButton = function (action, label, query) {
BlockDialogMorph.prototype.setScope = function (varType) {
this.isGlobal = (varType === 'gobal');
this.isGlobal = (varType === 'global');
this.scopes.children.forEach(function (c) {
c.refresh();
});
@ -1754,7 +1775,9 @@ function BlockEditorMorph(definition, target) {
}
BlockEditorMorph.prototype.init = function (definition, target) {
var scripts, proto, scriptsFrame, block, comment, myself = this;
var scripts, proto, scriptsFrame, block, comment, myself = this,
isLive = Process.prototype.enableLiveCoding ||
Process.prototype.enableSingleStepping;
// additional properties:
this.definition = definition;
@ -1789,7 +1812,9 @@ BlockEditorMorph.prototype.init = function (definition, target) {
comment.block = proto;
}
if (definition.body !== null) {
proto.nextBlock(definition.body.expression.fullCopy());
proto.nextBlock(isLive ? definition.body.expression
: definition.body.expression.fullCopy()
);
}
scripts.add(proto);
proto.fixBlockColor(null, true);
@ -1819,8 +1844,10 @@ BlockEditorMorph.prototype.init = function (definition, target) {
this.addBody(scriptsFrame);
this.addButton('ok', 'OK');
this.addButton('updateDefinition', 'Apply');
this.addButton('cancel', 'Cancel');
if (!isLive) {
this.addButton('updateDefinition', 'Apply');
this.addButton('cancel', 'Cancel');
}
this.setExtent(new Point(375, 300)); // normal initial extent
this.fixLayout();
@ -1962,6 +1989,7 @@ BlockEditorMorph.prototype.updateDefinition = function () {
this.definition.variableNames = this.variableNames();
this.definition.scripts = [];
this.definition.editorDimensions = this.bounds.copy();
this.definition.cachedIsRecursive = null; // flush the cache, don't update
this.body.contents.children.forEach(function (morph) {
if (morph instanceof PrototypeHatBlockMorph) {
@ -3282,7 +3310,7 @@ VariableDialogMorph.prototype.createTypeButtons = function () {
var myself = this;
this.addTypeButton(
function () {myself.setType('gobal'); },
function () {myself.setType('global'); },
"for all sprites",
function () {return myself.isGlobal; }
);
@ -3297,7 +3325,7 @@ VariableDialogMorph.prototype.addTypeButton
= BlockDialogMorph.prototype.addTypeButton;
VariableDialogMorph.prototype.setType = function (varType) {
this.isGlobal = (varType === 'gobal');
this.isGlobal = (varType === 'global');
this.types.children.forEach(function (c) {
c.refresh();
});

101
gui.js
Wyświetl plik

@ -68,11 +68,11 @@ fontHeight, hex_sha512, sb, CommentMorph, CommandBlockMorph,
BlockLabelPlaceHolderMorph, Audio, SpeechBubbleMorph, ScriptFocusMorph,
XML_Element, WatcherMorph, BlockRemovalDialogMorph, saveAs, TableMorph,
isSnapObject, isRetinaEnabled, disableRetinaSupport, enableRetinaSupport,
isRetinaSupported*/
isRetinaSupported, SliderMorph*/
// Global stuff ////////////////////////////////////////////////////////
modules.gui = '2016-August-12';
modules.gui = '2016-October-27';
// Declarations
@ -536,6 +536,7 @@ IDE_Morph.prototype.createControlBar = function () {
// assumes the logo has already been created
var padding = 5,
button,
slider,
stopButton,
pauseButton,
startButton,
@ -718,6 +719,23 @@ IDE_Morph.prototype.createControlBar = function () {
this.controlBar.add(startButton);
this.controlBar.startButton = startButton;
// steppingSlider
slider = new SliderMorph(
61,
1,
Process.prototype.flashTime * 100 + 1,
6,
'horizontal'
);
slider.action = function (num) {
Process.prototype.flashTime = (num - 1) / 100;
myself.controlBar.refreshResumeSymbol();
};
slider.alpha = MorphicPreferences.isFlat ? 0.1 : 0.3;
slider.setExtent(new Point(50, 14));
this.controlBar.add(slider);
this.controlBar.steppingSlider = slider;
// projectButton
button = new PushButtonMorph(
this,
@ -814,6 +832,9 @@ IDE_Morph.prototype.createControlBar = function () {
}
);
slider.setCenter(myself.controlBar.center());
slider.setRight(stageSizeButton.left() - padding);
settingsButton.setCenter(myself.controlBar.center());
settingsButton.setLeft(this.left());
@ -823,9 +844,41 @@ IDE_Morph.prototype.createControlBar = function () {
projectButton.setCenter(myself.controlBar.center());
projectButton.setRight(cloudButton.left() - padding);
this.refreshSlider();
this.updateLabel();
};
this.controlBar.refreshSlider = function () {
if (Process.prototype.enableSingleStepping && !myself.isAppMode) {
slider.drawNew();
slider.show();
} else {
slider.hide();
}
this.refreshResumeSymbol();
};
this.controlBar.refreshResumeSymbol = function () {
var pauseSymbols;
if (Process.prototype.enableSingleStepping &&
Process.prototype.flashTime > 0.5) {
myself.stage.threads.pauseAll(myself.stage);
pauseSymbols = [
new SymbolMorph('pause', 12),
new SymbolMorph('stepForward', 14)
];
} else {
pauseSymbols = [
new SymbolMorph('pause', 12),
new SymbolMorph('pointRight', 14)
];
}
pauseButton.labelString = pauseSymbols;
pauseButton.createLabel();
pauseButton.fixLayout();
pauseButton.refresh();
};
this.controlBar.updateLabel = function () {
var suffix = myself.world().isDevMode ?
' - ' + localize('development mode') : '';
@ -967,6 +1020,7 @@ IDE_Morph.prototype.createPalette = function (forSearching) {
}
this.palette.isDraggable = false;
this.palette.acceptsDrops = true;
this.palette.enableAutoScrolling = false;
this.palette.contents.acceptsDrops = false;
this.palette.reactToDropOf = function (droppedMorph) {
@ -1767,6 +1821,11 @@ IDE_Morph.prototype.toggleVariableFrameRate = function () {
}
};
IDE_Morph.prototype.toggleSingleStepping = function () {
this.stage.threads.toggleSingleStepping();
this.controlBar.refreshSlider();
};
IDE_Morph.prototype.startFastTracking = function () {
this.stage.isFastTracked = true;
this.stage.fps = 0;
@ -2551,6 +2610,14 @@ IDE_Morph.prototype.settingsMenu = function () {
'EXPERIMENTAL! check to enable\n live custom control structures',
true
);
addPreference(
'Visible stepping',
'toggleSingleStepping',
Process.prototype.enableSingleStepping,
'uncheck to turn off\nvisible stepping',
'check to turn on\n visible stepping (slow)',
false
);
menu.addLine(); // everything below this line is stored in the project
addPreference(
'Thread safe scripts',
@ -2982,7 +3049,7 @@ IDE_Morph.prototype.aboutSnap = function () {
module, btn1, btn2, btn3, btn4, licenseBtn, translatorsBtn,
world = this.world();
aboutTxt = 'Snap! 4.0.8.7\nBuild Your Own Blocks\n\n'
aboutTxt = 'Snap! 4.0.9\nBuild Your Own Blocks\n\n'
+ 'Copyright \u24B8 2016 Jens M\u00F6nig and '
+ 'Brian Harvey\n'
+ 'jens@moenig.org, bh@cs.berkeley.edu\n\n'
@ -3013,7 +3080,10 @@ IDE_Morph.prototype.aboutSnap = function () {
+ '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/';
+ 'If not, see http://www.gnu.org/licenses/\n\n'
+ 'Want to use Snap! but scared by the open-source license?\n'
+ 'Get in touch with us, we\'ll make it work.';
creditsTxt = localize('Contributors')
+ '\n\nNathan Dinsmore: Saving/Loading, Snap-Logo Design, '
@ -3021,8 +3091,10 @@ IDE_Morph.prototype.aboutSnap = function () {
+ '\nKartik Chandra: Paint Editor'
+ '\nMichael Ball: Time/Date UI, many bugfixes'
+ '\nBartosz Leper: Retina Display Support'
+ '\nBernat Romagosa: Countless contributions'
+ '\n"Ava" Yuan Yuan, Dylan Servilla: Graphic Effects'
+ '\nKyle Hotchkiss: Block search design'
+ '\nBrian Broll: Many bugfixes and optimizations'
+ '\nIan Reynolds: UI Design, Event Bindings, '
+ 'Sound primitives'
+ '\nIvan Motyashov: Initial Squeak Porting'
@ -5440,16 +5512,19 @@ ProjectDialogMorph.prototype.setSource = function (source) {
if (myself.task === 'open') {
src = localStorage['-snap-project-' + item.name];
xml = myself.ide.serializer.parse(src);
myself.notesText.text = xml.childNamed('notes').contents
|| '';
myself.notesText.drawNew();
myself.notesField.contents.adjustBounds();
myself.preview.texture = xml.childNamed('thumbnail').contents
|| null;
myself.preview.cachedTexture = null;
myself.preview.drawNew();
if (src) {
xml = myself.ide.serializer.parse(src);
myself.notesText.text = xml.childNamed('notes').contents
|| '';
myself.notesText.drawNew();
myself.notesField.contents.adjustBounds();
myself.preview.texture =
xml.childNamed('thumbnail').contents || null;
myself.preview.cachedTexture = null;
myself.preview.drawNew();
}
}
myself.edit();
};

Wyświetl plik

@ -3004,3 +3004,89 @@ http://snap.berkeley.edu/run#cloud:Username=jens&ProjectName=rotation
* Morphic: replace deprecated KeyboardEvent.keyIdentifier with .key
== v4.0.8.7 ====
*** in development ***
160915
------
* new single stepping feature (like Scratch 1.4) with flashing blocks
* slider for single-stepping speed
* pausing now flashes the currently active blocks
160916
------
* enable single stepping for clone-scripts w. multiple blocks flashing per script
* enable single stepping for custom block definitions
* Objects: fixed #1410 (duplicating a sprite does not duplicate its sounds)
160918
------
* Treat single-stepping as thread safe (reverted on 160923)
* Allow user to trigger one step at a time, both in normal and single-stepping mode
160919
------
* new “stepForward” symbol
* dragging the single-step speed slider all the way to the left turns the “resume” side of the “pause” button into “stepForward”
160920
------
* atomic synching of single-stepping
160921
------
* remove shift-click-to-forward-one-frame option for the “resume” button
160922
------
* renamed “single stepping” to “visible stepping”, thanks, Brian!
* updated German translation
160923
------
* custom block execution: only yield if directly recursive and unwrapped (“speed up”)
* new feature: “wait 0” or “wait <empty>” now yields once, unless warped
* revert treating visual stepping as thread safe, because of music scheduling
160924
------
* dont update the recursion cache when updating a custom block definition
160929
------
* Objects: fixed #1437
161007
------
* Blocks: [Keyboard-Entry] if an inserted block has inputs, go to the first one
161010
------
* Morphic: configure autoscrolling
* GUI: suppress autoscrolling for the palette and the project dialog
161011
------
* Blocks: make sure to fix multi-args when deleting a custom reporter definition
161011
------
* Objects: fixed #1456 (collect message names from all scripts, including custom block definitions)
161020
------
* Blocks: Tweak Keyboard-Entry
161021
------
* Threads: Fixed #1422
161024
------
* Text Editing Tweaks, thanks, Bernat!!
* Store: fixed #1472
* Threads: Tweak continuations
161027
------
== v4.0.9 ====

Wyświetl plik

@ -185,7 +185,7 @@ SnapTranslator.dict.de = {
'translator_e-mail':
'jens@moenig.org', // optional
'last_changed':
'2016-07-12', // this, too, will appear in the Translators tab
'2016-10-27', // this, too, will appear in the Translators tab
// GUI
// control bar:
@ -821,6 +821,8 @@ SnapTranslator.dict.de = {
'Tabellenunterstützung',
'Table lines':
'Tabellen mit Linien',
'Visible stepping':
'Programmausführung verfolgen',
'Thread safe scripts':
'Threadsicherheit',
'uncheck to allow\nscript reentrance':

Wyświetl plik

@ -1,27 +1,27 @@
/*
lang-de.js
lang-de.js
German translation for SNAP!
German translation for SNAP!
written by Jens Mönig
written by Jens Mönig
Copyright (C) 2012 by Jens Mönig
Copyright (C) 2012 by Jens Mönig
This file is part of Snap!.
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.
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.
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/>.
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/>.
@ -175,8 +175,10 @@ SnapTranslator.dict.fr = {
à \u00E0
É \u00C9
è \u00E8
é \u00E9
ê \u00EA
ç \u00E7
ï \u00EF
ô \u00F4
ù \u00F9
° \u00B0
@ -195,7 +197,7 @@ SnapTranslator.dict.fr = {
'translator_e-mail':
'i.scool@mac.com', // optional
'last_changed':
'2016-01-27', // this, too, will appear in the Translators tab
'2016-10-27', // this, too, will appear in the Translators tab
// GUI
// control bar:
@ -431,8 +433,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 am %interaction':
'Quand je suis %interaction',
'when I receive %msgHat':
'Quand je re\u00E7ois %msgHat',
'broadcast %msg':
@ -461,10 +463,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 %stopOthersChoices':
'arr\u00EAter %stopOthersChoices',
'stop %stopChoices':
'arr\u00EAter %stopChoices',
'stop all %stop':
'arr\u00EAter tout %stop',
'run %cmdRing %inputs':
@ -489,14 +491,14 @@ SnapTranslator.dict.fr = {
'supprime ce clone',
'pause all %pause':
'mettre en pause %pause',
'all but this script':
'tout sauf ce script',
'other scripts in sprite':
'les autres scripts de ce lutin',
'this script':
'ce script',
'this block':
'ce bloc',
'all but this script':
'tout sauf ce script',
'other scripts in sprite':
'les autres scripts de ce lutin',
'this script':
'ce script',
'this block':
'ce bloc',
// sensing:
'touching %col ?':
@ -527,6 +529,8 @@ SnapTranslator.dict.fr = {
'chronom\u00E8tre',
'%att of %spr':
'%att de %spr',
'my %get':
'attribut %get',
'http:// %s':
'http:// %s',
'turbo mode?':
@ -671,6 +675,8 @@ SnapTranslator.dict.fr = {
'Exporter le projet comme texte...',
'Export project...':
'Exporter le projet...',
'save project data as XML\nto your downloads folder':
'sauvegarder le projet au\nformat XML dans votre\ndossier T\u00E9l\u00E9chargements',
'show project data as XML\nin a new browser window':
'ouvrir le projet au format XML\ndans une nouvelle fen\u00EAtre de votre navigateur',
'Export blocks...':
@ -722,7 +728,7 @@ SnapTranslator.dict.fr = {
+ 'lors du glisser-d\u00E9poser d\u0027un reporter',
'uncheck to allow dropped\nreporters to kick out others':
'd\u00E9cocher pour ne pas pr\u00E9f\u00E9rer des entr\u00E9es vides \n'
+ 'lors du glisser-d\u00E9poser d\u0027un reporter',
+ 'lors du glisser-d\u00E9poser d\u0027un reporter',
'Long form input dialog':
'Bo\u00EEte d\u0027entr\u00E9e en mode d\u00E9taill\u00E9',
'check to always show slot\ntypes in the input dialog':
@ -745,10 +751,10 @@ SnapTranslator.dict.fr = {
'Cliquetis',
'uncheck to turn\nblock clicking\nsound off':
'd\u00E9cocher pour d\u00E9sactiver le cliquetis \n'
+'lors de l\u0027embo\u00EEtement des blocs' ,
+'lors de l\u0027embo\u00EEtement des blocs' ,
'check to turn\nblock clicking\nsound on':
'cocher pour activer le cliquetis \n'
+'lors de l\u0027embo\u00EEtement des blocs',
+'lors de l\u0027embo\u00EEtement des blocs',
'Turbo mode':
'Mode turbo',
'check to prioritize\nscript execution':
@ -760,7 +766,7 @@ SnapTranslator.dict.fr = {
'check for alternative\nGUI design':
'cocher pour un style d\'interface alternatif',
'uncheck for default\nGUI design':
'd\u00E9cocher pour le style classique d\'interface',
'd\u00E9cocher pour le style classique d\'interface',
'Keyboard Editing':
'\u00C9dition au clavier',
'uncheck to disable\nkeyboard editing support':
@ -771,10 +777,10 @@ SnapTranslator.dict.fr = {
'Scripts réentrants',
'check to disallow\nscript reentrance':
'cocher pour interdire\n la r\u00E9entrance des scripts\n'
+ 'et les ex\u00E9cuter s\u00E9par\u00E9ment',
+ 'et les ex\u00E9cuter s\u00E9par\u00E9ment',
'uncheck to allow\nscript reentrance':
'd\u00E9cocher pour permettre\n la r\u00E9entrance des scripts\n'
+ 'o\u00F9 certains s\'ex\u00E9cutent en paral\u00E8lle',
+ 'o\u00F9 certains s\'ex\u00E9cutent en paral\u00E8lle',
'Prefer smooth animations':
'Vitesse d\'animation fixe',
'uncheck for greater speed\nat variable frame rates':
@ -884,6 +890,12 @@ SnapTranslator.dict.fr = {
'Oui',
'No':
'Non',
'Open':
'Ouvrir',
'Browser':
'Navigateur',
'Examples':
'Exemples',
// help
'Help':
@ -1020,8 +1032,8 @@ SnapTranslator.dict.fr = {
'tabulations',
'cr':
'retours de ligne',
'letter':
'lettres',
'letter':
'lettres',
// About Snap
'About Snap':
@ -1095,8 +1107,28 @@ SnapTranslator.dict.fr = {
'Vide',
// graphical effects
'color':
'couleur',
'fisheye':
'fisheye',
'whirl':
'tourbillon',
'pixelate':
'pixelisation',
'mosaic':
'mosa\u00EFque',
'saturation':
'saturation',
'brightness':
'luminosit\u00E9',
'ghost':
'transparence',
'negative':
'n\u00E9gatif',
'comic':
'moir\u00E9',
'confetti':
'confetti',
// keys
'space':
@ -1230,145 +1262,145 @@ SnapTranslator.dict.fr = {
'any':
'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...',
// 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 permanance 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',
'Plain prototype labels':
'\u00C9tiquettes simples de d\u00E9finition',
'uncheck to always show (+) symbols\nin block prototype labels':
'd\u00E9cocher pour montrer en permanance 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',
'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',
'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',
'Inheritance support':
'Support de l\'h\u00E9ritage',
'current %dates':
'date courante %dates',
'year':
'current %dates':
'date courante %dates',
'year':
'ann\u00E9e',
'month':
'month':
'mois',
'date':
'date':
'jour',
'hour':
'hour':
'heure',
'minute':
'minute':
'minute',
'second':
'second':
'seconde',
'time in milliseconds':
'heure en millisecondes',
'day of week':
'jour de la semaine',
'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',
'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',
'clicked':
'cliqu\u00E9',
'pressed':
'press\u00E9',
'dropped':
'd\u00E9pos\u00E9',
'mouse-entered':
'survol\u00E9',
'mouse-departed':
'quitt\u00E9',
'when %b':
'Quand %b',
'JavaScript function ( %mult%s ) { %code }':
'fonction JavaScript ( %mult%s ) { %code }',
'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.',
// 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':
// Paint.js
'undo':
'd\u00E9faire',
'Paintbrush tool\n(free draw)':
'Pinceau\n(dessin \u00E0 main lev\u00E9e)',
'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':
'Fixer 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':
'Paintbrush tool\n(free draw)':
'Pinceau\n(dessin \u00E0 main lev\u00E9e)',
'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':
'Fixer 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':
'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)':
'Contraindre les proportions de la forme ?\n(vous pouvez aussi maintenir appuy\u00E9 Maj)'
'flip \u2194':
'miroir \u2194',
'flip \u2195':
'miroir \u2195',
'Brush size':
'Taille de pinceau',
'Constrain proportions of shapes?\n(you can also hold shift)':
'Contraindre les proportions de la forme ?\n(vous pouvez aussi maintenir appuy\u00E9 Maj)'
};

Wyświetl plik

@ -42,7 +42,7 @@
/*global modules, contains*/
modules.locale = '2016-July-14';
modules.locale = '2016-October-27';
// Global stuff
@ -160,7 +160,7 @@ SnapTranslator.dict.de = {
'translator_e-mail':
'jens@moenig.org',
'last_changed':
'2016-07-12'
'2016-10-27'
};
SnapTranslator.dict.it = {
@ -259,7 +259,7 @@ SnapTranslator.dict.fr = {
'translator_e-mail':
'i.scool@mac.com',
'last_changed':
'2016-02-24'
'2016-10-27'
};
SnapTranslator.dict.si = {

Wyświetl plik

@ -1103,7 +1103,7 @@
/*global window, HTMLCanvasElement, FileReader, Audio, FileList*/
var morphicVersion = '2016-August-12';
var morphicVersion = '2016-October-27';
var modules = {}; // keep track of additional loaded modules
var useBlurredShadows = getBlurredShadowSupport(); // check for Chrome-bug
@ -1245,6 +1245,11 @@ function fontHeight(height) {
return minHeight * 1.2; // assuming 1/5 font size for ascenders
}
function isWordChar(aCharacter) {
// can't use \b or \w because they ignore diacritics
return aCharacter.match(/[A-zÀ-ÿ0-9]/);
}
function newCanvas(extentPoint, nonRetina) {
// answer a new empty instance of Canvas, don't display anywhere
// nonRetina - optional Boolean "false"
@ -4960,7 +4965,8 @@ CursorMorph.prototype.init = function (aStringOrTextMorph) {
CursorMorph.prototype.initializeClipboardHandler = function () {
// Add hidden text box for copying and pasting
var myself = this;
var myself = this,
wrrld = this.target.world();
this.clipboardHandler = document.createElement('textarea');
this.clipboardHandler.style.position = 'absolute';
@ -4986,6 +4992,9 @@ CursorMorph.prototype.initializeClipboardHandler = function () {
'keydown',
function (event) {
myself.processKeyDown(event);
if (event.shiftKey) {
wrrld.currentKey = 16;
}
this.value = myself.target.selection();
this.select();
@ -4998,7 +5007,15 @@ CursorMorph.prototype.initializeClipboardHandler = function () {
},
false
);
this.clipboardHandler.addEventListener(
'keyup',
function (event) {
wrrld.currentKey = null;
},
false
);
this.clipboardHandler.addEventListener(
'input',
function (event) {
@ -5056,28 +5073,47 @@ CursorMorph.prototype.processKeyPress = function (event) {
CursorMorph.prototype.processKeyDown = function (event) {
// this.inspectKeyEvent(event);
var shift = event.shiftKey;
var shift = event.shiftKey,
wordNavigation = event.ctrlKey || event.altKey,
selecting = this.target.selection().length > 0;
this.keyDownEventUsed = false;
if (event.ctrlKey && (!event.altKey)) {
this.ctrl(event.keyCode, event.shiftKey);
// notify target's parent of key event
this.target.escalateEvent('reactToKeystroke', event);
return;
}
if (event.metaKey) {
this.cmd(event.keyCode, event.shiftKey);
// notify target's parent of key event
this.target.escalateEvent('reactToKeystroke', event);
return;
}
switch (event.keyCode) {
case 37:
this.goLeft(shift);
if (selecting && !shift && !wordNavigation) {
this.gotoSlot(Math.min(this.target.startMark, this.target.endMark));
this.target.clearSelection();
} else {
this.goLeft(
shift,
wordNavigation ?
this.slot - this.target.previousWordFrom(this.slot)
: 1);
}
this.keyDownEventUsed = true;
break;
case 39:
this.goRight(shift);
if (selecting && !shift && !wordNavigation) {
this.gotoSlot(Math.max(this.target.startMark, this.target.endMark));
this.target.clearSelection();
} else {
this.goRight(
shift,
wordNavigation ?
this.target.nextWordFrom(this.slot) - this.slot
: 1);
}
this.keyDownEventUsed = true;
break;
case 38:
@ -5168,9 +5204,9 @@ CursorMorph.prototype.gotoSlot = function (slot) {
}
};
CursorMorph.prototype.goLeft = function (shift) {
CursorMorph.prototype.goLeft = function (shift, howMany) {
this.updateSelection(shift);
this.gotoSlot(this.slot - 1);
this.gotoSlot(this.slot - (howMany || 1));
this.updateSelection(shift);
};
@ -5213,7 +5249,7 @@ CursorMorph.prototype.gotoPos = function (aPoint) {
CursorMorph.prototype.updateSelection = function (shift) {
if (shift) {
if (!this.target.endMark && !this.target.startMark) {
if (isNil(this.target.endMark) && isNil(this.target.startMark)) {
this.target.startMark = this.slot;
this.target.endMark = this.slot;
} else if (this.target.endMark !== this.slot) {
@ -7793,7 +7829,7 @@ StringMorph.prototype.renderWithBlanks = function (context, startX, y) {
});
};
// StringMorph mesuring:
// StringMorph measuring:
StringMorph.prototype.slotPosition = function (slot) {
// answer the position point of the given index ("slot")
@ -7818,8 +7854,10 @@ StringMorph.prototype.slotPosition = function (slot) {
};
StringMorph.prototype.slotAt = function (aPoint) {
// answer the slot (index) closest to the given point
// answer the slot (index) closest to the given point taking
// in account how far from the middle of the character it is,
// so the cursor can be moved accordingly
var txt = this.isPassword ?
this.password('*', this.text.length) : this.text,
idx = 0,
@ -7837,7 +7875,14 @@ StringMorph.prototype.slotAt = function (aPoint) {
}
}
}
return idx - 1;
// see where our click fell with respect to the middle of the char
if (aPoint.x - this.left() >
charX - context.measureText(txt[idx - 1]).width / 2) {
return idx;
} else {
return idx - 1;
}
};
StringMorph.prototype.upFrom = function (slot) {
@ -7860,6 +7905,41 @@ StringMorph.prototype.endOfLine = function () {
return this.text.length;
};
StringMorph.prototype.previousWordFrom = function (aSlot) {
// answer the slot (index) slots indicating the position of the
// previous word to the left of aSlot
var index = aSlot - 1;
// while the current character is non-word one, we skip it, so that
// if we are in the middle of a non-alphanumeric sequence, we'll get
// right to the beginning of the previous word
while (index > 0 && !isWordChar(this.text[index])) {
index -= 1;
}
// while the current character is a word one, we skip it until we
// find the beginning of the current word
while (index > 0 && isWordChar(this.text[index - 1])) {
index -= 1;
}
return index;
};
StringMorph.prototype.nextWordFrom = function (aSlot) {
var index = aSlot;
while (index < this.endOfLine() && !isWordChar(this.text[index])) {
index += 1;
}
while (index < this.endOfLine() && isWordChar(this.text[index])) {
index += 1;
}
return index;
};
StringMorph.prototype.rawHeight = function () {
// answer my corrected fontSize
return this.height() / 1.2;
@ -8025,13 +8105,13 @@ StringMorph.prototype.selectionStartSlot = function () {
StringMorph.prototype.clearSelection = function () {
if (!this.currentlySelecting &&
this.startMark === 0 &&
this.endMark === 0) {
isNil(this.startMark) &&
isNil(this.endMark)) {
return;
}
this.currentlySelecting = false;
this.startMark = 0;
this.endMark = 0;
this.startMark = null;
this.endMark = null;
this.drawNew();
this.changed();
};
@ -8047,8 +8127,13 @@ StringMorph.prototype.deleteSelection = function () {
};
StringMorph.prototype.selectAll = function () {
var cursor;
if (this.isEditable) {
this.startMark = 0;
cursor = this.root().cursor;
if (cursor) {
cursor.gotoSlot(this.text.length);
}
this.endMark = this.text.length;
this.drawNew();
this.changed();
@ -8056,13 +8141,31 @@ StringMorph.prototype.selectAll = function () {
};
StringMorph.prototype.mouseDownLeft = function (pos) {
if (this.isEditable) {
if (this.world().currentKey === 16) {
this.shiftClick(pos);
} else if (this.isEditable) {
this.clearSelection();
} else {
this.escalateEvent('mouseDownLeft', pos);
}
};
StringMorph.prototype.shiftClick = function (pos) {
var cursor = this.root().cursor;
if (cursor) {
if (!this.startMark) {
this.startMark = cursor.slot;
}
cursor.gotoPos(pos);
this.endMark = cursor.slot;
this.drawNew();
this.changed();
}
this.currentlySelecting = false;
this.escalateEvent('mouseDownLeft', pos);
};
StringMorph.prototype.mouseClickLeft = function (pos) {
var cursor;
if (this.isEditable) {
@ -8079,18 +8182,79 @@ StringMorph.prototype.mouseClickLeft = function (pos) {
}
};
StringMorph.prototype.mouseDoubleClick = function (pos) {
// selects the word at pos
// if there is no word, we select whatever is between
// the previous and next words
var slot = this.slotAt(pos);
if (this.isEditable) {
this.edit();
if (slot === this.text.length) {
slot -= 1;
}
if (isWordChar(this.text[slot])) {
this.selectWordAt(slot);
} else {
this.selectBetweenWordsAt(slot);
}
} else {
this.escalateEvent('mouseDoubleClick', pos);
}
};
StringMorph.prototype.selectWordAt = function (slot) {
var cursor = this.root().cursor;
if (slot === 0 || isWordChar(this.text[slot - 1])) {
cursor.gotoSlot(this.previousWordFrom(slot));
this.startMark = cursor.slot;
this.endMark = this.nextWordFrom(cursor.slot);
} else {
cursor.gotoSlot(slot);
this.startMark = slot;
this.endMark = this.nextWordFrom(slot);
}
this.drawNew();
this.changed();
};
StringMorph.prototype.selectBetweenWordsAt = function (slot) {
var cursor = this.root().cursor;
cursor.gotoSlot(this.nextWordFrom(this.previousWordFrom(slot)));
this.startMark = cursor.slot;
this.endMark = cursor.slot;
while (this.endMark < this.text.length
&& !isWordChar(this.text[this.endMark])) {
this.endMark += 1;
}
this.drawNew();
this.changed();
};
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();
this.root().cursor.gotoPos(pos);
this.startMark = this.slotAt(pos);
this.endMark = this.startMark;
this.currentlySelecting = true;
if (!already) {this.escalateEvent('mouseDownLeft', pos); }
if (this.world().currentKey === 16) {
this.shiftClick(pos);
} else {
this.clearSelection();
if (this.isEditable && (!this.isDraggable)) {
this.edit();
this.root().cursor.gotoPos(pos);
this.startMark = this.slotAt(pos);
this.endMark = this.startMark;
this.currentlySelecting = true;
if (!already) {this.escalateEvent('mouseDownLeft', pos); }
}
}
};
this.mouseMove = function (pos) {
@ -8410,8 +8574,10 @@ TextMorph.prototype.slotPosition = function (slot) {
};
TextMorph.prototype.slotAt = function (aPoint) {
// answer the slot (index) closest to the given point
// answer the slot (index) closest to the given point taking
// in account how far from the middle of the character it is,
// so the cursor can be moved accordingly
var charX = 0,
row = 0,
col = 0,
@ -8423,11 +8589,19 @@ TextMorph.prototype.slotAt = function (aPoint) {
row += 1;
}
row = Math.max(row, 1);
while (aPoint.x - this.left() > charX) {
charX += context.measureText(this.lines[row - 1][col]).width;
col += 1;
}
return this.lineSlots[Math.max(row - 1, 0)] + col - 1;
// see where our click fell with respect to the middle of the char
if (aPoint.x - this.left() >
charX - context.measureText(this.lines[row - 1][col]).width / 2) {
return this.lineSlots[Math.max(row - 1, 0)] + col;
} else {
return this.lineSlots[Math.max(row - 1, 0)] + col - 1;
}
};
TextMorph.prototype.upFrom = function (slot) {
@ -8469,6 +8643,10 @@ TextMorph.prototype.endOfLine = function (slot) {
this.lines[this.columnRow(slot).y].length - 1;
};
TextMorph.prototype.previousWordFrom = StringMorph.prototype.previousWordFrom;
TextMorph.prototype.nextWordFrom = StringMorph.prototype.nextWordFrom;
// TextMorph editing:
TextMorph.prototype.edit = StringMorph.prototype.edit;
@ -8486,8 +8664,17 @@ TextMorph.prototype.selectAll = StringMorph.prototype.selectAll;
TextMorph.prototype.mouseDownLeft = StringMorph.prototype.mouseDownLeft;
TextMorph.prototype.shiftClick = StringMorph.prototype.shiftClick;
TextMorph.prototype.mouseClickLeft = StringMorph.prototype.mouseClickLeft;
TextMorph.prototype.mouseDoubleClick = StringMorph.prototype.mouseDoubleClick;
TextMorph.prototype.selectWordAt = StringMorph.prototype.selectWordAt;
TextMorph.prototype.selectBetweenWordsAt
= StringMorph.prototype.selectBetweenWordsAt;
TextMorph.prototype.enableSelecting = StringMorph.prototype.enableSelecting;
TextMorph.prototype.disableSelecting = StringMorph.prototype.disableSelecting;
@ -9255,7 +9442,8 @@ ScrollFrameMorph.prototype.init = function (scroller, size, sliderColor) {
ScrollFrameMorph.uber.init.call(this);
this.scrollBarSize = size || MorphicPreferences.scrollBarSize;
this.autoScrollTrigger = null;
this.isScrollingByDragging = true; // change if desired
this.enableAutoScrolling = true; // change to suppress
this.isScrollingByDragging = true; // change to suppress
this.hasVelocity = true; // dto.
this.padding = 0; // around the scrollable area
this.growth = 0; // pixels or Point to grow right/left when near edge
@ -10300,7 +10488,12 @@ HandMorph.prototype.processMouseMove = function (event) {
// autoScrolling support:
if (myself.children.length > 0) {
if (newMorph instanceof ScrollFrameMorph) {
if (newMorph instanceof ScrollFrameMorph &&
newMorph.enableAutoScrolling &&
newMorph.contents.allChildren().some(function (any) {
return any.wantsDropOf(myself.children[0]);
})
) {
if (!newMorph.bounds.insetBy(
MorphicPreferences.scrollBarSize * 3
).containsPoint(myself.bounds.origin)) {
@ -10563,8 +10756,8 @@ WorldMorph.prototype.init = function (aCanvas, fillPage) {
this.broken = [];
this.hand = new HandMorph(this);
this.keyboardReceiver = null;
this.lastEditedText = null;
this.cursor = null;
this.lastEditedText = null;
this.activeMenu = null;
this.activeHandle = null;
this.virtualKeyboard = null;
@ -11328,9 +11521,6 @@ WorldMorph.prototype.edit = function (aStringOrTextMorph) {
if (this.cursor) {
this.cursor.destroy();
}
if (this.lastEditedText) {
this.lastEditedText.clearSelection();
}
this.cursor = new CursorMorph(aStringOrTextMorph);
aStringOrTextMorph.parent.add(this.cursor);
this.keyboardReceiver = this.cursor;
@ -11348,6 +11538,11 @@ WorldMorph.prototype.edit = function (aStringOrTextMorph) {
this.slide(aStringOrTextMorph);
}
}
if (this.lastEditedText !== aStringOrTextMorph) {
aStringOrTextMorph.escalateEvent('freshTextEdit', aStringOrTextMorph);
}
this.lastEditedText = aStringOrTextMorph;
};
WorldMorph.prototype.slide = function (aStringOrTextMorph) {
@ -11393,10 +11588,10 @@ WorldMorph.prototype.slide = function (aStringOrTextMorph) {
WorldMorph.prototype.stopEditing = function () {
if (this.cursor) {
this.lastEditedText = this.cursor.target;
this.cursor.target.escalateEvent('reactToEdit', this.cursor.target);
this.cursor.target.clearSelection();
this.cursor.destroy();
this.cursor = null;
this.lastEditedText.escalateEvent('reactToEdit', this.lastEditedText);
}
this.keyboardReceiver = null;
if (this.virtualKeyboard) {
@ -11404,6 +11599,7 @@ WorldMorph.prototype.stopEditing = function () {
document.body.removeChild(this.virtualKeyboard);
this.virtualKeyboard = null;
}
this.lastEditedText = null;
this.worldCanvas.focus();
};

Wyświetl plik

@ -82,7 +82,7 @@ SpeechBubbleMorph, RingMorph, isNil, FileReader, TableDialogMorph,
BlockEditorMorph, BlockDialogMorph, PrototypeHatBlockMorph, localize,
TableMorph, TableFrameMorph, normalizeCanvas, BooleanSlotMorph*/
modules.objects = '2016-July-19';
modules.objects = '2016-October-27';
var SpriteMorph;
var StageMorph;
@ -1409,9 +1409,11 @@ SpriteMorph.prototype.fullCopy = function (forClone) {
c.costumes = new List(arr);
arr = [];
this.sounds.asArray().forEach(function (sound) {
arr.push(sound);
var snd = forClone ? sound : sound.copy();
arr.push(snd);
});
c.sounds = new List(arr);
arr = [];
c.nestingScale = 1;
c.rotatesWithAnchor = true;
c.anchor = null;
@ -2950,8 +2952,10 @@ SpriteMorph.prototype.setColor = function (aColor) {
y = this.yPosition();
if (!this.color.eq(aColor)) {
this.color = aColor.copy();
this.drawNew();
this.gotoXY(x, y);
if (!this.costume) {
this.drawNew();
this.silentGotoXY(x, y);
}
}
};
@ -3050,17 +3054,21 @@ SpriteMorph.prototype.overlappingImage = function (otherSprite) {
SpriteMorph.prototype.doStamp = function () {
var stage = this.parent,
context = stage.penTrails().getContext('2d'),
isWarped = this.isWarped;
isWarped = this.isWarped,
originalAlpha = context.globalAlpha;
if (isWarped) {
this.endWarp();
}
context.save();
context.scale(1 / stage.scale, 1 / stage.scale);
context.globalAlpha = this.alpha;
context.drawImage(
this.image,
(this.left() - stage.left()),
(this.top() - stage.top())
);
context.globalAlpha = originalAlpha;
context.restore();
this.changed();
if (isWarped) {
@ -4000,14 +4008,33 @@ SpriteMorph.prototype.yCenter = function () {
// SpriteMorph message broadcasting
SpriteMorph.prototype.allMessageNames = function () {
var msgs = [];
this.scripts.allChildren().forEach(function (morph) {
var txt;
if (morph.selector) {
if (contains(
['receiveMessage', 'doBroadcast', 'doBroadcastAndWait'],
morph.selector
)) {
var msgs = [],
all = this.scripts.children.slice();
this.customBlocks.forEach(function (def) {
if (def.body) {
all.push(def.body.expression);
}
def.scripts.forEach(function (scr) {
all.push(scr);
});
});
if (this.globalBlocks) {
this.globalBlocks.forEach(function (def) {
if (def.body) {
all.push(def.body.expression);
}
def.scripts.forEach(function (scr) {
all.push(scr);
});
});
}
all.forEach(function (script) {
script.allChildren().forEach(function (morph) {
var txt;
if (morph.selector && contains(
['receiveMessage', 'doBroadcast', 'doBroadcastAndWait'],
morph.selector
)) {
txt = morph.inputs()[0].evaluate();
if (isString(txt) && txt !== '') {
if (!contains(msgs, txt)) {
@ -4015,7 +4042,7 @@ SpriteMorph.prototype.allMessageNames = function () {
}
}
}
}
});
});
return msgs;
};
@ -5435,7 +5462,7 @@ StageMorph.prototype.reactToDropOf = function (morph, hand) {
// StageMorph stepping
StageMorph.prototype.step = function () {
var current, elapsed, leftover, world = this.world();
var current, elapsed, leftover, ide, world = this.world();
// handle keyboard events
if (world.keyboardReceiver === null) {
@ -5471,6 +5498,14 @@ StageMorph.prototype.step = function () {
this.changed();
} else {
this.threads.step();
// single-stepping hook:
if (this.threads.wantsToPause) {
ide = this.parentThatIsA(IDE_Morph);
if (ide) {
ide.controlBar.pauseButton.refresh();
}
}
}
// update watchers
@ -6938,13 +6973,13 @@ Costume.prototype.edit = function (aWorld, anIDE, isnew, oncancel, onsubmit) {
function (img, rc) {
myself.contents = img;
myself.rotationCenter = rc;
if (anIDE.currentSprite instanceof SpriteMorph) {
// don't shrinkwrap stage costumes
myself.shrinkWrap();
}
myself.version = Date.now();
aWorld.changed();
if (anIDE) {
if (anIDE.currentSprite instanceof SpriteMorph) {
// don't shrinkwrap stage costumes
myself.shrinkWrap();
}
anIDE.currentSprite.wearCostume(myself);
anIDE.hasChangedMedia = true;
}

Wyświetl plik

@ -61,7 +61,7 @@ normalizeCanvas*/
// Global stuff ////////////////////////////////////////////////////////
modules.store = '2016-August-03';
modules.store = '2016-October-27';
// XML_Serializer ///////////////////////////////////////////////////////
@ -630,8 +630,8 @@ SnapSerializer.prototype.loadSprites = function (xmlString, ide) {
myself.objects[model.attributes.id] = sprite;
}
if (model.attributes.name) {
sprite.name = model.attributes.name;
project.sprites[model.attributes.name] = sprite;
sprite.name = ide.newSpriteName(model.attributes.name);
project.sprites[sprite.name] = sprite;
}
if (model.attributes.color) {
sprite.color = myself.loadColor(model.attributes.color);
@ -1619,8 +1619,8 @@ Costume.prototype.toXML = function (serializer) {
this.name,
this.rotationCenter.x,
this.rotationCenter.y,
this instanceof SVG_Costume ?
this.contents.src : this.contents.toDataURL('image/png')
this instanceof SVG_Costume ? this.contents.src
: normalizeCanvas(this.contents).toDataURL('image/png')
);
};

Wyświetl plik

@ -61,7 +61,7 @@ StageMorph, SpriteMorph, StagePrompterMorph, Note, modules, isString, copy,
isNil, WatcherMorph, List, ListWatcherMorph, alert, console, TableMorph,
TableFrameMorph, isSnapObject*/
modules.threads = '2016-August-12';
modules.threads = '2016-October-27';
var ThreadManager;
var Process;
@ -175,6 +175,7 @@ function invoke(
function ThreadManager() {
this.processes = [];
this.wantsToPause = false; // single stepping support
}
ThreadManager.prototype.pauseCustomHatBlocks = false;
@ -275,6 +276,25 @@ ThreadManager.prototype.step = function () {
// for sprites that are currently picked up, then filter out any
// processes that have been terminated
var isInterrupted;
if (Process.prototype.enableSingleStepping) {
this.processes.forEach(function (proc) {
if (proc.isInterrupted) {
proc.runStep();
isInterrupted = true;
} else {
proc.lastYield = Date.now();
}
});
this.wantsToPause = (Process.prototype.flashTime > 0.5);
if (isInterrupted) {
if (this.wantsToPause) {
this.pauseAll();
}
return;
}
}
this.processes.forEach(function (proc) {
if (!proc.homeContext.receiver.isPickedUp() && !proc.isDead) {
proc.runStep();
@ -290,6 +310,7 @@ ThreadManager.prototype.removeTerminatedProcesses = function () {
var result;
if ((!proc.isRunning() && !proc.errorFlag) || proc.isDead) {
if (proc.topBlock instanceof BlockMorph) {
proc.unflash();
proc.topBlock.removeHighlight();
}
if (proc.prompter) {
@ -371,6 +392,18 @@ ThreadManager.prototype.doWhen = function (block, stopIt) {
}
};
ThreadManager.prototype.toggleSingleStepping = function () {
Process.prototype.enableSingleStepping =
!Process.prototype.enableSingleStepping;
if (!Process.prototype.enableSingleStepping) {
this.processes.forEach(function (proc) {
if (!proc.isPaused) {
proc.unflash();
}
});
}
};
// Process /////////////////////////////////////////////////////////////
/*
@ -429,6 +462,8 @@ ThreadManager.prototype.doWhen = function (block, stopIt) {
procedureCount number counting procedure call entries,
used to tag custom block calls, so "stop block"
invocations can catch them
flashingContext for single stepping
isInterrupted boolean, indicates intra-step flashing of blocks
*/
Process.prototype = {};
@ -436,6 +471,8 @@ Process.prototype.constructor = Process;
Process.prototype.timeout = 500; // msecs after which to force yield
Process.prototype.isCatchingErrors = true;
Process.prototype.enableLiveCoding = false; // experimental
Process.prototype.enableSingleStepping = false; // experimental
Process.prototype.flashTime = 0; // experimental
function Process(topBlock, onComplete, rightAway) {
this.topBlock = topBlock || null;
@ -459,6 +496,8 @@ function Process(topBlock, onComplete, rightAway) {
this.exportResult = false;
this.onComplete = onComplete || null;
this.procedureCount = 0;
this.flashingContext = null; // experimental, for single-stepping
this.isInterrupted = false; // experimental, for single-stepping
if (topBlock) {
this.homeContext.receiver = topBlock.receiver();
@ -490,9 +529,10 @@ Process.prototype.runStep = function (deadline) {
if (this.isPaused) { // allow pausing in between atomic steps:
return this.pauseStep();
}
this.readyToYield = false;
while (!this.readyToYield
this.isInterrupted = false;
while (!this.readyToYield && !this.isInterrupted
&& this.context
&& (Date.now() - this.lastYield < this.timeout)
) {
@ -510,6 +550,7 @@ Process.prototype.runStep = function (deadline) {
}
this.evaluateContext();
}
this.lastYield = Date.now();
this.isFirstStep = false;
@ -544,13 +585,20 @@ Process.prototype.stop = function () {
};
Process.prototype.pause = function () {
if (this.readyToTerminate) {
return;
}
this.isPaused = true;
this.flashPausedContext();
if (this.context && this.context.startTime) {
this.pauseOffset = Date.now() - this.context.startTime;
}
};
Process.prototype.resume = function () {
if (!this.enableSingleStepping) {
this.unflash();
}
this.isPaused = false;
this.pauseOffset = null;
};
@ -586,7 +634,7 @@ Process.prototype.evaluateContext = function () {
return this.evaluateBlock(exp, exp.inputs().length);
}
if (isString(exp)) {
return this[exp]();
return this[exp].apply(this, this.context.inputs);
}
this.popContext(); // default: just ignore it
};
@ -607,6 +655,7 @@ Process.prototype.evaluateBlock = function (block, argCount) {
if (argCount > inputs.length) {
this.evaluateNextInput(block);
} else {
if (this.flashContext()) {return; } // yield to flash the block
if (this[selector]) {
rcvr = this;
}
@ -636,11 +685,13 @@ Process.prototype.reportOr = function (block) {
if (inputs.length < 1) {
this.evaluateNextInput(block);
} else if (inputs[0]) {
if (this.flashContext()) {return; }
this.returnValueToParentContext(true);
this.popContext();
} else if (inputs.length < 2) {
this.evaluateNextInput(block);
} else {
if (this.flashContext()) {return; }
this.returnValueToParentContext(inputs[1] === true);
this.popContext();
}
@ -652,11 +703,13 @@ Process.prototype.reportAnd = function (block) {
if (inputs.length < 1) {
this.evaluateNextInput(block);
} else if (!inputs[0]) {
if (this.flashContext()) {return; }
this.returnValueToParentContext(false);
this.popContext();
} else if (inputs.length < 2) {
this.evaluateNextInput(block);
} else {
if (this.flashContext()) {return; }
this.returnValueToParentContext(inputs[1] === true);
this.popContext();
}
@ -664,6 +717,7 @@ Process.prototype.reportAnd = function (block) {
Process.prototype.doReport = function (block) {
var outer = this.context.outerContext;
if (this.flashContext()) {return; } // flash the block here, special form
if (this.isClicked && (block.topBlock() === this.topBlock)) {
this.isShowingResult = true;
}
@ -736,6 +790,7 @@ Process.prototype.evaluateArgLabel = function (argLabel) {
Process.prototype.evaluateInput = function (input) {
// evaluate the input unless it is bound to an implicit parameter
var ans;
if (this.flashContext()) {return; } // yield to flash the current argMorph
if (input.bindingID) {
if (this.isCatchingErrors) {
try {
@ -879,7 +934,8 @@ Process.prototype.reify = function (topBlock, parameterNames, isCustomBlock) {
i = 0;
if (topBlock) {
context.expression = this.enableLiveCoding ?
context.expression = this.enableLiveCoding ||
this.enableSingleStepping ?
topBlock : topBlock.fullCopy();
context.expression.show(); // be sure to make visible if in app mode
@ -898,7 +954,8 @@ Process.prototype.reify = function (topBlock, parameterNames, isCustomBlock) {
}
} else {
context.expression = this.enableLiveCoding ? [this.context.expression]
context.expression = this.enableLiveCoding ||
this.enableSingleStepping ? [this.context.expression]
: [this.context.expression.fullCopy()];
}
@ -1182,6 +1239,15 @@ Process.prototype.reportCallCC = function (aContext) {
Process.prototype.runContinuation = function (aContext, args) {
var parms = args.asArray();
// determine whether the continuations is to show the result
// in a value-balloon becuse the user has directly clicked on a reporter
if (aContext.expression === 'expectReport' && parms.length) {
this.stop();
this.homeContext.inputs[0] = parms[0];
return;
}
this.context.parentContext = aContext.copyForContinuationCall();
// passing parameter if any was passed
if (parms.length === 1) {
@ -1286,8 +1352,9 @@ Process.prototype.evaluateCustomBlock = function () {
if (caller && !caller.tag) {
caller.tag = this.procedureCount;
}
// yield commands unless explicitly "warped"
if (!this.isAtomic) {
// yield commands unless explicitly "warped" or directly recursive
if (!this.isAtomic &&
this.context.expression.definition.isDirectlyRecursive()) {
this.readyToYield = true;
}
}
@ -1911,6 +1978,11 @@ Process.prototype.doWait = function (secs) {
this.context.startTime = Date.now();
}
if ((Date.now() - this.context.startTime) >= (secs * 1000)) {
if (!this.isAtomic && (secs === 0)) {
// "wait 0 secs" is a plain "yield"
// that can be overridden by "warp"
this.readyToYield = true;
}
return null;
}
this.pushContext('doYield');
@ -3276,6 +3348,67 @@ Process.prototype.reportFrameCount = function () {
return this.frameCount;
};
// Process single-stepping
Process.prototype.flashContext = function () {
var expr = this.context.expression;
if (this.enableSingleStepping &&
!this.isAtomic &&
expr instanceof SyntaxElementMorph &&
!(expr instanceof CommandSlotMorph) &&
!this.context.isFlashing &&
expr.world()) {
this.unflash();
expr.flash();
this.context.isFlashing = true;
this.flashingContext = this.context;
if (this.flashTime > 0 && (this.flashTime <= 0.5)) {
this.pushContext('doIdle');
this.context.addInput(this.flashTime);
} else {
this.pushContext('doInterrupt');
}
return true;
}
return false;
};
Process.prototype.flashPausedContext = function () {
var flashable = this.context ? this.context.lastFlashable() : null;
if (flashable) {
this.unflash();
flashable.expression.flash();
flashable.isFlashing = true;
this.flashingContext = flashable;
}
};
Process.prototype.doInterrupt = function () {
this.popContext();
if (!this.isAtomic) {
this.isInterrupted = true;
}
};
Process.prototype.doIdle = function (secs) {
if (!this.context.startTime) {
this.context.startTime = Date.now();
}
if ((Date.now() - this.context.startTime) < (secs * 1000)) {
this.pushContext('doInterrupt');
return;
}
this.popContext();
};
Process.prototype.unflash = function () {
if (this.flashingContext) {
this.flashingContext.expression.unflash();
this.flashingContext.isFlashing = false;
this.flashingContext = null;
}
};
// Context /////////////////////////////////////////////////////////////
/*
@ -3298,6 +3431,7 @@ Process.prototype.reportFrameCount = function () {
(if expression is a BlockMorph)
pc the index of the next block to evaluate
(if expression is an array)
isContinuation flag for marking a transient continuation context
startTime time when the context was first evaluated
startValue initial value for interpolated operations
activeAudio audio buffer for interpolated operations, don't persist
@ -3306,6 +3440,7 @@ Process.prototype.reportFrameCount = function () {
emptySlots caches the number of empty slots for reification
tag string or number to optionally identify the Context,
as a "return" target (for the "stop block" primitive)
isFlashing flag for single-stepping
*/
function Context(
@ -3325,12 +3460,14 @@ function Context(
}
this.inputs = [];
this.pc = 0;
this.isContinuation = false;
this.startTime = null;
this.activeAudio = null;
this.activeNote = null;
this.isCustomBlock = false; // marks the end of a custom block's stack
this.emptySlots = 0; // used for block reification
this.tag = null; // lexical catch-tag for custom blocks
this.isFlashing = false; // for single-stepping
}
Context.prototype.toString = function () {
@ -3394,7 +3531,9 @@ Context.prototype.continuation = function () {
} else if (this.parentContext) {
cont = this.parentContext;
} else {
return new Context(null, 'doStop');
cont = new Context(null, 'expectReport');
cont.isContinuation = true;
return cont;
}
cont = cont.copyForContinuation();
cont.tag = null;
@ -3464,6 +3603,19 @@ Context.prototype.stopMusic = function () {
}
};
// Context single-stepping:
Context.prototype.lastFlashable = function () {
// for experimental single-stepping when pausing
if (this.expression instanceof SyntaxElementMorph &&
!(this.expression instanceof CommandSlotMorph)) {
return this;
} else if (this.parentContext) {
return this.parentContext.lastFlashable();
}
return null;
};
// Context debugging
Context.prototype.stackSize = function () {

File diff suppressed because one or more lines are too long