diff options
| -rw-r--r-- | blocks.js | 102 | ||||
| -rw-r--r-- | gui.js | 8 | ||||
| -rwxr-xr-x | history.txt | 29 | ||||
| -rwxr-xr-x | lang-pt.js | 2 | ||||
| -rw-r--r-- | morphic.js | 5 | ||||
| -rw-r--r-- | objects.js | 24 | ||||
| -rw-r--r-- | store.js | 10 | ||||
| -rw-r--r-- | threads.js | 190 |
8 files changed, 196 insertions, 174 deletions
@@ -155,7 +155,7 @@ DialogBoxMorph, BlockInputFragmentMorph, PrototypeHatBlockMorph, Costume*/ // Global stuff //////////////////////////////////////////////////////// -modules.blocks = '2014-July-29'; +modules.blocks = '2014-September-22'; var SyntaxElementMorph; @@ -396,10 +396,11 @@ SyntaxElementMorph.prototype.allInputs = function () { SyntaxElementMorph.prototype.allEmptySlots = function () { /* answer empty input slots of all children excluding myself, - but omit those in nested rings (lambdas) + but omit those in nested rings (lambdas) and JS-Function primitives */ var empty = []; - if (!(this instanceof RingMorph)) { + if (!(this instanceof RingMorph) && + (this.selector !== 'reportJSFunction')) { this.children.forEach(function (morph) { if (morph.isEmptySlot && morph.isEmptySlot()) { empty.push(morph); @@ -1621,7 +1622,7 @@ SyntaxElementMorph.prototype.isEmptySlot = function () { // SyntaxElementMorph speech bubble feedback: -SyntaxElementMorph.prototype.showBubble = function (value) { +SyntaxElementMorph.prototype.showBubble = function (value, exportPic) { var bubble, txt, img, @@ -1695,11 +1696,28 @@ SyntaxElementMorph.prototype.showBubble = function (value) { this.rightCenter().add(new Point(2, 0)), isClickable ); + if (exportPic) { + this.exportPictureWithResult(bubble); + } if (sf) { bubble.keepWithin(sf); } }; +SyntaxElementMorph.prototype.exportPictureWithResult = function (aBubble) { + var scr = this.fullImage(), + bub = aBubble.fullImageClassic(), + taller = Math.max(0, bub.height - scr.height), + pic = newCanvas(new Point( + scr.width + bub.width + 2, + scr.height + taller + )), + ctx = pic.getContext('2d'); + ctx.drawImage(scr, 0, pic.height - scr.height); + ctx.drawImage(bub, scr.width + 2, 0); + window.open(pic.toDataURL()); +}; + // SyntaxElementMorph code mapping /* @@ -2026,13 +2044,29 @@ BlockMorph.prototype.userMenu = function () { var menu = new MenuMorph(this), world = this.world(), myself = this, + shiftClicked = world.currentKey === 16, alternatives, + top, blck; menu.addItem( "help...", 'showHelp' ); + if (shiftClicked) { + top = this.topBlock(); + if (top instanceof ReporterBlockMorph) { + menu.addItem( + "script pic with result...", + function () { + top.ExportResultPic(); + }, + 'open a new window\n' + + 'with a picture of both\nthis script and its result', + new Color(100, 0, 0) + ); + } + } if (this.isTemplate) { if (!(this.parent instanceof SyntaxElementMorph)) { if (this.selector !== 'evaluateCustomBlock') { @@ -4115,6 +4149,23 @@ ReporterBlockMorph.prototype.mouseClickLeft = function (pos) { } }; +// ReporterBlock exporting picture with result bubble + +ReporterBlockMorph.prototype.ExportResultPic = function () { + var top = this.topBlock(), + receiver = top.receiver(), + stage; + if (top !== this) {return; } + if (receiver) { + stage = receiver.parentThatIsA(StageMorph); + if (stage) { + stage.threads.stopProcess(top); + stage.threads.startProcess(top, false, true); + } + } +}; + + // ReporterBlockMorph deleting ReporterBlockMorph.prototype.userDestroy = function () { @@ -6704,9 +6755,6 @@ InputSlotMorph.prototype.soundsMenu = function () { InputSlotMorph.prototype.getVarNamesDict = function () { var block = this.parentThatIsA(BlockMorph), rcvr, - proto, - rings, - declarations, tempVars = [], dict; @@ -6714,28 +6762,26 @@ InputSlotMorph.prototype.getVarNamesDict = function () { return {}; } rcvr = block.receiver(); - - proto = detect(block.allParents(), function (morph) { - return morph instanceof PrototypeHatBlockMorph; - }); - if (proto) { - tempVars = proto.inputs()[0].inputFragmentNames(); - } - - rings = block.allParents().filter(function (block) { - return block instanceof RingMorph; - }); - rings.forEach(function (block) { - tempVars = tempVars.concat(block.inputs()[1].evaluate()); - }); - - declarations = block.allParents().filter(function (block) { - return block.selector === 'doDeclareVariables'; - }); - declarations.forEach(function (block) { - tempVars = tempVars.concat(block.inputs()[0].evaluate()); + block.allParents().forEach(function (morph) { + if (morph instanceof PrototypeHatBlockMorph) { + tempVars.push.apply( + tempVars, + morph.inputs()[0].inputFragmentNames() + ); + } else if (morph instanceof BlockMorph) { + morph.inputs().forEach(function (inp) { + if (inp instanceof TemplateSlotMorph) { + tempVars.push(inp.contents()); + } else if (inp instanceof MultiArgMorph) { + inp.children.forEach(function (m) { + if (m instanceof TemplateSlotMorph) { + tempVars.push(m.contents()); + } + }); + } + }); + } }); - if (rcvr) { dict = rcvr.variables.allNamesDict(); tempVars.forEach(function (name) { @@ -69,7 +69,7 @@ SpeechBubbleMorph*/ // Global stuff //////////////////////////////////////////////////////// -modules.gui = '2014-July-29'; +modules.gui = '2014-September-22'; // Declarations @@ -1831,7 +1831,9 @@ IDE_Morph.prototype.duplicateSprite = function (sprite) { }; IDE_Morph.prototype.removeSprite = function (sprite) { - var idx = this.sprites.asArray().indexOf(sprite) + 1; + var idx, myself = this; + sprite.parts.forEach(function (part) {myself.removeSprite(part); }); + idx = this.sprites.asArray().indexOf(sprite) + 1; this.stage.threads.stopAllForReceiver(sprite); sprite.destroy(); this.stage.watchers().forEach(function (watcher) { @@ -1857,7 +1859,7 @@ IDE_Morph.prototype.newSpriteName = function (name, ignoredSprite) { stem = (ix < 0) ? name : name.substring(0, ix), count = 1, newName = stem, - all = this.sprites.asArray().filter( + all = this.sprites.asArray().concat(this.stage).filter( function (each) {return each !== ignoredSprite; } ).map( function (each) {return each.name; } diff --git a/history.txt b/history.txt index 0568bda..25993eb 100755 --- a/history.txt +++ b/history.txt @@ -2250,3 +2250,32 @@ ______ * updated German translation * Morphic: fixed #497 (prevent bubble shadows from getting cut-off) * Blocks: keep result-bubbles within the enclosing scripting pane + +140730 +------ +* Objects: propagate HIDE and SHOW to nested sprite parts +* GUI: propagate DELETE to nested sprite parts +* Blocks, Threads: export script pic with result bubble (shift-context-menu of reporter scripts) +* updated Portuguese translation, thanks, Manuel! + +140813 +------ +* Threads, Blocks: enable Zombiefication of JS-Functions +* Morphic: Fix #563 (Paste into Chrome), thanks, @Muon, for the hint! + +140917 +------ +* Threads, Objects, Store: Refactor variables handling, introducing Variable objects, all functionality stays the same + +140918 +------ +* Threads: fixed #174, replace UpvarReferences with references to Variable objects, fixes upvar scope issues + +140922 +------ +* Blocks: Make upvars mutable +* GUI: fixed #585 (sprite name conflict with stage). Thanks, Michael, for the report! + +140929 +------ +* Threads: fixed #591 fully copy local variables for sprite duplicates and (Scratch-like) clones @@ -835,6 +835,8 @@ SnapTranslator.dict.pt = { // actores: 'edit': 'editar', + 'move': + 'mover', 'detach from': 'soltar de', 'detach all parts': @@ -1035,7 +1035,7 @@ /*global window, HTMLCanvasElement, getMinimumFontHeight, FileReader, Audio, FileList, getBlurredShadowSupport*/ -var morphicVersion = '2014-July-29'; +var morphicVersion = '2014-August-13'; var modules = {}; // keep track of additional loaded modules var useBlurredShadows = getBlurredShadowSupport(); // check for Chrome-bug @@ -10321,7 +10321,8 @@ WorldMorph.prototype.initEventListeners = function () { } event.preventDefault(); } - if (event.ctrlKey || event.metaKey) { + if ((event.ctrlKey || event.metaKey) && + (event.keyIdentifier !== 'U+0056')) { // allow pasting-in event.preventDefault(); } }, @@ -125,7 +125,7 @@ PrototypeHatBlockMorph*/ // Global stuff //////////////////////////////////////////////////////// -modules.objects = '2014-July-29'; +modules.objects = '2014-September-17'; var SpriteMorph; var StageMorph; @@ -2708,6 +2708,23 @@ SpriteMorph.prototype.removeClone = function () { // SpriteMorph primitives +// SpriteMorph hiding and showing: + +/* + override the inherited behavior to also hide/show all + nested parts. +*/ + +SpriteMorph.prototype.hide = function () { + SpriteMorph.uber.hide.call(this); + this.parts.forEach(function (part) {part.hide(); }); +}; + +SpriteMorph.prototype.show = function () { + SpriteMorph.uber.show.call(this); + this.parts.forEach(function (part) {part.show(); }); +}; + // SpriteMorph pen color SpriteMorph.prototype.setColor = function (aColor) { @@ -6718,7 +6735,8 @@ WatcherMorph.prototype.update = function () { if (this.target && this.getter) { this.updateLabel(); if (this.target instanceof VariableFrame) { - newValue = this.target.vars[this.getter]; + newValue = this.target.vars[this.getter] ? + this.target.vars[this.getter].value : undefined; } else { newValue = this.target[this.getter](); } @@ -6803,7 +6821,7 @@ WatcherMorph.prototype.fixLayout = function () { this.sliderMorph.button.pressColor.b += 100; this.sliderMorph.setHeight(fontSize); this.sliderMorph.action = function (num) { - myself.target.vars[myself.getter] = Math.round(num); + myself.target.vars[myself.getter].value = Math.round(num); }; this.add(this.sliderMorph); } @@ -57,11 +57,11 @@ newCanvas, Costume, Sound, Audio, IDE_Morph, ScriptsMorph, BlockMorph, ArgMorph, InputSlotMorph, TemplateSlotMorph, CommandSlotMorph, FunctionSlotMorph, MultiArgMorph, ColorSlotMorph, nop, CommentMorph, isNil, localize, sizeOf, ArgLabelMorph, SVG_Costume, MorphicPreferences, -SyntaxElementMorph*/ +SyntaxElementMorph, Variable*/ // Global stuff //////////////////////////////////////////////////////// -modules.store = '2014-July-29'; +modules.store = '2014-September-17'; // XML_Serializer /////////////////////////////////////////////////////// @@ -730,8 +730,8 @@ SnapSerializer.prototype.loadVariables = function (varFrame, element) { return; } value = child.children[0]; - varFrame.vars[child.attributes.name] = value ? - myself.loadValue(value) : 0; + varFrame.vars[child.attributes.name] = new Variable(value ? + myself.loadValue(value) : 0); }); }; @@ -1509,7 +1509,7 @@ Sound.prototype.toXML = function (serializer) { VariableFrame.prototype.toXML = function (serializer) { var myself = this; return Object.keys(this.vars).reduce(function (vars, v) { - var val = myself.vars[v], + var val = myself.vars[v].value, dta; if (val === undefined || val === null) { dta = serializer.format('<variable name="@"/>', v); @@ -40,8 +40,8 @@ ThreadManager Process Context + Variable VariableFrame - UpvarReference credits @@ -83,13 +83,12 @@ ArgLabelMorph, localize, XML_Element, hex_sha512*/ // Global stuff //////////////////////////////////////////////////////// -modules.threads = '2014-July-28'; +modules.threads = '2014-September-29'; var ThreadManager; var Process; var Context; var VariableFrame; -var UpvarReference; function snapEquals(a, b) { if (a instanceof List || (b instanceof List)) { @@ -134,7 +133,11 @@ ThreadManager.prototype.toggleProcess = function (block) { } }; -ThreadManager.prototype.startProcess = function (block, isThreadSafe) { +ThreadManager.prototype.startProcess = function ( + block, + isThreadSafe, + exportResult +) { var active = this.findProcess(block), top = block.topBlock(), newProc; @@ -147,6 +150,7 @@ ThreadManager.prototype.startProcess = function (block, isThreadSafe) { } top.addHighlight(); newProc = new Process(block.topBlock()); + newProc.exportResult = exportResult; this.processes.push(newProc); return newProc; }; @@ -232,11 +236,17 @@ ThreadManager.prototype.removeTerminatedProcesses = function () { if (proc.topBlock instanceof ReporterBlockMorph) { if (proc.homeContext.inputs[0] instanceof List) { - proc.topBlock.showBubble(new ListWatcherMorph( - proc.homeContext.inputs[0] - )); + proc.topBlock.showBubble( + new ListWatcherMorph( + proc.homeContext.inputs[0] + ), + proc.exportResult + ); } else { - proc.topBlock.showBubble(proc.homeContext.inputs[0]); + proc.topBlock.showBubble( + proc.homeContext.inputs[0], + proc.exportResult + ); } } } else { @@ -302,6 +312,8 @@ ThreadManager.prototype.findProcess = function (block) { httpRequest active instance of an HttpRequest or null pauseOffset msecs between the start of an interpolated operation and when the process was paused + exportResult boolean flag indicating whether a picture of the top + block along with the result bubble shoud be exported */ Process.prototype = {}; @@ -325,6 +337,7 @@ function Process(topBlock) { this.isPaused = false; this.pauseOffset = null; this.frameCount = 0; + this.exportResult = false; if (topBlock) { this.homeContext.receiver = topBlock.receiver(); @@ -586,8 +599,7 @@ Process.prototype.evaluateSequence = function (arr) { outer = this.context.outerContext, isLambda = this.context.isLambda, isImplicitLambda = this.context.isImplicitLambda, - isCustomBlock = this.context.isCustomBlock, - upvars = this.context.upvars; + isCustomBlock = this.context.isCustomBlock; if (pc === (arr.length - 1)) { // tail call elimination this.context = new Context( this.context.parentContext, @@ -598,9 +610,6 @@ Process.prototype.evaluateSequence = function (arr) { this.context.isLambda = isLambda; this.context.isImplicitLambda = isImplicitLambda; this.context.isCustomBlock = isCustomBlock; - if (upvars) { - this.context.upvars = new UpvarReference(upvars); - } } else { if (pc >= arr.length) { this.popContext(); @@ -760,7 +769,7 @@ Process.prototype.evaluate = function ( if (!context) {return null; } if (context instanceof Function) { return context.apply( - this.homeContext.receiver, + this.blockReceiver(), args.asArray().concat([this]) ); } @@ -846,9 +855,6 @@ Process.prototype.evaluate = function ( } } } - if (this.context.upvars) { - runnable.upvars = new UpvarReference(this.context.upvars); - } if (runnable.expression instanceof CommandBlockMorph) { runnable.expression = runnable.expression.blockSequence(); @@ -1012,7 +1018,6 @@ Process.prototype.evaluateCustomBlock = function () { extra, i, value, - upvars, outer; if (!context) {return null; } @@ -1047,26 +1052,14 @@ Process.prototype.evaluateCustomBlock = function () { outer.variables.addVar(context.inputs[i], value); // if the parameter is an upvar, - // create an UpvarReference to it + // create a reference to the variable it points to if (declarations[context.inputs[i]][0] === '%upvar') { - if (!upvars) { // lazy initialization - upvars = new UpvarReference(this.context.upvars); - } - upvars.addReference( - value, - context.inputs[i], - outer.variables - ); + this.context.outerContext.variables.vars[value] = + outer.variables.vars[context.inputs[i]]; } } } - if (upvars) { - runnable.upvars = upvars; - } else if (this.context.upvars) { - runnable.upvars = new UpvarReference(this.context.upvars); - } - if (runnable.expression instanceof CommandBlockMorph) { runnable.expression = runnable.expression.blockSequence(); } @@ -1107,11 +1100,8 @@ Process.prototype.doChangeVar = function (varName, value) { Process.prototype.reportGetVar = function () { // assumes a getter block whose blockSpec is a variable name - var varName = this.context.expression.blockSpec; - return this.context.variables.getVar( - varName, - this.context.upvars + this.context.expression.blockSpec ); }; @@ -1318,8 +1308,7 @@ Process.prototype.doIf = function () { outer = this.context.outerContext, // for tail call elimination isLambda = this.context.isLambda, isImplicitLambda = this.context.isImplicitLambda, - isCustomBlock = this.context.isCustomBlock, - upvars = this.context.upvars; + isCustomBlock = this.context.isCustomBlock; this.popContext(); if (args[0]) { @@ -1328,7 +1317,6 @@ Process.prototype.doIf = function () { this.context.isLambda = isLambda; this.context.isImplicitLambda = isImplicitLambda; this.context.isCustomBlock = isCustomBlock; - this.context.upvars = new UpvarReference(upvars); } } this.pushContext(); @@ -1339,8 +1327,7 @@ Process.prototype.doIfElse = function () { outer = this.context.outerContext, // for tail call elimination isLambda = this.context.isLambda, isImplicitLambda = this.context.isImplicitLambda, - isCustomBlock = this.context.isCustomBlock, - upvars = this.context.upvars; + isCustomBlock = this.context.isCustomBlock; this.popContext(); if (args[0]) { @@ -1358,7 +1345,6 @@ Process.prototype.doIfElse = function () { this.context.isLambda = isLambda; this.context.isImplicitLambda = isImplicitLambda; this.context.isCustomBlock = isCustomBlock; - this.context.upvars = new UpvarReference(upvars); } this.pushContext(); @@ -1537,8 +1523,7 @@ Process.prototype.doRepeat = function (counter, body) { outer = this.context.outerContext, // for tail call elimination isLambda = this.context.isLambda, isImplicitLambda = this.context.isImplicitLambda, - isCustomBlock = this.context.isCustomBlock, - upvars = this.context.upvars; + isCustomBlock = this.context.isCustomBlock; if (counter < 1) { // was '=== 0', which caused infinite loops on non-ints return null; @@ -1550,7 +1535,6 @@ Process.prototype.doRepeat = function (counter, body) { this.context.isLambda = isLambda; this.context.isImplicitLambda = isImplicitLambda; this.context.isCustomBlock = isCustomBlock; - this.context.upvars = new UpvarReference(upvars); this.context.addInput(counter - 1); @@ -2728,7 +2712,6 @@ Process.prototype.inputOption = function (dta) { // Process stack Process.prototype.pushContext = function (expression, outerContext) { - var upvars = this.context ? this.context.upvars : null; this.context = new Context( this.context, expression, @@ -2737,9 +2720,6 @@ Process.prototype.pushContext = function (expression, outerContext) { this.context ? // check needed due to tail call elimination this.context.receiver : this.homeContext.receiver ); - if (upvars) { - this.context.upvars = new UpvarReference(upvars); - } }; Process.prototype.popContext = function () { @@ -2785,7 +2765,6 @@ Process.prototype.reportFrameCount = function () { null or a String denoting a selector, e.g. 'doYield' receiver the object to which the expression applies, if any variables the current VariableFrame, if any - upvars the current UpvarReference, if any (default: null) inputs an array of input values computed so far (if expression is a BlockMorph) pc the index of the next block to evaluate @@ -2815,7 +2794,6 @@ function Context( this.variables.parentFrame = this.outerContext.variables; this.receiver = this.outerContext.receiver; } - this.upvars = null; // set to an UpvarReference in custom blocks this.inputs = []; this.pc = 0; this.startTime = null; @@ -2968,6 +2946,20 @@ Context.prototype.stackSize = function () { return 1 + this.parentContext.stackSize(); }; +// Variable ///////////////////////////////////////////////////////////////// + +function Variable(value) { + this.value = value; +} + +Variable.prototype.toString = function () { + return 'a Variable [' + this.value + ']'; +}; + +Variable.prototype.copy = function () { + return new Variable(this.value); +}; + // VariableFrame /////////////////////////////////////////////////////// function VariableFrame(parentFrame, owner) { @@ -2981,8 +2973,11 @@ VariableFrame.prototype.toString = function () { }; VariableFrame.prototype.copy = function () { - var frame = new VariableFrame(this.parentFrame); - frame.vars = copy(this.vars); + var frame = new VariableFrame(this.parentFrame), + myself = this; + this.names().forEach(function (vName) { + frame.addVar(vName, myself.getVar(vName)); + }); return frame; }; @@ -3035,7 +3030,7 @@ VariableFrame.prototype.setVar = function (name, value) { */ var frame = this.find(name); if (frame) { - frame.vars[name] = value; + frame.vars[name].value = value; } }; @@ -3049,21 +3044,20 @@ VariableFrame.prototype.changeVar = function (name, delta) { var frame = this.find(name), value; if (frame) { - value = parseFloat(frame.vars[name]); + value = parseFloat(frame.vars[name].value); if (isNaN(value)) { - frame.vars[name] = delta; + frame.vars[name].value = delta; } else { - frame.vars[name] = value + parseFloat(delta); + frame.vars[name].value = value + parseFloat(delta); } } }; -VariableFrame.prototype.getVar = function (name, upvars) { +VariableFrame.prototype.getVar = function (name) { var frame = this.silentFind(name), - value, - upvarReference; + value; if (frame) { - value = frame.vars[name]; + value = frame.vars[name].value; return (value === 0 ? 0 : value === false ? false : value === '' ? '' @@ -3073,12 +3067,6 @@ VariableFrame.prototype.getVar = function (name, upvars) { // empty input with a Binding-ID called without an argument return ''; } - if (upvars) { - upvarReference = upvars.find(name); - if (upvarReference) { - return upvarReference.getVar(name); - } - } throw new Error( 'a variable of name \'' + name @@ -3087,7 +3075,7 @@ VariableFrame.prototype.getVar = function (name, upvars) { }; VariableFrame.prototype.addVar = function (name, value) { - this.vars[name] = (value === 0 ? 0 + this.vars[name] = new Variable(value === 0 ? 0 : value === false ? false : value === '' ? '' : value || 0); }; @@ -3144,67 +3132,3 @@ VariableFrame.prototype.allNames = function () { } return answer; }; - -// Variable ///////////////////////////////////////////////////////////////// - -function Variable(value) { - this.value = value; -} - -Variable.prototype.toString = function () { - return 'a Variable [' + this.value + ']'; -}; - -Variable.prototype.copy = function () { - return new Variable(this.value); -}; - -// UpvarReference /////////////////////////////////////////////////////////// - -// ... quasi-inherits some features from VariableFrame - -function UpvarReference(parent) { - this.vars = {}; // structure: {upvarName : [varName, varFrame]} - this.parentFrame = parent || null; -} - -UpvarReference.prototype.addReference = function ( - upvarName, - varName, - varFrame -) { - this.vars[upvarName] = [varName, varFrame]; -}; - -UpvarReference.prototype.find = function (name) { -/* - answer the closest upvar reference containing - the specified variable, or answer null. -*/ - if (this.vars[name] !== undefined) { - return this; - } - if (this.parentFrame) { - return this.parentFrame.find(name); - } - return null; -}; - -UpvarReference.prototype.getVar = function (name) { - var varName = this.vars[name][0], - varFrame = this.vars[name][1], - value = varFrame.vars[varName]; - return (value === 0 ? 0 : value || 0); // don't return null -}; - -// UpvarReference tools - -UpvarReference.prototype.toString = function () { - return 'an UpvarReference {' + this.names() + '}'; -}; - -// UpvarReference quasi-inheritance from VariableFrame - -UpvarReference.prototype.names = VariableFrame.prototype.names; -UpvarReference.prototype.allNames = VariableFrame.prototype.allNames; -UpvarReference.prototype.allNamesDict = VariableFrame.prototype.allNamesDict; |
