merged with master

upd4.1
Bernat Romagosa 2017-09-04 13:41:10 +02:00
commit 85ba23bd32
29 zmienionych plików z 14016 dodań i 2521 usunięć

2689
blocks.js 100755 → 100644

Plik diff jest za duży Load Diff

306
byob.js 100755 → 100644
Wyświetl plik

@ -103,11 +103,12 @@ StringMorph, nop, newCanvas, radians, BoxMorph, ArrowMorph, PushButtonMorph,
contains, InputSlotMorph, ToggleButtonMorph, IDE_Morph, MenuMorph, copy,
ToggleElementMorph, Morph, fontHeight, StageMorph, SyntaxElementMorph,
SnapSerializer, CommentMorph, localize, CSlotMorph, MorphicPreferences,
SymbolMorph, isNil, CursorMorph, VariableFrame, WatcherMorph, Variable*/
SymbolMorph, isNil, CursorMorph, VariableFrame, WatcherMorph, Variable,
BooleanSlotMorph, XML_Serializer*/
// Global stuff ////////////////////////////////////////////////////////
modules.byob = '2017-January-03';
modules.byob = '2017-September-01';
// Declarations
@ -171,7 +172,7 @@ CustomBlockDefinition.prototype.blockInstance = function () {
CustomBlockDefinition.prototype.templateInstance = function () {
var block;
block = this.blockInstance();
block.refreshDefaults();
block.refreshDefaults(this);
block.isDraggable = false;
block.isTemplate = true;
return block;
@ -209,11 +210,16 @@ CustomBlockDefinition.prototype.prototypeInstance = function () {
// CustomBlockDefinition duplicating
CustomBlockDefinition.prototype.copyAndBindTo = function (sprite) {
CustomBlockDefinition.prototype.copyAndBindTo = function (sprite, headerOnly) {
var c = copy(this);
delete c[XML_Serializer.prototype.idProperty];
c.receiver = sprite; // only for (kludgy) serialization
c.declarations = copy(this.declarations); // might have to go deeper
if (headerOnly) { // for serializing inherited method signatures
c.body = null;
return c;
}
if (c.body) {
c.body = Process.prototype.reify.call(
null,
@ -222,7 +228,6 @@ CustomBlockDefinition.prototype.copyAndBindTo = function (sprite) {
);
c.body.outerContext = null;
}
return c;
};
@ -236,6 +241,8 @@ CustomBlockDefinition.prototype.blockSpec = function () {
parts.forEach(function (part) {
if (part[0] === '%' && part.length > 1) {
spec = myself.typeOf(part.slice(1));
} else if (part === '$nl') {
spec = '%br';
} else {
spec = part;
}
@ -365,12 +372,11 @@ CustomBlockDefinition.prototype.isDirectlyRecursive = function () {
if (!this.body) {
this.cachedIsRecursive = false;
} else {
myspec = this.spec;
myspec = this.blockSpec();
this.cachedIsRecursive = this.body.expression.anyChild(
function (morph) {
return morph instanceof BlockMorph &&
morph.definition &&
morph.definition.spec === myspec;
return morph.isCustomBlock &&
morph.blockSpec === myspec;
}
);
}
@ -427,6 +433,18 @@ CustomBlockDefinition.prototype.scriptsModel = function () {
return scripts;
};
// CustomBlockDefinition purging deleted blocks
CustomBlockDefinition.prototype.purgeCorpses = function () {
// remove blocks that have been marked for deletion
if (this.body && this.body.expression.isCorpse) {
this.body = null;
}
this.scripts = this.scripts.filter(function (topBlock) {
return !topBlock.isCorpse;
});
};
// CustomCommandBlockMorph /////////////////////////////////////////////
// CustomCommandBlockMorph inherits from CommandBlockMorph:
@ -435,6 +453,10 @@ CustomCommandBlockMorph.prototype = new CommandBlockMorph();
CustomCommandBlockMorph.prototype.constructor = CustomCommandBlockMorph;
CustomCommandBlockMorph.uber = CommandBlockMorph.prototype;
// CustomCommandBlockMorph shared settings:
CustomCommandBlockMorph.prototype.isCustomBlock = true;
// CustomCommandBlockMorph instance creation:
function CustomCommandBlockMorph(definition, isProto) {
@ -443,6 +465,7 @@ function CustomCommandBlockMorph(definition, isProto) {
CustomCommandBlockMorph.prototype.init = function (definition, isProto) {
this.definition = definition; // mandatory
this.isGlobal = definition ? definition.isGlobal : false;
this.isPrototype = isProto || false; // optional
CustomCommandBlockMorph.uber.init.call(this, true); // silently
this.category = definition.category;
@ -457,6 +480,9 @@ CustomCommandBlockMorph.prototype.init = function (definition, isProto) {
CustomCommandBlockMorph.prototype.initializeVariables = function (oldVars) {
var myself = this;
this.variables = new VariableFrame();
if (!this.isGlobal) {
return;
}
this.definition.variableNames.forEach(function (name) {
var v = oldVars ? oldVars[name] : null;
myself.variables.addVar(
@ -466,12 +492,20 @@ CustomCommandBlockMorph.prototype.initializeVariables = function (oldVars) {
});
};
CustomCommandBlockMorph.prototype.refresh = function (silently) {
var def = this.definition,
CustomCommandBlockMorph.prototype.refresh = function (aDefinition, silently) {
var def = aDefinition || this.definition,
newSpec = this.isPrototype ?
def.spec : def.blockSpec(),
oldInputs;
// make sure local custom blocks don't hold on to a method.
// future performance optimization plan:
// null out the definition for local blocks here,
// and then cache them again when invoking them
if (!this.isGlobal && !this.isPrototype) {
this.definition = null;
}
this.setCategory(def.category);
if (this.blockSpec !== newSpec) {
oldInputs = this.inputs();
@ -480,7 +514,7 @@ CustomCommandBlockMorph.prototype.refresh = function (silently) {
} else {
this.fixBlockColor();
}
this.setSpec(newSpec, silently);
this.setSpec(newSpec, silently, def);
this.fixLabelColor();
this.restoreInputs(oldInputs);
} else { // update all input slots' drop-downs
@ -491,7 +525,7 @@ CustomCommandBlockMorph.prototype.refresh = function (silently) {
});
}
// find unnahmed upvars and label them
// find unnamed upvars and label them
// to their internal definition (default)
this.cachedInputs = null;
this.inputs().forEach(function (inp, idx) {
@ -502,7 +536,9 @@ CustomCommandBlockMorph.prototype.refresh = function (silently) {
// initialize block vars
// preserve values of unchanged variable names
this.initializeVariables(this.variables.vars);
if (this.isGlobal) {
this.initializeVariables(this.variables.vars);
}
// make (double) sure I'm colored correctly
this.forceNormalColoring();
@ -538,13 +574,15 @@ CustomCommandBlockMorph.prototype.restoreInputs = function (oldInputs) {
this.cachedInputs = null;
};
CustomCommandBlockMorph.prototype.refreshDefaults = function () {
CustomCommandBlockMorph.prototype.refreshDefaults = function (definition) {
// fill my editable slots with the defaults specified in my definition
var inputs = this.inputs(), idx = 0, myself = this;
inputs.forEach(function (inp) {
if (inp instanceof InputSlotMorph) {
inp.setContents(myself.definition.defaultValueOfInputIdx(idx));
if (inp instanceof InputSlotMorph || inp instanceof BooleanSlotMorph) {
inp.setContents(
(definition || myself.definition).defaultValueOfInputIdx(idx)
);
}
idx += 1;
});
@ -720,7 +758,7 @@ CustomCommandBlockMorph.prototype.parseSpec = function (spec) {
if (!this.isPrototype) {
return CustomCommandBlockMorph.uber.parseSpec.call(this, spec);
}
return this.definition.parseSpec.call(this, spec);
return CustomBlockDefinition.prototype.parseSpec(spec);
};
CustomCommandBlockMorph.prototype.mouseClickLeft = function () {
@ -731,7 +769,11 @@ CustomCommandBlockMorph.prototype.mouseClickLeft = function () {
};
CustomCommandBlockMorph.prototype.edit = function () {
var myself = this, editor, block, hat;
var myself = this,
def = this.definition,
editor, block,
hat,
rcvr;
if (this.isPrototype) {
block = this.definition.blockInstance();
@ -756,8 +798,21 @@ CustomCommandBlockMorph.prototype.edit = function () {
myself.isInUse()
);
} else {
// check for local custom block inheritance
rcvr = this.scriptTarget();
if (!this.isGlobal) {
if (contains(
Object.keys(rcvr.inheritedBlocks()),
this.blockSpec
)
) {
this.duplicateBlockDefinition();
return;
}
def = rcvr.getMethod(this.blockSpec);
}
Morph.prototype.trackChanges = false;
editor = new BlockEditorMorph(this.definition, this.receiver());
editor = new BlockEditorMorph(def, rcvr);
editor.popUp();
Morph.prototype.trackChanges = true;
editor.changed();
@ -805,8 +860,12 @@ CustomCommandBlockMorph.prototype.attachTargets = function () {
CustomCommandBlockMorph.prototype.isInUse = function () {
// answer true if an instance of my definition is found
// in any of my receiver's scripts or block definitions
// NOTE: for sprite-local blocks only to be used in a situation
// where the user actively clicks on a block in the IDE,
// e.g. to edit it (and change its type)
var def = this.definition,
ide = this.receiver().parentThatIsA(IDE_Morph);
rcvr = this.scriptTarget(),
ide = rcvr.parentThatIsA(IDE_Morph);
if (def.isGlobal && ide) {
return ide.sprites.asArray().concat([ide.stage]).some(
function (any, idx) {
@ -814,15 +873,16 @@ CustomCommandBlockMorph.prototype.isInUse = function () {
}
);
}
return this.receiver().usesBlockInstance(def);
return rcvr.allDependentInvocationsOf(this.blockSpec).length > 0;
};
// CustomCommandBlockMorph menu:
CustomCommandBlockMorph.prototype.userMenu = function () {
var hat = this.parentThatIsA(PrototypeHatBlockMorph),
rcvr = this.receiver(),
rcvr = this.scriptTarget(),
myself = this,
shiftClicked = this.world().currentKey === 16,
menu;
function monitor(vName) {
@ -872,27 +932,29 @@ CustomCommandBlockMorph.prototype.userMenu = function () {
this.topBlock().scriptPic(),
ide.projectName || localize('Untitled') + ' ' +
localize('script pic'),
true // request opening a new window
false // request opening a new window
);
},
'open a new window\nwith a picture of this script'
);
if (hat.inputs().length < 2) {
menu.addItem(
"block variables...",
function () {
hat.enableBlockVars();
},
'experimental -\nunder construction'
);
} else {
menu.addItem(
"remove block variables...",
function () {
hat.enableBlockVars(false);
},
'experimental -\nunder construction'
);
if (this.isGlobal) {
if (hat.inputs().length < 2) {
menu.addItem(
"block variables...",
function () {
hat.enableBlockVars();
},
'experimental -\nunder construction'
);
} else {
menu.addItem(
"remove block variables...",
function () {
hat.enableBlockVars(false);
},
'experimental -\nunder construction'
);
}
}
} else {
menu = this.constructor.uber.userMenu.call(this);
@ -901,9 +963,28 @@ CustomCommandBlockMorph.prototype.userMenu = function () {
} else {
menu.addLine();
}
if (shiftClicked) {
// menu.addItem("export definition...", 'exportBlockDefinition');
menu.addItem(
"duplicate block definition...",
'duplicateBlockDefinition',
null,
new Color(100, 0, 0)
);
}
// menu.addItem("export definition...", 'exportBlockDefinition');
menu.addItem("delete block definition...", 'deleteBlockDefinition');
// if global or own method - let the user delete the definition
if (this.isGlobal ||
contains(
Object.keys(rcvr.ownBlocks()),
this.blockSpec
)
) {
menu.addItem(
"delete block definition...",
'deleteBlockDefinition'
);
}
this.variables.names().forEach(function (vName) {
monitor(vName);
@ -920,29 +1001,57 @@ CustomCommandBlockMorph.prototype.exportBlockDefinition = function () {
ide.saveXMLAs(xml, this.spec);
};
CustomCommandBlockMorph.prototype.duplicateBlockDefinition = function () {
var rcvr = this.scriptTarget(),
ide = this.parentThatIsA(IDE_Morph),
def = this.isGlobal ? this.definition : rcvr.getMethod(this.blockSpec),
dup = def.copyAndBindTo(rcvr);
if (this.isGlobal) {
ide.stage.globalBlocks.push(dup);
} else {
rcvr.customBlocks.push(dup);
}
ide.flushPaletteCache();
ide.refreshPalette();
new BlockEditorMorph(dup, rcvr).popUp();
};
CustomCommandBlockMorph.prototype.deleteBlockDefinition = function () {
var idx, rcvr, stage, ide, myself = this, block;
var idx, stage, ide, method, block,
rcvr = this.scriptTarget(),
myself = this;
if (this.isPrototype) {
return null; // under construction...
}
block = myself.definition.blockInstance();
method = this.isGlobal? this.definition
: rcvr.getLocalMethod(this.blockSpec);
block = method.blockInstance();
block.addShadow();
new DialogBoxMorph(
this,
function () {
rcvr = myself.receiver();
rcvr.deleteAllBlockInstances(myself.definition);
if (myself.definition.isGlobal) {
rcvr.deleteAllBlockInstances(method);
if (method.isGlobal) {
stage = rcvr.parentThatIsA(StageMorph);
idx = stage.globalBlocks.indexOf(myself.definition);
idx = stage.globalBlocks.indexOf(method);
if (idx !== -1) {
stage.globalBlocks.splice(idx, 1);
}
} else {
idx = rcvr.customBlocks.indexOf(myself.definition);
// delete local definition
idx = rcvr.customBlocks.indexOf(method);
if (idx !== -1) {
rcvr.customBlocks.splice(idx, 1);
}
// refresh instances of inherited method, if any
method = rcvr.getMethod(myself.blockSpec);
if (method) {
rcvr.allDependentInvocationsOf(myself.blockSpec).forEach(
function (block) {
block.refresh(method);
}
);
}
}
ide = rcvr.parentThatIsA(IDE_Morph);
if (ide) {
@ -987,13 +1096,15 @@ CustomCommandBlockMorph.prototype.relabel = function (alternatives) {
};
CustomCommandBlockMorph.prototype.alternatives = function () {
var rcvr = this.receiver(),
var rcvr = this.scriptTarget(),
stage = rcvr.parentThatIsA(StageMorph),
allDefs = rcvr.customBlocks.concat(stage.globalBlocks),
type = this instanceof CommandBlockMorph ? 'command'
: (this.isPredicate ? 'predicate' : 'reporter'),
myself = this;
return allDefs.filter(function (each) {
return each !== myself.definition &&
each.type === myself.definition.type;
each.type === type;
});
};
@ -1005,6 +1116,10 @@ CustomReporterBlockMorph.prototype = new ReporterBlockMorph();
CustomReporterBlockMorph.prototype.constructor = CustomReporterBlockMorph;
CustomReporterBlockMorph.uber = ReporterBlockMorph.prototype;
// CustomReporterBlockMorph shared settings:
CustomReporterBlockMorph.prototype.isCustomBlock = true;
// CustomReporterBlockMorph instance creation:
function CustomReporterBlockMorph(definition, isPredicate, isProto) {
@ -1017,6 +1132,7 @@ CustomReporterBlockMorph.prototype.init = function (
isProto
) {
this.definition = definition; // mandatory
this.isGlobal = definition ? definition.isGlobal : false;
this.isPrototype = isProto || false; // optional
CustomReporterBlockMorph.uber.init.call(this, isPredicate, true); // sil.
this.category = definition.category;
@ -1031,10 +1147,11 @@ CustomReporterBlockMorph.prototype.init = function (
CustomReporterBlockMorph.prototype.initializeVariables =
CustomCommandBlockMorph.prototype.initializeVariables;
CustomReporterBlockMorph.prototype.refresh = function () {
CustomCommandBlockMorph.prototype.refresh.call(this, true);
CustomReporterBlockMorph.prototype.refresh = function (aDefinition) {
var def = aDefinition || this.definition;
CustomCommandBlockMorph.prototype.refresh.call(this, aDefinition, true);
if (!this.isPrototype) {
this.isPredicate = (this.definition.type === 'predicate');
this.isPredicate = (def.type === 'predicate');
}
if (this.parent instanceof SyntaxElementMorph) {
this.parent.cachedInputs = null;
@ -1099,6 +1216,9 @@ CustomReporterBlockMorph.prototype.isInUse
CustomReporterBlockMorph.prototype.userMenu
= CustomCommandBlockMorph.prototype.userMenu;
CustomReporterBlockMorph.prototype.duplicateBlockDefinition
= CustomCommandBlockMorph.prototype.duplicateBlockDefinition;
CustomReporterBlockMorph.prototype.deleteBlockDefinition
= CustomCommandBlockMorph.prototype.deleteBlockDefinition;
@ -1773,11 +1893,11 @@ BlockEditorMorph.prototype.init = function (definition, target) {
// override inherited properites:
this.key = 'editBlock' + definition.spec;
this.labelString = 'Block Editor';
this.labelString = this.definition.isGlobal ? 'Block Editor' : 'Method';
this.createLabel();
// create scripting area
scripts = new ScriptsMorph(target);
scripts = new ScriptsMorph();
scripts.rejectsHats = true;
scripts.isDraggable = false;
scripts.color = IDE_Morph.prototype.groupColor;
@ -1944,14 +2064,23 @@ BlockEditorMorph.prototype.consolidateDoubles = function () {
this.destroy();
};
BlockEditorMorph.prototype.refreshAllBlockInstances = function () {
var template = this.target.paletteBlockInstance(this.definition);
BlockEditorMorph.prototype.refreshAllBlockInstances = function (oldSpec) {
var def = this.definition,
template = this.target.paletteBlockInstance(def);
this.target.allBlockInstances(this.definition).forEach(
function (block) {
block.refresh();
}
);
if (this.definition.isGlobal) {
this.target.allBlockInstances(this.definition).forEach(
function (block) {
block.refresh();
}
);
} else {
this.target.allDependentInvocationsOf(oldSpec).forEach(
function (block) {
block.refresh(def);
}
);
}
if (template) {
template.refreshDefaults();
}
@ -1959,6 +2088,7 @@ BlockEditorMorph.prototype.refreshAllBlockInstances = function () {
BlockEditorMorph.prototype.updateDefinition = function () {
var head, ide,
oldSpec = this.definition.blockSpec(),
pos = this.body.contents.position(),
element,
myself = this;
@ -1984,6 +2114,9 @@ BlockEditorMorph.prototype.updateDefinition = function () {
});
if (head) {
if (this.definition.category !== head.blockCategory) {
this.target.shadowAttribute('scripts');
}
this.definition.category = head.blockCategory;
this.definition.type = head.type;
if (head.comment) {
@ -1995,8 +2128,7 @@ BlockEditorMorph.prototype.updateDefinition = function () {
}
this.definition.body = this.context(head);
this.refreshAllBlockInstances();
this.refreshAllBlockInstances(oldSpec);
ide = this.target.parentThatIsA(IDE_Morph);
ide.flushPaletteCache();
ide.refreshPalette();
@ -2024,7 +2156,7 @@ BlockEditorMorph.prototype.context = function (prototypeHat) {
new List(this.definition.inputNames()),
true // ignore empty slots for custom block reification
);
stackFrame.outerContext = null; //;
stackFrame.outerContext = null;
return stackFrame;
};
@ -2463,6 +2595,8 @@ BlockLabelFragmentMorph.prototype.userMenu = function () {
name
);
});
menu.addLine();
menu.addItem('\u23CE ' + localize('new line'), 'nl');
return menu;
};
@ -2807,7 +2941,13 @@ InputSlotDialogMorph.prototype.getInput = function () {
}
if (lbl) {
this.fragment.labelString = lbl;
this.fragment.defaultValue = this.slots.defaultInputField.getValue();
if (contains(['%b', '%boolUE'], this.fragment.type)) {
this.fragment.defaultValue =
this.slots.defaultSwitch.evaluate();
} else {
this.fragment.defaultValue =
this.slots.defaultInputField.getValue();
}
return lbl;
} else if (!this.noDelete) {
this.fragment.isDeleted = true;
@ -2922,6 +3062,7 @@ InputSlotDialogMorph.prototype.symbolMenu = function () {
'$' + symbol
]);
});
symbols.push(['\u23CE ' + localize('new line'), '$nl']);
return symbols;
};
@ -2932,7 +3073,7 @@ InputSlotDialogMorph.prototype.deleteFragment = function () {
InputSlotDialogMorph.prototype.createSlotTypeButtons = function () {
// populate my 'slots' area with radio buttons, labels and input fields
var myself = this, defLabel, defInput,
var myself = this, defLabel, defInput, defSwitch,
oldFlag = Morph.prototype.trackChanges;
Morph.prototype.trackChanges = false;
@ -2974,7 +3115,7 @@ InputSlotDialogMorph.prototype.createSlotTypeButtons = function () {
defLabel.setColor(new Color(255, 255, 255));
defLabel.refresh = function () {
if (myself.isExpanded && contains(
['%s', '%n', '%txt', '%anyUE'],
['%s', '%n', '%txt', '%anyUE', '%b', '%boolUE'],
myself.fragment.type
)) {
defLabel.show();
@ -2991,7 +3132,10 @@ InputSlotDialogMorph.prototype.createSlotTypeButtons = function () {
defInput.contents().drawNew();
defInput.setWidth(50);
defInput.refresh = function () {
if (defLabel.isVisible) {
if (myself.isExpanded && contains(
['%s', '%n', '%txt', '%anyUE'],
myself.fragment.type
)) {
defInput.show();
if (myself.fragment.type === '%n') {
defInput.setIsNumeric(true);
@ -3006,6 +3150,21 @@ InputSlotDialogMorph.prototype.createSlotTypeButtons = function () {
this.slots.add(defInput);
defInput.drawNew();
defSwitch = new BooleanSlotMorph(this.fragment.defaultValue);
defSwitch.refresh = function () {
if (myself.isExpanded && contains(
['%b', '%boolUE'],
myself.fragment.type
)) {
defSwitch.show();
} else {
defSwitch.hide();
}
};
this.slots.defaultSwitch = defSwitch;
this.slots.add(defSwitch);
defSwitch.drawNew();
Morph.prototype.trackChanges = oldFlag;
};
@ -3188,6 +3347,13 @@ InputSlotDialogMorph.prototype.fixSlotsLayout = function () {
0
))
);
this.slots.defaultSwitch.setCenter(
this.slots.defaultInputLabel.center().add(new Point(
this.slots.defaultSwitch.width() / 2
+ this.slots.defaultInputLabel.width() / 2 + 5,
0
))
);
Morph.prototype.trackChanges = oldFlag;
this.slots.changed();
};

201
gui.js
Wyświetl plik

@ -74,7 +74,7 @@ isRetinaSupported, SliderMorph, Animation*/
// Global stuff ////////////////////////////////////////////////////////
modules.gui = '2017-April-10';
modules.gui = '2017-September-01';
// Declarations
@ -489,6 +489,8 @@ IDE_Morph.prototype.openIn = function (world) {
} else if (location.hash.substr(0, 7) === '#signup') {
this.createCloudAccount();
}
this.loadNewProject = false;
}
if (this.userLanguage) {
@ -1063,6 +1065,7 @@ IDE_Morph.prototype.createPalette = function (forSearching) {
myself.currentSprite.wearCostume(null);
droppedMorph.perish();
} else if (droppedMorph instanceof BlockMorph) {
myself.stage.threads.stopAllForBlock(droppedMorph);
if (hand && hand.grabOrigin.origin instanceof ScriptsMorph) {
hand.grabOrigin.origin.clearDropInfo();
hand.grabOrigin.origin.lastDroppedBlock = droppedMorph;
@ -1539,7 +1542,7 @@ IDE_Morph.prototype.createCorral = function () {
frame.alpha = 0;
this.sprites.asArray().forEach(function (morph) {
if (!morph.isClone) {
if (!morph.isTemporary) {
template = new SpriteIconMorph(morph, template);
frame.contents.add(template);
}
@ -2189,13 +2192,25 @@ IDE_Morph.prototype.newCamSprite = function () {
IDE_Morph.prototype.duplicateSprite = function (sprite) {
var duplicate = sprite.fullCopy();
duplicate.setPosition(this.world().hand.position());
duplicate.appearIn(this);
duplicate.keepWithin(this.stage);
this.selectSprite(duplicate);
};
IDE_Morph.prototype.instantiateSprite = function (sprite) {
var instance = sprite.fullCopy(true),
hats = instance.allHatBlocksFor('__clone__init__');
instance.appearIn(this);
if (hats.length) {
instance.initClone(hats);
} else {
instance.setPosition(this.world().hand.position());
instance.keepWithin(this.stage);
}
this.selectSprite(instance);
};
IDE_Morph.prototype.removeSprite = function (sprite) {
var idx, myself = this;
sprite.parts.forEach(function (part) {myself.removeSprite(part); });
@ -2215,7 +2230,9 @@ IDE_Morph.prototype.removeSprite = function (sprite) {
this.fixLayout();
this.currentSprite = detect(
this.stage.children,
function (morph) {return morph instanceof SpriteMorph; }
function (morph) {
return morph instanceof SpriteMorph && !morph.isTemporary;
}
) || this.stage;
this.selectSprite(this.currentSprite);
@ -2238,6 +2255,13 @@ IDE_Morph.prototype.newSpriteName = function (name, ignoredSprite) {
return newName;
};
// IDE_Morph deleting scripts
IDE_Morph.prototype.removeBlock = function (aBlock, justThis) {
this.stage.threads.stopAllForBlock(aBlock);
aBlock.destroy(justThis);
};
// IDE_Morph menus
IDE_Morph.prototype.userMenu = function () {
@ -2498,6 +2522,38 @@ IDE_Morph.prototype.settingsMenu = function () {
'check for higher resolution,\nuses more computing resources'
);
}
addPreference(
'Input sliders',
'toggleInputSliders',
MorphicPreferences.useSliderForInput,
'uncheck to disable\ninput sliders for\nentry fields',
'check to enable\ninput sliders for\nentry fields'
);
if (MorphicPreferences.useSliderForInput) {
addPreference(
'Execute on slider change',
'toggleSliderExecute',
ArgMorph.prototype.executeOnSliderEdit,
'uncheck to suppress\nrunning scripts\nwhen moving the slider',
'check to run\nthe edited script\nwhen moving the slider'
);
}
addPreference(
'Turbo mode',
'toggleFastTracking',
this.stage.isFastTracked,
'uncheck to run scripts\nat normal speed',
'check to prioritize\nscript execution'
);
addPreference(
'Visible stepping',
'toggleSingleStepping',
Process.prototype.enableSingleStepping,
'uncheck to turn off\nvisible stepping',
'check to turn on\n visible stepping (slow)',
false
);
menu.addLine(); // everything visible below is persistent
addPreference(
'Blurred shadows',
'toggleBlurredShadows',
@ -2552,22 +2608,6 @@ IDE_Morph.prototype.settingsMenu = function () {
'check to enable\nvirtual keyboard support\nfor mobile devices',
true
);
addPreference(
'Input sliders',
'toggleInputSliders',
MorphicPreferences.useSliderForInput,
'uncheck to disable\ninput sliders for\nentry fields',
'check to enable\ninput sliders for\nentry fields'
);
if (MorphicPreferences.useSliderForInput) {
addPreference(
'Execute on slider change',
'toggleSliderExecute',
ArgMorph.prototype.executeOnSliderEdit,
'uncheck to suppress\nrunning scripts\nwhen moving the slider',
'check to run\nthe edited script\nwhen moving the slider'
);
}
addPreference(
'Clicking sound',
function () {
@ -2590,13 +2630,6 @@ IDE_Morph.prototype.settingsMenu = function () {
'check to enable\nIDE animations',
true
);
addPreference(
'Turbo mode',
'toggleFastTracking',
this.stage.isFastTracked,
'uncheck to run scripts\nat normal speed',
'check to prioritize\nscript execution'
);
addPreference(
'Cache Inputs',
function () {
@ -2646,7 +2679,7 @@ IDE_Morph.prototype.settingsMenu = function () {
ScriptsMorph.prototype.enableNestedAutoWrapping,
'uncheck to confine auto-wrapping\nto top-level block stacks',
'check to enable auto-wrapping\ninside nested block stacks',
false
true
);
addPreference(
'Project URLs',
@ -2702,7 +2735,7 @@ IDE_Morph.prototype.settingsMenu = function () {
ScriptsMorph.prototype.enableKeyboard,
'uncheck to disable\nkeyboard editing support',
'check to enable\nkeyboard editing support',
false
true
);
addPreference(
'Table support',
@ -2718,7 +2751,7 @@ IDE_Morph.prototype.settingsMenu = function () {
List.prototype.enableTables,
'uncheck to disable\nmulti-column list views',
'check for multi-column\nlist view support',
false
true
);
if (List.prototype.enableTables) {
addPreference(
@ -2735,7 +2768,7 @@ IDE_Morph.prototype.settingsMenu = function () {
TableMorph.prototype.highContrast,
'uncheck for less contrast\nmulti-column list views',
'check for higher contrast\ntable views',
false
true
);
}
addPreference(
@ -2749,14 +2782,6 @@ IDE_Morph.prototype.settingsMenu = function () {
'EXPERIMENTAL! check to enable\n live custom control structures',
true
);
addPreference(
'Visible stepping',
'toggleSingleStepping',
Process.prototype.enableSingleStepping,
'uncheck to turn off\nvisible stepping',
'check to turn on\n visible stepping (slow)',
false
);
menu.addLine(); // everything below this line is stored in the project
addPreference(
'Thread safe scripts',
@ -3237,7 +3262,7 @@ IDE_Morph.prototype.aboutSnap = function () {
module, btn1, btn2, btn3, btn4, licenseBtn, translatorsBtn,
world = this.world();
aboutTxt = 'Snap! 4.0.10.1\nBuild Your Own Blocks\n\n'
aboutTxt = 'Snap! 4.1 - dev -\nBuild Your Own Blocks\n\n'
+ 'Copyright \u24B8 2017 Jens M\u00F6nig and '
+ 'Brian Harvey\n'
+ 'jens@moenig.org, bh@cs.berkeley.edu\n\n'
@ -3287,6 +3312,7 @@ IDE_Morph.prototype.aboutSnap = function () {
+ '\nIan Reynolds: UI Design, Event Bindings, '
+ 'Sound primitives'
+ '\nIvan Motyashov: Initial Squeak Porting'
+ '\nLucas Karahadian: Piano Keyboard Design'
+ '\nDavide Della Casa: Morphic Optimizations'
+ '\nAchal Dave: Web Audio'
+ '\nJoe Otto: Morphic Testing and Debugging';
@ -3462,7 +3488,7 @@ IDE_Morph.prototype.newProject = function () {
StageMorph.prototype.codeMappings = {};
StageMorph.prototype.codeHeaders = {};
StageMorph.prototype.enableCodeMapping = false;
StageMorph.prototype.enableInheritance = false;
StageMorph.prototype.enableInheritance = true;
StageMorph.prototype.enableSublistIDs = false;
SpriteMorph.prototype.useFlatLineEnds = false;
Process.prototype.enableLiveCoding = false;
@ -3943,7 +3969,7 @@ IDE_Morph.prototype.exportProjectSummary = function (useDropShadows) {
'<!DOCTYPE html>' + html.toString(),
'text/html;charset=utf-8',
pname,
true // request opening a new window.
false // request opening a new window.
);
};
@ -3971,7 +3997,7 @@ IDE_Morph.prototype.rawOpenProjectString = function (str) {
StageMorph.prototype.codeMappings = {};
StageMorph.prototype.codeHeaders = {};
StageMorph.prototype.enableCodeMapping = false;
StageMorph.prototype.enableInheritance = false;
StageMorph.prototype.enableInheritance = true;
StageMorph.prototype.enableSublistIDs = false;
Process.prototype.enableLiveCoding = false;
if (Process.prototype.isCatchingErrors) {
@ -4016,7 +4042,7 @@ IDE_Morph.prototype.rawOpenCloudDataString = function (str) {
StageMorph.prototype.codeMappings = {};
StageMorph.prototype.codeHeaders = {};
StageMorph.prototype.enableCodeMapping = false;
StageMorph.prototype.enableInheritance = false;
StageMorph.prototype.enableInheritance = true;
StageMorph.prototype.enableSublistIDs = false;
Process.prototype.enableLiveCoding = false;
if (Process.prototype.isCatchingErrors) {
@ -5373,7 +5399,7 @@ ProjectDialogMorph.prototype.init = function (ide, task) {
this.srcBar = null;
this.nameField = null;
this.filterField = null;
this.magnifiyingGlass = null;
this.magnifyingGlass = null;
this.listField = null;
this.preview = null;
this.notesText = null;
@ -5658,13 +5684,13 @@ ProjectDialogMorph.prototype.buildFilterField = function () {
var myself = this;
this.filterField = new InputFieldMorph('');
this.magnifiyingGlass =
this.magnifyingGlass =
new SymbolMorph(
'magnifiyingGlass',
'magnifyingGlass',
this.filterField.height(),
this.titleBarColor.darker(50));
this.body.add(this.magnifiyingGlass);
this.body.add(this.magnifyingGlass);
this.body.add(this.filterField);
this.filterField.reactToKeystroke = function (evt) {
@ -6267,9 +6293,9 @@ ProjectDialogMorph.prototype.fixLayout = function () {
this.body.height() - inputField.height() - this.padding
);
if (this.magnifiyingGlass) {
this.magnifiyingGlass.setTop(inputField.top());
this.magnifiyingGlass.setLeft(this.listField.left());
if (this.magnifyingGlass) {
this.magnifyingGlass.setTop(inputField.top());
this.magnifyingGlass.setLeft(this.listField.left());
}
this.preview.setRight(this.body.right());
@ -6872,10 +6898,30 @@ SpriteIconMorph.prototype.userMenu = function () {
menu.addItem("show", 'showSpriteOnStage');
menu.addLine();
menu.addItem("duplicate", 'duplicateSprite');
if (StageMorph.prototype.enableInheritance) {
menu.addItem("clone", 'instantiateSprite');
}
menu.addItem("delete", 'removeSprite');
menu.addLine();
if (StageMorph.prototype.enableInheritance) {
/* version that hides refactoring capability unless shift-clicked
if (this.world().currentKey === 16) { // shift-clicked
menu.addItem(
"parent...",
'chooseExemplar',
null,
new Color(100, 0, 0)
);
}
*/
menu.addItem("parent...", 'chooseExemplar');
if (this.object.exemplar) {
menu.addItem(
"release",
'releaseSprite',
'make temporary and\nhide in the sprite corral'
);
}
}
if (this.object.anchor) {
menu.addItem(
@ -6900,6 +6946,13 @@ SpriteIconMorph.prototype.duplicateSprite = function () {
}
};
SpriteIconMorph.prototype.instantiateSprite = function () {
var ide = this.parentThatIsA(IDE_Morph);
if (ide) {
ide.instantiateSprite(this.object);
}
};
SpriteIconMorph.prototype.removeSprite = function () {
var ide = this.parentThatIsA(IDE_Morph);
if (ide) {
@ -6915,6 +6968,10 @@ SpriteIconMorph.prototype.chooseExemplar = function () {
this.object.chooseExemplar();
};
SpriteIconMorph.prototype.releaseSprite = function () {
this.object.release();
};
SpriteIconMorph.prototype.showSpriteOnStage = function () {
this.object.showOnStage();
};
@ -7161,6 +7218,7 @@ CostumeIconMorph.prototype.userMenu = function () {
};
CostumeIconMorph.prototype.editCostume = function () {
this.disinherit();
if (this.object instanceof SVG_Costume) {
this.object.editRotationPointOnly(this.world());
} else {
@ -7179,6 +7237,7 @@ CostumeIconMorph.prototype.editRotationPointOnly = function () {
};
CostumeIconMorph.prototype.renameCostume = function () {
this.disinherit();
var costume = this.object,
wardrobe = this.parentThatIsA(WardrobeMorph),
ide = this.parentThatIsA(IDE_Morph);
@ -7230,7 +7289,7 @@ CostumeIconMorph.prototype.exportCostume = function () {
// don't show SVG costumes in a new tab (shows text)
ide.saveFileAs(this.object.contents.src, 'text/svg', this.object.name);
} else { // rasterized Costume
ide.saveCanvasAs(this.object.contents, this.object.name, true);
ide.saveCanvasAs(this.object.contents, this.object.name, false);
}
};
@ -7239,9 +7298,21 @@ CostumeIconMorph.prototype.exportCostume = function () {
CostumeIconMorph.prototype.createBackgrounds
= SpriteIconMorph.prototype.createBackgrounds;
// CostumeIconMorph inheritance
CostumeIconMorph.prototype.disinherit = function () {
var wardrobe = this.parentThatIsA(WardrobeMorph),
idx = this.parent.children.indexOf(this);
if (wardrobe.sprite.inheritsAttribute('costumes')) {
wardrobe.sprite.shadowAttribute('costumes');
this.object = wardrobe.sprite.costumes.at(idx - 2);
}
};
// CostumeIconMorph drag & drop
CostumeIconMorph.prototype.prepareToBeGrabbed = function () {
this.disinherit();
this.mouseClickLeft(); // select me
this.removeCostume();
};
@ -7594,6 +7665,7 @@ WardrobeMorph.prototype.step = function () {
// Wardrobe ops
WardrobeMorph.prototype.removeCostumeAt = function (idx) {
this.sprite.shadowAttribute('costumes');
this.sprite.costumes.remove(idx);
this.updateList();
};
@ -7606,6 +7678,7 @@ WardrobeMorph.prototype.paintNew = function () {
ide = this.parentThatIsA(IDE_Morph),
myself = this;
cos.edit(this.world(), ide, true, null, function () {
myself.sprite.shadowAttribute('costumes');
myself.sprite.addCostume(cos);
myself.updateList();
if (ide) {
@ -7650,6 +7723,7 @@ WardrobeMorph.prototype.reactToDropOf = function (icon) {
idx += 1;
}
});
this.sprite.shadowAttribute('costumes');
this.sprite.costumes.add(costume, idx + 1);
this.updateList();
icon.mouseClickLeft(); // select
@ -7828,6 +7902,7 @@ SoundIconMorph.prototype.renameSound = function () {
var sound = this.object,
ide = this.parentThatIsA(IDE_Morph),
myself = this;
this.disinherit();
(new DialogBoxMorph(
null,
function (answer) {
@ -7858,9 +7933,21 @@ SoundIconMorph.prototype.createBackgrounds
SoundIconMorph.prototype.createLabel
= SpriteIconMorph.prototype.createLabel;
// SoundIconMorph inheritance
SoundIconMorph.prototype.disinherit = function () {
var jukebox = this.parentThatIsA(JukeboxMorph),
idx = this.parent.children.indexOf(this);
if (jukebox.sprite.inheritsAttribute('sounds')) {
jukebox.sprite.shadowAttribute('sounds');
this.object = jukebox.sprite.sounds.at(idx);
}
};
// SoundIconMorph drag & drop
SoundIconMorph.prototype.prepareToBeGrabbed = function () {
this.disinherit();
this.removeSound();
};
@ -7883,7 +7970,7 @@ function JukeboxMorph(aSprite, sliderColor) {
JukeboxMorph.prototype.init = function (aSprite, sliderColor) {
// additional properties
this.sprite = aSprite || new SpriteMorph();
this.costumesVersion = null;
this.soundsVersion = null;
this.spriteVersion = null;
// initialize inherited properties
@ -7934,6 +8021,7 @@ JukeboxMorph.prototype.updateList = function () {
myself.addContents(icon);
y = icon.bottom() + padding;
});
this.soundsVersion = this.sprite.costumes.lastChanged;
Morph.prototype.trackChanges = oldFlag;
this.changed();
@ -7950,13 +8038,14 @@ JukeboxMorph.prototype.updateSelection = function () {
// Jukebox stepping
/*
JukeboxMorph.prototype.step = function () {
if (this.soundsVersion !== this.sprite.sounds.lastChanged) {
this.updateList();
}
if (this.spriteVersion !== this.sprite.version) {
this.updateSelection();
}
};
*/
// Jukebox ops
@ -7982,6 +8071,8 @@ JukeboxMorph.prototype.reactToDropOf = function (icon) {
idx += 1;
}
});
this.sprite.shadowAttribute('sounds');
this.sprite.sounds.add(costume, idx);
this.updateList();
};

Wyświetl plik

@ -3407,3 +3407,232 @@ Fixes:
== v4.0.10.1 - maintenance release - ===
== v4.1 - development - ===
170411
------
* Objects: export text from variable watchers to new browser tab by default
170505
------
* attribute inheritance support for x, y, dir and size
170509
------
* added tools to the library browser
* added attributes to the “delete” blocks drop-down menu
170512
------
* exposed costumes as an attribute
* added inheritance support for the wardrobe (costumes)
* added inheritance support for costume #
170530
------
* let clones share the orginals scripts without shallow-copying them
170531
------
* added inheritance support for scripts, partly done, copy-on-write is still missing
170602
------
* added a thread count indicator to shared-script highlights
170619
------
* threads: fixed #1767 (optimized thread-launch and highlighting)
* threads, blocks: optimized thread count indicator for “glow” halos
170620
------
* threads, blocks, ide: make sure to stop active processes when deleting a block
* objects: migrate experimental “wardrobe” reporters to the new “my costumes” reporter
170621
------
* objects: stop all scripts for a sprite when shadowing or inheriting its scripts
170622
------
* Morphic: support for copy-on-write worlds (“selectForEdit”)
* Blocks, Objects: shadow inherited scripts on dragging & dropping blocks and comments
170623
------
* Blocks: shadow inherited scripts on deleting blocks and comments via the context menu
* Blocks: shadow inherited scripts on “clean up”
* Blocks, Objects: shadow inherited scripts on keyboard entry
* Blocks: shadow inherited scripts on input edit, ringify/unringify, relabel action
170624
------
* Threads: tweaked error-catching & handling for receiver-less scripts
* Blocks: experimented with first-class sounds, deferred for now
* corrected a typo in the German translation. Thanks, Jadga, for reporting this!
170626
------
* Objects, Blocks, Threads, Tables, Store: First-Class Sounds
* Block: new musical “notes” symbol
* Objects, Blocks, Threads, GUI: inheritance support for sounds
170627
------
* Objects: Inheritance of costumes and sounds - propagate changes
* Store: Tweaked loading sprites with inherited complex attributes (costumes, sounds)
170629
------
* Objects: manage inheritance relationships when setting a prototype or deleting a sprite
170630
------
* Objects: reflect attribute inheritance status by ghosting / un-ghosting stage monitors
* Objects: migrate experimental “jukebox” reporters to the new “my sounds” reporter
170703
------
* Objects, Threads, GUI, Blocks, YPR: renamed Sprite::isClone to Sprite:isTemporary
170704
------
* Morphic: Simplify contains()
* Unify Scratch-style clones and Snap-specimens, part 1: implement clones as specimens
170705
------
* Objects, GUI: UI for OOP
170707
------
* Objects, GUI, Store: tweak naming of instantiating to “clone”, enable inheritance by default
* Objects, GUI: run “When I start as clone” scripts when manually cloning a sprite, only position at hand pointer if no such scripts exist
* Morphic, Objects: confine turtle direction readout to 0-360 degrees, thanks, Cynthia for the bug report!!
170708
------
* Threads: Assert data types to list operations -> meaningful error messages
* GUI: enable inheritance per default, must be user-enabled for existing projects
170709
------
* Objects, Threads: added experimental (only shown in dev mode) “tell ... to ..." and “ask ... for ...” primitives
170711
------
* Objects: fixed an inheritance glitch for clones
* Objects: fixed variable inheritance for traditional Scratch-like clones
* Objects: tweaked inheritance indication for stage watchers
* Objects: fixed custom block inheritance for traditional Scratch-like clones
* Objects: optimized deleting traditional Scratch-like cones
170712
------
* Blocks: fixed #1800. Thanks, Ken, for the bug report!
* Objects, Threads: “new clone of ...” primitive, made TELL, ASK primitives official
* Objects: only refresh certain propagated inherited attributes on being dropped
* Objects: renamed “delete” primitive to “inherit”
170715
------
* BYOB: shadow inherited scripts when changing the category of an inherited method
170725
------
* Objects: fixed rotation-bug when flipping costumes in "only turn left/right" mode"
* BYOB: changed Block Editor label to "Method" for methods
* GUI: moved settings 'Keyboard Editing', 'Nested auto-wrapping', "Table support" and "Table lines" to hidden (default is "on" for all)
170726
------
* Threads: programmatically hide and show primitives in the palette. Thanks, Cynthia Solomon, for this idea!
* Objects: added "pen trails" reporter primitive and stage context menu entry
* Threads, Blocks: added 'costume' and 'sound' as first-class data types
* Lists, Store, Objects, Threads: enable type-assertion for list elements (costumes, sounds)
170727
------
* Objects: don't shadow costume # when editing a costume
* Blocks, Objects: remodelled context menu for inheritance to use check-boxes
* Blocks, Objects, Threads: fold two "stop" commands into one
* Objects: Allow two-item lists as arguments for "point towards" and "go to" primitives
170731
------
* Blocks, Objects: fixed PianoMenu to work with block zoom etc.
* Threads, Objects, Blocks, Store: added “instruments”: sine, square, sawtooth, triangle waves
170801
------
* Morphic: Tweaks by Craig Latta (thanks!)
170802
------
* Blocks: Improve PianoKeyboard for keyboard navigation & entry
* Blocks, Widgets: Moved PianoKeyboard code to widgets.js
* Blocks, Widgets: Added sound feedback to PianoKeyboard
* New file: symbols.js (moved out of blocks.js)
* Updated credits to reflect Michaels piano keyboard design contribution
* Threads: simplified “instrument” access
* Threads: enable multiple instruments per sprite in parallel threads
* GUI, Widgets: Changed piano keyboard design credits to Lucas Karahadian
* GUI: fixed #1820
170803
------
* enable exporting script pics in the newest Chrome version, which disables opening tabs on dataURLs
170804
------
* GUI: enable exporting project summaries in the newest Chrome version, which disables opening tabs on dataURLs
170829
------
* Threads: allow two-item lists as x-y coordinate arguments for “distance to” reporter
* GUI, Objects: enable exporting costumes and variable-data in the newest Chrome version, which disables opening tabs on dataURLs
* Blocks, Threads: added “temporary?” as gettable and settable attribute for clones
170830
------
* Blocks, Threads: Confine programmatically setting the “temporary?” attribute to dev mode
* BYOB: enable exporting script pics of custom blocks in the newest Chrome version, which disables opening tabs on dataURLs
* Morphic, Objects: fixed #1843
* Croation translation update, thanks, Zeljko Hrvoj!
v4.1 Features:
* polymorphic sprite-local custom blocks
* inheritance of sprite-local custom blocks
* inheritance of sprite attributes (x, y, direction, size, costumes, costume #, sounds, scripts)
* first-class costumes and sounds
* localization support when typing expressions
* support for user-forced line-breaks in custom block labels
* ternary Boolean slot setting: support to limit Boolean input slots to “true/false” outside of rings and in palette
* support for default values in custom block Boolean slots
* experimental: duplicate block definition (hidden in shift-click context menu)
* support for codification of String, Number and Boolean value types
* costume icons indicate svg costumes
* spritess rotation centers can be adjusted onstage
* clones share their original sprites scripts, not a shallow-copy of them
* a highlight-colored balloon indicates the number of active processes per shared script
* new musical “notes” symbol
* turn on the “Inheritance support” setting per default
* Assert data types to list operations for more meaningful error messages
* programmatically hide and show primitives in the palette
* new "pen trails" reporter primitive and stage context menu entry
* two-item lists as x-y coordinate arguments for "point towards”, "go to" and “distance to” primitives
* Piano keyboard as drop-down menu for entering musical notes, Thanks, Michael!
* Basic “instruments” support: sine, square, sawtooth and triangle waves
Fixes:
* changed keyboard shortcut indicator for “find blocks” to “^”
* prevent Snap from “hanging” when encountering certain errors in visible stepping
* only mark implicit parameters if no formal ones exist
* optimized thread-launch and script highlighting to a single frame instead of formerly two
* changed direction attribute of sprites to automatically confine to 0-360 degrees
* fixed rotation-bug when flipping costumes in "only turn left/right" mode"
* fixed variable renaming (“refactoring”) bugs, thanks, Bernat!
* fixed “fill” block crash when applying the same color twice
* fixed some typos

16
lang-de.js 100755 → 100644
Wyświetl plik

@ -185,7 +185,7 @@ SnapTranslator.dict.de = {
'translator_e-mail':
'jens@moenig.org', // optional
'last_changed':
'2017-01-10', // this, too, will appear in the Translators tab
'2017-06-24', // this, too, will appear in the Translators tab
// GUI
// control bar:
@ -845,6 +845,8 @@ SnapTranslator.dict.de = {
'einschalten f\u00fcr flache\nPinselstrichenden',
'uncheck for round ends of lines':
'auschalten f\u00fcr runde\nPinselstrichenden',
'Ternary Boolean slots':
'Ternäre Bool\'sche Inputs',
'Inheritance support':
'Prototypische Vererbung',
@ -895,6 +897,8 @@ SnapTranslator.dict.de = {
'ausschalten, um den Inhalt\nim Projekt zu speichern',
'check to prevent contents\nfrom being saved':
'einschalten, um das Speichern des Inhalts\nim Projekt zu verhindern',
'new line':
'neue Zeile',
// custom blocks:
'delete block definition...':
@ -907,6 +911,10 @@ SnapTranslator.dict.de = {
'Bearbeiten',
'move':
'Verschieben',
'pivot':
'Angelpunkt',
'edit the costume\'s\nrotation center':
'Drehpunkt des Kostüms\nanzeigen und verschieben',
'detach from':
'Abtrennen von',
'detach all parts':
@ -936,7 +944,7 @@ SnapTranslator.dict.de = {
'redrop':
'Wiederherstellen',
'scripts pic...':
'Bild aller Scripte...',
'Bild aller Skripte...',
'open a new window\nwith a picture of all scripts':
'ein neues Browserfenster mit einem\nBild aller Skripte \u00f6ffnen',
'make a block...':
@ -1356,6 +1364,10 @@ SnapTranslator.dict.de = {
'e^':
'e^',
// Boolean expressions keyboard entry
'not':
'nicht',
// delimiters
'letter':
'Buchstabe',

Wyświetl plik

@ -177,7 +177,7 @@ SnapTranslator.dict.hr = {
ß \u00df
*/
// translations meta information 4.0.2.IMA NOVA VERZIJA
// translations meta information 4.0.10.2
'language_name':
'Hrvatski', // the name as it should appear in the language menu
'language_translator':
@ -185,7 +185,7 @@ SnapTranslator.dict.hr = {
'translator_e-mail':
'zeljko.hrvoj@zg.t-com.hr', // optional
'last_changed':
'2015-09-15', // this, too, will appear in the Translators tab
'2017-08-15', // this, too, will appear in the Translators tab
// GUI
// control bar:
@ -212,7 +212,7 @@ SnapTranslator.dict.hr = {
'Variables':
'Varijable',
'Lists':
'Popisi',
'Liste',
'Other':
'Ostalo',
@ -225,6 +225,8 @@ SnapTranslator.dict.hr = {
'Skripte',
'Costumes':
'Kostimi',
'Backgrounds':
'Pozadine',
'Sounds':
'Zvukovi',
@ -248,8 +250,8 @@ SnapTranslator.dict.hr = {
// tab help
'costumes tab help':
'Slike uvozi\u0161 povla\u010Denjem s jedne druge\n'
+ 'web stranice ili ra\u010Dunala',
'Slike uvozi\u0161 povla\u010Denjem s druge\n'
+ 'web stranice ili s ra\u010Dunala',
'import a sound from your computer\nby dragging it into here':
'Zvuk uvozi\u0161 tako, da ga povu\u010De\u0161 ovdje',
@ -287,11 +289,11 @@ SnapTranslator.dict.hr = {
// motion:
'Stage selected:\nno motion primitives':
'Scena je izabrana, ali je\njo\u0161 bez blokova '
'Scena je izabrana: \n- bez blokova '
+ 'kretanja',
'move %n steps':
'pomakni se %n koraka',
'pomak %n koraka',
'turn %clockwise %n degrees':
'okreni se %clockwise %n stupnjeva',
'turn %counterclockwise %n degrees':
@ -301,9 +303,9 @@ SnapTranslator.dict.hr = {
'point towards %dst':
'okreni se prema %dst',
'go to x: %n y: %n':
'kreni prema x: %n y: %n',
'kreni na x: %n y: %n',
'go to %dst':
'kreni prema %dst',
'kreni na %dst',
'glide %n secs to x: %n y: %n':
'kli\u017Ei %n s do x: %n y: %n',
'change x by %n':
@ -355,7 +357,7 @@ SnapTranslator.dict.hr = {
'size':
'veli\u010Dina',
'show':
'pogledaj',
'poka\u017Ei',
'hide':
'sakrij',
'go to front':
@ -396,19 +398,19 @@ SnapTranslator.dict.hr = {
'pen up':
'olovku digni',
'set pen color to %clr':
'postavi boju olovke na %clr',
'boja olovke %clr',
'change pen color by %n':
'promijeni boju olovke za %n',
'set pen color to %n':
'postavi boju olovke na %n',
'boja olovke %n',
'change pen shade by %n':
'promijeni sjenu olovke za %n',
'set pen shade to %n':
'postavi sjenu olovke na %n',
'sjena olovke %n',
'change pen size by %n':
'promijeni veli\u010Dinu olovke za %n',
'set pen size to %n':
'postavi veli\u010Dinu olovke na %n',
'veli\u010Dina olovke %n',
'stamp':
'pe\u010Dat',
@ -430,21 +432,21 @@ SnapTranslator.dict.hr = {
'mouse-departed':
'mi\u0161 napusti',
'when I receive %msgHat':
'kad \u010Dujem %msgHat',
'kad spazim doga\u0111aj %msgHat',
'broadcast %msg':
'razglasi %msg',
'objavljujem doga\u0111aj %msg',
'broadcast %msg and wait':
'razglasi %msg i \u010Dekaj odg.',
'objavljujem doga\u0111aj %msg i \u010Dekam',
'Message name':
'Ime poruke',
'Ime doga\u0111aja',
'message':
'poruka',
'doga\u0111aj',
'any message':
'bilo koja poruka',
'bilo koji doga\u0111aj',
'wait %n secs':
'\u010Dekaj %n s',
'\u010Dekam %n s',
'wait until %b':
'\u010Dekaj dok je %b',
'\u010Dekam dok ne bude %b',
'forever %c':
'zauvijek %c',
'repeat %n %c':
@ -456,7 +458,7 @@ SnapTranslator.dict.hr = {
'if %b %c else %c':
'ako %b %c ina\u010De %c',
'report %s':
'izvje\u0161\u0107e %s',
'vrati vrijednost ili tvrdnju %s',
'stop %stopChoices':
'zaustavi %stopChoices',
'all':
@ -565,7 +567,7 @@ SnapTranslator.dict.hr = {
'world':
'svijet',
'letter %n of %s':
'slovo %n od %s',
'znak %n od %s',
'length of %s':
'duljina od %s',
'unicode of %s':
@ -599,35 +601,35 @@ SnapTranslator.dict.hr = {
'hide variable %var':
'sakrij varijablu %var',
'script variables %scriptVars':
'skriptne varijable %scriptVars',
'skriptna varijabla %scriptVars',
// lists:
'list %exp':
'popis %exp',
'lista %exp',
'%s in front of %l':
'%s ispred %l',
'item %idx of %l':
'element %idx od %l',
'element %idx liste %l',
'all but first of %l':
'svi osim prvog od %l',
'svi osim prvog iz liste %l',
'length of %l':
'duljina od %l',
'duljina liste %l',
'%l contains %s':
'%l sadr\u017Ei %s',
'lista %l sadr\u017Ei %s',
'thing':
'stvar',
'add %s to %l':
'dodaj %s na %l',
'dodaj %s na listu %l',
'delete %ida of %l':
'obri\u0161i %ida od %l',
'obri\u0161i %ida iz liste %l',
'insert %s at %idx of %l':
'ubaci %s na mjesto %idx od %l',
'ubaci %s na %idx mjesto liste %l',
'replace item %idx of %l with %s':
'zamijeni element %idx od %l sa %s',
'zamijeni %idx element liste %l sa %s',
// other
'Make a block':
'Napravi blok',
'Napravi novi blok',
// menus
// snap menu
@ -680,7 +682,21 @@ SnapTranslator.dict.hr = {
'Izvoz blokova',
'show global custom block definitions as XML\nin a new browser window':
'prikaz globalnih definicija korisni\u010Dkih blokova u XML obliku\nu novom prozoru preglednika',
'Import tools':
'Unused blocks...':
'Nekori\u0161teni blokovi',
'find unused global custom blocks\nand remove their definitions':
'Napravljeni blokovi - nekori\u0161teni',
'Remove unused blocks':
'Makni nekori\u0161tene blokove',
'there are currently no unused\nglobal custom blocks in this project':
'nema nekori\u0161tenih blokova\nu ovom projektu',
'unused block(s) removed':
'nekori\u0161tenih blokova izbrisano',
'Export summary...':
'Izvezi sa\u017Eetak...',
'open a new browser browser window\n with a summary of this project':
'otvara novi prozor preglednika\nsa sa\u017Eetkom projekta',
'Import tools':
'Uvezi alate',
'load the official library of\npowerful blocks':
'u\u010Ditaj slu\u017Ebenu biblioteku\ns naprednim blokovima',
@ -779,8 +795,16 @@ SnapTranslator.dict.hr = {
'ozna\u010Di da se omogu\u0107i\u0161 IDE-\nanimacije',
'Flat design':
'Flat design',
'Nested auto-wrapping':
'Automatsko ugnje\u017E\u0111ivanje',
'Keyboard Editing':
'Ure\u0111ivanje tipkovnicom',
'Table support':
'Podr\u0161ka za tablice',
'Table lines':
'Linije tablica',
'Visible stepping':
'Prikazuj izvr\u0161avanje blokova',
'Thread safe scripts':
'Skripte - vi\u0161estrukost',
'uncheck to allow\nscript reentrance':
@ -810,7 +834,7 @@ SnapTranslator.dict.hr = {
'Input Names:':
'Imena parametara:',
'input list:':
'popis parametara:',
'lista parametara:',
// context menus:
'help':
@ -886,7 +910,7 @@ SnapTranslator.dict.hr = {
'open a new window\nwith a picture of all scripts':
'otvori novi prozor\nsa slikom svih skripti',
'make a block...':
'napravi blok...',
'napravi novi blok...',
// costumes
'rename':
@ -1001,15 +1025,15 @@ SnapTranslator.dict.hr = {
'for all sprites':
'za sve objekte',
'for this sprite only':
'samo za taj objekt',
'samo za trenutni objekt',
// block dialog
'Change block':
'Promijeni blok',
'Command':
'Naredba',
'Potprogram',
'Reporter':
'Vrijednost',
'Funkcija',
'Predicate':
'Tvrdnja',
@ -1024,7 +1048,7 @@ SnapTranslator.dict.hr = {
'Obri\u0161i korisni\u010Dki blok',
'block deletion dialog text':
'Da li da obri\u0161em taj blok\n' +
'sa svim primjerima?',
'sa svim potprogramima?',
// input dialog
'Create input name':
@ -1034,7 +1058,7 @@ SnapTranslator.dict.hr = {
'Edit label fragment':
'Uredi oznaku',
'Title text':
'Tekst naslova',
'Tekst u naslovu',
'Input name':
'Ime parametra',
'Delete':
@ -1046,7 +1070,7 @@ SnapTranslator.dict.hr = {
'Text':
'Tekst',
'List':
'Popis',
'Lista',
'Any type':
'Bilo koji tip',
'Boolean (T/F)':
@ -1064,7 +1088,7 @@ SnapTranslator.dict.hr = {
'Default Value:':
'Default vrijednost:',
'Multiple inputs (value is list of inputs)':
'Vi\u0161e parametara (vrijednost je popis parametara)',
'Vi\u0161e parametara (vrijednost je lista parametara)',
'Upvar - make internal variable visible to caller':
'Interna varijabla vidljiva pozivaocu',
@ -1086,7 +1110,7 @@ SnapTranslator.dict.hr = {
'current module versions:':
'Verzije u\u010Ditanih modula:',
'Contributors':
'Doprinosioci',
'Doprinijeli',
'Translations':
'Prijevodi',
@ -1114,7 +1138,7 @@ SnapTranslator.dict.hr = {
// coments
'add comment here...':
'ovdje dodaj komentar...',
'ovdje napi\u0161i komentar...',
// drow downs
// directions
@ -1267,7 +1291,7 @@ SnapTranslator.dict.hr = {
// delimiters
'letter':
'slovo',
'znak',
'whitespace':
'razmak',
'line':
@ -1285,11 +1309,11 @@ SnapTranslator.dict.hr = {
'Boolean':
'logi\u010Dki',
'list':
'popis',
'lista',
'command':
'naredba',
'potprogram',
'reporter':
'vrijednost',
'funkcija',
'predicate':
'tvrdnja',
@ -1297,5 +1321,307 @@ SnapTranslator.dict.hr = {
'last':
'zadnji',
'any':
'bilo koji'
'bilo koji',
//added manually
'grow':
've\u0107e',
'shrink':
'manje',
'flip ↔':
'izvrni ↔',
'flip ↕':
'izvrni ↕',
'Export all scripts as pic...':
'Izvezi sve skripte kao sliku...',
'show a picture of all scripts\nand block definitions':
'pokaž \u017Ei sliku svih skripti\ni definicija blokova',
'current %dates':
'trenutni %dates',
'year':
'godina',
'month':
'mjesec',
'date':
'dan',
'day of week':
'dan u tjednu',
'hour':
'sat',
'minute':
'minuta',
'second':
'sekunda',
'time in milliseconds':
'vrijeme u ms',
'find blocks...':
'na\u0111i blokove...',
'costume name':
'ime kostima',
'Open':
'Otvori',
'Share':
'Dijeli',
'Snap!Cloud':
'Snap!Cloud',
'Cloud':
'Oblak',
'could not connect to:':
'ne mo\u017Ee se spojiti na:',
'Service:':
'Servis:',
'login':
'prijava',
'ERROR: INVALID PASSWORD':
'Gre\u0161ka: neva\u017Ee\u0107a zaporka',
'Browser':
'Preglednik',
'Sign up':
'Registracija',
'Signup':
'Registracija',
'Sign in':
'Prijava',
'Logout':
'Odjava',
'Change Password...':
'Promjena zaporke…',
'Change Password':
'Promjena zaporke',
'Account created.':
'Ra\u010Dun je kreiran.',
'An e-mail with your password\nhas been sent to the address provided':
'E-mail sa zaporkom je\nposlan na upisanu adresu',
'now connected.':
'sad sam spojen.',
'disconnected.':
'odspojen.',
'Reset password':
'Obnova zaporke',
'Reset Password...':
'Obnova zaporke…',
'User name:':
'Korisni\u010Dko ime:',
'Password:':
'Zaporka:',
'Old password:':
'Stara zaporka:',
'New password:':
'Nova zaporka:',
'Repeat new password:':
'Ponovi novu zaporku:',
'Birth date:':
'Datum ro\u0111enja:',
'January':
'Sije\u010Danj',
'February':
'Velja\u010Da',
'March':
'O\u017Eujak',
'April':
'Travanj',
'May':
'Svibanj',
'June':
'Lipanj',
'July':
'Srpanj',
'August':
'Kolovoz',
'September':
'Rujan',
'October':
'Listopad',
'November':
'Studeni',
'December':
'Prosinac',
'year:':
'godina:',
' or before':
' ili prije',
'E-mail address:':
'E-mail adresa:',
'E-mail address of parent or guardian:':
'E-mail adresa roditelja ili staratelja:',
'Terms of Service...':
'Uvjeti kori\u0161tenja...',
'Privacy...':
'Privatnost...',
'I have read and agree\nto the Terms of Service':
'Pro\u010Ditao/la sam i sla\u017Eem se\ns uvjetima kori\u0161tenja',
'stay signed in on this computer\nuntil logging out':
'ostani prijavljen na\nra\u010Dunalu do odjave',
'please fill out\nthis field':
'molimo ispunite\novo polje',
'User name must be four\ncharacters or longer':
'Korisni\u010Dko ime mora\nimati vi\u0161e od 4 znaka',
'please provide a valid\nemail address':
'molimo upi\u0161ite\nva\u017Ee\u0107u e-mail adresu',
'password must be six\ncharacters or longer':
'zaporka mora imati\n6 znakova ili vi\u0161e',
'passwords do\nnot match':
'zaporke se\nne podudaraju',
'please agree to\nthe TOS':
'molimo prihvatite\nuvjete',
'Examples':
'Primjeri',
'You are not logged in':
'Niste prijavljeni',
'Updating\nproject list...':
'Osvje\u017Eavam\nlistu projekata...',
'Opening project...':
'Otvaram projekt...',
'Fetching project\nfrom the cloud...':
'Povla\u010Denje projekta\niz oblaka...',
'Saving project\nto the cloud...':
'Spremanje projekta\nu oblak...',
'Sprite Nesting':
'Ugnje\u017E\u0111ivanje objekata',
'uncheck to disable\nsprite composition':
'odznačite da onemogućite\nkombiniranje objekata',
'Codification support':
'Podr\u0161ka za kodiranje',
'check for block\nto text mapping features':
'označi za pretvaranje\nblokova u kod',
'saved.':
'spremljeno.',
'options...':
'opcije...',
'read-only':
'samo za \u010Ditanje',
'Input Slot Options':
'Opcije ulaznog utora',
'Enter one option per line.Optionally use "=" as key/value delimiter\ne.g.\n the answer=42':
'Upi\u0161ite jednu opciju po liniji.\nKoristite "=" kao key/value delimiter\nnpr: odgovor=42',
'paint a new sprite':
'nacrtaj novi objekt',
'Paint a new costume':
'nacrtaj novi kostim',
'add a new Turtle sprite':
'dodaj novi objekt',
'undo':
'poni\u0161ti',
'Brush size':
'Veli\u010Dina olovke',
'Constrain proportions of shapes?\n(you can also hold shift)':
'Odr\u017Eati proporcije?\n(mo\u017Eete i dr\u017Eati shift)',
'Eraser tool':
'Brisalica\n(gumica)',
'Paintbrush tool\n(free draw)':
'Olovka\n(slobodno crtanje)',
'Line tool\n(shift: vertical/horizontal)':
'Línije\n(shift: okomite/vodoravne)',
'Stroked Rectangle\n(shift: square)':
'Pravokutnik\n(shift: kvadrat)',
'Filled Rectangle\n(shift: square)':
'Ispunjeni pravokutnik\n(shift: kvadrat)',
'Stroked Ellipse\n(shift: circle)':
'Elipsa\n(shift: krug)',
'Filled Ellipse\n(shift: circle)':
'Ispunjena elipsa\n(shift: krug)',
'Fill a region':
'Ispuna podru\u010Dja',
'Set the rotation center':
'Postavi centar rotacije',
'Pipette tool\n(pick a color anywhere)':
'Kapaljka\n(pokupit \u0107e uzorak boje)',
'Paint Editor':
'Ure\u0111iva\u010D slika',
'square':
'kvadrat',
'pointRight':
'pointRight',
'gears':
'gears',
'file':
'file',
'fullScreen':
'pun ekran',
'normalScreen':
'normal ekran',
'smallStage':
'mala scena',
'normalStage':
'normal scena',
'turtle':
'objekt',
'turtleOutline':
'obris objekta',
'pause':
'pauza',
'flag':
'zastava',
'octagon':
'oktogon',
'cloud':
'oblak',
'cloudOutline':
'obris oblaka',
'cloudGradient':
'gradijent oblaka',
'turnRight':
'okreni desno',
'turnLeft':
'okreni lijevo',
'storage':
'pohrana',
'poster':
'poster',
'flash':
'flash',
'brush':
'olovka',
'rectangle':
'pravokutnik',
'rectangleSolid':
'ispunjeni pravokutnik',
'circle':
'krug',
'circleSolid':
'ispunjeni krug',
'crosshairs':
'kri\u017Ei\u0107',
'paintbucket':
'kanta s bojom',
'eraser':
'brisalica\n(gumica)',
'pipette':
'kapaljka',
'speechBubble':
'balon\u010Di\u0107 teksta',
'speechBubbleOutline':
'obris balon\u010Di\u0107a teksta',
'arrowUp':
'strelica gore',
'arrowUpOutline':
'obris strelice gore',
'arrowLeft':
'strelica lijevo',
'arrowLeftOutline':
'obris strelice lijevo',
'arrowDown':
'strelica dolje',
'arrowDownOutline':
'obris strelice dolje',
'arrowRight':
'strelica desno',
'arrowRightOutline':
'obris strelice desno',
'robot':
'robot',
'turn pen trails into new costume...':
'pretvori trag olovke u novi kostim...',
'turn all pen trails and stamps\ninto a new costume for the\ncurrently selected sprite':
'pretvori sve tragove olovke\ni \u017Eigove u novi kostim\nza trenutni objekt',
'pen':
'pero',
'tip':
'vrh',
'middle':
'sredina',
'last changed':
'zadnja promjena'
};

Wyświetl plik

@ -175,11 +175,11 @@ SnapTranslator.dict.nl = {
'language_name':
'Nederlands', // the name as it should appear in the language menu
'language_translator':
'Sjoerd Dirk Meijer, Frank Sierens', // your name for the Translators tab
'Sjoerd Dirk Meijer, Frank Sierens, Jan-Gerard van der Toorn', // your name for the Translators tab
'translator_e-mail':
'sjoerddirk@fromScratchEd.nl, frank.sierens@telenet.be', // optional
'sjoerddirk@fromScratchEd.nl, frank.sierens@telenet.be, jg.2019@xs4all.nl', // optional
'last_changed':
'2015-12-15', // this, too, will appear in the Translators tab
'2017-09-01', // this, too, will appear in the Translators tab
// GUI
// control bar:
@ -219,12 +219,14 @@ SnapTranslator.dict.nl = {
'Scripts',
'Costumes':
'Uiterlijken',
'Backgrounds':
'Achtergronden',
'Sounds':
'Geluiden',
// names:
'Sprite':
'Sprite',
'Object',
'Stage':
'Speelveld',
@ -238,7 +240,7 @@ SnapTranslator.dict.nl = {
// new sprite button:
'add a new sprite':
'een nieuwe sprite toevoegen',
'een nieuw object toevoegen',
// tab help
'costumes tab help':
@ -280,7 +282,7 @@ SnapTranslator.dict.nl = {
// motion:
'Stage selected:\nno motion primitives':
'Toneel geselecteerd: geen standaardbeweging mogelijk',
'Speelveld geselecteerd: geen standaardbeweging mogelijk',
'move %n steps':
'neem %n stappen',
@ -403,14 +405,28 @@ SnapTranslator.dict.nl = {
'maak pengrootte %n',
'stamp':
'stempel',
'fill':
'vul',
// control:
'when %greenflag clicked':
'wanneer %greenflag wordt aangeklikt',
'when %keyHat key pressed':
'wanneer %keyHat wordt ingedrukt',
'when I am clicked':
'wanneer er op mij wordt geklikt',
'when I am %interaction':
'wanneer ik %interaction word',
'clicked':
'aangeklikt',
'pressed':
'ingedrukt',
'dropped':
'losgelaten',
'mouse-entered':
'aangeraakt met de muis',
'mouse-departed':
'niet meer met de muis aangeraakt',
'when %b':
'wanneer %b',
'when I receive %msgHat':
'wanneer ik %msgHat ontvang',
'broadcast %msg':
@ -439,12 +455,20 @@ SnapTranslator.dict.nl = {
'als %b %c anders %c',
'report %s':
'rapporteer %s',
'stop block':
'stop blok',
'stop script':
'stop script',
'stop all %stop':
'stop alle %stop',
'stop %stopChoices':
'stop %stopChoices',
'all':
'alles',
'this script':
'dit Script',
'this block':
'dit blok',
'stop %stopOthersChoices':
'stop %stopOthersChoices',
'all but this script':
'alle scripts behalve deze',
'other scripts in sprite':
'andere scripts van dit object',
'pause all %pause':
'pauzeer alles %pause',
'run %cmdRing %inputs':
@ -497,6 +521,8 @@ SnapTranslator.dict.nl = {
'tijd',
'%att of %spr':
'%att van %spr',
'my %get':
'Eigenschap %get',
'http:// %s':
'http:// %s',
'turbo mode?':
@ -532,6 +558,8 @@ SnapTranslator.dict.nl = {
'onwaar',
'join %words':
'voeg %words samen',
'split %s by %delim':
'splits %s bij %delim',
'hello':
'hallo',
'world':
@ -628,6 +656,10 @@ SnapTranslator.dict.nl = {
'Openen...',
'Save':
'opslaan',
'Save to disk':
'Opslaan op schijf',
'store this project\nin the downloads folder\n(in supporting browsers)':
'Sla dit projekt op\nin de downloads folder\n(allen voor browsers die dit ondersteunen)',
'Save As...':
'Opslaan als...',
'Import...':
@ -644,6 +676,32 @@ SnapTranslator.dict.nl = {
'Blokken exporteren...',
'show global custom block definitions as XML\nin a new browser window':
'toon globale aangepaste blokdefinities\nals XML in browser',
'Unused blocks...':
'Ongebruikte blokken...',
'find unused global custom blocks\nand remove their definitions':
'zoek ongebruikte globale aangepaste blokken\nen ruim ze op',
'Remove unused blocks':
'Ruim ongebruikte blokken op',
'there are currently no unused\nglobal custom blocks in this project':
'er zijn nu geen ongebruikge globale\naangepaste blokkenin dit project',
'unused block(s) removed':
'ongebruikte blokken opgeruimd',
'Export summary...':
'Exporteer samenvatting...',
'open a new browser browser window\n with a summary of this project':
'open een nieuw browser scherm\nmet een samenvatting van dit project',
'Contents':
'inhoud',
'Kind of':
'Soort van',
'Part of':
'Een onderdeel van',
'Parts':
'Onderdelen',
'Blocks':
'Blokken',
'For all Sprites':
'Voor alle objecten',
'Import tools':
'Importeer tools',
'load the official library of\npowerful blocks':
@ -664,6 +722,16 @@ SnapTranslator.dict.nl = {
'Taal...',
'Zoom blocks...':
'Blokken inzoomen...',
'Stage size...':
'Afmeting speelveld...',
'Stage size':
'Sspeelveld afmeting',
'Stage width':
'Speelveld breedte',
'Stage height':
'Speelveld hoogte',
'Default':
'Standaard',
'Blurred shadows':
'Onscherpe schaduwen',
'uncheck to use solid drop\nshadows and highlights':
@ -690,6 +758,12 @@ SnapTranslator.dict.nl = {
'uitschakelen om lege functies\n anderen uit te sluiten',
'Long form input dialog':
'Lang formulier-invoerscherm',
'Plain prototype labels':
'Eenvoudige protoype-labels',
'uncheck to always show (+) symbols\nin block prototype labels':
'uitvinken om altijd (+) symbolen\nte tonen in blok prototype labels',
'check to hide (+) symbols\nin block prototype labels':
'aanvinken om (+) symbolen in\nblock prototye labels te verbergen',
'check to always show slot\ntypes in the input dialog':
'aanvinken om data type in\ninvoerscherm te zien',
'uncheck to use the input\ndialog in short form':
@ -724,6 +798,18 @@ SnapTranslator.dict.nl = {
'uitvinken voor scripuitvoering\nop normale snelheid',
'check to enable\nIDE animations':
'aanvinken om IDE-animaties\ntoe te laten',
'Flat design':
'Eenvoudige layout',
'Nested auto-wrapping':
'Automatisch omvatten',
'Keyboard Editing':
'Bewerken met toetsenbord',
'Table support':
'Gebruik tabellen',
'Table lines':
'Tabellen met lijntjes',
'Visible stepping':
'Stapsgewijs programma verloop',
'Thread safe scripts':
'Thread-veilige scripts',
'uncheck to allow\nscript reentrance':
@ -736,6 +822,14 @@ SnapTranslator.dict.nl = {
'uitvinken voor hogere snelheid\nbij variabele framerates',
'check for smooth, predictable\nanimations across computers':
'aanvinken voor vloeiende,\nvoorspelbare animaties tussen computers',
'Flat line ends':
'Rechte lijn uiteinden',
'check for flat ends of lines':
'aanvinken voor rechte\nuiteinden van lijnen',
'uncheck for round ends of lines':
'uitvinken voor ronde\nuiteinden van lijnen',
'Inheritance support':
'Gebruik overerving',
// inputs
'with inputs':
@ -778,6 +872,12 @@ SnapTranslator.dict.nl = {
'omringen',
'unringify':
'niet omringen',
'transient':
'niet blijvend',
'uncheck to save contents\nin the project':
'uitvinken om de inhoud\nin het project op te slaan',
'check to prevent contents\nfrom being saved':
'aanvinken om te verhinderen dat\nde inhoud wordt opgeslagen',
// custom blocks:
'delete block definition...':
@ -788,6 +888,8 @@ SnapTranslator.dict.nl = {
// sprites:
'edit':
'bewerken',
'move':
'verplaatsen',
'detach from':
'losmaken van',
'detach all parts':
@ -814,6 +916,8 @@ SnapTranslator.dict.nl = {
'ongedaan maken',
'undo the last\nblock drop\nin this pane':
'de laatste blokbeweging\nongedaan maken',
'redrop':
'opnieuw uitvoeren',
'scripts pic...':
'scripts-afbeelding...',
'open a new window\nwith a picture of all scripts':
@ -841,6 +945,18 @@ SnapTranslator.dict.nl = {
'rename sound':
'geluid hernoemen',
// lists and tables
'list view...':
'lijstweergave...',
'table view...':
'tabelweergave...',
'open in dialog...':
'in nieuw venster openen...',
'reset columns':
'kolommen terugzetten',
'items':
'elementen',
// dialogs
// buttons
'OK':
@ -934,9 +1050,17 @@ SnapTranslator.dict.nl = {
// variable dialog
'for all sprites':
'voor alle sprite',
'voor alle objecten',
'for this sprite only':
'alleen voor deze sprite',
'alleen voor dit object',
// variables refactoring
'rename only\nthis reporter':
'hernoem alleen\ndit blok',
'rename all...':
'hernoem alle...',
'rename all blocks that\naccess this variable':
'alle blokken hernoemen,\ndie naar deze variabele verwijzen',
// block dialog
'Change block':
@ -1076,8 +1200,28 @@ SnapTranslator.dict.nl = {
'Leeg',
// graphical effects
'color':
'kleur',
'fisheye':
'vissenoog',
'whirl':
'draaikolk',
'pixelate':
'blokkig',
'mosaic':
'mosaiek',
'saturation':
'verzadiging',
'brightness':
'helderheid',
'ghost':
'geest',
'spook',
'negative':
'negatief',
'comic':
'strepenpatroon',
'confetti':
'kleureffect',
// keys
'space':
@ -1090,6 +1234,8 @@ SnapTranslator.dict.nl = {
'pijltje naar rechts',
'left arrow':
'pijltje naar links',
'any key':
'willekeurige toets',
'a':
'a',
'b':
@ -1170,8 +1316,10 @@ SnapTranslator.dict.nl = {
// math functions
'abs':
'abs',
'ceiling':
'afgerond omhoog',
'floor':
'afgerond',
'afgerond omlaag',
'sqrt':
'wortel',
'sin':
@ -1191,6 +1339,18 @@ SnapTranslator.dict.nl = {
'e^':
'e^',
// delimiters
'letter':
'letter',
'whitespace':
'spatie',
'line':
'regel',
'tab':
'tab',
'cr':
'regelterugloop',
// data types
'number':
'getal',
@ -1206,11 +1366,46 @@ SnapTranslator.dict.nl = {
'functie',
'predicate':
'predicaat',
'sprite':
'object',
// list indices
'last':
'laatste',
'any':
'willekeurig'
};
'willekeurig',
// attributes
'neighbors':
'buren',
'self':
'zelf',
'other sprites':
'andere objecten',
'parts':
'onderdelen',
'anchor':
'ankerpunt',
'parent':
'ouder',
'children':
'kind',
'clones':
'kloon',
'other clones':
'andere klonen',
'dangling?':
'slingeren?',
'rotation x':
'draaipunt x',
'rotation y':
'draaipunt y',
'center x':
'middelpunt x',
'center y':
'middelpunt y',
'name':
'naam',
'stage':
'speelveld',
};

Wyświetl plik

@ -182,7 +182,7 @@ SnapTranslator.dict.ru = {
'translator_e-mail':
'svetlanap@berkeley.edu, tema@school830.ru', // optional
'last_changed':
'2016-06-21', // this, too, will appear in the Translators tab
'2017-09-01', // this, too, will appear in the Translators tab
// GUI
// control bar:
@ -222,6 +222,8 @@ SnapTranslator.dict.ru = {
'Скрипты',
'Costumes':
'Костюмы',
'Backgrounds':
'Фоны',
'Sounds':
'Звуки',
@ -310,7 +312,7 @@ SnapTranslator.dict.ru = {
'set y to %n':
'установить y %n',
'if on edge, bounce':
'на грани развернуться',
'на границе развернуться',
'x position':
'x позиция',
'y position':
@ -411,9 +413,9 @@ SnapTranslator.dict.ru = {
// control:
'when %greenflag clicked':
'когда щелкнуть на %greenflag',
'при нажатии на %greenflag',
'when %keyHat key pressed':
'когда нажать %keyHat клавишу',
'при нажатии клавиши %keyHat',
'when I am %interaction':
'когда меня %interaction',
'clicked':
@ -623,6 +625,8 @@ SnapTranslator.dict.ru = {
'встав. %s в полож. %idx в %l',
'replace item %idx of %l with %s':
'заменить элем. %idx в %l на %s',
'empty? %l':
'пустой? %l',
// other
'Make a block':
@ -661,25 +665,37 @@ SnapTranslator.dict.ru = {
'file menu import hint':
'загрузить экспортированный проект\nили библиотеку блоков, маску или звук',
'Export project as plain text...':
'Экспорт проект как текстовый файл...',
'Экспортировать проект как текстовый файл...',
'Export project...':
'Экспорт проект...',
'Экспортировать проект...',
'save project data as XML\nto your downloads folder':
'сохранить и скачать проект в виде XML файла',
'Export summary...':
'Экспортируемая информация...',
'show project data as XML\nin a new browser window':
'open a new browser browser window\n with a summary of this project':
'представить проектные данные как XML\nв новом окне браузера',
'Export blocks...':
'Экспорт блоки...',
'Экспортировать блоки...',
'show global custom block definitions as XML\nin a new browser window':
'представить определения глобальных пользовательских блоков как XML\nв новом окне браузера',
'Unused blocks...':
'Неиспользуемые блоки...',
'find unused global custom blocks\nand remove their definitions':
'поиск и удаление неиспользуемых блоков',
'Import tools':
'Импортировать сервисные ср-ва',
'load the official library of\npowerful blocks':
'загрузить служебную библиотеку блоков',
'Backgrounds...':
'Фоны...',
'Libraries...':
'Библиотеки...',
'load the official library of\npowerful blocks':
'загрузить служебную библиотеку блоков',
'Select categories of additional blocks to add to this project.':
'выбрать дополнительные библиотеки блоков\nдля добавления к проекту',
'Select a costume from the media library':
'Выбор костюма из библиотеки изображений',
'Select a sound from the media library':
'Выбор звука из медиа-библиотеки',
// settings menu
'Language...':
@ -737,7 +753,7 @@ SnapTranslator.dict.ru = {
'check to enable\ninput sliders for\nentry fields':
'поставить метку - разрешить использование бегунков\nпри заполнении полей ввода',
'Clicking sound':
'Щелк-звук',
'Звук щелчка',
'uncheck to turn\nblock clicking\nsound off':
'убрать метку - выключить звук\nпри щелчке на блок',
'check to turn\nblock clicking\nsound on':
@ -748,6 +764,44 @@ SnapTranslator.dict.ru = {
'убрать метку - отключить\nIDE aнимацию',
'check to enable\nIDE animations':
'поставить метку - разрешить\nIDE aнимацию',
'Turbo mode':
'Режим Турбо',
'check to prioritize\nscript execution':
'отметьте, чтобы ускорить выполнение скрипта',
'uncheck to run scripts\nat normal speed':
'снимите флажок для выполнения скрипта\nс нормальной скоростью',
'Flat design':
'Плоский дизайн',
'check for alternative\nGUI design':
'отметьте для включения\nальтернативного дизайна среды разработки',
'uncheck for default\nGUI design':
'снимите флажок для включения\nстандартного дизайна среды разработки',
'Nested auto-wrapping':
'Nested auto-wrapping',
'Keyboard Editing':
'Редактирование с клавиатуры',
'check to enable\nkeyboard editing support':
'отметьте, чтобы включить\nвозможность программирования с помощью клавиатуры (Shift+Клик на блок)',
'uncheck to disable\nkeyboard editing support':
'снимите флажок, чтобы программировать\nтолько мышью без использования клавиатуры',
'Table support':
'Поддержка таблиц',
'uncheck to disable\nmulti-column list views':
'снимите флажок для отключения\nвозможности отображения списка в виде таблицы',
'check for multi-column\nlist view support':
'отметьте для включения\nвозможности отображения списка в виде таблицы',
'Table lines':
'Выделить линии у таблицы',
'uncheck for less contrast\nmulti-column list views':
'снимите флажок, чтобы линии таблицы в окне отображения таблиц\nстали менее контрасными',
'check for higher contrast\ntable views':
'отметьте, чтобы линии таблицы в окне отображения таблиц\nстали более контрасными',
'Visible stepping':
'Отбражение шагов выполнения',
'check to turn on\n visible stepping (slow)':
'отметьте, чтобы отображались\nшаги выполнения скрипта (медленно)',
'uncheck to turn off\nvisible stepping':
'снимите флажок, чтобы отключить отображение\nшагов выполнения скрипта',
'Thread safe scripts':
'Защищенность скрипта в многопоточном режиме',
'uncheck to allow\nscript reentrancy':
@ -838,6 +892,22 @@ SnapTranslator.dict.ru = {
'переименовать звук',
// dialogs
'Import library':
'Загрузка библиотек',
'Table view':
'Табличный вид',
'Save project':
'Сохранение проекта',
'Export Project As...':
'Экспортировать проект как...',
'Cloud':
'Облако',
'Browser':
'Браузер',
'Examples':
'Примеры',
// buttons
'OK':
'OK',
@ -849,6 +919,10 @@ SnapTranslator.dict.ru = {
'Да',
'No':
'Нет',
'Open':
'Открыть',
'Empty':
'Пусто',
// help
'Help':
@ -1052,6 +1126,8 @@ SnapTranslator.dict.ru = {
// keys
'space':
'пробел',
'any key':
'любая клавиша',
'up arrow':
'стрелка вверх',
'down arrow':
@ -1140,6 +1216,10 @@ SnapTranslator.dict.ru = {
// math functions
'abs':
'абсолютное значение',
'ceiling':
'округление до большего',
'floor':
'округление до меньшего',
'sqrt':
'квадратный корень',
'sin':

Wyświetl plik

@ -1,3 +1,4 @@
tools.xml Tools Standard library of powerful blocks (for, map, etc.)
iteration-composition.xml Iteration, composition Traditional loop constructs (while, until, etc.) plus the Lisp "named let" (a generalization of FOR) plus functional iteration (repeated invocation of a function) and function composition.
list-utilities.xml List utilities Some standard functions on lists (append, reverse, etc.)
stream-tools.xml Streams (lazy lists) A variation on the list data type in which each list item aren't computed until it's needed, so you can construct million-item lists without really taking up all that time or memory, or even infinite-sized lists. (A block that reports all the prime numbers is included as an example.)
@ -7,7 +8,6 @@ word-sentence.xml Words, sentences One of the big ideas in Logo that they left o
cases.xml Multi-branched conditional (switch) Like "switch" in C-like languages or "cond" in Lisp. Thanks to Nathan Dinsmore for inventing the idea of a separate block for each branch!
leap-library.xml LEAP Motion controller Report hand positions from LEAP Motion controller (leapmotion.com).
setrgb.xml Set RGB or HSV pen color Set or report pen color as RGB (red, blue, green) or HSV (hue, saturation, value).
penTrails.xml Save and restore pictures drawn by pen Save and restore lines drawn and costumes stamped on the stage, or make a sprite costume from saved pen trails.
try-catch.xml Catch errors in a script Run a script; if an error happens, instead of stopping the script with a red halo, run another script to handle the error. Also includes a block to cause an error with a message given as input. Also includes a block to create a script variable and give it a value.
multiline.xml Allow multi-line text input to a block In general, text inputs allow only a single line. The MULTILINE block accepts multi-line text input and can be used in text input slots of other blocks.
Eisenbergification.xml Provide getters and setters for all GUI-controlled global settings Eisenberg's Law: Anything that can be done from the GUI should be doable from the programming language, and vice versa.

Plik diff jest za duży Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Wyświetl plik

@ -1 +0,0 @@
<blocks app="Snap! 4.0, http://snap.berkeley.edu" version="1"><block-definition s="label %&apos;text&apos; of size %&apos;size&apos;" type="command" category="pen"><comment x="0" y="0" w="90" collapsed="false">LABEL will stamp text on the stage at the given font size. The direction of the text is the direction the sprite is facing, and color will match the pen color.</comment><header></header><code></code><inputs><input type="%txt">Hello!</input><input type="%n">12</input></inputs><script><block s="doRun"><block s="reportJSFunction"><list><l>text</l><l>size</l></list><l>var stage = this.parentThatIsA(StageMorph),&#xD; context = stage.penTrails().getContext(&apos;2d&apos;),&#xD; rotation = radians(this.direction() - 90),&#xD; trans = new Point(&#xD; this.center().x - stage.left(),&#xD; this.center().y - stage.top()&#xD; ),&#xD; isWarped = this.Warped,&#xD; len,&#xD; pos;&#xD;&#xD;if (isWarped) {endWarp(); }&#xD;context.save();&#xD;context.font = size + &apos;px monospace&apos;;&#xD;context.textAlign = &apos;left&apos;;&#xD;context.textBaseline = &apos;alphabetic&apos;;&#xD;context.fillStyle = this.color.toString();&#xD;len = context.measureText(text).width;&#xD;trans = trans.multiplyBy(1 / stage.scale);&#xD;context.translate(trans.x, trans.y);&#xD;context.rotate(rotation);&#xD;context.fillText(text, 0, 0);&#xD;context.translate(-trans.x, -trans.y);&#xD;context.restore();&#xD;pos = new Point(&#xD; len * Math.sin(radians(this.direction())),&#xD; len * Math.cos(radians(this.direction())));&#xD;pos = pos.add(new Point(this.xPosition(), this.yPosition()));&#xD;this.gotoXY(pos.x, pos.y, false);&#xD;this.changed();&#xD;if (isWarped) {this.startWarp(); }&#xD;stage.changed();</l></block><list><block var="text"/><block var="size"/></list></block></script></block-definition><block-definition s="button %&apos;text&apos;" type="command" category="pen"><comment x="0" y="0" w="176" collapsed="false">Make a new sprite, have it run this block to turn into a button. This will clear anything drawn on the stage.</comment><header></header><code></code><inputs><input type="%s">Push me!</input></inputs><script><block s="clear"></block><block s="up"></block><block s="gotoXY"><l>0</l><l>0</l></block><block s="setHeading"><l>90</l></block><block s="setColor"><color>3,0,0,1</color></block><custom-block s="label %txt of size %n"><block var="text"/><l>30</l></custom-block><block s="doDeclareVariables"><list><l>width</l></list></block><block s="doSetVar"><l>width</l><block s="xPosition"></block></block><block s="gotoXY"><l>0</l><l>10</l></block><block s="setColor"><color>251,255,13,1</color></block><block s="setSize"><l>30</l></block><block s="down"></block><block s="forward"><block var="width"/></block><block s="up"></block><block s="setSize"><l>1</l></block><block s="setColor"><color>3,0,0,1</color></block><block s="gotoXY"><l>0</l><l>0</l></block><custom-block s="label %txt of size %n"><block var="text"/><l>30</l></custom-block><custom-block s="make costume named %s from pen trail %s"><block var="text"/><custom-block s="pen trails"></custom-block></custom-block><block s="gotoXY"><l>0</l><l>0</l></block><block s="clear"></block></script></block-definition><block-definition s="pen trails" type="reporter" category="pen"><header></header><code></code><inputs></inputs><script><block s="doReport"><block s="evaluate"><block s="reportJSFunction"><list></list><l>var cst = new Costume(&#xD; this.parentThatIsA(StageMorph).trailsCanvas&#xD;);&#xD;cst.shrinkWrap();&#xD;return cst;</l></block><list></list></block></block></script></block-definition><block-definition s="set pen trails to: %&apos;costume&apos;" type="command" category="pen"><header></header><code></code><inputs><input type="%s" readonly="true"></input></inputs><script><block s="doRun"><block s="reportJSFunction"><list><l>cst</l></list><l>var stage = this.parentThatIsA(StageMorph);&#xD;stage.trailsCanvas = cst.contents;&#xD;stage.changed();</l></block><list><block var="costume"/></list></block></script></block-definition><block-definition s="make costume named %&apos;name&apos; from pen trail %&apos;trail&apos;" type="command" category="looks"><header></header><code></code><inputs><input type="%s">pen trail</input><input type="%s"></input></inputs><script><block s="doRun"><block s="reportJSFunction"><list><l>name</l><l>trail</l></list><l>trail.name = this.newCostumeName(name, null);&#xD;this.addCostume(trail);&#xD;this.wearCostume(trail);</l></block><list><block var="name"/><block var="trail"/></list></block></script></block-definition></blocks>

Plik diff jest za duży Load Diff

File diff suppressed because one or more lines are too long

5
lists.js 100755 → 100644
Wyświetl plik

@ -7,7 +7,7 @@
written by Jens Mönig and Brian Harvey
jens@moenig.org, bh@cs.berkeley.edu
Copyright (C) 2016 by Jens Mönig and Brian Harvey
Copyright (C) 2017 by Jens Mönig and Brian Harvey
This file is part of Snap!.
@ -62,7 +62,7 @@ CellMorph, ArrowMorph, MenuMorph, snapEquals, Morph, isNil, localize,
MorphicPreferences, TableDialogMorph, SpriteBubbleMorph, SpeechBubbleMorph,
TableFrameMorph, TableMorph, Variable, isSnapObject*/
modules.lists = '2016-July-14';
modules.lists = '2017-September-01';
var List;
var ListWatcherMorph;
@ -105,6 +105,7 @@ var ListWatcherMorph;
// List instance creation:
function List(array) {
this.type = null; // for UI lists, such as costumes, sounds, sprites
this.contents = array || [];
this.first = null;
this.rest = null;

14
locale.js 100755 → 100644
Wyświetl plik

@ -42,7 +42,7 @@
/*global modules, contains*/
modules.locale = '2017-January-13';
modules.locale = '2017-September-01';
// Global stuff
@ -160,7 +160,7 @@ SnapTranslator.dict.de = {
'translator_e-mail':
'jens@moenig.org',
'last_changed':
'2017-01-10'
'2017-06-24'
};
SnapTranslator.dict.it = {
@ -281,7 +281,7 @@ SnapTranslator.dict.ru = {
'translator_e-mail':
'svetlanap@berkeley.edu, tema@school830.ru',
'last_changed':
'2016-06-21'
'2017-09-01'
};
SnapTranslator.dict.es = {
@ -299,11 +299,11 @@ SnapTranslator.dict.nl = {
'language_name':
'Nederlands',
'language_translator':
'Frank Sierens, Sjoerd Dirk Meijer',
'Sjoerd Dirk Meijer, Frank Sierens, Jan-Gerard van der Toorn',
'translator_e-mail':
'frank.sierens@telenet.be, sjoerddirk@fromScratchEd.nl',
'sjoerddirk@fromScratchEd.nl, frank.sierens@telenet.be, jg.2019@xs4all.nl',
'last_changed':
'2015-12-15'
'2017-09-01'
};
SnapTranslator.dict.pl = {
@ -501,7 +501,7 @@ SnapTranslator.dict.hr = {
'translator_e-mail':
'zeljko.hrvoj@zg.t-com.hr',
'last_changed':
'2015-09-15'
'2017-08-15'
};
SnapTranslator.dict.bg = {

142
morphic.js 100755 → 100644
Wyświetl plik

@ -551,10 +551,23 @@
Right before a morph is picked up its
selectForEdit
and
prepareToBeGrabbed(handMorph)
method is invoked, if it is present. Immediately after the pick-up
the former parent's
methods are invoked, each if it is present. the optional
selectForEdit
if implemented, must return the object that is to be picked up.
In addition to just returning the original object chosen by the user
your method can also modify the target's environment and instead return
a copy of the selected morph if, for example, you would like to implement
a copy-on-write mechanism such as in Snap.
Immediately after the pick-up the former parent's
reactToGrabOf(grabbedMorph)
@ -583,6 +596,17 @@
method.
Right before dropping a morph the designated new parent's optional
selectForEdit
method is invoked if it is present. Again, if implemented this method
must return the new parent for the morph that is about to be dropped.
Again, in addition to just returning the designeted drop-target
your method can also modify its environment and instead return
a copy of the new parent if, for example, you would like to implement
a copy-on-write mechanism such as in Snap.
Right after a morph has been dropped its
justDropped(handMorph)
@ -1128,7 +1152,7 @@
Davide Della Casa contributed performance optimizations for Firefox.
Jason N (@cyderize) contributed native copy & paste for text editing.
Bartosz Leper contributed retina display support.
Brian Harvey contributed to the design and implemenatation of submenus.
Brian Harvey contributed to the design and implementation of submenus.
- Jens Mönig
*/
@ -1137,7 +1161,7 @@
/*global window, HTMLCanvasElement, FileReader, Audio, FileList*/
var morphicVersion = '2017-April-10';
var morphicVersion = '2017-September-01';
var modules = {}; // keep track of additional loaded modules
var useBlurredShadows = getBlurredShadowSupport(); // check for Chrome-bug
@ -1229,9 +1253,7 @@ function isNil(thing) {
function contains(list, element) {
// answer true if element is a member of list
return list.some(function (any) {
return any === element;
});
return list.indexOf(element) !== -1;
}
function detect(list, predicate) {
@ -3227,7 +3249,31 @@ Morph.prototype.drawNew = function () {
this.image = newCanvas(this.extent());
var context = this.image.getContext('2d');
context.fillStyle = this.color.toString();
context.fillRect(0, 0, this.width(), this.height());
/*
Chrome issue:
when filling a rectangular area, versions of Chrome beginning with
57.0.2987.133 start introducing vertical transparent stripes
to the right of the rectangle.
The following code replaces the original fillRect() call with
an explicit almost-rectangular path that miraculously makes
sure the whole rectangle gets filled correctly.
Important: This needs to be monitored in the future so we can
revert to sane code once this Chrome issue has been resolved again.
*/
// context.fillRect(0, 0, this.width(), this.height()); // taken out
context.beginPath();
context.moveTo(0, 0);
context.lineTo(this.image.width, 0);
context.lineTo(this.image.width, this.image.height);
context.lineTo(0, this.image.height + 0.0001); // yeah, I luv Chrome!
context.closePath();
context.fill();
if (this.cachedTexture) {
this.drawCachedTexture();
} else if (this.texture) {
@ -3612,7 +3658,7 @@ Morph.prototype.getPixelColor = function (aPoint) {
data.data[0],
data.data[1],
data.data[2],
data.data[3]
data.data[3] / 255
);
};
@ -4432,11 +4478,14 @@ HandleMorph.prototype.init = function (
this.target = target || null;
this.minExtent = new Point(minX || 0, minY || 0);
this.inset = new Point(insetX || 0, insetY || insetX || 0);
this.type = type || 'resize'; // can also be 'move', 'moveCenter'
this.type = type || 'resize'; // also: 'move', 'moveCenter', 'movePivot'
HandleMorph.uber.init.call(this);
this.color = new Color(255, 255, 255);
this.isDraggable = false;
this.noticesTransparentClick = true;
if (this.type === 'movePivot') {
size *= 2;
}
this.setExtent(new Point(size, size));
};
@ -4445,20 +4494,27 @@ HandleMorph.prototype.init = function (
HandleMorph.prototype.drawNew = function () {
this.normalImage = newCanvas(this.extent());
this.highlightImage = newCanvas(this.extent());
this.drawOnCanvas(
this.normalImage,
this.color,
new Color(100, 100, 100)
);
this.drawOnCanvas(
this.highlightImage,
new Color(100, 100, 255),
new Color(255, 255, 255)
);
if (this.type === 'movePivot') {
this.drawCrosshairsOnCanvas(this.normalImage, 0.6);
this.drawCrosshairsOnCanvas(this.highlightImage, 0.5);
} else {
this.drawOnCanvas(
this.normalImage,
this.color,
new Color(100, 100, 100)
);
this.drawOnCanvas(
this.highlightImage,
new Color(100, 100, 255),
new Color(255, 255, 255)
);
}
this.image = this.normalImage;
if (this.target) {
if (this.type === 'moveCenter') {
this.setCenter(this.target.center());
} else if (this.type === 'movePivot') {
this.setCenter(this.target.rotationCenter());
} else { // 'resize', 'move'
this.setPosition(
this.target.bottomRight().subtract(
@ -4471,6 +4527,25 @@ HandleMorph.prototype.drawNew = function () {
}
};
HandleMorph.prototype.drawCrosshairsOnCanvas = function (aCanvas, fract) {
var ctx = aCanvas.getContext('2d'),
r = aCanvas.width / 2;
ctx.fillStyle = 'rgba(255, 255, 255, 0.5)';
ctx.arc(r, r, r * 0.9, radians(0), radians(360), false);
ctx.fill();
ctx.strokeStyle = 'black';
ctx.lineWidth = 1;
ctx.beginPath();
ctx.arc(r, r, r * fract, radians(0), radians(360), false);
ctx.stroke();
ctx.moveTo(0, r);
ctx.lineTo(aCanvas.width, r);
ctx.stroke();
ctx.moveTo(r, 0);
ctx.lineTo(r, aCanvas.height);
ctx.stroke();
};
HandleMorph.prototype.drawOnCanvas = function (
aCanvas,
color,
@ -4574,7 +4649,7 @@ HandleMorph.prototype.mouseDownLeft = function (pos) {
if (!this.target) {
return null;
}
if (this.type === 'moveCenter') {
if (this.type.indexOf('move') === 0) {
offset = pos.subtract(this.center());
} else {
offset = pos.subtract(this.bounds.origin);
@ -4597,6 +4672,9 @@ HandleMorph.prototype.mouseDownLeft = function (pos) {
);
} else if (this.type === 'moveCenter') {
myself.target.setCenter(newPos);
} else if (this.type === 'movePivot') {
myself.target.setPivot(newPos);
myself.setCenter(this.target.rotationCenter());
} else { // type === 'move'
myself.target.setPosition(
newPos.subtract(this.target.extent())
@ -4760,7 +4838,7 @@ PenMorph.prototype.drawNew = function (facing) {
// PenMorph access:
PenMorph.prototype.setHeading = function (degrees) {
this.heading = parseFloat(degrees) % 360;
this.heading = ((+degrees % 360) + 360) % 360;
this.drawNew();
this.changed();
};
@ -5188,6 +5266,7 @@ CursorMorph.prototype.initializeClipboardHandler = function () {
this.clipboardHandler = document.createElement('textarea');
this.clipboardHandler.style.position = 'absolute';
this.clipboardHandler.style.top = window.outerHeight;
this.clipboardHandler.style.right = '101%'; // placed just out of view
document.body.appendChild(this.clipboardHandler);
@ -5899,7 +5978,7 @@ SpeechBubbleMorph.prototype.init = function (
border,
borderColor,
padding,
isThought
isThought // bool or anything but "true" to draw no hook at all
) {
this.isPointingRight = true; // orientation of text
this.contents = contents || '';
@ -6065,7 +6144,7 @@ SpeechBubbleMorph.prototype.outlinePath = function (
radians(180),
false
);
if (this.isThought) {
if (this.isThought === true) { // use anything but "true" to draw nothing
// close large bubble:
context.lineTo(
inset,
@ -8469,15 +8548,18 @@ StringMorph.prototype.mouseDoubleClick = function (pos) {
slot -= 1;
}
if (isWordChar(this.text[slot])) {
if (this.text[slot] && isWordChar(this.text[slot])) {
this.selectWordAt(slot);
} else {
} else if (this.text[slot]) {
this.selectBetweenWordsAt(slot);
} else {
// special case for when we click right after the
// last slot in multi line TextMorphs
this.selectAll();
}
} else {
this.escalateEvent('mouseDoubleClick', pos);
}
};
StringMorph.prototype.selectWordAt = function (slot) {
@ -10294,9 +10376,9 @@ ListMorph.prototype.buildListContents = function () {
myself.doubleClickAction
);
});
this.listContents.setPosition(this.contents.position());
this.listContents.isListContents = true;
this.listContents.drawNew();
this.listContents.setPosition(this.contents.position());
this.addContents(this.listContents);
};
@ -10628,6 +10710,7 @@ HandMorph.prototype.drop = function () {
if (this.children.length !== 0) {
morphToDrop = this.children[0];
target = this.dropTargetFor(morphToDrop);
target = target.selectForEdit ? target.selectForEdit() : target;
this.changed();
target.add(morphToDrop);
morphToDrop.cachedFullImage = null;
@ -10836,7 +10919,8 @@ HandMorph.prototype.processMouseMove = function (event) {
MorphicPreferences.grabThreshold)) {
this.setPosition(this.grabPosition);
if (this.morphToGrab.isDraggable) {
morph = this.morphToGrab;
morph = this.morphToGrab.selectForEdit ?
this.morphToGrab.selectForEdit() : this.morphToGrab;
this.grab(morph);
} else if (this.morphToGrab.isTemplate) {
morph = this.morphToGrab.fullCopy();

Wyświetl plik

@ -7,9 +7,9 @@
written by Jens Mönig
jens@moenig.org
Copyright (C) 2016 by Jens Mönig
Copyright (C) 2017 by Jens Mönig
this documentation last changed: November 25, 2016
this documentation last changed: September 01, 2017
This file is part of Snap!.
@ -552,10 +552,23 @@
Right before a morph is picked up its
selectForEdit
and
prepareToBeGrabbed(handMorph)
method is invoked, if it is present. Immediately after the pick-up
the former parent's
methods are invoked, each if it is present. the optional
selectForEdit
if implemented, must return the object that is to be picked up.
In addition to just returning the original object chosen by the user
your method can also modify the target's environment and instead return
a copy of the selected morph if, for example, you would like to implement
a copy-on-write mechanism such as in Snap.
Immediately after the pick-up the former parent's
reactToGrabOf(grabbedMorph)
@ -584,6 +597,17 @@
method.
Right before dropping a morph the designated new parent's optional
selectForEdit
method is invoked if it is present. Again, if implemented this method
must return the new parent for the morph that is about to be dropped.
Again, in addition to just returning the designeted drop-target
your method can also modify its environment and instead return
a copy of the new parent if, for example, you would like to implement
a copy-on-write mechanism such as in Snap.
Right after a morph has been dropped its
justDropped(handMorph)
@ -1129,5 +1153,6 @@
Davide Della Casa contributed performance optimizations for Firefox.
Jason N (@cyderize) contributed native copy & paste for text editing.
Bartosz Leper contributed retina display support.
Brian Harvey contributed to the design and implementation of submenus.
- Jens Mönig

1297
objects.js 100755 → 100644

Plik diff jest za duży Load Diff

Wyświetl plik

@ -747,7 +747,7 @@ PaintCanvasMorph.prototype.floodfill = function (sourcepoint) {
ctx = this.paper.getContext("2d"),
img = ctx.getImageData(0, 0, width, height),
data = img.data,
stack = [Math.round(sourcepoint.y) * width + sourcepoint.x],
stack = [Math.round(Math.round(sourcepoint.y) * width + sourcepoint.x)],
currentpoint,
read,
sourcecolor,

Wyświetl plik

@ -14,6 +14,7 @@
<script type="text/javascript" src="lists.js"></script>
<script type="text/javascript" src="byob.js"></script>
<script type="text/javascript" src="tables.js"></script>
<script type="text/javascript" src="symbols.js"></script>
<script type="text/javascript" src="xml.js"></script>
<script type="text/javascript" src="store.js"></script>
<script type="text/javascript" src="locale.js"></script>

Wyświetl plik

@ -14,6 +14,7 @@
<script type="text/javascript" src="lists.js"></script>
<script type="text/javascript" src="byob.js"></script>
<script type="text/javascript" src="tables.js"></script>
<script type="text/javascript" src="symbols.js"></script>
<script type="text/javascript" src="xml.js"></script>
<script type="text/javascript" src="store.js"></script>
<script type="text/javascript" src="locale.js"></script>

288
store.js 100755 → 100644
Wyświetl plik

@ -7,7 +7,7 @@
written by Jens Mönig
jens@moenig.org
Copyright (C) 2016 by Jens Mönig
Copyright (C) 2017 by Jens Mönig
This file is part of Snap!.
@ -57,11 +57,11 @@ BlockMorph, ArgMorph, InputSlotMorph, TemplateSlotMorph, CommandSlotMorph,
FunctionSlotMorph, MultiArgMorph, ColorSlotMorph, nop, CommentMorph, isNil,
localize, sizeOf, ArgLabelMorph, SVG_Costume, MorphicPreferences,
SyntaxElementMorph, Variable, isSnapObject, console, BooleanSlotMorph,
normalizeCanvas*/
normalizeCanvas, contains*/
// Global stuff ////////////////////////////////////////////////////////
modules.store = '2016-December-27';
modules.store = '2017-September-01';
// XML_Serializer ///////////////////////////////////////////////////////
@ -252,7 +252,7 @@ SnapSerializer.uber = XML_Serializer.prototype;
// SnapSerializer constants:
SnapSerializer.prototype.app = 'Snap! 4.0, http://snap.berkeley.edu';
SnapSerializer.prototype.app = 'Snap! 4.1, http://snap.berkeley.edu';
SnapSerializer.prototype.thumbnailSize = new Point(160, 120);
@ -412,12 +412,14 @@ SnapSerializer.prototype.rawLoadProjectModel = function (xmlNode) {
project.stage.setExtent(StageMorph.prototype.dimensions);
SpriteMorph.prototype.useFlatLineEnds =
model.stage.attributes.lines === 'flat';
BooleanSlotMorph.prototype.isTernary =
model.stage.attributes.ternary !== 'false';
project.stage.isThreadSafe =
model.stage.attributes.threadsafe === 'true';
StageMorph.prototype.enableCodeMapping =
model.stage.attributes.codify === 'true';
StageMorph.prototype.enableInheritance =
model.stage.attributes.inheritance === 'true';
model.stage.attributes.inheritance !== 'false';
StageMorph.prototype.enableSublistIDs =
model.stage.attributes.sublistIDs === 'true';
@ -476,6 +478,7 @@ SnapSerializer.prototype.rawLoadProjectModel = function (xmlNode) {
if (exemplar) {
sprite.setExemplar(exemplar);
}
sprite.inheritedAttributes = sprite.inheritanceInfo.delegated || [];
}
if (sprite.nestingInfo) { // only sprites may have nesting info
anchor = myself.project.sprites[sprite.nestingInfo.anchor];
@ -486,11 +489,32 @@ SnapSerializer.prototype.rawLoadProjectModel = function (xmlNode) {
}
});
myself.project.stage.children.forEach(function (sprite) {
delete sprite.inheritanceInfo;
var costume;
if (sprite.nestingInfo) { // only sprites may have nesting info
sprite.nestingScale = +(sprite.nestingInfo.scale || sprite.scale);
delete sprite.nestingInfo;
}
['scripts', 'costumes', 'sounds'].forEach(function (att) {
if (sprite.inheritsAttribute(att)) {
sprite.refreshInheritedAttribute(att);
}
});
if (sprite.inheritsAttribute('costumes')) {
costume = sprite.costumes.asArray()[
sprite.inheritanceInfo.costumeNumber - 1
];
if (costume) {
if (costume.loaded) {
sprite.wearCostume(costume, true);
} else {
costume.loaded = function () {
sprite.wearCostume(costume, true);
this.loaded = true;
};
}
}
}
delete sprite.inheritanceInfo;
});
/* Global Variables */
@ -574,6 +598,12 @@ SnapSerializer.prototype.rawLoadProjectModel = function (xmlNode) {
watcher.cellMorph.contentsMorph.handle.drawNew();
}
});
// clear sprites' inherited methods caches, if any
myself.project.stage.children.forEach(function (sprite) {
sprite.inheritedMethodsCache = [];
});
this.objects = {};
return project;
};
@ -714,22 +744,61 @@ SnapSerializer.prototype.loadMediaModel = function (xmlNode) {
SnapSerializer.prototype.loadObject = function (object, model) {
// private
var blocks = model.require('blocks');
var blocks = model.require('blocks'),
dispatches = model.childNamed('dispatches');
// load the instrument
if (model.attributes.instrument) {
object.instrument = +model.attributes.instrument;
}
this.loadInheritanceInfo(object, model);
this.loadNestingInfo(object, model);
this.loadCostumes(object, model);
this.loadSounds(object, model);
// load costumes unless they're inherited
if (!(object.inheritanceInfo &&
(object.inheritanceInfo.delegated instanceof Array) &&
contains(object.inheritanceInfo.delegated, 'costumes'))) {
this.loadCostumes(object, model);
}
// load sounds unless they're inherited
if (!(object.inheritanceInfo &&
(object.inheritanceInfo.delegated instanceof Array) &&
contains(object.inheritanceInfo.delegated, 'sounds'))) {
this.loadSounds(object, model);
}
this.loadCustomBlocks(object, blocks);
if (dispatches) {
this.loadCustomBlocks(object, dispatches, false, true);
}
this.populateCustomBlocks(object, blocks);
this.loadVariables(object.variables, model.require('variables'));
this.loadScripts(object.scripts, model.require('scripts'));
this.loadVariables(object.variables, model.require('variables'), object);
// load scripts unless they're inherited
if (!(object.inheritanceInfo &&
(object.inheritanceInfo.delegated instanceof Array) &&
contains(object.inheritanceInfo.delegated, 'scripts'))) {
this.loadScripts(object, object.scripts, model.require('scripts'));
}
// note: the dispatches cache isn't cleared until after
// *all* objects are loaded
};
SnapSerializer.prototype.loadInheritanceInfo = function (object, model) {
// private
var info = model.childNamed('inherit');
var info = model.childNamed('inherit'),
delegated;
if (info) {
object.inheritanceInfo = info.attributes;
delegated = info.childNamed('list');
if (delegated) {
object.inheritanceInfo.delegated =
this.loadValue(delegated).asArray();
}
object.inheritanceInfo.costumeNumber = model.attributes.costume;
}
};
@ -747,6 +816,7 @@ SnapSerializer.prototype.loadCostumes = function (object, model) {
costume;
if (costumes) {
object.costumes = this.loadValue(costumes.require('list'));
object.costumes.type = 'costume';
}
if (Object.prototype.hasOwnProperty.call(
model.attributes,
@ -755,10 +825,10 @@ SnapSerializer.prototype.loadCostumes = function (object, model) {
costume = object.costumes.asArray()[model.attributes.costume - 1];
if (costume) {
if (costume.loaded) {
object.wearCostume(costume);
object.wearCostume(costume, true);
} else {
costume.loaded = function () {
object.wearCostume(costume);
object.wearCostume(costume, true);
this.loaded = true;
};
}
@ -771,10 +841,11 @@ SnapSerializer.prototype.loadSounds = function (object, model) {
var sounds = model.childNamed('sounds');
if (sounds) {
object.sounds = this.loadValue(sounds.require('list'));
object.sounds.type = 'sound';
}
};
SnapSerializer.prototype.loadVariables = function (varFrame, element) {
SnapSerializer.prototype.loadVariables = function (varFrame, element, object) {
// private
var myself = this;
@ -786,7 +857,8 @@ SnapSerializer.prototype.loadVariables = function (varFrame, element) {
value = child.children[0];
v = new Variable();
v.isTransient = (child.attributes.transient === 'true');
v.value = (v.isTransient || !value ) ? 0 : myself.loadValue(value);
v.value = (v.isTransient || !value ) ? 0
: myself.loadValue(value, object);
varFrame.vars[child.attributes.name] = v;
});
};
@ -794,7 +866,8 @@ SnapSerializer.prototype.loadVariables = function (varFrame, element) {
SnapSerializer.prototype.loadCustomBlocks = function (
object,
element,
isGlobal
isGlobal,
isDispatch
) {
// private
var myself = this;
@ -810,10 +883,14 @@ SnapSerializer.prototype.loadCustomBlocks = function (
definition.category = child.attributes.category || 'other';
definition.type = child.attributes.type || 'command';
definition.isGlobal = (isGlobal === true);
if (definition.isGlobal) {
object.globalBlocks.push(definition);
if (isDispatch) {
object.inheritedMethodsCache.push(definition);
} else {
object.customBlocks.push(definition);
if (definition.isGlobal) {
object.globalBlocks.push(definition);
} else {
object.customBlocks.push(definition);
}
}
names = definition.parseSpec(definition.spec).filter(
@ -836,7 +913,9 @@ SnapSerializer.prototype.loadCustomBlocks = function (
i += 1;
definition.declarations[names[i]] = [
child.attributes.type,
child.contents,
contains(['%b', '%boolUE'], child.attributes.type) ?
(child.contents ? child.contents === 'true' : null)
: child.contents,
options ? options.contents : undefined,
child.attributes.readonly === 'true'
];
@ -885,7 +964,7 @@ SnapSerializer.prototype.populateCustomBlocks = function (
if (script) {
definition.body = new Context(
null,
script ? myself.loadScript(script) : null,
script ? myself.loadScript(script, object) : null,
null,
object
);
@ -893,14 +972,14 @@ SnapSerializer.prototype.populateCustomBlocks = function (
}
scripts = child.childNamed('scripts');
if (scripts) {
definition.scripts = myself.loadScriptsArray(scripts);
definition.scripts = myself.loadScriptsArray(scripts, object);
}
delete definition.names;
});
};
SnapSerializer.prototype.loadScripts = function (scripts, model) {
SnapSerializer.prototype.loadScripts = function (object, scripts, model) {
// private
var myself = this,
scale = SyntaxElementMorph.prototype.scale;
@ -908,7 +987,7 @@ SnapSerializer.prototype.loadScripts = function (scripts, model) {
model.children.forEach(function (child) {
var element;
if (child.tag === 'script') {
element = myself.loadScript(child);
element = myself.loadScript(child, object);
if (!element) {
return;
}
@ -935,7 +1014,7 @@ SnapSerializer.prototype.loadScripts = function (scripts, model) {
});
};
SnapSerializer.prototype.loadScriptsArray = function (model) {
SnapSerializer.prototype.loadScriptsArray = function (model, object) {
// private - answer an array containting the model's scripts
var myself = this,
scale = SyntaxElementMorph.prototype.scale,
@ -943,7 +1022,7 @@ SnapSerializer.prototype.loadScriptsArray = function (model) {
model.children.forEach(function (child) {
var element;
if (child.tag === 'script') {
element = myself.loadScript(child);
element = myself.loadScript(child, object);
if (!element) {
return;
}
@ -968,19 +1047,19 @@ SnapSerializer.prototype.loadScriptsArray = function (model) {
return scripts;
};
SnapSerializer.prototype.loadScript = function (model) {
SnapSerializer.prototype.loadScript = function (model, object) {
// private
var topBlock, block, nextBlock,
myself = this;
model.children.forEach(function (child) {
nextBlock = myself.loadBlock(child);
nextBlock = myself.loadBlock(child, false, object);
if (!nextBlock) {
return;
}
if (block) {
if (block.nextBlock && (nextBlock instanceof CommandBlockMorph)) {
block.nextBlock(nextBlock);
} else { // +++
} else {
console.log(
'SNAP: expecting a command but getting a reporter:\n' +
' ' + block.blockSpec + '\n' +
@ -1005,9 +1084,10 @@ SnapSerializer.prototype.loadComment = function (model) {
return comment;
};
SnapSerializer.prototype.loadBlock = function (model, isReporter) {
SnapSerializer.prototype.loadBlock = function (model, isReporter, object) {
// private
var block, info, inputs, isGlobal, rm, receiver;
var block, info, inputs, isGlobal, receiver, migration,
migrationOffset = 0;
if (model.tag === 'block') {
if (Object.prototype.hasOwnProperty.call(
model.attributes,
@ -1018,6 +1098,7 @@ SnapSerializer.prototype.loadBlock = function (model, isReporter) {
);
}
/*
// disable JavaScript functions, commented out for now
if (model.attributes.s === 'reportJSFunction' &&
!Process.prototype.enableJS) {
if (window.confirm('enable JavaScript?')) {
@ -1028,23 +1109,13 @@ SnapSerializer.prototype.loadBlock = function (model, isReporter) {
}
*/
block = SpriteMorph.prototype.blockForSelector(model.attributes.s);
migration = SpriteMorph.prototype.blockMigrations[model.attributes.s];
if (migration) {
migrationOffset = migration.offset;
}
} else if (model.tag === 'custom-block') {
isGlobal = model.attributes.scope ? false : true;
receiver = isGlobal ? this.project.stage
: this.project.sprites[model.attributes.scope];
rm = model.childNamed('receiver');
if (rm && rm.children[0]) {
receiver = this.loadValue(
model.childNamed('receiver').children[0]
);
}
if (!receiver) {
if (!isGlobal) {
receiver = this.project.stage;
} else {
return this.obsoleteBlock(isReporter);
}
}
receiver = isGlobal ? this.project.stage : object;
if (isGlobal) {
info = detect(receiver.globalBlocks, function (block) {
return block.blockSpec() === model.attributes.s;
@ -1058,8 +1129,11 @@ SnapSerializer.prototype.loadBlock = function (model, isReporter) {
);
}
} else {
// lookup in inherited methods
info = detect(receiver.customBlocks, function (block) {
return block.blockSpec() === model.attributes.s;
}) || detect(receiver.inheritedMethodsCache, function (block) {
return block.blockSpec() === model.attributes.s;
});
}
if (!info) {
@ -1081,14 +1155,14 @@ SnapSerializer.prototype.loadBlock = function (model, isReporter) {
inputs = block.inputs();
model.children.forEach(function (child, i) {
if (child.tag === 'variables') {
this.loadVariables(block.variables, child);
this.loadVariables(block.variables, child, object);
} else if (child.tag === 'comment') {
block.comment = this.loadComment(child);
block.comment.block = block;
} else if (child.tag === 'receiver') {
nop(); // ignore
} else {
this.loadInput(child, inputs[i], block);
this.loadInput(child, inputs[i + migrationOffset], block, object);
}
}, this);
block.cachedInputs = null;
@ -1106,17 +1180,20 @@ SnapSerializer.prototype.obsoleteBlock = function (isReporter) {
return block;
};
SnapSerializer.prototype.loadInput = function (model, input, block) {
SnapSerializer.prototype.loadInput = function (model, input, block, object) {
// private
var inp, val, myself = this;
if (isNil(input)) {
return;
}
if (model.tag === 'script') {
inp = this.loadScript(model);
inp = this.loadScript(model, object);
if (inp) {
input.add(inp);
input.fixLayout();
}
} else if (model.tag === 'autolambda' && model.children[0]) {
inp = this.loadBlock(model.children[0], true);
inp = this.loadBlock(model.children[0], true, object);
if (inp) {
input.silentReplaceInput(input.children[0], inp);
input.fixLayout();
@ -1130,12 +1207,13 @@ SnapSerializer.prototype.loadInput = function (model, input, block) {
myself.loadInput(
item,
input.children[input.children.length - 2],
input
input,
object
);
});
input.fixLayout();
} else if (model.tag === 'block' || model.tag === 'custom-block') {
block.silentReplaceInput(input, this.loadBlock(model, true));
block.silentReplaceInput(input, this.loadBlock(model, true, object));
} else if (model.tag === 'color') {
input.setColor(this.loadColor(model.contents));
} else {
@ -1149,9 +1227,9 @@ SnapSerializer.prototype.loadInput = function (model, input, block) {
}
};
SnapSerializer.prototype.loadValue = function (model) {
SnapSerializer.prototype.loadValue = function (model, object) {
// private
var v, i, lst, items, el, center, image, name, audio, option, bool,
var v, i, lst, items, el, center, image, name, audio, option, bool, origin,
myself = this;
function record() {
@ -1168,6 +1246,7 @@ SnapSerializer.prototype.loadValue = function (model) {
myself.mediaDict[model.attributes.mediaID] = v;
}
}
switch (model.tag) {
case 'ref':
if (Object.prototype.hasOwnProperty.call(model.attributes, 'id')) {
@ -1204,12 +1283,12 @@ SnapSerializer.prototype.loadValue = function (model) {
if (!value) {
v.first = 0;
} else {
v.first = myself.loadValue(value);
v.first = myself.loadValue(value, object);
}
var tail = model.childNamed('list') ||
model.childNamed('ref');
if (tail) {
v.rest = myself.loadValue(tail);
v.rest = myself.loadValue(tail, object);
} else {
if (i < (items.length - 1)) {
v.rest = new List();
@ -1227,7 +1306,7 @@ SnapSerializer.prototype.loadValue = function (model) {
if (!value) {
return 0;
}
return myself.loadValue(value);
return myself.loadValue(value, object);
});
return v;
case 'sprite':
@ -1263,14 +1342,29 @@ SnapSerializer.prototype.loadValue = function (model) {
case 'context':
v = new Context(null);
record();
el = model.childNamed('origin');
if (el) {
el = el.childNamed('ref') || el.childNamed('sprite');
if (el) {
v.origin = this.loadValue(el);
}
}
el = model.childNamed('receiver');
if (el) {
el = el.childNamed('ref') || el.childNamed('sprite');
if (el) {
v.receiver = this.loadValue(el);
}
}
origin = v.origin || v.receiver || object; // for local blocks look up
el = model.childNamed('script');
if (el) {
v.expression = this.loadScript(el);
v.expression = this.loadScript(el, origin);
} else {
el = model.childNamed('block') ||
model.childNamed('custom-block');
if (el) {
v.expression = this.loadBlock(el);
v.expression = this.loadBlock(el, origin);
} else {
el = model.childNamed('l');
if (el) {
@ -1299,13 +1393,6 @@ SnapSerializer.prototype.loadValue = function (model) {
// and remember the number of detected empty slots
v.emptySlots = i;
}
el = model.childNamed('receiver');
if (el) {
el = el.childNamed('ref') || el.childNamed('sprite');
if (el) {
v.receiver = this.loadValue(el);
}
}
el = model.childNamed('inputs');
if (el) {
el.children.forEach(function (item) {
@ -1316,11 +1403,11 @@ SnapSerializer.prototype.loadValue = function (model) {
}
el = model.childNamed('variables');
if (el) {
this.loadVariables(v.variables, el);
this.loadVariables(v.variables, el, origin);
}
el = model.childNamed('context');
if (el) {
v.outerContext = this.loadValue(el);
v.outerContext = this.loadValue(el, origin);
}
if (v.outerContext && v.receiver &&
!v.outerContext.variables.parentFrame) {
@ -1396,6 +1483,7 @@ SnapSerializer.prototype.loadValue = function (model) {
)) {
myself.mediaDict[model.attributes.mediaID] = v;
}
record();
return v;
}
return undefined;
@ -1508,7 +1596,9 @@ StageMorph.prototype.toXML = function (serializer) {
'<thumbnail>$</thumbnail>' +
'<stage name="@" width="@" height="@" ' +
'costume="@" tempo="@" threadsafe="@" ' +
'%' +
'lines="@" ' +
'ternary="@" ' +
'codify="@" ' +
'inheritance="@" ' +
'sublistIDs="@" ' +
@ -1537,7 +1627,10 @@ StageMorph.prototype.toXML = function (serializer) {
this.getCostumeIdx(),
this.getTempo(),
this.isThreadSafe,
this.instrument ?
' instrument="' + parseInt(this.instrument) + '" ' : '',
SpriteMorph.prototype.useFlatLineEnds ? 'flat' : 'round',
BooleanSlotMorph.prototype.isTernary,
this.enableCodeMapping,
this.enableInheritance,
this.enableSublistIDs,
@ -1564,22 +1657,28 @@ StageMorph.prototype.toXML = function (serializer) {
SpriteMorph.prototype.toXML = function (serializer) {
var stage = this.parentThatIsA(StageMorph),
ide = stage ? stage.parentThatIsA(IDE_Morph) : null,
idx = ide ? ide.sprites.asArray().indexOf(this) + 1 : 0;
idx = ide ? ide.sprites.asArray().indexOf(this) + 1 : 0,
noCostumes = this.inheritsAttribute('costumes'),
noSounds = this.inheritsAttribute('sounds'),
noScripts = this.inheritsAttribute('scripts');
return serializer.format(
'<sprite name="@" idx="@" x="@" y="@"' +
' heading="@"' +
' scale="@"' +
' rotation="@"' +
'%' +
' draggable="@"' +
'%' +
' costume="@" color="@,@,@" pen="@" ~>' +
'%' + // inheritance info
'%' + // nesting info
'<costumes>%</costumes>' +
'<sounds>%</sounds>' +
'<variables>%</variables>' +
(noCostumes ? '%' : '<costumes>%</costumes>') +
(noSounds ? '%' : '<sounds>%</sounds>') +
'<blocks>%</blocks>' +
'<scripts>%</scripts>' +
'<variables>%</variables>' +
(this.exemplar ? '<dispatches>%</dispatches>' : '%') +
(noScripts ? '%' : '<scripts>%</scripts>') +
'</sprite>',
this.name,
idx,
@ -1588,6 +1687,8 @@ SpriteMorph.prototype.toXML = function (serializer) {
this.heading,
this.scale,
this.rotationStyle,
this.instrument ?
' instrument="' + parseInt(this.instrument) + '" ' : '',
this.isDraggable,
this.isVisible ? '' : ' hidden="true"',
this.getCostumeIdx(),
@ -1599,8 +1700,12 @@ SpriteMorph.prototype.toXML = function (serializer) {
// inheritance info
this.exemplar
? '<inherit exemplar="' +
this.exemplar.name
+ '"/>'
this.exemplar.name +
'">' +
(this.inheritedAttributes.length ?
serializer.store(new List(this.inheritedAttributes))
: '') +
'</inherit>'
: '',
// nesting info
@ -1617,12 +1722,12 @@ SpriteMorph.prototype.toXML = function (serializer) {
+ '"/>'
: '',
serializer.store(this.costumes, this.name + '_cst'),
serializer.store(this.sounds, this.name + '_snd'),
noCostumes ? '' : serializer.store(this.costumes, this.name + '_cst'),
noSounds ? '' : serializer.store(this.sounds, this.name + '_snd'),
!this.customBlocks ? '' : serializer.store(this.customBlocks),
serializer.store(this.variables),
!this.customBlocks ?
'' : serializer.store(this.customBlocks),
serializer.store(this.scripts)
this.exemplar ? serializer.store(this.inheritedMethods()) : '',
noScripts ? '' : serializer.store(this.scripts)
);
};
@ -1813,25 +1918,19 @@ ReporterBlockMorph.prototype.toScriptXML = function (
};
CustomCommandBlockMorph.prototype.toBlockXML = function (serializer) {
var scope = this.definition.isGlobal ? undefined
: this.definition.receiver.name;
var scope = this.isGlobal ? undefined : 'local';
return serializer.format(
'<custom-block s="@"%>%%%%</custom-block>',
'<custom-block s="@"%>%%%</custom-block>',
this.blockSpec,
this.definition.isGlobal ?
this.isGlobal ?
'' : serializer.format(' scope="@"', scope),
serializer.store(this.inputs()),
this.definition.variableNames.length ?
this.isGlobal && this.definition.variableNames.length ?
'<variables>' +
this.variables.toXML(serializer) +
'</variables>'
: '',
this.comment ? this.comment.toXML(serializer) : '',
scope && !this.definition.receiver[serializer.idProperty] ?
'<receiver>' +
serializer.store(this.definition.receiver) +
'</receiver>'
: ''
this.comment ? this.comment.toXML(serializer) : ''
);
};
@ -2026,7 +2125,7 @@ Context.prototype.toXML = function (serializer) {
}
return serializer.format(
'<context ~><inputs>%</inputs><variables>%</variables>' +
'%<receiver>%</receiver>%</context>',
'%<receiver>%</receiver><origin>%</origin>%</context>',
this.inputs.reduce(
function (xml, input) {
return xml + serializer.format('<input>$</input>', input);
@ -2036,6 +2135,7 @@ Context.prototype.toXML = function (serializer) {
this.variables ? serializer.store(this.variables) : '',
this.expression ? serializer.store(this.expression) : '',
this.receiver ? serializer.store(this.receiver) : '',
this.receiver ? serializer.store(this.origin) : '',
this.outerContext ? serializer.store(this.outerContext) : ''
);
};

1556
symbols.js 100644

Plik diff jest za duży Load Diff

Wyświetl plik

@ -66,11 +66,11 @@
/*global modules, Point, newCanvas, Morph, fontHeight, SliderMorph, List,
MorphicPreferences, FrameMorph, HandleMorph, DialogBoxMorph, isString,
SpriteMorph, Context, Costume, ArgMorph, BlockEditorMorph,
SyntaxElementMorph, MenuMorph, SpriteBubbleMorph, SpeechBubbleMorph,
SpriteMorph, Context, Costume, ArgMorph, BlockEditorMorph, SymbolMorph,
SyntaxElementMorph, MenuMorph, SpriteBubbleMorph, SpeechBubbleMorph, Sound,
CellMorph, ListWatcherMorph, isNil, BoxMorph, Variable, isSnapObject*/
modules.tables = '2016-May-02';
modules.tables = '2017-September-01';
var Table;
var TableCellMorph;
@ -401,6 +401,8 @@ TableCellMorph.prototype.dataRepresentation = function (dta) {
return dta.image();
} else if (dta instanceof Costume) {
return dta.thumbnail(new Point(40, 40));
} else if (dta instanceof Sound) {
return new SymbolMorph('notes', 30).image;
} else if (dta instanceof List) {
return this.listSymbol;
// return new ListWatcherMorph(dta).fullImageClassic();

393
threads.js 100755 → 100644
Wyświetl plik

@ -54,14 +54,14 @@
// Global stuff ////////////////////////////////////////////////////////
/*global ArgMorph, BlockMorph, CommandBlockMorph, CommandSlotMorph, Morph,
MultiArgMorph, Point, ReporterBlockMorph, SyntaxElementMorph, contains,
degrees, detect, nop, radians, ReporterSlotMorph, CSlotMorph, RingMorph,
MultiArgMorph, Point, ReporterBlockMorph, SyntaxElementMorph, contains, Costume,
degrees, detect, nop, radians, ReporterSlotMorph, CSlotMorph, RingMorph, Sound,
IDE_Morph, ArgLabelMorph, localize, XML_Element, hex_sha512, TableDialogMorph,
StageMorph, SpriteMorph, StagePrompterMorph, Note, modules, isString, copy,
isNil, WatcherMorph, List, ListWatcherMorph, alert, console, TableMorph,
TableFrameMorph, ColorSlotMorph, isSnapObject*/
modules.threads = '2017-January-11';
modules.threads = '2017-September-01';
var ThreadManager;
var Process;
@ -109,7 +109,7 @@ function snapEquals(a, b) {
function invoke(
action, // a BlockMorph or a Context, a reified ("ringified") block
contextArgs, // optional List of arguments for the context, or null
receiver, // optional sprite or environment
receiver, // sprite or environment, optional for contexts
timeout, // msecs
timeoutErrorMsg, // string
suppressErrors // bool
@ -126,23 +126,23 @@ function invoke(
// Use ThreadManager::startProcess with a callback instead
var proc = new Process(),
deadline = (timeout ? Date.now() + timeout : null),
rcvr;
deadline = (timeout ? Date.now() + timeout : null);
if (action instanceof Context) {
if (receiver) {
if (receiver) { // optional
action = proc.reportContextFor(receiver);
}
proc.initializeFor(action, contextArgs || new List());
} else if (action instanceof BlockMorph) {
proc.topBlock = action;
rcvr = receiver || action.receiver();
if (rcvr) {
if (receiver) {
proc.homeContext = new Context();
proc.homeContext.receiver = rcvr;
if (rcvr.variables) {
proc.homeContext.variables.parentFrame = rcvr.variables;
proc.homeContext.receiver = receiver;
if (receiver.variables) {
proc.homeContext.variables.parentFrame = receiver.variables;
}
} else {
throw new Error('expecting a receiver but getting ' + receiver);
}
proc.context = new Context(
null,
@ -180,25 +180,27 @@ function ThreadManager() {
ThreadManager.prototype.pauseCustomHatBlocks = false;
ThreadManager.prototype.toggleProcess = function (block) {
var active = this.findProcess(block);
ThreadManager.prototype.toggleProcess = function (block, receiver) {
var active = this.findProcess(block, receiver);
if (active) {
active.stop();
} else {
return this.startProcess(block, null, null, null, true);
return this.startProcess(block, receiver, null, null, null, true);
}
};
ThreadManager.prototype.startProcess = function (
block,
receiver,
isThreadSafe,
exportResult,
exportResult, // bool
callback,
isClicked,
rightAway
) {
var active = this.findProcess(block),
top = block.topBlock(),
var top = block.topBlock(),
active = this.findProcess(top, receiver),
glow,
newProc;
if (active) {
if (isThreadSafe) {
@ -207,12 +209,22 @@ ThreadManager.prototype.startProcess = function (
active.stop();
this.removeTerminatedProcesses();
}
newProc = new Process(block.topBlock(), callback, rightAway);
newProc = new Process(top, receiver, callback, isClicked);
newProc.exportResult = exportResult;
newProc.isClicked = isClicked || false;
if (!newProc.homeContext.receiver.isClone) {
// show a highlight around the running stack
// if there are more than one active processes
// for a block, display the thread count
// next to it
glow = top.getHighlight();
if (glow) {
glow.threadCount = this.processesForBlock(top).length + 1;
glow.updateReadout();
} else {
top.addHighlight();
}
this.processes.push(newProc);
if (rightAway) {
newProc.runStep();
@ -234,15 +246,21 @@ ThreadManager.prototype.stopAllForReceiver = function (rcvr, excpt) {
this.processes.forEach(function (proc) {
if (proc.homeContext.receiver === rcvr && proc !== excpt) {
proc.stop();
if (rcvr.isClone) {
if (rcvr.isTemporary) {
proc.isDead = true;
}
}
});
};
ThreadManager.prototype.stopProcess = function (block) {
var active = this.findProcess(block);
ThreadManager.prototype.stopAllForBlock = function (aTopBlock) {
this.processesForBlock(aTopBlock, true).forEach(function (proc) {
proc.stop();
});
};
ThreadManager.prototype.stopProcess = function (block, receiver) {
var active = this.findProcess(block, receiver);
if (active) {
active.stop();
}
@ -305,13 +323,25 @@ ThreadManager.prototype.step = function () {
ThreadManager.prototype.removeTerminatedProcesses = function () {
// and un-highlight their scripts
var remaining = [];
var remaining = [],
count,
myself = this;
this.processes.forEach(function (proc) {
var result;
var result,
glow;
if ((!proc.isRunning() && !proc.errorFlag) || proc.isDead) {
if (proc.topBlock instanceof BlockMorph) {
proc.unflash();
proc.topBlock.removeHighlight();
// adjust the thread count indicator, if any
count = myself.processesForBlock(proc.topBlock).length;
if (count) {
glow = proc.topBlock.getHighlight() ||
proc.topBlock.addHighlight();
glow.threadCount = count;
glow.updateReadout();
} else {
proc.topBlock.removeHighlight();
}
}
if (proc.prompter) {
proc.prompter.destroy();
@ -332,12 +362,14 @@ ThreadManager.prototype.removeTerminatedProcesses = function () {
new TableMorph(result, 10)
)
: new ListWatcherMorph(result),
proc.exportResult
proc.exportResult,
proc.receiver
);
} else {
proc.topBlock.showBubble(
result,
proc.exportResult
proc.exportResult,
proc.receiver
);
}
}
@ -349,19 +381,28 @@ ThreadManager.prototype.removeTerminatedProcesses = function () {
this.processes = remaining;
};
ThreadManager.prototype.findProcess = function (block) {
ThreadManager.prototype.findProcess = function (block, receiver) {
var top = block.topBlock();
return detect(
this.processes,
function (each) {
return each.topBlock === top;
return each.topBlock === top && (each.receiver === receiver);
}
);
};
ThreadManager.prototype.doWhen = function (block, stopIt) {
ThreadManager.prototype.processesForBlock = function (block, only) {
var top = only ? block : block.topBlock();
return this.processes.filter(function (each) {
return each.topBlock === top &&
each.isRunning() &&
!each.isDead;
});
};
ThreadManager.prototype.doWhen = function (block, receiver, stopIt) {
if (this.pauseCustomHatBlocks) {return; }
if ((!block) || this.findProcess(block)) {
if ((!block) || this.findProcess(block, receiver)) {
return;
}
var pred = block.inputs()[0], world;
@ -376,12 +417,20 @@ ThreadManager.prototype.doWhen = function (block, stopIt) {
if (invoke(
pred,
null,
block.receiver(), // needed for shallow copied clones - was null
receiver,
50,
'the predicate takes\ntoo long for a\ncustom hat block',
true // suppress errors => handle them right here instead
) === true) {
this.startProcess(block, null, null, null, null, true); // atomic
this.startProcess(
block,
receiver,
null,
null,
null,
null,
true // atomic
);
}
} catch (error) {
block.addErrorHighlight();
@ -434,6 +483,9 @@ ThreadManager.prototype.toggleSingleStepping = function () {
are children
receiver object (sprite) to which the process applies,
cached from the top block
instrument musical instrument type, cached from the receiver,
so a single sprite can play several instruments
at once
context the Context describing the current state
of this process
homeContext stores information relevant to the whole process,
@ -476,9 +528,10 @@ Process.prototype.enableSingleStepping = false; // experimental
Process.prototype.flashTime = 0; // experimental
// Process.prototype.enableJS = false;
function Process(topBlock, onComplete, rightAway) {
function Process(topBlock, receiver, onComplete, yieldFirst) {
this.topBlock = topBlock || null;
this.receiver = receiver;
this.instrument = receiver ? receiver.instrument : null;
this.readyToYield = false;
this.readyToTerminate = false;
this.isDead = false;
@ -486,7 +539,7 @@ function Process(topBlock, onComplete, rightAway) {
this.isShowingResult = false;
this.errorFlag = false;
this.context = null;
this.homeContext = new Context();
this.homeContext = new Context(null, null, null, receiver);
this.lastYield = Date.now();
this.isFirstStep = true;
this.isAtomic = false;
@ -502,7 +555,6 @@ function Process(topBlock, onComplete, rightAway) {
this.isInterrupted = false; // experimental, for single-stepping
if (topBlock) {
this.homeContext.receiver = topBlock.receiver();
this.homeContext.variables.parentFrame =
this.homeContext.receiver.variables;
this.context = new Context(
@ -510,7 +562,7 @@ function Process(topBlock, onComplete, rightAway) {
topBlock.blockSequence(),
this.homeContext
);
if (!rightAway) {
if (yieldFirst) {
this.pushContext('doYield'); // highlight top block
}
}
@ -651,7 +703,7 @@ Process.prototype.evaluateBlock = function (block, argCount) {
}
// first evaluate all inputs, then apply the primitive
var rcvr = this.context.receiver || this.topBlock.receiver(),
var rcvr = this.context.receiver || this.receiver,
inputs = this.context.inputs;
if (argCount > inputs.length) {
@ -917,7 +969,8 @@ Process.prototype.handleError = function (error, element) {
+ error.name
+ '\n'
+ error.message,
this.exportResult
this.exportResult,
this.receiver
);
};
@ -941,7 +994,7 @@ Process.prototype.reify = function (topBlock, parameterNames, isCustomBlock) {
topBlock : topBlock.fullCopy();
context.expression.show(); // be sure to make visible if in app mode
if (!isCustomBlock) {
if (!isCustomBlock && !parameterNames.length()) {
// mark all empty slots with an identifier
context.expression.allEmptySlots().forEach(function (slot) {
i += 1;
@ -963,7 +1016,8 @@ Process.prototype.reify = function (topBlock, parameterNames, isCustomBlock) {
context.inputs = parameterNames.asArray();
context.receiver
= this.context ? this.context.receiver : topBlock.receiver();
= this.context ? this.context.receiver : this.receiver;
context.origin = context.receiver; // for serialization
return context;
};
@ -1136,6 +1190,9 @@ Process.prototype.initializeFor = function (context, args) {
value,
exit;
// remember the receiver
this.context = context.receiver;
// assign parameters if any were passed
if (parms.length > 0) {
@ -1269,8 +1326,11 @@ Process.prototype.runContinuation = function (aContext, args) {
Process.prototype.evaluateCustomBlock = function () {
var caller = this.context.parentContext,
context = this.context.expression.definition.body,
declarations = this.context.expression.definition.declarations,
block = this.context.expression,
method = block.isGlobal ? block.definition
: this.blockReceiver().getMethod(block.blockSpec),
context = method.body,
declarations = method.declarations,
args = new List(this.context.inputs),
parms = args.asArray(),
runnable,
@ -1284,14 +1344,14 @@ Process.prototype.evaluateCustomBlock = function () {
outer = new Context();
outer.receiver = this.context.receiver;
outer.variables.parentFrame = this.context.expression.variables;
outer.variables.parentFrame = block.variables;
// block (instance) var support, experimental:
// only splice in block vars if any are defined, because block vars
// can cause race conditions in global block definitions that
// access sprite-local variables at the same time.
if (this.context.expression.definition.variableNames.length) {
this.context.expression.variables.parentFrame = outer.receiver ?
if (method.variableNames.length) {
block.variables.parentFrame = outer.receiver ?
outer.receiver.variables : null;
} else {
// original code without block variables:
@ -1329,7 +1389,7 @@ Process.prototype.evaluateCustomBlock = function () {
}
// tag return target
if (this.context.expression.definition.type !== 'command') {
if (method.type !== 'command') {
if (caller) {
// tag caller, so "report" can catch it later
caller.tag = 'exit';
@ -1360,8 +1420,7 @@ Process.prototype.evaluateCustomBlock = function () {
caller.tag = this.procedureCount;
}
// yield commands unless explicitly "warped" or directly recursive
if (!this.isAtomic &&
this.context.expression.definition.isDirectlyRecursive()) {
if (!this.isAtomic && method.isDirectlyRecursive()) {
this.readyToYield = true;
}
}
@ -1434,6 +1493,9 @@ Process.prototype.doShowVar = function (varName) {
if (name instanceof Context) {
if (name.expression.selector === 'reportGetVar') {
name = name.expression.blockSpec;
} else {
this.doChangePrimitiveVisibility(name.expression, false);
return;
}
}
if (this.homeContext.receiver) {
@ -1493,6 +1555,9 @@ Process.prototype.doHideVar = function (varName) {
if (name instanceof Context) {
if (name.expression.selector === 'reportGetVar') {
name = name.expression.blockSpec;
} else {
this.doChangePrimitiveVisibility(name.expression, true);
return;
}
}
if (!name) {
@ -1536,23 +1601,79 @@ Process.prototype.doRemoveTemporaries = function () {
}
};
// Process hiding and showing primitives primitives :-)
Process.prototype.doChangePrimitiveVisibility = function (aBlock, hideIt) {
var ide = this.homeContext.receiver.parentThatIsA(IDE_Morph),
dict,
cat;
if (!ide || (aBlock.selector === 'evaluateCustomBlock')) {
return;
}
if (hideIt) {
StageMorph.prototype.hiddenPrimitives[aBlock.selector] = true;
} else {
delete StageMorph.prototype.hiddenPrimitives[aBlock.selector];
}
dict = {
doWarp: 'control',
reifyScript: 'operators',
reifyReporter: 'operators',
reifyPredicate: 'operators',
doDeclareVariables: 'variables'
};
cat = dict[this.selector] || this.category;
if (cat === 'lists') {cat = 'variables'; }
ide.flushBlocksCache(cat);
ide.refreshPalette();
};
// Process sprite inheritance primitives
Process.prototype.doDeleteAttr = function (attrName) {
// currently only variables are deletable
var name = attrName,
rcvr = this.blockReceiver();
if (name instanceof Context) {
if (name.expression.selector === 'reportGetVar') {
name = name.expression.blockSpec;
} else { // attribute
name = {
xPosition: 'x position',
yPosition: 'y position',
direction: 'direction',
getCostumeIdx: 'costume #',
size: 'size'
}[name.expression.selector];
if (!isNil(name)) {
rcvr.inheritAttribute(name);
}
return; // error: cannot delete attribute...
}
}
if (name instanceof Array) {
return rcvr.inheritAttribute(this.inputOption(name));
}
if (contains(rcvr.inheritedVariableNames(true), name)) {
rcvr.deleteVariable(name);
}
};
// experimental message passing primitives
Process.prototype.doTellTo = function (sprite, context) {
this.doRun(
this.reportAttributeOf(context, sprite),
new List()
);
};
Process.prototype.reportAskFor = function (sprite, context) {
this.evaluate(
this.reportAttributeOf(context, sprite),
new List()
);
};
// Process lists primitives
Process.prototype.reportNewList = function (elements) {
@ -1560,23 +1681,26 @@ Process.prototype.reportNewList = function (elements) {
};
Process.prototype.reportCONS = function (car, cdr) {
// this.assertType(cdr, 'list');
this.assertType(cdr, 'list');
return new List().cons(car, cdr);
};
Process.prototype.reportCDR = function (list) {
// this.assertType(list, 'list');
this.assertType(list, 'list');
return list.cdr();
};
Process.prototype.doAddToList = function (element, list) {
// this.assertType(list, 'list');
this.assertType(list, 'list');
if (list.type) {
this.assertType(element, list.type);
}
list.add(element);
};
Process.prototype.doDeleteFromList = function (index, list) {
var idx = index;
// this.assertType(list, 'list');
this.assertType(list, 'list');
if (this.inputOption(index) === 'all') {
return list.clear();
}
@ -1593,7 +1717,10 @@ Process.prototype.doDeleteFromList = function (index, list) {
Process.prototype.doInsertInList = function (element, index, list) {
var idx = index;
// this.assertType(list, 'list');
this.assertType(list, 'list');
if (list.type) {
this.assertType(element, list.type);
}
if (index === '') {
return null;
}
@ -1608,7 +1735,10 @@ Process.prototype.doInsertInList = function (element, index, list) {
Process.prototype.doReplaceInList = function (index, list, element) {
var idx = index;
// this.assertType(list, 'list');
this.assertType(list, 'list');
if (list.type) {
this.assertType(element, list.type);
}
if (index === '') {
return null;
}
@ -1623,7 +1753,7 @@ Process.prototype.doReplaceInList = function (index, list, element) {
Process.prototype.reportListItem = function (index, list) {
var idx = index;
// this.assertType(list, 'list');
this.assertType(list, 'list');
if (index === '') {
return '';
}
@ -1637,12 +1767,12 @@ Process.prototype.reportListItem = function (index, list) {
};
Process.prototype.reportListLength = function (list) {
// this.assertType(list, 'list');
this.assertType(list, 'list');
return list.length();
};
Process.prototype.reportListContainsItem = function (list, element) {
// this.assertType(list, 'list');
this.assertType(list, 'list');
return list.contains(element);
};
@ -1732,7 +1862,7 @@ Process.prototype.doStopThis = function (choice) {
this.doStopBlock();
break;
default:
nop();
this.doStopOthers(choice);
}
};
@ -2048,7 +2178,7 @@ Process.prototype.doThinkFor = function (data, secs) {
Process.prototype.blockReceiver = function () {
return this.context ? this.context.receiver || this.homeContext.receiver
: this.homeContext.receiver;
: this.homeContext.receiver || this.receiver;
};
// Process sound primitives (interpolated)
@ -2165,7 +2295,6 @@ Process.prototype.doBroadcast = function (message) {
trg,
rcvrs,
myself = this,
hats = [],
procs = [];
if (message instanceof List && (message.length() === 2)) {
@ -2196,40 +2325,19 @@ Process.prototype.doBroadcast = function (message) {
stage.lastMessage = message; // the actual data structure
rcvrs.forEach(function (morph) {
if (isSnapObject(morph)) {
hats = hats.concat(morph.allHatBlocksFor(msg));
morph.allHatBlocksFor(msg).forEach(function (block) {
procs.push(stage.threads.startProcess(
block,
morph,
stage.isThreadSafe
));
});
}
});
hats.forEach(function (block) {
procs.push(stage.threads.startProcess(block, stage.isThreadSafe));
});
}
return procs;
};
// old purely global broadcast code, commented out and retained in case
// we need to revert
/*
Process.prototype.doBroadcast = function (message) {
var stage = this.homeContext.receiver.parentThatIsA(StageMorph),
hats = [],
procs = [];
if (message !== '') {
stage.lastMessage = message;
stage.children.concat(stage).forEach(function (morph) {
if (isSnapObject(morph)) {
hats = hats.concat(morph.allHatBlocksFor(message));
}
});
hats.forEach(function (block) {
procs.push(stage.threads.startProcess(block, stage.isThreadSafe));
});
}
return procs;
};
*/
Process.prototype.doBroadcastAndWait = function (message) {
if (!this.context.activeSends) {
this.context.activeSends = this.doBroadcast(message);
@ -2266,7 +2374,7 @@ Process.prototype.reportIsA = function (thing, typeString) {
Process.prototype.assertType = function (thing, typeString) {
// make sure "thing" is a particular type or any of a number of types
// and raise an error if not
// unused as of now because of performance considerations
// use responsibly wrt performance implications
var thingType = this.reportTypeOf(thing);
if (thingType === typeString) {return true; }
if (typeString instanceof Array && contains(typeString, thingType)) {
@ -2305,6 +2413,12 @@ Process.prototype.reportTypeOf = function (thing) {
if (thing instanceof StageMorph) {
return 'stage';
}
if (thing instanceof Costume) {
return 'costume';
}
if (thing instanceof Sound) {
return 'sound';
}
if (thing instanceof Context) {
if (thing.expression instanceof RingMorph) {
return thing.expression.dataType();
@ -2679,7 +2793,7 @@ Process.prototype.getObjectsNamed = function (name, thisObj, stageObj) {
those = [];
function check(obj) {
return obj instanceof SpriteMorph && obj.isClone ?
return obj instanceof SpriteMorph && obj.isTemporary ?
obj.cloneOriginName === name : obj.name === name;
}
@ -2703,6 +2817,13 @@ Process.prototype.doFaceTowards = function (name) {
if (this.inputOption(name) === 'mouse-pointer') {
thisObj.faceToXY(this.reportMouseX(), this.reportMouseY());
} else {
if (name instanceof List) {
thisObj.faceToXY(
name.at(1),
name.at(2)
);
return;
}
thatObj = this.getOtherObject(name, this.homeContext.receiver);
if (thatObj) {
thisObj.faceToXY(
@ -2722,6 +2843,13 @@ Process.prototype.doGotoObject = function (name) {
if (this.inputOption(name) === 'mouse-pointer') {
thisObj.gotoXY(this.reportMouseX(), this.reportMouseY());
} else {
if (name instanceof List) {
thisObj.gotoXY(
name.at(1),
name.at(2)
);
return;
}
thatObj = this.getOtherObject(name, this.homeContext.receiver);
if (thatObj) {
thisObj.gotoXY(
@ -2752,6 +2880,22 @@ Process.prototype.createClone = function (name) {
}
};
Process.prototype.newClone = function (name) {
var thisObj = this.blockReceiver(),
thatObj;
if (!name) {return; }
if (thisObj) {
if (this.inputOption(name) === 'myself') {
return thisObj.newClone(!this.isFirstStep);
}
thatObj = this.getOtherObject(name, thisObj);
if (thatObj) {
return thatObj.newClone(!this.isFirstStep);
}
}
};
// Process sensing primitives
Process.prototype.reportTouchingObject = function (name) {
@ -2875,6 +3019,9 @@ Process.prototype.reportDistanceTo = function (name) {
point = rc;
if (this.inputOption(name) === 'mouse-pointer') {
point = thisObj.world().hand.position();
} else if (name instanceof List) {
return new Point(thisObj.xPosition(), thisObj.yPosition())
.distanceTo(new Point(name.at(1), name.at(2)));
}
stage = thisObj.parentThatIsA(StageMorph);
thatObj = this.getOtherObject(name, thisObj, stage);
@ -2956,18 +3103,21 @@ Process.prototype.reportGet = function (query) {
return thisObj.exemplar || '';
case 'children':
return new List(thisObj.specimens ? thisObj.specimens() : []);
case 'temporary?':
return thisObj.isTemporary || false;
case 'clones':
stage = thisObj.parentThatIsA(StageMorph);
objName = thisObj.name || thisObj.cloneOriginName;
return new List(
stage.children.filter(function (each) {
return each.isClone &&
return each.isTemporary &&
(each !== thisObj) &&
(each.cloneOriginName === objName);
})
);
case 'other clones':
return thisObj.isClone ? this.reportGet(['clones']) : new List();
return thisObj.isTemporary ?
this.reportGet(['clones']) : new List();
case 'neighbors':
stage = thisObj.parentThatIsA(StageMorph);
neighborhood = thisObj.bounds.expandBy(new Point(
@ -2995,6 +3145,10 @@ Process.prototype.reportGet = function (query) {
return thisObj.name;
case 'stage':
return thisObj.parentThatIsA(StageMorph);
case 'costumes':
return thisObj.reportCostumes();
case 'sounds':
return thisObj.sounds;
}
}
return '';
@ -3038,6 +3192,17 @@ Process.prototype.doSet = function (attribute, value) {
// needed: circularity avoidance
rcvr.setExemplar(value);
break;
case 'temporary?':
this.assertType(rcvr, 'sprite');
this.assertType(value, 'Boolean');
if (rcvr.world().isDevMode) {
if (value) {
rcvr.release();
} else {
rcvr.perpetuate();
}
}
break;
case 'dangling?':
this.assertType(rcvr, 'sprite');
this.assertType(value, 'Boolean');
@ -3214,8 +3379,27 @@ Process.prototype.doMapCode = function (aContext, aString) {
}
};
Process.prototype.doMapStringCode = function (aString) {
StageMorph.prototype.codeMappings.string = aString || '<#1>';
Process.prototype.doMapValueCode = function (type, aString) {
var tp = this.inputOption(type);
switch (tp) {
case 'String':
StageMorph.prototype.codeMappings.string = aString || '<#1>';
break;
case 'Number':
StageMorph.prototype.codeMappings.number = aString || '<#1>';
break;
case 'true':
StageMorph.prototype.codeMappings.boolTrue = aString || 'true';
break;
case 'false':
StageMorph.prototype.codeMappings.boolFalse = aString || 'true';
break;
default:
throw new Error(
localize('unsupported data type') + ' ' + tp
);
}
};
Process.prototype.doMapListCode = function (part, kind, aString) {
@ -3297,7 +3481,7 @@ Process.prototype.doPlayNoteForSecs = function (pitch, secs) {
if (!this.context.startTime) {
this.context.startTime = Date.now();
this.context.activeNote = new Note(pitch);
this.context.activeNote.play();
this.context.activeNote.play(this.instrument);
}
if ((Date.now() - this.context.startTime) >= (secs * 1000)) {
if (this.context.activeNote) {
@ -3310,6 +3494,11 @@ Process.prototype.doPlayNoteForSecs = function (pitch, secs) {
this.pushContext();
};
Process.prototype.doSetInstrument = function (num) {
this.instrument = +num;
this.receiver.instrument = +num;
};
// Process constant input options
Process.prototype.inputOption = function (dta) {
@ -3433,6 +3622,7 @@ Process.prototype.unflash = function () {
outerContext the Context holding my lexical scope
expression SyntaxElementMorph, an array of blocks to evaluate,
null or a String denoting a selector, e.g. 'doYield'
origin the object of origin, only used for serialization
receiver the object to which the expression applies, if any
variables the current VariableFrame, if any
inputs an array of input values computed so far
@ -3461,6 +3651,7 @@ function Context(
this.parentContext = parentContext || null;
this.expression = expression || null;
this.receiver = receiver || null;
this.origin = receiver || null; // only for serialization
this.variables = new VariableFrame();
if (this.outerContext) {
this.variables.parentFrame = this.outerContext.variables;
@ -3641,7 +3832,7 @@ function Variable(value, isTransient) {
}
Variable.prototype.toString = function () {
return 'a ' + this.isTransient ? 'transient ' : '' + 'Variable [' +
return 'a ' + (this.isTransient ? 'transient ' : '') + 'Variable [' +
this.value + ']';
};

Wyświetl plik

@ -30,6 +30,11 @@
needs blocks.js and objects.js
credits
-------
Lucas Karahadian contributed a first prototype of the piano keyboard
I. hierarchy
-------------
the following tree lists all constructors hierarchically,
@ -41,11 +46,15 @@
DialogBoxMorph
InputFieldMorph
TriggerMorph*
MenuItemMorph*
PianoKeyMorph
PushButtonMorph
ToggleButtonMorph
TabMorph
ToggleMorph
ToggleElementMorph
MenuMorph*
PianoMenuMorph
* from Morphic.js
@ -63,6 +72,8 @@
DialogBoxMorph
AlignmentMorph
InputFieldMorph
PianoMenuMorph
PianoKeyMorph
*/
@ -72,9 +83,9 @@
newCanvas, StringMorph, Morph, TextMorph, nop, detect, StringFieldMorph,
HTMLCanvasElement, fontHeight, SymbolMorph, localize, SpeechBubbleMorph,
ArrowMorph, MenuMorph, isString, isNil, SliderMorph, MorphicPreferences,
ScrollFrameMorph*/
ScrollFrameMorph, MenuItemMorph, Note*/
modules.widgets = '2017-January-03';
modules.widgets = '2017-September-01';
var PushButtonMorph;
var ToggleButtonMorph;
@ -84,6 +95,8 @@ var ToggleElementMorph;
var DialogBoxMorph;
var AlignmentMorph;
var InputFieldMorph;
var PianoMenuMorph;
var PianoKeyMorph;
// PushButtonMorph /////////////////////////////////////////////////////
@ -3301,3 +3314,384 @@ InputFieldMorph.prototype.drawRectBorder = function (context) {
context.lineTo(this.width() - shift, this.height() - this.edge);
context.stroke();
};
// PianoMenuMorph //////////////////////////////////////////////////////
/*
I am a menu that looks like a piano keyboard.
*/
// PianoMenuMorph inherits from MenuMorph
PianoMenuMorph.prototype = new MenuMorph();
PianoMenuMorph.prototype.constructor = PianoMenuMorph;
PianoMenuMorph.uber = MenuMorph.prototype;
// PianoMenuMorph instance creation:
function PianoMenuMorph(target, environment, fontSize, soundType) {
this.init(target, environment, fontSize, soundType);
}
PianoMenuMorph.prototype.init = function (
target,
environment,
fontSize,
soundType // number 1 - 4: 'sine', 'square', 'sawtooth' or 'triangle'
) {
var choices, key;
this.soundType = soundType;
PianoMenuMorph.uber.init.call(this, target, null, environment, fontSize);
choices = {
'C (48)' : 48,
'D (50)' : 50,
'C# (49)' : 49,
'E (52)' : 52,
'Eb (51)' : 51,
'F (53)' : 53,
'G (55)' : 55,
'F# (54)' : 54,
'A (57)' : 57,
'G# (56)' : 56,
'B (59)' : 59,
'Bb (58)' : 58,
'C (60)' : 60,
'D (62)' : 62,
'C# (61)' : 61,
'E (64)' : 64,
'Eb (63)' : 63,
'F (65)' : 65,
'G (67)' : 67,
'F# (66)' : 66,
'A (69)' : 69,
'G# (68)' : 68,
'B (71)' : 71,
'Bb (70)' : 70,
'C (72)' : 72
};
for (key in choices) {
if (Object.prototype.hasOwnProperty.call(choices, key)) {
this.addItem(key, choices[key]);
}
}
this.drawNew();
};
PianoMenuMorph.prototype.drawNew = function () {
var myself = this,
item,
fb,
x,
y,
label,
blackkey,
key,
keycolor,
keywidth,
keyheight,
keyposition;
this.children.forEach(function (m) {
m.destroy();
});
this.children = [];
if (!this.isListContents) {
this.edge = MorphicPreferences.isFlat ? 0 : 5;
this.border = MorphicPreferences.isFlat ? 1 : 2;
}
this.color = new Color(255, 255, 255);
this.borderColor = new Color(60, 60, 60);
this.silentSetExtent(new Point(0, 0));
x = this.left() + 1;
y = this.top() + (this.fontSize * 1.5) + 2;
label = new StringMorph('', this.fontSize);
this.items.forEach(function (tuple) {
blackkey = tuple[0][1] !== " ";
key = new BoxMorph(1, 1);
if (blackkey) {
keycolor = new Color(0, 0, 0);
keywidth = myself.fontSize; // 9;
keyheight = myself.fontSize * 2.5;
keyposition = new Point(x + 2 - (myself.fontSize * 2), y);
} else {
keycolor = new Color(255, 255, 255);
keywidth = myself.fontSize * 1.5;
keyheight = myself.fontSize * 4;
keyposition = new Point(x + 1, y);
x += keywidth - 1;
}
key.setColor(keycolor);
key.setWidth(keywidth);
key.setHeight(keyheight);
item = new PianoKeyMorph(
myself.target,
tuple[1],
[key, tuple[0]],
myself.fontSize || MorphicPreferences.menuFontSize,
MorphicPreferences.menuFontName,
myself.environment,
tuple[2], // bubble help hint
tuple[3], // color
tuple[4], // bold
tuple[5], // italic
tuple[6], // doubleclick action
label // String to change
);
item.setPosition(keyposition);
myself.add(item);
});
fb = this.fullBounds();
label.setPosition(new Point((fb.width() / 2) - this.fontSize, 2));
this.add(label);
fb = this.fullBounds();
this.silentSetExtent(fb.extent().add(2));
MenuMorph.uber.drawNew.call(this);
};
// PianoMenuMorph keyboard selecting a key:
PianoMenuMorph.prototype.select = function(aPianoKeyItem) {
this.unselectAllItems();
aPianoKeyItem.mouseEnter();
this.selection = aPianoKeyItem;
this.world.keyboardReceiver = this;
this.hasFocus = true;
};
PianoMenuMorph.prototype.unselectAllItems = function () {
this.children.forEach(function (item) {
if (item instanceof MenuItemMorph) {
item.mouseLeave();
}
});
this.changed();
};
PianoMenuMorph.prototype.selectKey = function (midiNum) {
var key;
if (isNil(midiNum)) {
return;
}
key = detect(
this.children,
function (each) {
return each.action === midiNum;
}
);
if (key) {
this.select(key);
} else {
this.selectKey(48);
}
};
// PianoMenuMorph keyboard navigation & entry:
PianoMenuMorph.prototype.processKeyDown = function (event) {
// console.log(event.keyCode);
switch (event.keyCode) {
case 13: // 'enter'
case 32: // 'space'
if (this.selection) {
this.selection.mouseClickLeft();
}
return;
case 27: // 'esc'
return this.destroy();
case 37: // 'left arrow'
case 40: // 'down arrow'
case 189: // -
return this.selectDown();
case 38: // 'up arrow'
case 39: // 'right arrow'
case 187: // +
case 220: // #
return this.selectUp();
default:
switch(event.key) {
case 'C':
return this.selectKey(48);
case 'c':
return this.selectKey(60);
case 'D':
return this.selectKey(50);
case 'd':
return this.selectKey(62);
case 'E':
return this.selectKey(52);
case 'e':
return this.selectKey(64);
case 'F':
return this.selectKey(53);
case 'f':
return this.selectKey(65);
case 'G':
return this.selectKey(55);
case 'g':
return this.selectKey(67);
case 'A':
return this.selectKey(57);
case 'a':
return this.selectKey(69);
case 'B':
case 'H':
return this.selectKey(59);
case 'b':
case 'h':
return this.selectKey(71);
default:
nop();
}
}
};
PianoMenuMorph.prototype.selectUp = function () {
var next = 48;
if (this.selection) {
next = this.selection.action + 1;
if (next > 72) {
next = 48;
}
}
this.selectKey(next);
};
PianoMenuMorph.prototype.selectDown = function () {
var next = 48;
if (this.selection) {
next = this.selection.action - 1;
if (next < 48) {
next = 72;
}
}
this.selectKey(next);
};
PianoMenuMorph.prototype.destroy = function () {
this.children.forEach(function (key) {
if (key.note) {
key.note.stop();
}
});
PianoMenuMorph.uber.destroy.call(this);
};
// PianoKeyMorph ///////////////////////////////////////////////////////
PianoKeyMorph.prototype = new MenuItemMorph();
PianoKeyMorph.prototype.constructor = PianoKeyMorph;
PianoKeyMorph.uber = MenuItemMorph.prototype;
function PianoKeyMorph(
target,
action,
labelString, // can also be a Morph or a Canvas or a tuple: [icon, string]
fontSize,
fontStyle,
environment,
hint,
color,
bold,
italic,
doubleClickAction, // optional when used as list morph item
label
) {
this.init(
target,
action,
labelString,
fontSize,
fontStyle,
environment,
hint,
color,
bold,
italic,
doubleClickAction,
label
);
this.feedback = label;
}
PianoKeyMorph.prototype.init = function (
target,
action,
labelString,
fontSize,
fontStyle,
environment,
hint,
color,
bold,
italic,
doubleClickAction,
label
) {
// additional "note" property for sound output:
this.note = new Note(action);
PianoKeyMorph.uber.init.call(
this,
target,
action,
labelString,
fontSize,
fontStyle,
environment,
hint,
color,
bold,
italic,
doubleClickAction,
label
);
};
PianoKeyMorph.prototype.createLabel = function () {
var icon;
if (this.label !== null) {
this.label.destroy();
}
// assume its pattern is: [icon, string]
this.label = new Morph();
icon = this.createIcon(this.labelString[0]);
this.label.add(icon);
this.label.drawNew();
this.silentSetExtent(icon.extent());
this.label.bounds = this.position().extent(this.label.extent());
this.label.silentSetExtent(new Point(0, 0));
this.add(this.label);
};
PianoKeyMorph.prototype.mouseEnter = function () {
var piano = this.parentThatIsA(PianoMenuMorph),
soundType = piano ? piano.soundType : 1,
myself = this;
if (piano) {
piano.unselectAllItems();
piano.selection = this;
piano.world.keyboardReceiver = piano;
piano.hasFocus = true;
}
this.label.children[0].hide();
this.image = this.highlightImage;
this.changed();
this.feedback.text = this.labelString[1];
this.feedback.changed();
this.feedback.drawNew();
this.feedback.changed();
this.note.play(soundType);
setTimeout(
function () {
myself.note.stop();
},
400
);
};
PianoKeyMorph.prototype.mouseLeave = function () {
this.note.stop();
this.label.children[0].show();
this.image = this.normalImage;
this.changed();
};

4
ypr.js
Wyświetl plik

@ -8,7 +8,7 @@ The above copyright notice and this permission notice shall be included in all c
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Last changed 2013-04-03 by Jens Moenig (disabled text area overlay)
Last changed 2017-07-04 by Jens Moenig (disabled text area overlay, introduced Sprite::isTemporary)
*/
@ -392,7 +392,7 @@ var sb = (function (sb) {
sb.addFields(-1, 'Slider', 'BorderedMorph', 'slider,value,setValueSelector,sliderShadow,sliderColor,descending,model');
sb.addFields(-2, 'AbstractSound', '', '');
sb.addFields(-3, 'ScriptableScratchMorph', 'Morph', 'objName,vars,blocksBin,customBlocks,isClone,media,costume');
sb.addFields(-3, 'ScriptableScratchMorph', 'Morph', 'objName,vars,blocksBin,customBlocks,isTemporary,media,costume');
sb.addFields(-4, 'ArgMorph', 'BorderedMorph', 'labelMorph');
sb.addFields(-5, 'PasteUpMorph', 'BorderedMorph', '');
sb.addFields(-6, 'ScratchMedia', '', 'mediaName');