diff --git a/blocks.js b/blocks.js index ce2a714..4258610 100644 --- a/blocks.js +++ b/blocks.js @@ -155,7 +155,7 @@ DialogBoxMorph, BlockInputFragmentMorph, PrototypeHatBlockMorph*/ // Global stuff //////////////////////////////////////////////////////// -modules.blocks = '2013-June-25'; +modules.blocks = '2013-July-04'; var SyntaxElementMorph; var BlockMorph; @@ -1535,10 +1535,10 @@ SyntaxElementMorph.prototype.showBubble = function (value) { it's not part of Snap's evaluator and not needed for Snap itself */ -SyntaxElementMorph.prototype.mappedCode = function () { +SyntaxElementMorph.prototype.mappedCode = function (definitions) { var result = this.evaluate(); if (result instanceof BlockMorph) { - return result.mappedCode(); + return result.mappedCode(definitions); } return result; }; @@ -1858,6 +1858,10 @@ BlockMorph.prototype.userMenu = function () { } if (StageMorph.prototype.enableCodeMapping) { menu.addLine(); + menu.addItem( + 'header mapping...', + 'mapToHeader' + ); menu.addItem( 'code mapping...', 'mapToCode' @@ -1941,6 +1945,11 @@ BlockMorph.prototype.userMenu = function () { menu.addLine(); menu.addItem("ringify", 'ringify'); if (StageMorph.prototype.enableCodeMapping) { + menu.addLine(); + menu.addItem( + 'header mapping...', + 'mapToHeader' + ); menu.addItem( 'code mapping...', 'mapToCode' @@ -2182,6 +2191,45 @@ BlockMorph.prototype.showHelp = function () { it's not part of Snap's evaluator and not needed for Snap itself */ +BlockMorph.prototype.mapToHeader = function () { + // open a dialog box letting the user map header code via the GUI + var key = this.selector.substr(0, 5) === 'reify' ? + 'reify' : this.selector, + block = this.codeDefinitionHeader(), + myself = this, + help, + pic; + block.addShadow(new Point(3, 3)); + pic = block.fullImageClassic(); + if (this.definition) { + help = 'Enter code that corresponds to the block\'s definition. ' + + 'Use the formal parameter\nnames as shown and
to ' + + 'reference the definition body\'s generated text code.'; + } else { + help = 'Enter code that corresponds to the block\'s definition. ' + + 'Choose your own\nformal parameter names (ignoring the ones ' + + 'shown .'; + } + new DialogBoxMorph( + this, + function (code) { + if (key === 'evaluateCustomBlock') { + myself.definition.codeHeader = code; + } else { + StageMorph.prototype.codeHeaders[key] = code; + } + }, + this + ).promptCode( + 'Header mapping', + key === 'evaluateCustomBlock' ? this.definition.codeHeader || '' + : StageMorph.prototype.codeHeaders[key] || '', + this.world(), + pic, + help + ); +}; + BlockMorph.prototype.mapToCode = function () { // open a dialog box letting the user map code via the GUI var key = this.selector.substr(0, 5) === 'reify' ? @@ -2206,10 +2254,26 @@ BlockMorph.prototype.mapToCode = function () { key === 'evaluateCustomBlock' ? this.definition.codeMapping || '' : StageMorph.prototype.codeMappings[key] || '', this.world(), - pic + pic, + 'Enter code that corresponds to the block\'s operation ' + + '(usually a single\nfunction invocation). Use <#n> to ' + + 'reference actual arguments as shown.' ); }; +BlockMorph.prototype.mapHeader = function (aString, key) { + // primitive for programatically mapping header code + var sel = key || this.selector.substr(0, 5) === 'reify' ? + 'reify' : this.selector; + if (aString) { + if (this.definition) { // custom block + this.definition.codeHeader = aString; + } else { + StageMorph.prototype.codeHeaders[sel] = aString; + } + } +}; + BlockMorph.prototype.mapCode = function (aString, key) { // primitive for programatically mapping code var sel = key || this.selector.substr(0, 5) === 'reify' ? @@ -2223,19 +2287,63 @@ BlockMorph.prototype.mapCode = function (aString, key) { } }; -BlockMorph.prototype.mappedCode = function () { +BlockMorph.prototype.mappedCode = function (definitions) { var key = this.selector.substr(0, 5) === 'reify' ? 'reify' : this.selector, code, codeLines, count = 1, + header, + headers, + headerLines, + body, + bodyLines, + defKey = this.definition ? this.definition.spec : key, + defs = definitions || {}, parts = []; code = key === 'reportGetVar' ? this.blockSpec : this.definition ? this.definition.codeMapping || '' : StageMorph.prototype.codeMappings[key] || ''; + + // map header + if (key !== 'reportGetVar' && !defs.hasOwnProperty(defKey)) { + defs[defKey] = null; // create the property for recursive definitions + if (this.definition) { + header = this.definition.codeHeader || ''; + if (header.indexOf(''), + bodyLines.join('\n' + prefix) + ); + headerLines[idx] = headerLines[idx].replace( + new RegExp('', 'g'), + bodyLines.join('\n') + ); + }); + header = headerLines.join('\n'); + } + defs[defKey] = header; + } else { + defs[defKey] = StageMorph.prototype.codeHeaders[defKey]; + } + } + codeLines = code.split('\n'); this.inputs().forEach(function (input) { - parts.push(input.mappedCode().toString()); + parts.push(input.mappedCode(defs).toString()); }); parts.forEach(function (part) { var partLines = part.split('\n'), @@ -2258,11 +2366,46 @@ BlockMorph.prototype.mappedCode = function () { }); code = codeLines.join('\n'); if (this.nextBlock && this.nextBlock()) { // Command - code += ('\n' + this.nextBlock().mappedCode()); + code += ('\n' + this.nextBlock().mappedCode(defs)); + } + if (!definitions) { // top-level, add headers + headers = []; + Object.keys(defs).forEach(function (each) { + if (defs[each]) { + headers.push(defs[each]); + } + }); + if (headers.length) { + return headers.join('\n\n') + + '\n\n' + + code; + } } return code; }; +BlockMorph.prototype.codeDefinitionHeader = function () { + var block = this.definition ? new PrototypeHatBlockMorph(this.definition) + : SpriteMorph.prototype.blockForSelector(this.selector), + hat = new HatBlockMorph(), + count = 1; + + if (this.definition) {return block; } + block.inputs().forEach(function (input) { + var part = new TemplateSlotMorph('#' + count); + block.silentReplaceInput(input, part); + count += 1; + }); + block.isPrototype = true; + hat.setCategory("control"); + hat.setSpec('%s'); + hat.silentReplaceInput(hat.inputs()[0], block); + if (this.category === 'control') { + hat.alternateBlockColor(); + } + return hat; +}; + BlockMorph.prototype.codeMappingHeader = function () { var block = this.definition ? this.definition.blockInstance() : SpriteMorph.prototype.blockForSelector(this.selector), @@ -5402,11 +5545,11 @@ CSlotMorph.prototype.getSpec = function () { return '%c'; }; -CSlotMorph.prototype.mappedCode = function () { +CSlotMorph.prototype.mappedCode = function (definitions) { var code = StageMorph.prototype.codeMappings.reify || '<#1>', codeLines = code.split('\n'), nested = this.nestedBlock(), - part = nested ? nested.mappedCode() : '', + part = nested ? nested.mappedCode(definitions) : '', partLines = (part.toString()).split('\n'), rx = new RegExp('<#1>', 'g'); @@ -8541,7 +8684,7 @@ MultiArgMorph.prototype.mapToCode = function (key, label) { ); }; -MultiArgMorph.prototype.mappedCode = function () { +MultiArgMorph.prototype.mappedCode = function (definitions) { var block = this.parentThatIsA(BlockMorph), key = '', code, @@ -8564,7 +8707,7 @@ MultiArgMorph.prototype.mappedCode = function () { delim = StageMorph.prototype.codeMappings[key + 'delim'] || ' '; this.inputs().forEach(function (input) { - parts.push(itemCode.replace(/<#1>/g, input.mappedCode())); + parts.push(itemCode.replace(/<#1>/g, input.mappedCode(definitions))); }); parts.forEach(function (part) { if (count) { diff --git a/byob.js b/byob.js index d8c0813..8954c6a 100644 --- a/byob.js +++ b/byob.js @@ -105,7 +105,7 @@ CommentMorph, localize, CSlotMorph, SpeechBubbleMorph, MorphicPreferences*/ // Global stuff //////////////////////////////////////////////////////// -modules.byob = '2013-June-18'; +modules.byob = '2013-July-04'; // Declarations @@ -139,6 +139,7 @@ function CustomBlockDefinition(spec, receiver) { this.declarations = {}; // {'inputName' : [type, default]} this.comment = null; this.codeMapping = null; // experimental, generate text code + this.codeHeader = null; // experimental, generate text code // don't serialize (not needed for functionality): this.receiver = receiver || null; // for serialization only (pointer) diff --git a/gui.js b/gui.js index f0880ff..d13df20 100644 --- a/gui.js +++ b/gui.js @@ -68,7 +68,7 @@ sb, CommentMorph, CommandBlockMorph*/ // Global stuff //////////////////////////////////////////////////////// -modules.gui = '2013-July-02'; +modules.gui = '2013-July-04'; // Declarations @@ -1681,8 +1681,7 @@ IDE_Morph.prototype.applySavedSettings = function () { zoom = this.getSetting('zoom'), language = this.getSetting('language'), click = this.getSetting('click'), - longform = this.getSetting('longform'), - code = this.getSetting('code'); + longform = this.getSetting('longform'); // design if (design === 'flat') { @@ -1714,11 +1713,6 @@ IDE_Morph.prototype.applySavedSettings = function () { if (longform) { InputSlotDialogMorph.prototype.isLaunchingExpanded = true; } - - // code mapping - if (code && !StageMorph.prototype.enableCodeMapping) { - StageMorph.prototype.enableCodeMapping = true; - } }; IDE_Morph.prototype.saveSetting = function (key, value) { @@ -1763,7 +1757,7 @@ IDE_Morph.prototype.addNewSprite = function () { this.selectSprite(sprite); }; -IDE_Morph.prototype.paintNewSprite = function() { +IDE_Morph.prototype.paintNewSprite = function () { var sprite = new SpriteMorph(this.globalVariables), cos = new Costume(), myself = this; @@ -1779,7 +1773,7 @@ IDE_Morph.prototype.paintNewSprite = function() { this.world(), this, true, - function() {myself.removeSprite(sprite); }, + function () {myself.removeSprite(sprite); }, function () { sprite.addCostume(cos); sprite.wearCostume(cos); @@ -2158,26 +2152,7 @@ IDE_Morph.prototype.settingsMenu = function () { 'check for alternative\nGUI design', false ); - addPreference( - 'Code mapping', - function () { - StageMorph.prototype.enableCodeMapping = - !StageMorph.prototype.enableCodeMapping; - if (StageMorph.prototype.enableCodeMapping) { - myself.saveSetting('code', true); - } else { - myself.removeSetting('code'); - } - myself.currentSprite.blocksCache.variables = null; - myself.currentSprite.paletteCache.variables = null; - myself.refreshPalette(); - }, - StageMorph.prototype.enableCodeMapping, - 'uncheck to disable\nblock to text mapping features', - 'check for block\nto text mapping features', - false - ); - menu.addLine(); // everything below this line is made persistent + menu.addLine(); // everything below this line is stored in the project addPreference( 'Thread safe scripts', function () {stage.isThreadSafe = !stage.isThreadSafe; }, @@ -2192,6 +2167,20 @@ IDE_Morph.prototype.settingsMenu = function () { 'uncheck for greater speed\nat variable frame rates', 'check for smooth, predictable\nanimations across computers' ); + addPreference( + 'Codification support', + function () { + StageMorph.prototype.enableCodeMapping = + !StageMorph.prototype.enableCodeMapping; + myself.currentSprite.blocksCache.variables = null; + myself.currentSprite.paletteCache.variables = null; + myself.refreshPalette(); + }, + StageMorph.prototype.enableCodeMapping, + 'uncheck to disable\nblock to text mapping features', + 'check for block\nto text mapping features', + false + ); menu.popup(world, pos); }; @@ -2580,6 +2569,9 @@ IDE_Morph.prototype.newProject = function () { this.currentSprite = new SpriteMorph(this.globalVariables); this.sprites = new List([this.currentSprite]); StageMorph.prototype.hiddenPrimitives = {}; + StageMorph.prototype.codeMappings = {}; + StageMorph.prototype.codeHeaders = {}; + StageMorph.prototype.enableCodeMapping = false; this.setProjectName(''); this.projectNotes = ''; this.createStage(); @@ -5115,7 +5107,7 @@ CostumeIconMorph.prototype.renameCostume = function () { ); }; -CostumeIconMorph.prototype.duplicateCostume = function() { +CostumeIconMorph.prototype.duplicateCostume = function () { var wardrobe = this.parentThatIsA(WardrobeMorph), ide = this.parentThatIsA(IDE_Morph), newcos = this.object.copy(), @@ -5494,11 +5486,11 @@ WardrobeMorph.prototype.removeCostumeAt = function (idx) { this.updateList(); }; -WardrobeMorph.prototype.paintNew = function() { +WardrobeMorph.prototype.paintNew = function () { var cos = new Costume(newCanvas(), "Untitled"), ide = this.parentThatIsA(IDE_Morph), myself = this; - cos.edit(this.world(), ide, true, null, function() { + cos.edit(this.world(), ide, true, null, function () { myself.sprite.addCostume(cos); myself.updateList(); if (ide) { diff --git a/history.txt b/history.txt index 61e7306..0abd8df 100755 --- a/history.txt +++ b/history.txt @@ -1778,3 +1778,7 @@ ______ ------ * Objects: took out "security margin" in Costume's shrinkWrap() method b/c Chrome no longer needs it -> fixed empty costume bug when drawing over the paint editor's bounds * GUI: Import libraries feature (in the project menu) + +130704 +------ +* Codification (text code mapping and block header support) diff --git a/objects.js b/objects.js index 3d014cd..038b61e 100644 --- a/objects.js +++ b/objects.js @@ -123,7 +123,7 @@ PrototypeHatBlockMorph*/ // Global stuff //////////////////////////////////////////////////////// -modules.objects = '2013-July-02'; +modules.objects = '2013-July-04'; var SpriteMorph; var StageMorph; @@ -1019,6 +1019,11 @@ SpriteMorph.prototype.initBlocks = function () { }, // Code mapping - experimental + doMapHeader: { // experimental + type: 'command', + category: 'other', + spec: 'map %cmdRing to header %code' + }, doMapCode: { // experimental type: 'command', category: 'other', @@ -1795,6 +1800,7 @@ SpriteMorph.prototype.blockTemplates = function (category) { blocks.push('='); if (StageMorph.prototype.enableCodeMapping) { + blocks.push(block('doMapHeader')); blocks.push(block('doMapCode')); blocks.push(block('doMapStringCode')); blocks.push(block('doMapListCode')); @@ -3223,6 +3229,7 @@ StageMorph.prototype.paletteTextColor StageMorph.prototype.hiddenPrimitives = {}; StageMorph.prototype.codeMappings = {}; +StageMorph.prototype.codeHeaders = {}; StageMorph.prototype.enableCodeMapping = false; // StageMorph instance creation @@ -4047,6 +4054,7 @@ StageMorph.prototype.blockTemplates = function (category) { blocks.push('='); if (StageMorph.prototype.enableCodeMapping) { + blocks.push(block('doMapHeader')); blocks.push(block('doMapCode')); blocks.push(block('doMapStringCode')); blocks.push(block('doMapListCode')); diff --git a/store.js b/store.js index 9736b6d..57b124a 100644 --- a/store.js +++ b/store.js @@ -61,7 +61,7 @@ SyntaxElementMorph*/ // Global stuff //////////////////////////////////////////////////////// -modules.store = '2013-June-19'; +modules.store = '2013-July-04'; // XML_Serializer /////////////////////////////////////////////////////// @@ -378,6 +378,8 @@ SnapSerializer.prototype.loadProjectModel = function (xmlNode) { project.stage.setExtent(StageMorph.prototype.dimensions); project.stage.isThreadSafe = model.stage.attributes.threadsafe === 'true'; + StageMorph.prototype.enableCodeMapping = + model.stage.attributes.codify === 'true'; model.hiddenPrimitives = model.project.childNamed('hidden'); if (model.hiddenPrimitives) { @@ -390,6 +392,13 @@ SnapSerializer.prototype.loadProjectModel = function (xmlNode) { ); } + model.codeHeaders = model.project.childNamed('headers'); + if (model.codeHeaders) { + model.codeHeaders.children.forEach(function (xml) { + StageMorph.prototype.codeHeaders[xml.tag] = xml.contents; + }); + } + model.codeMappings = model.project.childNamed('code'); if (model.codeMappings) { model.codeMappings.children.forEach(function (xml) { @@ -672,7 +681,7 @@ SnapSerializer.prototype.loadCustomBlocks = function ( // private var myself = this; element.children.forEach(function (child) { - var definition, names, inputs, code, comment, i; + var definition, names, inputs, header, code, comment, i; if (child.tag !== 'block-definition') { return; } @@ -711,6 +720,11 @@ SnapSerializer.prototype.loadCustomBlocks = function ( }); } + header = child.childNamed('header'); + if (header) { + definition.codeHeader = header.contents; + } + code = child.childNamed('code'); if (code) { definition.codeMapping = code.contents; @@ -1266,20 +1280,20 @@ StageMorph.prototype.toXML = function (serializer) { thumbdata = null; } - function codeMappings() { - var code = ''; - Object.keys(StageMorph.prototype.codeMappings).forEach( + function code(key) { + var str = ''; + Object.keys(StageMorph.prototype[key]).forEach( function (selector) { - code += ( + str += ( '<' + selector + '>' + XML_Element.prototype.escape( - StageMorph.prototype.codeMappings[selector] + StageMorph.prototype[key][selector] ) + '' + selector + '>' ); } ); - return code; + return str; } this.removeAllClones(); @@ -1288,6 +1302,7 @@ StageMorph.prototype.toXML = function (serializer) { '%
' +
'@
' +
'