summaryrefslogtreecommitdiff
path: root/blocks.js
diff options
context:
space:
mode:
Diffstat (limited to 'blocks.js')
-rw-r--r--blocks.js316
1 files changed, 315 insertions, 1 deletions
diff --git a/blocks.js b/blocks.js
index 4f1f093..1a325a8 100644
--- a/blocks.js
+++ b/blocks.js
@@ -153,7 +153,7 @@ DialogBoxMorph, BlockInputFragmentMorph, PrototypeHatBlockMorph*/
// Global stuff ////////////////////////////////////////////////////////
-modules.blocks = '2013-May-16';
+modules.blocks = '2013-June-18';
var SyntaxElementMorph;
var BlockMorph;
@@ -1061,6 +1061,34 @@ SyntaxElementMorph.prototype.labelPart = function (spec) {
part = new ReporterSlotMorph(true);
break;
+ // code mapping (experimental)
+
+ case '%codeListPart':
+ part = new InputSlotMorph(
+ null, // text
+ false, // numeric?
+ {
+ 'list' : ['list'],
+ 'item' : ['item'],
+ 'delimiter' : ['delimiter']
+ },
+ true // read-only
+ );
+ break;
+ case '%codeListKind':
+ part = new InputSlotMorph(
+ null, // text
+ false, // numeric?
+ {
+ 'collection' : ['collection'],
+ 'variables' : ['variables'],
+ 'parameters' : ['parameters']
+ },
+ true // read-only
+ );
+ break;
+
+
// symbols:
case '%turtle':
@@ -1479,6 +1507,22 @@ SyntaxElementMorph.prototype.showBubble = function (value) {
);
};
+// SyntaxElementMorph code mapping
+
+/*
+ code mapping lets you use blocks to generate arbitrary text-based
+ source code that can be exported and compiled / embedded elsewhere,
+ it's not part of Snap's evaluator and not needed for Snap itself
+*/
+
+SyntaxElementMorph.prototype.mappedCode = function () {
+ var result = this.evaluate();
+ if (result instanceof BlockMorph) {
+ return result.mappedCode();
+ }
+ return result;
+};
+
// SyntaxElementMorph layout update optimization
SyntaxElementMorph.prototype.startLayout = function () {
@@ -1790,6 +1834,13 @@ BlockMorph.prototype.userMenu = function () {
'hidePrimitive'
);
}
+ if (StageMorph.prototype.enableCodeMapping) {
+ menu.addLine();
+ menu.addItem(
+ 'code mapping...',
+ 'mapToCode'
+ );
+ }
return menu;
}
menu.addLine();
@@ -1867,6 +1918,12 @@ BlockMorph.prototype.userMenu = function () {
}
menu.addLine();
menu.addItem("ringify", 'ringify');
+ if (StageMorph.prototype.enableCodeMapping) {
+ menu.addItem(
+ 'code mapping...',
+ 'mapToCode'
+ );
+ }
return menu;
};
@@ -2095,6 +2152,99 @@ BlockMorph.prototype.showHelp = function () {
}
};
+// BlockMorph code mapping
+
+/*
+ code mapping lets you use blocks to generate arbitrary text-based
+ source code that can be exported and compiled / embedded elsewhere,
+ it's not part of Snap's evaluator and not needed for Snap itself
+*/
+
+BlockMorph.prototype.mapToCode = function () {
+ // open a dialog box letting the user map code via the GUI
+ var key = this.selector.substr(0, 5) === 'reify' ?
+ 'reify' : this.selector,
+ block = this.codeMappingHeader(),
+ myself = this,
+ pic;
+ block.addShadow(new Point(3, 3));
+ pic = block.fullImageClassic();
+ new DialogBoxMorph(
+ this,
+ function (code) {
+ if (key === 'evaluateCustomBlock') {
+ myself.definition.codeMapping = code;
+ } else {
+ StageMorph.prototype.codeMappings[key] = code;
+ }
+ },
+ this
+ ).prompt(
+ 'Code mapping',
+ key === 'evaluateCustomBlock' ? this.definition.codeMapping || ''
+ : StageMorph.prototype.codeMappings[key] || '',
+ this.world(),
+ pic
+ );
+};
+
+BlockMorph.prototype.mapCode = function (aString, key) {
+ // primitive for programatically mapping code
+ var sel = key || this.selector.substr(0, 5) === 'reify' ?
+ 'reify' : this.selector;
+ if (aString) {
+ if (this.definition) { // custom block
+ this.definition.codeMapping = aString;
+ } else {
+ StageMorph.prototype.codeMappings[sel] = aString;
+ }
+ }
+};
+
+BlockMorph.prototype.mappedCode = function () {
+ var key = this.selector.substr(0, 5) === 'reify' ?
+ 'reify' : this.selector,
+ code,
+ count = 1,
+ parts = [];
+ code = key === 'reportGetVar' ? this.blockSpec
+ : this.definition ? this.definition.codeMapping || ''
+ : StageMorph.prototype.codeMappings[key] || '';
+ this.inputs().forEach(function (input) {
+ parts.push(input.mappedCode());
+ });
+ parts.forEach(function (part) {
+ var rx = new RegExp('<#' + count + '>', 'g');
+ code = code.replace(rx, part);
+ count += 1;
+ });
+ if (this.nextBlock && this.nextBlock()) { // Command
+ code += this.nextBlock().mappedCode();
+ }
+ return code;
+};
+
+BlockMorph.prototype.codeMappingHeader = function () {
+ var block = this.definition ? this.definition.blockInstance()
+ : SpriteMorph.prototype.blockForSelector(this.selector),
+ hat = new HatBlockMorph(),
+ count = 1;
+
+ 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 drawing
BlockMorph.prototype.eraseHoles = function (context) {
@@ -5187,6 +5337,13 @@ CSlotMorph.prototype.getSpec = function () {
return '%c';
};
+CSlotMorph.prototype.mappedCode = function () {
+ var code = StageMorph.prototype.codeMappings.reify || '',
+ part = this.nestedBlock(),
+ nestedCode = part ? part.mappedCode() : '';
+ return code.replace(/<#1>/g, nestedCode);
+};
+
// CSlotMorph layout:
CSlotMorph.prototype.fixLayout = function () {
@@ -6113,6 +6270,60 @@ InputSlotMorph.prototype.reactToSliderEdit = function () {
}
};
+// InputSlotMorph menu:
+
+InputSlotMorph.prototype.userMenu = function () {
+ var menu = new MenuMorph(this);
+ if (!StageMorph.prototype.enableCodeMapping || this.isNumeric) {
+ return this.parent.userMenu();
+ }
+ menu.addItem(
+ 'code string mapping...',
+ 'mapToCode'
+ );
+ return menu;
+};
+
+// InputSlotMorph code mapping
+
+/*
+ code mapping lets you use blocks to generate arbitrary text-based
+ source code that can be exported and compiled / embedded elsewhere,
+ it's not part of Snap's evaluator and not needed for Snap itself
+*/
+
+InputSlotMorph.prototype.mapToCode = function () {
+ // private - open a dialog box letting the user map code via the GUI
+ new DialogBoxMorph(
+ this,
+ function (code) {
+ StageMorph.prototype.codeMappings.string = code;
+ },
+ this
+ ).prompt(
+ 'Code mapping - String <#1>',
+ StageMorph.prototype.codeMappings.string || '',
+ this.world()
+ );
+};
+
+InputSlotMorph.prototype.mappedCode = function () {
+ var code = StageMorph.prototype.codeMappings.string || '<#1>',
+ block = this.parentThatIsA(BlockMorph),
+ val = this.evaluate();
+
+ if (this.isNumeric) {return val; }
+ if (!isNaN(parseFloat(val))) {return val; }
+ if (!isString(val)) {return val; }
+ if (block && contains(
+ ['doSetVar', 'doChangeVar', 'doShowVar', 'doHideVar'],
+ block.selector
+ )) {
+ return val;
+ }
+ return code.replace(/<#1>/g, val);
+};
+
// InputSlotMorph evaluating:
InputSlotMorph.prototype.evaluate = function () {
@@ -8089,6 +8300,109 @@ MultiArgMorph.prototype.mouseClickLeft = function (pos) {
this.endLayout();
};
+// MultiArgMorph menu:
+
+MultiArgMorph.prototype.userMenu = function () {
+ var menu = new MenuMorph(this),
+ block = this.parentThatIsA(BlockMorph),
+ key = '',
+ myself = this;
+ if (!StageMorph.prototype.enableCodeMapping) {
+ return this.parent.userMenu();
+ }
+ if (block) {
+ if (block instanceof RingMorph) {
+ key = 'parms_';
+ } else if (block.selector === 'doDeclareVariables') {
+ key = 'tempvars_';
+ }
+ }
+ menu.addItem(
+ 'code list mapping...',
+ function () {myself.mapCodeList(key); }
+ );
+ menu.addItem(
+ 'code item mapping...',
+ function () {myself.mapCodeItem(key); }
+ );
+ menu.addItem(
+ 'code delimiter mapping...',
+ function () {myself.mapCodeDelimiter(key); }
+ );
+ return menu;
+};
+
+// MultiArgMorph code mapping
+
+/*
+ code mapping lets you use blocks to generate arbitrary text-based
+ source code that can be exported and compiled / embedded elsewhere,
+ it's not part of Snap's evaluator and not needed for Snap itself
+*/
+
+MultiArgMorph.prototype.mapCodeDelimiter = function (key) {
+ this.mapToCode(key + 'delim', 'list item delimiter');
+};
+
+MultiArgMorph.prototype.mapCodeList = function (key) {
+ this.mapToCode(key + 'list', 'list contents <#1>');
+};
+
+MultiArgMorph.prototype.mapCodeItem = function (key) {
+ this.mapToCode(key + 'item', 'list item <#1>');
+};
+
+MultiArgMorph.prototype.mapToCode = function (key, label) {
+ // private - open a dialog box letting the user map code via the GUI
+ new DialogBoxMorph(
+ this,
+ function (code) {
+ StageMorph.prototype.codeMappings[key] = code;
+ },
+ this
+ ).prompt(
+ 'Code mapping - ' + label,
+ StageMorph.prototype.codeMappings[key] || '',
+ this.world()
+ );
+};
+
+MultiArgMorph.prototype.mappedCode = function () {
+ var block = this.parentThatIsA(BlockMorph),
+ key = '',
+ code,
+ items = '',
+ itemCode,
+ delim,
+ count = 0,
+ parts = [];
+
+ if (block) {
+ if (block instanceof RingMorph) {
+ key = 'parms_';
+ } else if (block.selector === 'doDeclareVariables') {
+ key = 'tempvars_';
+ }
+ }
+
+ code = StageMorph.prototype.codeMappings[key + 'list'] || '<#1>';
+ itemCode = StageMorph.prototype.codeMappings[key + 'item'] || '<#1>';
+ delim = StageMorph.prototype.codeMappings[key + 'delim'] || ' ';
+
+ this.inputs().forEach(function (input) {
+ parts.push(itemCode.replace(/<#1>/g, input.mappedCode()));
+ });
+ parts.forEach(function (part) {
+ if (count) {
+ items += delim;
+ }
+ items += part;
+ count += 1;
+ });
+ code = code.replace(/<#1>/g, items);
+ return code;
+};
+
// MultiArgMorph arity evaluating:
MultiArgMorph.prototype.evaluate = function () {