kopia lustrzana https://github.com/backface/turtlestitch
Merge remote-tracking branch 'upstream/master'
commit
57bef2b736
43
blocks.js
43
blocks.js
|
@ -155,7 +155,7 @@ DialogBoxMorph, BlockInputFragmentMorph, PrototypeHatBlockMorph, Costume*/
|
||||||
|
|
||||||
// Global stuff ////////////////////////////////////////////////////////
|
// Global stuff ////////////////////////////////////////////////////////
|
||||||
|
|
||||||
modules.blocks = '2014-October-01';
|
modules.blocks = '2014-November-21';
|
||||||
|
|
||||||
|
|
||||||
var SyntaxElementMorph;
|
var SyntaxElementMorph;
|
||||||
|
@ -394,10 +394,10 @@ SyntaxElementMorph.prototype.allInputs = function () {
|
||||||
};
|
};
|
||||||
|
|
||||||
SyntaxElementMorph.prototype.allEmptySlots = function () {
|
SyntaxElementMorph.prototype.allEmptySlots = function () {
|
||||||
/*
|
// answer empty input slots of all children excluding myself,
|
||||||
answer empty input slots of all children excluding myself,
|
// but omit those in nested rings (lambdas) and JS-Function primitives.
|
||||||
but omit those in nested rings (lambdas) and JS-Function primitives
|
// Used by the evaluator when binding implicit formal parameters
|
||||||
*/
|
// to empty input slots
|
||||||
var empty = [];
|
var empty = [];
|
||||||
if (!(this instanceof RingMorph) &&
|
if (!(this instanceof RingMorph) &&
|
||||||
(this.selector !== 'reportJSFunction')) {
|
(this.selector !== 'reportJSFunction')) {
|
||||||
|
@ -412,6 +412,26 @@ SyntaxElementMorph.prototype.allEmptySlots = function () {
|
||||||
return empty;
|
return empty;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
SyntaxElementMorph.prototype.tagExitBlocks = function (stopTag, isCommand) {
|
||||||
|
// tag 'report' and 'stop this block' blocks of all children including
|
||||||
|
// myself, with either a stopTag (for "stop" blocks) or an indicator of
|
||||||
|
// being inside a command block definition, but omit those in nested
|
||||||
|
// rings (lambdas. Used by the evaluator when entering a procedure
|
||||||
|
if (this.selector === 'doReport') {
|
||||||
|
this.partOfCustomCommand = isCommand;
|
||||||
|
} else if (this.selector === 'doStopThis') {
|
||||||
|
this.exitTag = stopTag;
|
||||||
|
} else {
|
||||||
|
if (!(this instanceof RingMorph)) {
|
||||||
|
this.children.forEach(function (morph) {
|
||||||
|
if (morph.tagExitBlocks) {
|
||||||
|
morph.tagExitBlocks(stopTag, isCommand);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
SyntaxElementMorph.prototype.replaceInput = function (oldArg, newArg) {
|
SyntaxElementMorph.prototype.replaceInput = function (oldArg, newArg) {
|
||||||
var scripts = this.parentThatIsA(ScriptsMorph),
|
var scripts = this.parentThatIsA(ScriptsMorph),
|
||||||
replacement = newArg,
|
replacement = newArg,
|
||||||
|
@ -3169,9 +3189,14 @@ BlockMorph.prototype.snap = function () {
|
||||||
I inherit from BlockMorph adding the following most important
|
I inherit from BlockMorph adding the following most important
|
||||||
public accessors:
|
public accessors:
|
||||||
|
|
||||||
nextBlock() - set / get the block attached to my bottom
|
nextBlock() - set / get the block attached to my bottom
|
||||||
bottomBlock() - answer the bottom block of my stack
|
bottomBlock() - answer the bottom block of my stack
|
||||||
blockSequence() - answer an array of blocks starting with myself
|
blockSequence() - answer an array of blocks starting with myself
|
||||||
|
|
||||||
|
and the following "lexical awareness" indicators:
|
||||||
|
|
||||||
|
partOfCustomCommand - temporary bool set by the evaluator
|
||||||
|
exitTag - temporary string or number set by the evaluator
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// CommandBlockMorph inherits from BlockMorph:
|
// CommandBlockMorph inherits from BlockMorph:
|
||||||
|
@ -3189,6 +3214,8 @@ function CommandBlockMorph() {
|
||||||
CommandBlockMorph.prototype.init = function () {
|
CommandBlockMorph.prototype.init = function () {
|
||||||
CommandBlockMorph.uber.init.call(this);
|
CommandBlockMorph.uber.init.call(this);
|
||||||
this.setExtent(new Point(200, 100));
|
this.setExtent(new Point(200, 100));
|
||||||
|
this.partOfCustomCommand = false;
|
||||||
|
this.exitTag = null;
|
||||||
};
|
};
|
||||||
|
|
||||||
// CommandBlockMorph enumerating:
|
// CommandBlockMorph enumerating:
|
||||||
|
|
5
byob.js
5
byob.js
|
@ -106,7 +106,7 @@ SymbolMorph, isNil*/
|
||||||
|
|
||||||
// Global stuff ////////////////////////////////////////////////////////
|
// Global stuff ////////////////////////////////////////////////////////
|
||||||
|
|
||||||
modules.byob = '2014-September-30';
|
modules.byob = '2014-November-20';
|
||||||
|
|
||||||
// Declarations
|
// Declarations
|
||||||
|
|
||||||
|
@ -688,7 +688,8 @@ CustomCommandBlockMorph.prototype.labelPart = function (spec) {
|
||||||
return CustomCommandBlockMorph.uber.labelPart.call(this, spec);
|
return CustomCommandBlockMorph.uber.labelPart.call(this, spec);
|
||||||
}
|
}
|
||||||
if ((spec[0] === '%') && (spec.length > 1)) {
|
if ((spec[0] === '%') && (spec.length > 1)) {
|
||||||
part = new BlockInputFragmentMorph(spec.slice(1));
|
// part = new BlockInputFragmentMorph(spec.slice(1));
|
||||||
|
part = new BlockInputFragmentMorph(spec.replace(/%/g, ''));
|
||||||
} else {
|
} else {
|
||||||
part = new BlockLabelFragmentMorph(spec);
|
part = new BlockLabelFragmentMorph(spec);
|
||||||
part.fontSize = this.fontSize;
|
part.fontSize = this.fontSize;
|
||||||
|
|
Plik binarny nie jest wyświetlany.
Po Szerokość: | Wysokość: | Rozmiar: 7.9 KiB |
18
gui.js
18
gui.js
|
@ -69,7 +69,7 @@ SpeechBubbleMorph*/
|
||||||
|
|
||||||
// Global stuff ////////////////////////////////////////////////////////
|
// Global stuff ////////////////////////////////////////////////////////
|
||||||
|
|
||||||
modules.gui = '2014-October-06';
|
modules.gui = '2014-December-04';
|
||||||
|
|
||||||
// Declarations
|
// Declarations
|
||||||
|
|
||||||
|
@ -233,10 +233,6 @@ IDE_Morph.prototype.init = function (isAutoFill) {
|
||||||
IDE_Morph.prototype.openIn = function (world) {
|
IDE_Morph.prototype.openIn = function (world) {
|
||||||
var hash, usr, myself = this, urlLanguage = null;
|
var hash, usr, myself = this, urlLanguage = null;
|
||||||
|
|
||||||
this.buildPanes();
|
|
||||||
world.add(this);
|
|
||||||
world.userMenu = this.userMenu;
|
|
||||||
|
|
||||||
// get persistent user data, if any
|
// get persistent user data, if any
|
||||||
if (localStorage) {
|
if (localStorage) {
|
||||||
usr = localStorage['-snap-user'];
|
usr = localStorage['-snap-user'];
|
||||||
|
@ -245,10 +241,17 @@ IDE_Morph.prototype.openIn = function (world) {
|
||||||
if (usr) {
|
if (usr) {
|
||||||
SnapCloud.username = usr.username || null;
|
SnapCloud.username = usr.username || null;
|
||||||
SnapCloud.password = usr.password || null;
|
SnapCloud.password = usr.password || null;
|
||||||
|
if (SnapCloud.username) {
|
||||||
|
this.source = 'cloud';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.buildPanes();
|
||||||
|
world.add(this);
|
||||||
|
world.userMenu = this.userMenu;
|
||||||
|
|
||||||
// override SnapCloud's user message with Morphic
|
// override SnapCloud's user message with Morphic
|
||||||
SnapCloud.message = function (string) {
|
SnapCloud.message = function (string) {
|
||||||
var m = new MenuMorph(null, string),
|
var m = new MenuMorph(null, string),
|
||||||
|
@ -2511,8 +2514,9 @@ IDE_Morph.prototype.aboutSnap = function () {
|
||||||
+ 'jens@moenig.org, bh@cs.berkeley.edu\n\n'
|
+ 'jens@moenig.org, bh@cs.berkeley.edu\n\n'
|
||||||
|
|
||||||
+ 'Snap! is developed by the University of California, Berkeley\n'
|
+ 'Snap! is developed by the University of California, Berkeley\n'
|
||||||
+ ' with support from the National Science Foundation '
|
+ ' with support from the National Science Foundation, '
|
||||||
+ 'and MioSoft. \n'
|
+ 'MioSoft, \n'
|
||||||
|
+ 'and the Communications Design Group at SAP Labs. \n'
|
||||||
|
|
||||||
+ 'The design of Snap! is influenced and inspired by Scratch,\n'
|
+ 'The design of Snap! is influenced and inspired by Scratch,\n'
|
||||||
+ 'from the Lifelong Kindergarten group at the MIT Media Lab\n\n'
|
+ 'from the Lifelong Kindergarten group at the MIT Media Lab\n\n'
|
||||||
|
|
85
history.txt
85
history.txt
|
@ -2309,3 +2309,88 @@ ______
|
||||||
141008
|
141008
|
||||||
------
|
------
|
||||||
* Objects: fixed #608, #610
|
* Objects: fixed #608, #610
|
||||||
|
|
||||||
|
141106
|
||||||
|
------
|
||||||
|
* Morphic: Enable mouseMove events with right button pressed
|
||||||
|
|
||||||
|
141114
|
||||||
|
------
|
||||||
|
* Threads, Store: Fix reporting out of nested custom C-shaped blocks
|
||||||
|
|
||||||
|
141117
|
||||||
|
------
|
||||||
|
* Threads, Blocks: Treat REPORT blocks inside custom command definitions as STOP THIS BLOCK / IGNORE INPUTS
|
||||||
|
|
||||||
|
141120
|
||||||
|
------
|
||||||
|
* Lists: Fixed #642 avoid “freezing” when calling CONS on non-list/null
|
||||||
|
* Threads: Fixed #364 avoid “freezing” when calling LAUNCH on empty ring
|
||||||
|
* Threads: Added optional “onComplete” callback to Process, thanks, @bromagosa!
|
||||||
|
* GUI: Set Default Save location to Cloud on load, thanks, @cycomachead!
|
||||||
|
* GUI: Updated the “About” Dialog with a mention of support from CDG (SAP Labs)
|
||||||
|
* BYOB: Percent sign fix for block labels, thanks, @natashasandy!
|
||||||
|
* Threads: fix ‘line’ option in ‘split’ block for Windows files, thanks, @brianharvey!
|
||||||
|
* Morphic: fix slider range 1, thanks, @tonychenr !
|
||||||
|
* translation update, thanks, Manuel!
|
||||||
|
|
||||||
|
141121
|
||||||
|
------
|
||||||
|
* Threads, Blocks: Fix STOP THIS BLOCK’s lexical awareness
|
||||||
|
|
||||||
|
1411213
|
||||||
|
-------
|
||||||
|
* Threads: Fix “stop this block” primitive for tail-call-elimination
|
||||||
|
|
||||||
|
1411224
|
||||||
|
-------
|
||||||
|
* Threads: Fixed #318
|
||||||
|
* Objects: Fixed #416
|
||||||
|
* Objects: Fixed #372
|
||||||
|
* Threads: Fixed #644
|
||||||
|
* Store: Fixed #34
|
||||||
|
* Threads: Fixed #131
|
||||||
|
* snap.html, favicon.ico: new Favicon, thanks, Michael!
|
||||||
|
* Threads: improved whitespace detection for “split” primitive, thanks, Michael!
|
||||||
|
* Threads: tail-call-elimination for reporters experiment (commented out, under construction)
|
||||||
|
|
||||||
|
1411225
|
||||||
|
-------
|
||||||
|
* Threads: Evaluator optimizations (reducing the stack size for reporters)
|
||||||
|
* Threads: Full TCO (tail-call-elimination), now Snap! *is* Scheme :-)
|
||||||
|
|
||||||
|
1411225
|
||||||
|
-------
|
||||||
|
* Threads: Fixed #656
|
||||||
|
|
||||||
|
141201
|
||||||
|
------
|
||||||
|
* Objects: Hide hidden elements in the project thumbnail
|
||||||
|
* GUI: Point project dialog to cloud if already signed in, thanks, Michael!
|
||||||
|
* favicon: Transparent background, thanks, Michael!
|
||||||
|
|
||||||
|
141202
|
||||||
|
------
|
||||||
|
* New Kannada translation. Yay!! Thanks, Vinayakumar R!!
|
||||||
|
|
||||||
|
141203
|
||||||
|
------
|
||||||
|
* Morphic: Cache actual bounding box of the Pen arrow shape
|
||||||
|
* Threads, Objects: Improve edge-collision detection of default sprite “arrow” shape
|
||||||
|
|
||||||
|
141204
|
||||||
|
------
|
||||||
|
* Threads, Objects: Experimental “ForEach” primitive (hidden in dev mode)
|
||||||
|
* GUI: Another attempt at pointing the project dialog to the cloud if signed in
|
||||||
|
|
||||||
|
141205
|
||||||
|
------
|
||||||
|
* Morphic: Avoid auto-scaling artefacts in Safari on retina displays (resulting in “traces” when dragging items)
|
||||||
|
|
||||||
|
141206
|
||||||
|
------
|
||||||
|
* Store: Fixed #668
|
||||||
|
|
||||||
|
141211
|
||||||
|
------
|
||||||
|
* Threads: yield after each cycle in the experimental “forEach” primitive
|
||||||
|
|
Plik diff jest za duży
Load Diff
23
lang-pt.js
23
lang-pt.js
|
@ -650,11 +650,12 @@ SnapTranslator.dict.pt = {
|
||||||
'Save As...':
|
'Save As...':
|
||||||
'Guardar este projecto como…',
|
'Guardar este projecto como…',
|
||||||
'Import...':
|
'Import...':
|
||||||
'Importar para este projecto…',
|
'Importar…',
|
||||||
'file menu import hint':
|
'file menu import hint':
|
||||||
'Importar para este projecto\num projecto exportado,\n'
|
'Abrir um projecto exportado,\n'
|
||||||
+ 'uma biblioteca de blocos,\n'
|
+ 'substitundo o projecto corrente, ou\n'
|
||||||
+ 'um traje ou um som.',
|
+ 'importar uma biblioteca de blocos, um\n'
|
||||||
|
+ 'traje ou um som para o projecto corrente.',
|
||||||
'Export project as plain text...':
|
'Export project as plain text...':
|
||||||
'Exportar este projecto como texto simples…',
|
'Exportar este projecto como texto simples…',
|
||||||
'Export project...':
|
'Export project...':
|
||||||
|
@ -1616,7 +1617,7 @@ SnapTranslator.dict.pt = {
|
||||||
'unshared.':
|
'unshared.':
|
||||||
'deixado de partilhar.',
|
'deixado de partilhar.',
|
||||||
'Unshare':
|
'Unshare':
|
||||||
'Deixar de Partilhar',
|
'Não Partilhar',
|
||||||
'password has been changed.':
|
'password has been changed.':
|
||||||
'a sua palavra-passe foi alterada.',
|
'a sua palavra-passe foi alterada.',
|
||||||
'SVG costumes are\nnot yet fully supported\nin every browser':
|
'SVG costumes are\nnot yet fully supported\nin every browser':
|
||||||
|
@ -1641,6 +1642,18 @@ SnapTranslator.dict.pt = {
|
||||||
'Seleccionar um traje da biblioteca de média.',
|
'Seleccionar um traje da biblioteca de média.',
|
||||||
'edit rotation point only...':
|
'edit rotation point only...':
|
||||||
'editar apenas ponto de rotação…',
|
'editar apenas ponto de rotação…',
|
||||||
|
'Export Project As...':
|
||||||
|
'Exportar Projecto Como…',
|
||||||
|
'a variable of name \'':
|
||||||
|
'não existe uma variável «',
|
||||||
|
'\'\ndoes not exist in this context':
|
||||||
|
'»\nneste contexto',
|
||||||
|
'(temporary)':
|
||||||
|
'(temporária)',
|
||||||
|
'expecting':
|
||||||
|
'esperavam-se',
|
||||||
|
'input(s), but getting':
|
||||||
|
'argumento(s), mas foram passados',
|
||||||
|
|
||||||
// produção de código
|
// produção de código
|
||||||
'map %cmdRing to %codeKind %code':
|
'map %cmdRing to %codeKind %code':
|
||||||
|
|
5
lists.js
5
lists.js
|
@ -61,7 +61,7 @@ PushButtonMorph, SyntaxElementMorph, Color, Point, WatcherMorph,
|
||||||
StringMorph, SpriteMorph, ScrollFrameMorph, CellMorph, ArrowMorph,
|
StringMorph, SpriteMorph, ScrollFrameMorph, CellMorph, ArrowMorph,
|
||||||
MenuMorph, snapEquals, Morph, isNil, localize, MorphicPreferences*/
|
MenuMorph, snapEquals, Morph, isNil, localize, MorphicPreferences*/
|
||||||
|
|
||||||
modules.lists = '2014-July-28';
|
modules.lists = '2014-November-20';
|
||||||
|
|
||||||
var List;
|
var List;
|
||||||
var ListWatcherMorph;
|
var ListWatcherMorph;
|
||||||
|
@ -125,6 +125,9 @@ List.prototype.changed = function () {
|
||||||
|
|
||||||
List.prototype.cons = function (car, cdr) {
|
List.prototype.cons = function (car, cdr) {
|
||||||
var answer = new List();
|
var answer = new List();
|
||||||
|
if (!(cdr instanceof List || isNil(cdr))) {
|
||||||
|
throw new Error("cdr isn't a list: " + cdr);
|
||||||
|
}
|
||||||
answer.first = isNil(car) ? null : car;
|
answer.first = isNil(car) ? null : car;
|
||||||
answer.rest = cdr || null;
|
answer.rest = cdr || null;
|
||||||
answer.isLinked = true;
|
answer.isLinked = true;
|
||||||
|
|
14
locale.js
14
locale.js
|
@ -42,7 +42,7 @@
|
||||||
|
|
||||||
/*global modules, contains*/
|
/*global modules, contains*/
|
||||||
|
|
||||||
modules.locale = '2014-October-01';
|
modules.locale = '2014-December-02';
|
||||||
|
|
||||||
// Global stuff
|
// Global stuff
|
||||||
|
|
||||||
|
@ -427,3 +427,15 @@ SnapTranslator.dict.bn = {
|
||||||
'last_changed':
|
'last_changed':
|
||||||
'2014-07-02'
|
'2014-07-02'
|
||||||
};
|
};
|
||||||
|
|
||||||
|
SnapTranslator.dict.kn = {
|
||||||
|
// translations meta information
|
||||||
|
'language_name':
|
||||||
|
'\u0C95\u0CA8\u0CCD\u0CA8\u0CA1',
|
||||||
|
'language_translator':
|
||||||
|
'Vinayakumar R',
|
||||||
|
'translator_e-mail':
|
||||||
|
'vnkmr7620@gmail.com',
|
||||||
|
'last_changed':
|
||||||
|
'2014-12-02'
|
||||||
|
};
|
||||||
|
|
58
morphic.js
58
morphic.js
|
@ -464,9 +464,15 @@
|
||||||
|
|
||||||
MyMorph.prototype.mouseMove = function(pos) {};
|
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
|
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
|
Events may be "bubbled" up a morph's owner chain by calling
|
||||||
|
|
||||||
|
@ -1035,7 +1041,7 @@
|
||||||
/*global window, HTMLCanvasElement, getMinimumFontHeight, FileReader, Audio,
|
/*global window, HTMLCanvasElement, getMinimumFontHeight, FileReader, Audio,
|
||||||
FileList, getBlurredShadowSupport*/
|
FileList, getBlurredShadowSupport*/
|
||||||
|
|
||||||
var morphicVersion = '2014-September-30';
|
var morphicVersion = '2014-December-05';
|
||||||
var modules = {}; // keep track of additional loaded modules
|
var modules = {}; // keep track of additional loaded modules
|
||||||
var useBlurredShadows = getBlurredShadowSupport(); // check for Chrome-bug
|
var useBlurredShadows = getBlurredShadowSupport(); // check for Chrome-bug
|
||||||
|
|
||||||
|
@ -1925,7 +1931,9 @@ Rectangle.prototype.round = function () {
|
||||||
|
|
||||||
Rectangle.prototype.spread = function () {
|
Rectangle.prototype.spread = function () {
|
||||||
// round me by applying floor() to my origin and ceil() to my corner
|
// round me by applying floor() to my origin and ceil() to my corner
|
||||||
return this.origin.floor().corner(this.corner.ceil());
|
// expand by 1 to be on the safe side, this eliminates rounding
|
||||||
|
// artefacts caused by Safari's auto-scaling on retina displays
|
||||||
|
return this.origin.floor().corner(this.corner.ceil()).expandBy(1);
|
||||||
};
|
};
|
||||||
|
|
||||||
Rectangle.prototype.amountToTranslateWithin = function (aRect) {
|
Rectangle.prototype.amountToTranslateWithin = function (aRect) {
|
||||||
|
@ -3972,6 +3980,7 @@ PenMorph.prototype.init = function () {
|
||||||
this.size = 1;
|
this.size = 1;
|
||||||
this.wantsRedraw = false;
|
this.wantsRedraw = false;
|
||||||
this.penPoint = 'tip'; // or 'center"
|
this.penPoint = 'tip'; // or 'center"
|
||||||
|
this.penBounds = null; // rect around the visible arrow shape
|
||||||
|
|
||||||
HandleMorph.uber.init.call(this);
|
HandleMorph.uber.init.call(this);
|
||||||
this.setExtent(new Point(size, size));
|
this.setExtent(new Point(size, size));
|
||||||
|
@ -3994,11 +4003,9 @@ PenMorph.prototype.changed = function () {
|
||||||
// PenMorph display:
|
// PenMorph display:
|
||||||
|
|
||||||
PenMorph.prototype.drawNew = function (facing) {
|
PenMorph.prototype.drawNew = function (facing) {
|
||||||
/*
|
// my orientation can be overridden with the "facing" parameter to
|
||||||
my orientation can be overridden with the "facing" parameter to
|
// implement Scratch-style rotation styles
|
||||||
implement Scratch-style rotation styles
|
|
||||||
|
|
||||||
*/
|
|
||||||
var context, start, dest, left, right, len,
|
var context, start, dest, left, right, len,
|
||||||
direction = facing || this.heading;
|
direction = facing || this.heading;
|
||||||
|
|
||||||
|
@ -4021,6 +4028,15 @@ PenMorph.prototype.drawNew = function (facing) {
|
||||||
right = start.distanceAngle(len * 0.33, direction - 230);
|
right = start.distanceAngle(len * 0.33, direction - 230);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// cache penBounds
|
||||||
|
this.penBounds = new Rectangle(
|
||||||
|
Math.min(start.x, dest.x, left.x, right.x),
|
||||||
|
Math.min(start.y, dest.y, left.y, right.y),
|
||||||
|
Math.max(start.x, dest.x, left.x, right.x),
|
||||||
|
Math.max(start.y, dest.y, left.y, right.y)
|
||||||
|
);
|
||||||
|
|
||||||
|
// draw arrow shape
|
||||||
context.fillStyle = this.color.toString();
|
context.fillStyle = this.color.toString();
|
||||||
context.beginPath();
|
context.beginPath();
|
||||||
|
|
||||||
|
@ -4037,7 +4053,6 @@ PenMorph.prototype.drawNew = function (facing) {
|
||||||
context.lineWidth = 1;
|
context.lineWidth = 1;
|
||||||
context.stroke();
|
context.stroke();
|
||||||
context.fill();
|
context.fill();
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// PenMorph access:
|
// PenMorph access:
|
||||||
|
@ -5754,7 +5769,7 @@ SliderMorph.prototype.rangeSize = function () {
|
||||||
};
|
};
|
||||||
|
|
||||||
SliderMorph.prototype.ratio = function () {
|
SliderMorph.prototype.ratio = function () {
|
||||||
return this.size / this.rangeSize();
|
return this.size / (this.rangeSize() + 1);
|
||||||
};
|
};
|
||||||
|
|
||||||
SliderMorph.prototype.unitSize = function () {
|
SliderMorph.prototype.unitSize = function () {
|
||||||
|
@ -9346,11 +9361,11 @@ HandMorph.prototype.init = function (aWorld) {
|
||||||
this.world = aWorld;
|
this.world = aWorld;
|
||||||
this.mouseButton = null;
|
this.mouseButton = null;
|
||||||
this.mouseOverList = [];
|
this.mouseOverList = [];
|
||||||
this.mouseDownMorph = null;
|
|
||||||
this.morphToGrab = null;
|
this.morphToGrab = null;
|
||||||
this.grabOrigin = null;
|
this.grabOrigin = null;
|
||||||
this.temporaries = [];
|
this.temporaries = [];
|
||||||
this.touchHoldTimeout = null;
|
this.touchHoldTimeout = null;
|
||||||
|
this.contextMenuEnabled = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
HandMorph.prototype.changed = function () {
|
HandMorph.prototype.changed = function () {
|
||||||
|
@ -9494,9 +9509,10 @@ HandMorph.prototype.drop = function () {
|
||||||
*/
|
*/
|
||||||
|
|
||||||
HandMorph.prototype.processMouseDown = function (event) {
|
HandMorph.prototype.processMouseDown = function (event) {
|
||||||
var morph, expectedClick, actualClick;
|
var morph, actualClick;
|
||||||
|
|
||||||
this.destroyTemporaries();
|
this.destroyTemporaries();
|
||||||
|
this.contextMenuEnabled = true;
|
||||||
this.morphToGrab = null;
|
this.morphToGrab = null;
|
||||||
if (this.children.length !== 0) {
|
if (this.children.length !== 0) {
|
||||||
this.drop();
|
this.drop();
|
||||||
|
@ -9529,15 +9545,9 @@ HandMorph.prototype.processMouseDown = function (event) {
|
||||||
if (event.button === 2 || event.ctrlKey) {
|
if (event.button === 2 || event.ctrlKey) {
|
||||||
this.mouseButton = 'right';
|
this.mouseButton = 'right';
|
||||||
actualClick = 'mouseDownRight';
|
actualClick = 'mouseDownRight';
|
||||||
expectedClick = 'mouseClickRight';
|
|
||||||
} else {
|
} else {
|
||||||
this.mouseButton = 'left';
|
this.mouseButton = 'left';
|
||||||
actualClick = 'mouseDownLeft';
|
actualClick = 'mouseDownLeft';
|
||||||
expectedClick = 'mouseClickLeft';
|
|
||||||
}
|
|
||||||
this.mouseDownMorph = morph;
|
|
||||||
while (!this.mouseDownMorph[expectedClick]) {
|
|
||||||
this.mouseDownMorph = this.mouseDownMorph.parent;
|
|
||||||
}
|
}
|
||||||
while (!morph[actualClick]) {
|
while (!morph[actualClick]) {
|
||||||
morph = morph.parent;
|
morph = morph.parent;
|
||||||
|
@ -9596,7 +9606,7 @@ HandMorph.prototype.processMouseUp = function () {
|
||||||
expectedClick = 'mouseClickLeft';
|
expectedClick = 'mouseClickLeft';
|
||||||
} else {
|
} else {
|
||||||
expectedClick = 'mouseClickRight';
|
expectedClick = 'mouseClickRight';
|
||||||
if (this.mouseButton) {
|
if (this.mouseButton && this.contextMenuEnabled) {
|
||||||
context = morph;
|
context = morph;
|
||||||
contextMenu = context.contextMenu();
|
contextMenu = context.contextMenu();
|
||||||
while ((!contextMenu) &&
|
while ((!contextMenu) &&
|
||||||
|
@ -9654,16 +9664,18 @@ HandMorph.prototype.processMouseMove = function (event) {
|
||||||
// mouseOverNew = this.allMorphsAtPointer();
|
// mouseOverNew = this.allMorphsAtPointer();
|
||||||
mouseOverNew = this.morphAtPointer().allParents();
|
mouseOverNew = this.morphAtPointer().allParents();
|
||||||
|
|
||||||
if ((this.children.length === 0) &&
|
if (!this.children.length && this.mouseButton) {
|
||||||
(this.mouseButton === 'left')) {
|
|
||||||
topMorph = this.morphAtPointer();
|
topMorph = this.morphAtPointer();
|
||||||
morph = topMorph.rootForGrab();
|
morph = topMorph.rootForGrab();
|
||||||
if (topMorph.mouseMove) {
|
if (topMorph.mouseMove) {
|
||||||
topMorph.mouseMove(pos);
|
topMorph.mouseMove(pos, this.mouseButton);
|
||||||
|
if (this.mouseButton === 'right') {
|
||||||
|
this.contextMenuEnabled = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// if a morph is marked for grabbing, just grab it
|
// if a morph is marked for grabbing, just grab it
|
||||||
if (this.morphToGrab) {
|
if (this.mouseButton === 'left' && this.morphToGrab) {
|
||||||
if (this.morphToGrab.isDraggable) {
|
if (this.morphToGrab.isDraggable) {
|
||||||
morph = this.morphToGrab;
|
morph = this.morphToGrab;
|
||||||
this.grab(morph);
|
this.grab(morph);
|
||||||
|
|
76
objects.js
76
objects.js
|
@ -125,7 +125,7 @@ PrototypeHatBlockMorph*/
|
||||||
|
|
||||||
// Global stuff ////////////////////////////////////////////////////////
|
// Global stuff ////////////////////////////////////////////////////////
|
||||||
|
|
||||||
modules.objects = '2014-October-08';
|
modules.objects = '2014-December-04';
|
||||||
|
|
||||||
var SpriteMorph;
|
var SpriteMorph;
|
||||||
var StageMorph;
|
var StageMorph;
|
||||||
|
@ -1151,6 +1151,13 @@ SpriteMorph.prototype.initBlocks = function () {
|
||||||
category: 'lists',
|
category: 'lists',
|
||||||
spec: 'map %repRing over %l'
|
spec: 'map %repRing over %l'
|
||||||
},
|
},
|
||||||
|
doForEach: {
|
||||||
|
dev: true,
|
||||||
|
type: 'command',
|
||||||
|
category: 'lists',
|
||||||
|
spec: 'for %upvar in %l %cs',
|
||||||
|
defaults: [localize('each item')]
|
||||||
|
},
|
||||||
|
|
||||||
// Code mapping - experimental
|
// Code mapping - experimental
|
||||||
doMapCodeOrHeader: { // experimental
|
doMapCodeOrHeader: { // experimental
|
||||||
|
@ -1674,6 +1681,20 @@ SpriteMorph.prototype.blockTemplates = function (category) {
|
||||||
return menu;
|
return menu;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function addVar(pair) {
|
||||||
|
if (pair) {
|
||||||
|
if (myself.variables.silentFind(pair[0])) {
|
||||||
|
myself.inform('that name is already in use');
|
||||||
|
} else {
|
||||||
|
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 (cat === 'motion') {
|
if (cat === 'motion') {
|
||||||
|
|
||||||
blocks.push(block('forward'));
|
blocks.push(block('forward'));
|
||||||
|
@ -1967,15 +1988,7 @@ SpriteMorph.prototype.blockTemplates = function (category) {
|
||||||
function () {
|
function () {
|
||||||
new VariableDialogMorph(
|
new VariableDialogMorph(
|
||||||
null,
|
null,
|
||||||
function (pair) {
|
addVar,
|
||||||
if (pair && !myself.variables.silentFind(pair[0])) {
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
myself
|
myself
|
||||||
).prompt(
|
).prompt(
|
||||||
'Variable name',
|
'Variable name',
|
||||||
|
@ -2057,6 +2070,8 @@ SpriteMorph.prototype.blockTemplates = function (category) {
|
||||||
blocks.push(txt);
|
blocks.push(txt);
|
||||||
blocks.push('-');
|
blocks.push('-');
|
||||||
blocks.push(block('reportMap'));
|
blocks.push(block('reportMap'));
|
||||||
|
blocks.push('-');
|
||||||
|
blocks.push(block('doForEach'));
|
||||||
}
|
}
|
||||||
|
|
||||||
/////////////////////////////////
|
/////////////////////////////////
|
||||||
|
@ -2164,8 +2179,8 @@ SpriteMorph.prototype.freshPalette = function (category) {
|
||||||
var defs = SpriteMorph.prototype.blocks,
|
var defs = SpriteMorph.prototype.blocks,
|
||||||
hiddens = StageMorph.prototype.hiddenPrimitives;
|
hiddens = StageMorph.prototype.hiddenPrimitives;
|
||||||
return Object.keys(hiddens).some(function (any) {
|
return Object.keys(hiddens).some(function (any) {
|
||||||
return defs[any].category === category ||
|
return !isNil(defs[any]) && (defs[any].category === category
|
||||||
contains((more[category] || []), any);
|
|| contains((more[category] || []), any));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2204,7 +2219,7 @@ SpriteMorph.prototype.freshPalette = function (category) {
|
||||||
var hiddens = StageMorph.prototype.hiddenPrimitives,
|
var hiddens = StageMorph.prototype.hiddenPrimitives,
|
||||||
defs = SpriteMorph.prototype.blocks;
|
defs = SpriteMorph.prototype.blocks;
|
||||||
Object.keys(hiddens).forEach(function (sel) {
|
Object.keys(hiddens).forEach(function (sel) {
|
||||||
if (defs[sel].category === category) {
|
if (defs[sel] && (defs[sel].category === category)) {
|
||||||
delete StageMorph.prototype.hiddenPrimitives[sel];
|
delete StageMorph.prototype.hiddenPrimitives[sel];
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -3232,8 +3247,11 @@ SpriteMorph.prototype.setCenter = function (aPoint, justMe) {
|
||||||
|
|
||||||
SpriteMorph.prototype.nestingBounds = function () {
|
SpriteMorph.prototype.nestingBounds = function () {
|
||||||
// same as fullBounds(), except that it uses "parts" instead of children
|
// same as fullBounds(), except that it uses "parts" instead of children
|
||||||
var result;
|
// and special cases the costume-less "arrow" shape's bounding box
|
||||||
result = this.bounds;
|
var result = this.bounds;
|
||||||
|
if (!this.costume && this.penBounds) {
|
||||||
|
result = this.penBounds.translateBy(this.position());
|
||||||
|
}
|
||||||
this.parts.forEach(function (part) {
|
this.parts.forEach(function (part) {
|
||||||
if (part.isVisible) {
|
if (part.isVisible) {
|
||||||
result = result.merge(part.nestingBounds());
|
result = result.merge(part.nestingBounds());
|
||||||
|
@ -4858,6 +4876,20 @@ StageMorph.prototype.blockTemplates = function (category) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function addVar(pair) {
|
||||||
|
if (pair) {
|
||||||
|
if (myself.variables.silentFind(pair[0])) {
|
||||||
|
myself.inform('that name is already in use');
|
||||||
|
} else {
|
||||||
|
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 (cat === 'motion') {
|
if (cat === 'motion') {
|
||||||
|
|
||||||
txt = new TextMorph(localize(
|
txt = new TextMorph(localize(
|
||||||
|
@ -5099,15 +5131,7 @@ StageMorph.prototype.blockTemplates = function (category) {
|
||||||
function () {
|
function () {
|
||||||
new VariableDialogMorph(
|
new VariableDialogMorph(
|
||||||
null,
|
null,
|
||||||
function (pair) {
|
addVar,
|
||||||
if (pair && !myself.variables.silentFind(pair[0])) {
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
myself
|
myself
|
||||||
).prompt(
|
).prompt(
|
||||||
'Variable name',
|
'Variable name',
|
||||||
|
@ -5183,6 +5207,8 @@ StageMorph.prototype.blockTemplates = function (category) {
|
||||||
blocks.push(txt);
|
blocks.push(txt);
|
||||||
blocks.push('-');
|
blocks.push('-');
|
||||||
blocks.push(block('reportMap'));
|
blocks.push(block('reportMap'));
|
||||||
|
blocks.push('-');
|
||||||
|
blocks.push(block('doForEach'));
|
||||||
}
|
}
|
||||||
|
|
||||||
/////////////////////////////////
|
/////////////////////////////////
|
||||||
|
@ -5321,7 +5347,7 @@ StageMorph.prototype.thumbnail = function (extentPoint, excludedSprite) {
|
||||||
this.dimensions.y * this.scale
|
this.dimensions.y * this.scale
|
||||||
);
|
);
|
||||||
this.children.forEach(function (morph) {
|
this.children.forEach(function (morph) {
|
||||||
if (morph !== excludedSprite) {
|
if (morph.isVisible && (morph !== excludedSprite)) {
|
||||||
fb = morph.fullBounds();
|
fb = morph.fullBounds();
|
||||||
fimg = morph.fullImage();
|
fimg = morph.fullImage();
|
||||||
if (fimg.width && fimg.height) {
|
if (fimg.width && fimg.height) {
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
<head>
|
<head>
|
||||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
<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. Beta</title>
|
||||||
<link rel="shortcut icon" href="http://snap.berkeley.edu/fav3.gif" type="image/gif">
|
<link rel="shortcut icon" href="favicon.ico">
|
||||||
<script type="text/javascript" src="morphic.js"></script>
|
<script type="text/javascript" src="morphic.js"></script>
|
||||||
<script type="text/javascript" src="widgets.js"></script>
|
<script type="text/javascript" src="widgets.js"></script>
|
||||||
<script type="text/javascript" src="blocks.js"></script>
|
<script type="text/javascript" src="blocks.js"></script>
|
||||||
|
@ -33,5 +33,5 @@
|
||||||
</head>
|
</head>
|
||||||
<body style="margin: 0;">
|
<body style="margin: 0;">
|
||||||
<canvas id="world" tabindex="1" style="position: absolute;" />
|
<canvas id="world" tabindex="1" style="position: absolute;" />
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
11
store.js
11
store.js
|
@ -61,7 +61,7 @@ SyntaxElementMorph, Variable*/
|
||||||
|
|
||||||
// Global stuff ////////////////////////////////////////////////////////
|
// Global stuff ////////////////////////////////////////////////////////
|
||||||
|
|
||||||
modules.store = '2014-October-01';
|
modules.store = '2014-December-06';
|
||||||
|
|
||||||
|
|
||||||
// XML_Serializer ///////////////////////////////////////////////////////
|
// XML_Serializer ///////////////////////////////////////////////////////
|
||||||
|
@ -261,7 +261,9 @@ SnapSerializer.prototype.watcherLabels = {
|
||||||
yPosition: 'y position',
|
yPosition: 'y position',
|
||||||
direction: 'direction',
|
direction: 'direction',
|
||||||
getScale: 'size',
|
getScale: 'size',
|
||||||
|
getTempo: 'tempo',
|
||||||
getLastAnswer: 'answer',
|
getLastAnswer: 'answer',
|
||||||
|
getLastMessage: 'message',
|
||||||
getTimer: 'timer',
|
getTimer: 'timer',
|
||||||
getCostumeIdx: 'costume #',
|
getCostumeIdx: 'costume #',
|
||||||
reportMouseX: 'mouse x',
|
reportMouseX: 'mouse x',
|
||||||
|
@ -1199,6 +1201,10 @@ SnapSerializer.prototype.loadValue = function (model) {
|
||||||
if (el) {
|
if (el) {
|
||||||
v.outerContext = this.loadValue(el);
|
v.outerContext = this.loadValue(el);
|
||||||
}
|
}
|
||||||
|
if (v.outerContext && v.receiver &&
|
||||||
|
!v.outerContext.variables.parentFrame) {
|
||||||
|
v.outerContext.variables.parentFrame = v.receiver.variables;
|
||||||
|
}
|
||||||
return v;
|
return v;
|
||||||
case 'costume':
|
case 'costume':
|
||||||
center = new Point();
|
center = new Point();
|
||||||
|
@ -1824,9 +1830,8 @@ Context.prototype.toXML = function (serializer) {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
return serializer.format(
|
return serializer.format(
|
||||||
'<context% ~><inputs>%</inputs><variables>%</variables>' +
|
'<context ~><inputs>%</inputs><variables>%</variables>' +
|
||||||
'%<receiver>%</receiver>%</context>',
|
'%<receiver>%</receiver>%</context>',
|
||||||
this.isLambda ? ' lambda="lambda"' : '',
|
|
||||||
this.inputs.reduce(
|
this.inputs.reduce(
|
||||||
function (xml, input) {
|
function (xml, input) {
|
||||||
return xml + serializer.format('<input>$</input>', input);
|
return xml + serializer.format('<input>$</input>', input);
|
||||||
|
|
443
threads.js
443
threads.js
|
@ -83,7 +83,7 @@ ArgLabelMorph, localize, XML_Element, hex_sha512*/
|
||||||
|
|
||||||
// Global stuff ////////////////////////////////////////////////////////
|
// Global stuff ////////////////////////////////////////////////////////
|
||||||
|
|
||||||
modules.threads = '2014-October-01';
|
modules.threads = '2014-December-11';
|
||||||
|
|
||||||
var ThreadManager;
|
var ThreadManager;
|
||||||
var Process;
|
var Process;
|
||||||
|
@ -100,8 +100,18 @@ function snapEquals(a, b) {
|
||||||
|
|
||||||
var x = +a,
|
var x = +a,
|
||||||
y = +b,
|
y = +b,
|
||||||
|
i,
|
||||||
specials = [true, false, ''];
|
specials = [true, false, ''];
|
||||||
|
|
||||||
|
// "zum Schneckengang verdorben, was Adlerflug geworden wäre"
|
||||||
|
// collecting edge-cases that somebody complained about
|
||||||
|
// on Github. Folks, take it easy and keep it fun, okay?
|
||||||
|
// Shit like this is patently ugly and slows Snap down. Tnx!
|
||||||
|
for (i = 9; i <= 13; i += 1) {
|
||||||
|
specials.push(String.fromCharCode(i));
|
||||||
|
}
|
||||||
|
specials.push(String.fromCharCode(160));
|
||||||
|
|
||||||
// check for special values before coercing to numbers
|
// check for special values before coercing to numbers
|
||||||
if (isNaN(x) || isNaN(y) ||
|
if (isNaN(x) || isNaN(y) ||
|
||||||
[a, b].some(function (any) {return contains(specials, any) ||
|
[a, b].some(function (any) {return contains(specials, any) ||
|
||||||
|
@ -110,7 +120,7 @@ function snapEquals(a, b) {
|
||||||
y = b;
|
y = b;
|
||||||
}
|
}
|
||||||
|
|
||||||
// handle text comparision case-insensitive.
|
// handle text comparison case-insensitive.
|
||||||
if (isString(x) && isString(y)) {
|
if (isString(x) && isString(y)) {
|
||||||
return x.toLowerCase() === y.toLowerCase();
|
return x.toLowerCase() === y.toLowerCase();
|
||||||
}
|
}
|
||||||
|
@ -136,7 +146,8 @@ ThreadManager.prototype.toggleProcess = function (block) {
|
||||||
ThreadManager.prototype.startProcess = function (
|
ThreadManager.prototype.startProcess = function (
|
||||||
block,
|
block,
|
||||||
isThreadSafe,
|
isThreadSafe,
|
||||||
exportResult
|
exportResult,
|
||||||
|
callback
|
||||||
) {
|
) {
|
||||||
var active = this.findProcess(block),
|
var active = this.findProcess(block),
|
||||||
top = block.topBlock(),
|
top = block.topBlock(),
|
||||||
|
@ -149,7 +160,7 @@ ThreadManager.prototype.startProcess = function (
|
||||||
this.removeTerminatedProcesses();
|
this.removeTerminatedProcesses();
|
||||||
}
|
}
|
||||||
top.addHighlight();
|
top.addHighlight();
|
||||||
newProc = new Process(block.topBlock());
|
newProc = new Process(block.topBlock(), callback);
|
||||||
newProc.exportResult = exportResult;
|
newProc.exportResult = exportResult;
|
||||||
this.processes.push(newProc);
|
this.processes.push(newProc);
|
||||||
return newProc;
|
return newProc;
|
||||||
|
@ -207,11 +218,10 @@ ThreadManager.prototype.resumeAll = function (stage) {
|
||||||
};
|
};
|
||||||
|
|
||||||
ThreadManager.prototype.step = function () {
|
ThreadManager.prototype.step = function () {
|
||||||
/*
|
// run each process until it gives up control, skipping processes
|
||||||
run each process until it gives up control, skipping processes
|
// for sprites that are currently picked up, then filter out any
|
||||||
for sprites that are currently picked up, then filter out any
|
// processes that have been terminated
|
||||||
processes that have been terminated
|
|
||||||
*/
|
|
||||||
this.processes.forEach(function (proc) {
|
this.processes.forEach(function (proc) {
|
||||||
if (!proc.homeContext.receiver.isPickedUp() && !proc.isDead) {
|
if (!proc.homeContext.receiver.isPickedUp() && !proc.isDead) {
|
||||||
proc.runStep();
|
proc.runStep();
|
||||||
|
@ -225,8 +235,9 @@ ThreadManager.prototype.removeTerminatedProcesses = function () {
|
||||||
var remaining = [];
|
var remaining = [];
|
||||||
this.processes.forEach(function (proc) {
|
this.processes.forEach(function (proc) {
|
||||||
if (!proc.isRunning() && !proc.errorFlag && !proc.isDead) {
|
if (!proc.isRunning() && !proc.errorFlag && !proc.isDead) {
|
||||||
proc.topBlock.removeHighlight();
|
if (proc.topBlock instanceof BlockMorph) {
|
||||||
|
proc.topBlock.removeHighlight();
|
||||||
|
}
|
||||||
if (proc.prompter) {
|
if (proc.prompter) {
|
||||||
proc.prompter.destroy();
|
proc.prompter.destroy();
|
||||||
if (proc.homeContext.receiver.stopTalking) {
|
if (proc.homeContext.receiver.stopTalking) {
|
||||||
|
@ -235,18 +246,22 @@ ThreadManager.prototype.removeTerminatedProcesses = function () {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (proc.topBlock instanceof ReporterBlockMorph) {
|
if (proc.topBlock instanceof ReporterBlockMorph) {
|
||||||
if (proc.homeContext.inputs[0] instanceof List) {
|
if (proc.onComplete instanceof Function) {
|
||||||
proc.topBlock.showBubble(
|
proc.onComplete(proc.homeContext.inputs[0]);
|
||||||
new ListWatcherMorph(
|
|
||||||
proc.homeContext.inputs[0]
|
|
||||||
),
|
|
||||||
proc.exportResult
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
proc.topBlock.showBubble(
|
if (proc.homeContext.inputs[0] instanceof List) {
|
||||||
proc.homeContext.inputs[0],
|
proc.topBlock.showBubble(
|
||||||
proc.exportResult
|
new ListWatcherMorph(
|
||||||
);
|
proc.homeContext.inputs[0]
|
||||||
|
),
|
||||||
|
proc.exportResult
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
proc.topBlock.showBubble(
|
||||||
|
proc.homeContext.inputs[0],
|
||||||
|
proc.exportResult
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -295,9 +310,9 @@ ThreadManager.prototype.findProcess = function (block) {
|
||||||
are children
|
are children
|
||||||
receiver object (sprite) to which the process applies,
|
receiver object (sprite) to which the process applies,
|
||||||
cached from the top block
|
cached from the top block
|
||||||
context the Context describing the current state
|
context the Context describing the current state
|
||||||
of this process
|
of this process
|
||||||
homeContext stores information relevant to the whole process,
|
homeContext stores information relevant to the whole process,
|
||||||
i.e. its receiver, result etc.
|
i.e. its receiver, result etc.
|
||||||
isPaused boolean indicating whether to pause
|
isPaused boolean indicating whether to pause
|
||||||
readyToYield boolean indicating whether to yield control to
|
readyToYield boolean indicating whether to yield control to
|
||||||
|
@ -305,15 +320,20 @@ ThreadManager.prototype.findProcess = function (block) {
|
||||||
readyToTerminate boolean indicating whether the stop method has
|
readyToTerminate boolean indicating whether the stop method has
|
||||||
been called
|
been called
|
||||||
isDead boolean indicating a terminated clone process
|
isDead boolean indicating a terminated clone process
|
||||||
timeout msecs after which to force yield
|
timeout msecs after which to force yield
|
||||||
lastYield msecs when the process last yielded
|
lastYield msecs when the process last yielded
|
||||||
errorFlag boolean indicating whether an error was encountered
|
errorFlag boolean indicating whether an error was encountered
|
||||||
prompter active instance of StagePrompterMorph
|
prompter active instance of StagePrompterMorph
|
||||||
httpRequest active instance of an HttpRequest or null
|
httpRequest active instance of an HttpRequest or null
|
||||||
pauseOffset msecs between the start of an interpolated operation
|
pauseOffset msecs between the start of an interpolated operation
|
||||||
and when the process was paused
|
and when the process was paused
|
||||||
exportResult boolean flag indicating whether a picture of the top
|
exportResult boolean flag indicating whether a picture of the top
|
||||||
block along with the result bubble shoud be exported
|
block along with the result bubble shoud be exported
|
||||||
|
onComplete an optional callback function to be executed when
|
||||||
|
the process is done
|
||||||
|
procedureCount number counting procedure call entries,
|
||||||
|
used to tag custom block calls, so "stop block"
|
||||||
|
invocations can catch them
|
||||||
*/
|
*/
|
||||||
|
|
||||||
Process.prototype = {};
|
Process.prototype = {};
|
||||||
|
@ -321,7 +341,7 @@ Process.prototype.contructor = Process;
|
||||||
Process.prototype.timeout = 500; // msecs after which to force yield
|
Process.prototype.timeout = 500; // msecs after which to force yield
|
||||||
Process.prototype.isCatchingErrors = true;
|
Process.prototype.isCatchingErrors = true;
|
||||||
|
|
||||||
function Process(topBlock) {
|
function Process(topBlock, onComplete) {
|
||||||
this.topBlock = topBlock || null;
|
this.topBlock = topBlock || null;
|
||||||
|
|
||||||
this.readyToYield = false;
|
this.readyToYield = false;
|
||||||
|
@ -338,6 +358,8 @@ function Process(topBlock) {
|
||||||
this.pauseOffset = null;
|
this.pauseOffset = null;
|
||||||
this.frameCount = 0;
|
this.frameCount = 0;
|
||||||
this.exportResult = false;
|
this.exportResult = false;
|
||||||
|
this.onComplete = onComplete || null;
|
||||||
|
this.procedureCount = 0;
|
||||||
|
|
||||||
if (topBlock) {
|
if (topBlock) {
|
||||||
this.homeContext.receiver = topBlock.receiver();
|
this.homeContext.receiver = topBlock.receiver();
|
||||||
|
@ -361,13 +383,13 @@ Process.prototype.isRunning = function () {
|
||||||
// Process entry points
|
// Process entry points
|
||||||
|
|
||||||
Process.prototype.runStep = function () {
|
Process.prototype.runStep = function () {
|
||||||
/*
|
// a step is an an uninterruptable 'atom', it can consist
|
||||||
a step is an an uninterruptable 'atom', it can consist
|
// of several contexts, even of several blocks
|
||||||
of several contexts, even of several blocks
|
|
||||||
*/
|
|
||||||
if (this.isPaused) { // allow pausing in between atomic steps:
|
if (this.isPaused) { // allow pausing in between atomic steps:
|
||||||
return this.pauseStep();
|
return this.pauseStep();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.readyToYield = false;
|
this.readyToYield = false;
|
||||||
while (!this.readyToYield
|
while (!this.readyToYield
|
||||||
&& this.context
|
&& this.context
|
||||||
|
@ -435,8 +457,10 @@ Process.prototype.pauseStep = function () {
|
||||||
|
|
||||||
Process.prototype.evaluateContext = function () {
|
Process.prototype.evaluateContext = function () {
|
||||||
var exp = this.context.expression;
|
var exp = this.context.expression;
|
||||||
|
|
||||||
this.frameCount += 1;
|
this.frameCount += 1;
|
||||||
|
if (this.context.tag === 'exit') {
|
||||||
|
this.expectReport();
|
||||||
|
}
|
||||||
if (exp instanceof Array) {
|
if (exp instanceof Array) {
|
||||||
return this.evaluateSequence(exp);
|
return this.evaluateSequence(exp);
|
||||||
}
|
}
|
||||||
|
@ -460,7 +484,7 @@ Process.prototype.evaluateContext = function () {
|
||||||
|
|
||||||
Process.prototype.evaluateBlock = function (block, argCount) {
|
Process.prototype.evaluateBlock = function (block, argCount) {
|
||||||
// check for special forms
|
// check for special forms
|
||||||
if (contains(['reportOr', 'reportAnd'], block.selector)) {
|
if (contains(['reportOr', 'reportAnd', 'doReport'], block.selector)) {
|
||||||
return this[block.selector](block);
|
return this[block.selector](block);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -526,6 +550,35 @@ Process.prototype.reportAnd = function (block) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Process.prototype.doReport = function (block) {
|
||||||
|
var outer = this.context.outerContext;
|
||||||
|
if (this.context.expression.partOfCustomCommand) {
|
||||||
|
this.doStopCustomBlock();
|
||||||
|
this.popContext();
|
||||||
|
} else {
|
||||||
|
while (this.context && this.context.tag !== 'exit') {
|
||||||
|
if (this.context.expression === 'doStopWarping') {
|
||||||
|
this.doStopWarping();
|
||||||
|
} else {
|
||||||
|
this.popContext();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (this.context) {
|
||||||
|
if (this.context.expression === 'expectReport') {
|
||||||
|
// pop off inserted top-level exit context
|
||||||
|
this.popContext();
|
||||||
|
} else {
|
||||||
|
// un-tag and preserve original caller
|
||||||
|
this.context.tag = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// in any case evaluate (and ignore)
|
||||||
|
// the input, because it could be
|
||||||
|
// and HTTP Request for a hardware extension
|
||||||
|
this.pushContext(block.inputs()[0], outer);
|
||||||
|
};
|
||||||
|
|
||||||
// Process: Non-Block evaluation
|
// Process: Non-Block evaluation
|
||||||
|
|
||||||
Process.prototype.evaluateMultiSlot = function (multiSlot, argCount) {
|
Process.prototype.evaluateMultiSlot = function (multiSlot, argCount) {
|
||||||
|
@ -586,7 +639,6 @@ Process.prototype.evaluateInput = function (input) {
|
||||||
) || (input instanceof CSlotMorph && !input.isStatic)) {
|
) || (input instanceof CSlotMorph && !input.isStatic)) {
|
||||||
// I know, this still needs yet to be done right....
|
// I know, this still needs yet to be done right....
|
||||||
ans = this.reify(ans, new List());
|
ans = this.reify(ans, new List());
|
||||||
ans.isImplicitLambda = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -597,8 +649,6 @@ Process.prototype.evaluateInput = function (input) {
|
||||||
Process.prototype.evaluateSequence = function (arr) {
|
Process.prototype.evaluateSequence = function (arr) {
|
||||||
var pc = this.context.pc,
|
var pc = this.context.pc,
|
||||||
outer = this.context.outerContext,
|
outer = this.context.outerContext,
|
||||||
isLambda = this.context.isLambda,
|
|
||||||
isImplicitLambda = this.context.isImplicitLambda,
|
|
||||||
isCustomBlock = this.context.isCustomBlock;
|
isCustomBlock = this.context.isCustomBlock;
|
||||||
if (pc === (arr.length - 1)) { // tail call elimination
|
if (pc === (arr.length - 1)) { // tail call elimination
|
||||||
this.context = new Context(
|
this.context = new Context(
|
||||||
|
@ -607,8 +657,6 @@ Process.prototype.evaluateSequence = function (arr) {
|
||||||
this.context.outerContext,
|
this.context.outerContext,
|
||||||
this.context.receiver
|
this.context.receiver
|
||||||
);
|
);
|
||||||
this.context.isLambda = isLambda;
|
|
||||||
this.context.isImplicitLambda = isImplicitLambda;
|
|
||||||
this.context.isCustomBlock = isCustomBlock;
|
this.context.isCustomBlock = isCustomBlock;
|
||||||
} else {
|
} else {
|
||||||
if (pc >= arr.length) {
|
if (pc >= arr.length) {
|
||||||
|
@ -626,8 +674,8 @@ Process.prototype.evaluateSequence = function (arr) {
|
||||||
Caution: we cannot just revert to this version of the method, because to make
|
Caution: we cannot just revert to this version of the method, because to make
|
||||||
tail call elimination work many tweaks had to be done to various primitives.
|
tail call elimination work many tweaks had to be done to various primitives.
|
||||||
For the most part these tweaks are about schlepping the outer context (for
|
For the most part these tweaks are about schlepping the outer context (for
|
||||||
the variable bindings) and the isLambda flag along, and are indicated by a
|
the variable bindings) and the isCustomBlock flag along, and are indicated
|
||||||
short comment in the code. But to really revert would take a good measure
|
by a short comment in the code. But to really revert would take a good measure
|
||||||
of trial and error as well as debugging. In the developers file archive there
|
of trial and error as well as debugging. In the developers file archive there
|
||||||
is a version of threads.js dated 120119(2) which basically resembles the
|
is a version of threads.js dated 120119(2) which basically resembles the
|
||||||
last version before introducing tail call optimization on 120123.
|
last version before introducing tail call optimization on 120123.
|
||||||
|
@ -679,6 +727,10 @@ Process.prototype.doYield = function () {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Process.prototype.expectReport = function () {
|
||||||
|
this.handleError(new Error("reporter didn't report"));
|
||||||
|
};
|
||||||
|
|
||||||
// Process Exception Handling
|
// Process Exception Handling
|
||||||
|
|
||||||
Process.prototype.handleError = function (error, element) {
|
Process.prototype.handleError = function (error, element) {
|
||||||
|
@ -757,8 +809,8 @@ Process.prototype.reportJSFunction = function (parmNames, body) {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
Process.prototype.doRun = function (context, args, isCustomBlock) {
|
Process.prototype.doRun = function (context, args) {
|
||||||
return this.evaluate(context, args, true, isCustomBlock);
|
return this.evaluate(context, args, true);
|
||||||
};
|
};
|
||||||
|
|
||||||
Process.prototype.evaluate = function (
|
Process.prototype.evaluate = function (
|
||||||
|
@ -781,8 +833,9 @@ Process.prototype.evaluate = function (
|
||||||
}
|
}
|
||||||
|
|
||||||
var outer = new Context(null, null, context.outerContext),
|
var outer = new Context(null, null, context.outerContext),
|
||||||
|
caller = this.context.parentContext,
|
||||||
|
exit,
|
||||||
runnable,
|
runnable,
|
||||||
extra,
|
|
||||||
parms = args.asArray(),
|
parms = args.asArray(),
|
||||||
i,
|
i,
|
||||||
value;
|
value;
|
||||||
|
@ -796,25 +849,13 @@ Process.prototype.evaluate = function (
|
||||||
outer,
|
outer,
|
||||||
context.receiver
|
context.receiver
|
||||||
);
|
);
|
||||||
extra = new Context(runnable, 'doYield');
|
this.context.parentContext = runnable;
|
||||||
|
|
||||||
/*
|
if (context.expression instanceof ReporterBlockMorph) {
|
||||||
Note: if the context's expression is a ReporterBlockMorph,
|
// auto-"warp" nested reporters
|
||||||
the extra context gets popped off immediately without taking
|
this.readyToYield = (Date.now() - this.lastYield > this.timeout);
|
||||||
effect (i.e. it doesn't yield within evaluating a stack of
|
|
||||||
nested reporters)
|
|
||||||
*/
|
|
||||||
|
|
||||||
if (isCommand || (context.expression instanceof ReporterBlockMorph)) {
|
|
||||||
this.context.parentContext = extra;
|
|
||||||
} else {
|
|
||||||
this.context.parentContext = runnable;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
runnable.isLambda = true;
|
|
||||||
runnable.isImplicitLambda = context.isImplicitLambda;
|
|
||||||
runnable.isCustomBlock = false;
|
|
||||||
|
|
||||||
// assign parameters if any were passed
|
// assign parameters if any were passed
|
||||||
if (parms.length > 0) {
|
if (parms.length > 0) {
|
||||||
|
|
||||||
|
@ -849,8 +890,9 @@ Process.prototype.evaluate = function (
|
||||||
|
|
||||||
} else if (context.emptySlots !== 1) {
|
} else if (context.emptySlots !== 1) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
'expecting ' + context.emptySlots + ' input(s), '
|
localize('expecting') + ' ' + context.emptySlots + ' '
|
||||||
+ 'but getting ' + parms.length
|
+ localize('input(s), but getting') + ' '
|
||||||
|
+ parms.length
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -858,6 +900,23 @@ Process.prototype.evaluate = function (
|
||||||
|
|
||||||
if (runnable.expression instanceof CommandBlockMorph) {
|
if (runnable.expression instanceof CommandBlockMorph) {
|
||||||
runnable.expression = runnable.expression.blockSequence();
|
runnable.expression = runnable.expression.blockSequence();
|
||||||
|
if (!isCommand) {
|
||||||
|
if (caller) {
|
||||||
|
// tag caller, so "report" can catch it later
|
||||||
|
caller.tag = 'exit';
|
||||||
|
} else {
|
||||||
|
// top-level context, insert a tagged exit context
|
||||||
|
// which "report" can catch later
|
||||||
|
exit = new Context(
|
||||||
|
runnable.parentContext,
|
||||||
|
'expectReport',
|
||||||
|
outer,
|
||||||
|
outer.receiver
|
||||||
|
);
|
||||||
|
exit.tag = 'exit';
|
||||||
|
runnable.parentContext = exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -867,6 +926,9 @@ Process.prototype.fork = function (context, args) {
|
||||||
'continuations cannot be forked'
|
'continuations cannot be forked'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
if (!(context instanceof Context)) {
|
||||||
|
throw new Error('expecting a ring but getting ' + context);
|
||||||
|
}
|
||||||
|
|
||||||
var outer = new Context(null, null, context.outerContext),
|
var outer = new Context(null, null, context.outerContext),
|
||||||
runnable = new Context(null,
|
runnable = new Context(null,
|
||||||
|
@ -879,8 +941,6 @@ Process.prototype.fork = function (context, args) {
|
||||||
stage = this.homeContext.receiver.parentThatIsA(StageMorph),
|
stage = this.homeContext.receiver.parentThatIsA(StageMorph),
|
||||||
proc = new Process();
|
proc = new Process();
|
||||||
|
|
||||||
runnable.isLambda = true;
|
|
||||||
|
|
||||||
// assign parameters if any were passed
|
// assign parameters if any were passed
|
||||||
if (parms.length > 0) {
|
if (parms.length > 0) {
|
||||||
|
|
||||||
|
@ -915,8 +975,9 @@ Process.prototype.fork = function (context, args) {
|
||||||
|
|
||||||
} else if (context.emptySlots !== 1) {
|
} else if (context.emptySlots !== 1) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
'expecting ' + context.emptySlots + ' input(s), '
|
localize('expecting') + ' ' + context.emptySlots + ' '
|
||||||
+ 'but getting ' + parms.length
|
+ localize('input(s), but getting') + ' '
|
||||||
|
+ parms.length
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -933,68 +994,48 @@ Process.prototype.fork = function (context, args) {
|
||||||
stage.threads.processes.push(proc);
|
stage.threads.processes.push(proc);
|
||||||
};
|
};
|
||||||
|
|
||||||
Process.prototype.doReport = function (value, isCSlot) {
|
// Process stopping blocks primitives
|
||||||
while (this.context && !this.context.isLambda) {
|
|
||||||
if (this.context.expression === 'doStopWarping') {
|
|
||||||
this.doStopWarping();
|
|
||||||
} else {
|
|
||||||
this.popContext();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (this.context && this.context.isImplicitLambda) {
|
|
||||||
if (this.context.expression === 'doStopWarping') {
|
|
||||||
this.doStopWarping();
|
|
||||||
} else {
|
|
||||||
this.popContext();
|
|
||||||
}
|
|
||||||
return this.doReport(value, true);
|
|
||||||
}
|
|
||||||
if (this.context && this.context.isCustomBlock) {
|
|
||||||
// now I'm back at the custom block sequence.
|
|
||||||
// advance my pc to my expression's length
|
|
||||||
this.context.pc = this.context.expression.length - 1;
|
|
||||||
}
|
|
||||||
if (isCSlot) {
|
|
||||||
if (this.context &&
|
|
||||||
this.context.parentContext &&
|
|
||||||
this.context.parentContext.expression instanceof Array) {
|
|
||||||
this.popContext();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return value;
|
|
||||||
};
|
|
||||||
|
|
||||||
Process.prototype.doStopBlock = function () {
|
Process.prototype.doStopBlock = function () {
|
||||||
this.doReport();
|
var target = this.context.expression.exitTag;
|
||||||
|
if (isNil(target)) {
|
||||||
|
return this.doStopCustomBlock();
|
||||||
|
}
|
||||||
|
while (this.context &&
|
||||||
|
(isNil(this.context.tag) || (this.context.tag > target))) {
|
||||||
|
if (this.context.expression === 'doStopWarping') {
|
||||||
|
this.doStopWarping();
|
||||||
|
} else {
|
||||||
|
this.popContext();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.pushContext();
|
||||||
};
|
};
|
||||||
|
|
||||||
// Process evaluation variants, commented out for now (redundant)
|
Process.prototype.doStopCustomBlock = function () {
|
||||||
|
// fallback solution for "report" blocks inside
|
||||||
/*
|
// custom command definitions and untagged "stop" blocks
|
||||||
Process.prototype.doRunWithInputList = function (context, args) {
|
while (this.context && !this.context.isCustomBlock) {
|
||||||
// provide an extra selector for the palette
|
if (this.context.expression === 'doStopWarping') {
|
||||||
return this.doRun(context, args);
|
this.doStopWarping();
|
||||||
|
} else {
|
||||||
|
this.popContext();
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Process.prototype.evaluateWithInputList = function (context, args) {
|
|
||||||
// provide an extra selector for the palette
|
|
||||||
return this.evaluate(context, args);
|
|
||||||
};
|
|
||||||
|
|
||||||
Process.prototype.forkWithInputList = function (context, args) {
|
|
||||||
// provide an extra selector for the palette
|
|
||||||
return this.fork(context, args);
|
|
||||||
};
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Process continuations primitives
|
// Process continuations primitives
|
||||||
|
|
||||||
Process.prototype.doCallCC = function (aContext) {
|
Process.prototype.doCallCC = function (aContext, isReporter) {
|
||||||
this.evaluate(aContext, new List([this.context.continuation()]));
|
this.evaluate(
|
||||||
|
aContext,
|
||||||
|
new List([this.context.continuation()]),
|
||||||
|
!isReporter
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
Process.prototype.reportCallCC = function (aContext) {
|
Process.prototype.reportCallCC = function (aContext) {
|
||||||
this.doCallCC(aContext);
|
this.doCallCC(aContext, true);
|
||||||
};
|
};
|
||||||
|
|
||||||
Process.prototype.runContinuation = function (aContext, args) {
|
Process.prototype.runContinuation = function (aContext, args) {
|
||||||
|
@ -1012,19 +1053,21 @@ Process.prototype.runContinuation = function (aContext, args) {
|
||||||
// Process custom block primitives
|
// Process custom block primitives
|
||||||
|
|
||||||
Process.prototype.evaluateCustomBlock = function () {
|
Process.prototype.evaluateCustomBlock = function () {
|
||||||
var context = this.context.expression.definition.body,
|
var caller = this.context.parentContext,
|
||||||
|
context = this.context.expression.definition.body,
|
||||||
declarations = this.context.expression.definition.declarations,
|
declarations = this.context.expression.definition.declarations,
|
||||||
args = new List(this.context.inputs),
|
args = new List(this.context.inputs),
|
||||||
parms = args.asArray(),
|
parms = args.asArray(),
|
||||||
runnable,
|
runnable,
|
||||||
extra,
|
exit,
|
||||||
i,
|
i,
|
||||||
value,
|
value,
|
||||||
outer;
|
outer;
|
||||||
|
|
||||||
if (!context) {return null; }
|
if (!context) {return null; }
|
||||||
|
this.procedureCount += 1;
|
||||||
outer = new Context();
|
outer = new Context();
|
||||||
outer.receiver = this.context.receiver; // || this.homeContext.receiver;
|
outer.receiver = this.context.receiver;
|
||||||
outer.variables.parentFrame = outer.receiver ?
|
outer.variables.parentFrame = outer.receiver ?
|
||||||
outer.receiver.variables : null;
|
outer.receiver.variables : null;
|
||||||
|
|
||||||
|
@ -1032,15 +1075,10 @@ Process.prototype.evaluateCustomBlock = function () {
|
||||||
this.context.parentContext,
|
this.context.parentContext,
|
||||||
context.expression,
|
context.expression,
|
||||||
outer,
|
outer,
|
||||||
outer.receiver,
|
outer.receiver
|
||||||
true // is custom block
|
|
||||||
);
|
);
|
||||||
extra = new Context(runnable, 'doYield');
|
|
||||||
|
|
||||||
this.context.parentContext = extra;
|
|
||||||
|
|
||||||
runnable.isLambda = true;
|
|
||||||
runnable.isCustomBlock = true;
|
runnable.isCustomBlock = true;
|
||||||
|
this.context.parentContext = runnable;
|
||||||
|
|
||||||
// passing parameters if any were passed
|
// passing parameters if any were passed
|
||||||
if (parms.length > 0) {
|
if (parms.length > 0) {
|
||||||
|
@ -1062,9 +1100,43 @@ Process.prototype.evaluateCustomBlock = function () {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (runnable.expression instanceof CommandBlockMorph) {
|
// tag return target
|
||||||
runnable.expression = runnable.expression.blockSequence();
|
if (this.context.expression.definition.type !== 'command') {
|
||||||
|
if (caller) {
|
||||||
|
// tag caller, so "report" can catch it later
|
||||||
|
caller.tag = 'exit';
|
||||||
|
} else {
|
||||||
|
// top-level context, insert a tagged exit context
|
||||||
|
// which "report" can catch later
|
||||||
|
exit = new Context(
|
||||||
|
runnable.parentContext,
|
||||||
|
'expectReport',
|
||||||
|
outer,
|
||||||
|
outer.receiver
|
||||||
|
);
|
||||||
|
exit.tag = 'exit';
|
||||||
|
runnable.parentContext = exit;
|
||||||
|
}
|
||||||
|
// auto-"warp" nested reporters
|
||||||
|
this.readyToYield = (Date.now() - this.lastYield > this.timeout);
|
||||||
|
} else {
|
||||||
|
// tag all "stop this block" blocks with the current
|
||||||
|
// procedureCount as exitTag, and mark all "report" blocks
|
||||||
|
// as being inside a custom command definition
|
||||||
|
runnable.expression.tagExitBlocks(this.procedureCount, true);
|
||||||
|
|
||||||
|
// tag the caller with the current procedure count, so
|
||||||
|
// "stop this block" blocks can catch it, but only
|
||||||
|
// if the caller hasn't been tagged already
|
||||||
|
if (caller && !caller.tag) {
|
||||||
|
caller.tag = this.procedureCount;
|
||||||
|
}
|
||||||
|
// yield commands unless explicitly "warped"
|
||||||
|
if (!this.isAtomic) {
|
||||||
|
this.readyToYield = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
runnable.expression = runnable.expression.blockSequence();
|
||||||
};
|
};
|
||||||
|
|
||||||
// Process variables primitives
|
// Process variables primitives
|
||||||
|
@ -1148,7 +1220,7 @@ Process.prototype.doShowVar = function (varName) {
|
||||||
if (isGlobal || target.owner) {
|
if (isGlobal || target.owner) {
|
||||||
label = name;
|
label = name;
|
||||||
} else {
|
} else {
|
||||||
label = name + ' (temporary)';
|
label = name + ' ' + localize('(temporary)');
|
||||||
}
|
}
|
||||||
watcher = new WatcherMorph(
|
watcher = new WatcherMorph(
|
||||||
label,
|
label,
|
||||||
|
@ -1259,7 +1331,7 @@ Process.prototype.doInsertInList = function (element, index, list) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if (this.inputOption(index) === 'any') {
|
if (this.inputOption(index) === 'any') {
|
||||||
idx = this.reportRandom(1, list.length());
|
idx = this.reportRandom(1, list.length() + 1);
|
||||||
}
|
}
|
||||||
if (this.inputOption(index) === 'last') {
|
if (this.inputOption(index) === 'last') {
|
||||||
idx = list.length() + 1;
|
idx = list.length() + 1;
|
||||||
|
@ -1308,16 +1380,12 @@ Process.prototype.reportListContainsItem = function (list, element) {
|
||||||
Process.prototype.doIf = function () {
|
Process.prototype.doIf = function () {
|
||||||
var args = this.context.inputs,
|
var args = this.context.inputs,
|
||||||
outer = this.context.outerContext, // for tail call elimination
|
outer = this.context.outerContext, // for tail call elimination
|
||||||
isLambda = this.context.isLambda,
|
|
||||||
isImplicitLambda = this.context.isImplicitLambda,
|
|
||||||
isCustomBlock = this.context.isCustomBlock;
|
isCustomBlock = this.context.isCustomBlock;
|
||||||
|
|
||||||
this.popContext();
|
this.popContext();
|
||||||
if (args[0]) {
|
if (args[0]) {
|
||||||
if (args[1]) {
|
if (args[1]) {
|
||||||
this.pushContext(args[1].blockSequence(), outer);
|
this.pushContext(args[1].blockSequence(), outer);
|
||||||
this.context.isLambda = isLambda;
|
|
||||||
this.context.isImplicitLambda = isImplicitLambda;
|
|
||||||
this.context.isCustomBlock = isCustomBlock;
|
this.context.isCustomBlock = isCustomBlock;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1327,8 +1395,6 @@ Process.prototype.doIf = function () {
|
||||||
Process.prototype.doIfElse = function () {
|
Process.prototype.doIfElse = function () {
|
||||||
var args = this.context.inputs,
|
var args = this.context.inputs,
|
||||||
outer = this.context.outerContext, // for tail call elimination
|
outer = this.context.outerContext, // for tail call elimination
|
||||||
isLambda = this.context.isLambda,
|
|
||||||
isImplicitLambda = this.context.isImplicitLambda,
|
|
||||||
isCustomBlock = this.context.isCustomBlock;
|
isCustomBlock = this.context.isCustomBlock;
|
||||||
|
|
||||||
this.popContext();
|
this.popContext();
|
||||||
|
@ -1344,8 +1410,6 @@ Process.prototype.doIfElse = function () {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (this.context) {
|
if (this.context) {
|
||||||
this.context.isLambda = isLambda;
|
|
||||||
this.context.isImplicitLambda = isImplicitLambda;
|
|
||||||
this.context.isCustomBlock = isCustomBlock;
|
this.context.isCustomBlock = isCustomBlock;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1420,8 +1484,6 @@ Process.prototype.doStopOthers = function (choice) {
|
||||||
Process.prototype.doWarp = function (body) {
|
Process.prototype.doWarp = function (body) {
|
||||||
// execute my contents block atomically (more or less)
|
// execute my contents block atomically (more or less)
|
||||||
var outer = this.context.outerContext, // for tail call elimination
|
var outer = this.context.outerContext, // for tail call elimination
|
||||||
isLambda = this.context.isLambda,
|
|
||||||
isImplicitLambda = this.context.isImplicitLambda,
|
|
||||||
isCustomBlock = this.context.isCustomBlock,
|
isCustomBlock = this.context.isCustomBlock,
|
||||||
stage;
|
stage;
|
||||||
|
|
||||||
|
@ -1438,13 +1500,8 @@ Process.prototype.doWarp = function (body) {
|
||||||
stage.fps = 0; // variable frame rate
|
stage.fps = 0; // variable frame rate
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.pushContext('doYield');
|
this.pushContext('doYield');
|
||||||
|
|
||||||
this.context.isLambda = isLambda;
|
|
||||||
this.context.isImplicitLambda = isImplicitLambda;
|
|
||||||
this.context.isCustomBlock = isCustomBlock;
|
this.context.isCustomBlock = isCustomBlock;
|
||||||
|
|
||||||
if (!this.isAtomic) {
|
if (!this.isAtomic) {
|
||||||
this.pushContext('doStopWarping');
|
this.pushContext('doStopWarping');
|
||||||
}
|
}
|
||||||
|
@ -1523,28 +1580,19 @@ Process.prototype.doForever = function (body) {
|
||||||
Process.prototype.doRepeat = function (counter, body) {
|
Process.prototype.doRepeat = function (counter, body) {
|
||||||
var block = this.context.expression,
|
var block = this.context.expression,
|
||||||
outer = this.context.outerContext, // for tail call elimination
|
outer = this.context.outerContext, // for tail call elimination
|
||||||
isLambda = this.context.isLambda,
|
|
||||||
isImplicitLambda = this.context.isImplicitLambda,
|
|
||||||
isCustomBlock = this.context.isCustomBlock;
|
isCustomBlock = this.context.isCustomBlock;
|
||||||
|
|
||||||
if (counter < 1) { // was '=== 0', which caused infinite loops on non-ints
|
if (counter < 1) { // was '=== 0', which caused infinite loops on non-ints
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
this.popContext();
|
this.popContext();
|
||||||
|
|
||||||
this.pushContext(block, outer);
|
this.pushContext(block, outer);
|
||||||
|
|
||||||
this.context.isLambda = isLambda;
|
|
||||||
this.context.isImplicitLambda = isImplicitLambda;
|
|
||||||
this.context.isCustomBlock = isCustomBlock;
|
this.context.isCustomBlock = isCustomBlock;
|
||||||
|
|
||||||
this.context.addInput(counter - 1);
|
this.context.addInput(counter - 1);
|
||||||
|
|
||||||
this.pushContext('doYield');
|
this.pushContext('doYield');
|
||||||
if (body) {
|
if (body) {
|
||||||
this.pushContext(body.blockSequence());
|
this.pushContext(body.blockSequence());
|
||||||
}
|
}
|
||||||
|
|
||||||
this.pushContext();
|
this.pushContext();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1631,6 +1679,27 @@ Process.prototype.reportMap = function (reporter, list) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Process.prototype.doForEach = function (upvar, list, script) {
|
||||||
|
// perform a script for each element of a list, assigning the
|
||||||
|
// current iteration's element to a variable with the name
|
||||||
|
// specified in the "upvar" parameter, so it can be referenced
|
||||||
|
// within the script. Uses the context's - unused - fourth
|
||||||
|
// element as temporary storage for the current list index
|
||||||
|
|
||||||
|
if (isNil(this.context.inputs[3])) {this.context.inputs[3] = 1; }
|
||||||
|
var index = this.context.inputs[3];
|
||||||
|
this.context.outerContext.variables.addVar(upvar);
|
||||||
|
this.context.outerContext.variables.setVar(
|
||||||
|
upvar,
|
||||||
|
list.at(index)
|
||||||
|
);
|
||||||
|
if (index > list.length()) {return; }
|
||||||
|
this.context.inputs[3] += 1;
|
||||||
|
this.pushContext('doYield');
|
||||||
|
this.pushContext();
|
||||||
|
this.evaluate(script, new List(), true);
|
||||||
|
};
|
||||||
|
|
||||||
// Process interpolated primitives
|
// Process interpolated primitives
|
||||||
|
|
||||||
Process.prototype.doWait = function (secs) {
|
Process.prototype.doWait = function (secs) {
|
||||||
|
@ -2134,15 +2203,17 @@ Process.prototype.reportTextSplit = function (string, delimiter) {
|
||||||
str,
|
str,
|
||||||
del;
|
del;
|
||||||
if (!contains(types, strType)) {
|
if (!contains(types, strType)) {
|
||||||
throw new Error('expecting a text instad of a ' + strType);
|
throw new Error('expecting text instead of a ' + strType);
|
||||||
}
|
}
|
||||||
if (!contains(types, delType)) {
|
if (!contains(types, delType)) {
|
||||||
throw new Error('expecting a text delimiter instad of a ' + delType);
|
throw new Error('expecting a text delimiter instead of a ' + delType);
|
||||||
}
|
}
|
||||||
str = (string || '').toString();
|
str = (string || '').toString();
|
||||||
switch (this.inputOption(delimiter)) {
|
switch (this.inputOption(delimiter)) {
|
||||||
case 'line':
|
case 'line':
|
||||||
del = '\n';
|
// Unicode Compliant Line Splitting (Platform independent)
|
||||||
|
// http://www.unicode.org/reports/tr18/#Line_Boundaries
|
||||||
|
del = /\r\n|[\n\v\f\r\x85\u2028\u2029]/;
|
||||||
break;
|
break;
|
||||||
case 'tab':
|
case 'tab':
|
||||||
del = '\t';
|
del = '\t';
|
||||||
|
@ -2151,7 +2222,9 @@ Process.prototype.reportTextSplit = function (string, delimiter) {
|
||||||
del = '\r';
|
del = '\r';
|
||||||
break;
|
break;
|
||||||
case 'whitespace':
|
case 'whitespace':
|
||||||
return new List(str.trim().split(/[\t\r\n ]+/));
|
str = str.trim();
|
||||||
|
del = /\s+/;
|
||||||
|
break;
|
||||||
case 'letter':
|
case 'letter':
|
||||||
del = '';
|
del = '';
|
||||||
break;
|
break;
|
||||||
|
@ -2316,6 +2389,7 @@ Process.prototype.objectTouchingObject = function (thisObj, name) {
|
||||||
var myself = this,
|
var myself = this,
|
||||||
those,
|
those,
|
||||||
stage,
|
stage,
|
||||||
|
box,
|
||||||
mouse;
|
mouse;
|
||||||
|
|
||||||
if (this.inputOption(name) === 'mouse-pointer') {
|
if (this.inputOption(name) === 'mouse-pointer') {
|
||||||
|
@ -2327,9 +2401,14 @@ Process.prototype.objectTouchingObject = function (thisObj, name) {
|
||||||
} else {
|
} else {
|
||||||
stage = thisObj.parentThatIsA(StageMorph);
|
stage = thisObj.parentThatIsA(StageMorph);
|
||||||
if (stage) {
|
if (stage) {
|
||||||
if (this.inputOption(name) === 'edge' &&
|
if (this.inputOption(name) === 'edge') {
|
||||||
!stage.bounds.containsRectangle(thisObj.bounds)) {
|
box = thisObj.bounds;
|
||||||
return true;
|
if (!thisObj.costume && thisObj.penBounds) {
|
||||||
|
box = thisObj.penBounds.translateBy(thisObj.position());
|
||||||
|
}
|
||||||
|
if (!stage.bounds.containsRectangle(box)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (this.inputOption(name) === 'pen trails' &&
|
if (this.inputOption(name) === 'pen trails' &&
|
||||||
thisObj.isTouching(stage.penTrailsMorph())) {
|
thisObj.isTouching(stage.penTrailsMorph())) {
|
||||||
|
@ -2760,25 +2839,25 @@ Process.prototype.reportFrameCount = function () {
|
||||||
|
|
||||||
structure:
|
structure:
|
||||||
|
|
||||||
parentContext the Context to return to when this one has
|
parentContext the Context to return to when this one has
|
||||||
been evaluated.
|
been evaluated.
|
||||||
outerContext the Context holding my lexical scope
|
outerContext the Context holding my lexical scope
|
||||||
expression SyntaxElementMorph, an array of blocks to evaluate,
|
expression SyntaxElementMorph, an array of blocks to evaluate,
|
||||||
null or a String denoting a selector, e.g. 'doYield'
|
null or a String denoting a selector, e.g. 'doYield'
|
||||||
receiver the object to which the expression applies, if any
|
receiver the object to which the expression applies, if any
|
||||||
variables the current VariableFrame, if any
|
variables the current VariableFrame, if any
|
||||||
inputs an array of input values computed so far
|
inputs an array of input values computed so far
|
||||||
(if expression is a BlockMorph)
|
(if expression is a BlockMorph)
|
||||||
pc the index of the next block to evaluate
|
pc the index of the next block to evaluate
|
||||||
(if expression is an array)
|
(if expression is an array)
|
||||||
startTime time when the context was first evaluated
|
startTime time when the context was first evaluated
|
||||||
startValue initial value for interpolated operations
|
startValue initial value for interpolated operations
|
||||||
activeAudio audio buffer for interpolated operations, don't persist
|
activeAudio audio buffer for interpolated operations, don't persist
|
||||||
activeNote audio oscillator for interpolated ops, don't persist
|
activeNote audio oscillator for interpolated ops, don't persist
|
||||||
isLambda marker for return ops
|
|
||||||
isImplicitLambda marker for return ops
|
|
||||||
isCustomBlock marker for return ops
|
isCustomBlock marker for return ops
|
||||||
emptySlots caches the number of empty slots for reification
|
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)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function Context(
|
function Context(
|
||||||
|
@ -2801,22 +2880,19 @@ function Context(
|
||||||
this.startTime = null;
|
this.startTime = null;
|
||||||
this.activeAudio = null;
|
this.activeAudio = null;
|
||||||
this.activeNote = null;
|
this.activeNote = null;
|
||||||
this.isLambda = false; // marks the end of a lambda
|
|
||||||
this.isImplicitLambda = false; // marks the end of a C-shaped slot
|
|
||||||
this.isCustomBlock = false; // marks the end of a custom block's stack
|
this.isCustomBlock = false; // marks the end of a custom block's stack
|
||||||
this.emptySlots = 0; // used for block reification
|
this.emptySlots = 0; // used for block reification
|
||||||
|
this.tag = null; // lexical catch-tag for custom blocks
|
||||||
}
|
}
|
||||||
|
|
||||||
Context.prototype.toString = function () {
|
Context.prototype.toString = function () {
|
||||||
var pref = this.isLambda ? '\u03BB-' : '',
|
var expr = this.expression;
|
||||||
expr = this.expression;
|
|
||||||
|
|
||||||
if (expr instanceof Array) {
|
if (expr instanceof Array) {
|
||||||
if (expr.length > 0) {
|
if (expr.length > 0) {
|
||||||
expr = '[' + expr[0] + ']';
|
expr = '[' + expr[0] + ']';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return pref + 'Context >> ' + expr + ' ' + this.variables;
|
return 'Context >> ' + expr + ' ' + this.variables;
|
||||||
};
|
};
|
||||||
|
|
||||||
Context.prototype.image = function () {
|
Context.prototype.image = function () {
|
||||||
|
@ -2870,9 +2946,10 @@ Context.prototype.continuation = function () {
|
||||||
} else if (this.parentContext) {
|
} else if (this.parentContext) {
|
||||||
cont = this.parentContext;
|
cont = this.parentContext;
|
||||||
} else {
|
} else {
|
||||||
return new Context(null, 'doStop');
|
return new Context(null, 'doYield');
|
||||||
}
|
}
|
||||||
cont = cont.copyForContinuation();
|
cont = cont.copyForContinuation();
|
||||||
|
cont.tag = null;
|
||||||
cont.isContinuation = true;
|
cont.isContinuation = true;
|
||||||
return cont;
|
return cont;
|
||||||
};
|
};
|
||||||
|
@ -3003,9 +3080,9 @@ VariableFrame.prototype.find = function (name) {
|
||||||
var frame = this.silentFind(name);
|
var frame = this.silentFind(name);
|
||||||
if (frame) {return frame; }
|
if (frame) {return frame; }
|
||||||
throw new Error(
|
throw new Error(
|
||||||
'a variable of name \''
|
localize('a variable of name \'')
|
||||||
+ name
|
+ name
|
||||||
+ '\'\ndoes not exist in this context'
|
+ localize('\'\ndoes not exist in this context')
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -3070,9 +3147,9 @@ VariableFrame.prototype.getVar = function (name) {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
throw new Error(
|
throw new Error(
|
||||||
'a variable of name \''
|
localize('a variable of name \'')
|
||||||
+ name
|
+ name
|
||||||
+ '\'\ndoes not exist in this context'
|
+ localize('\'\ndoes not exist in this context')
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
File diff suppressed because one or more lines are too long
Ładowanie…
Reference in New Issue