kopia lustrzana https://github.com/backface/turtlestitch
merged with master
commit
85ba23bd32
Plik diff jest za duży
Load Diff
|
@ -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
201
gui.js
|
@ -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();
|
||||
};
|
||||
|
|
229
history.txt
229
history.txt
|
@ -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” block’s 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 orginal’s 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 Michael’s 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
|
||||
* sprites’s rotation centers can be adjusted onstage
|
||||
* clones share their original sprite’s 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
|
||||
|
|
|
@ -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',
|
||||
|
|
430
lang-hr.js
430
lang-hr.js
|
@ -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'
|
||||
|
||||
|
||||
};
|
||||
|
|
235
lang-nl.js
235
lang-nl.js
|
@ -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',
|
||||
};
|
||||
|
|
102
lang-ru.js
102
lang-ru.js
|
@ -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':
|
||||
|
|
|
@ -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
|
@ -1 +0,0 @@
|
|||
<blocks app="Snap! 4.0, http://snap.berkeley.edu" version="1"><block-definition s="label %'text' of size %'size'" 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),
 context = stage.penTrails().getContext('2d'),
 rotation = radians(this.direction() - 90),
 trans = new Point(
 this.center().x - stage.left(),
 this.center().y - stage.top()
 ),
 isWarped = this.Warped,
 len,
 pos;

if (isWarped) {endWarp(); }
context.save();
context.font = size + 'px monospace';
context.textAlign = 'left';
context.textBaseline = 'alphabetic';
context.fillStyle = this.color.toString();
len = context.measureText(text).width;
trans = trans.multiplyBy(1 / stage.scale);
context.translate(trans.x, trans.y);
context.rotate(rotation);
context.fillText(text, 0, 0);
context.translate(-trans.x, -trans.y);
context.restore();
pos = new Point(
 len * Math.sin(radians(this.direction())),
 len * Math.cos(radians(this.direction())));
pos = pos.add(new Point(this.xPosition(), this.yPosition()));
this.gotoXY(pos.x, pos.y, false);
this.changed();
if (isWarped) {this.startWarp(); }
stage.changed();</l></block><list><block var="text"/><block var="size"/></list></block></script></block-definition><block-definition s="button %'text'" 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(
 this.parentThatIsA(StageMorph).trailsCanvas
);
cst.shrinkWrap();
return cst;</l></block><list></list></block></block></script></block-definition><block-definition s="set pen trails to: %'costume'" 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);
stage.trailsCanvas = cst.contents;
stage.changed();</l></block><list><block var="costume"/></list></block></script></block-definition><block-definition s="make costume named %'name' from pen trail %'trail'" 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);
this.addCostume(trail);
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
|
@ -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;
|
||||
|
|
|
@ -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 = {
|
||||
|
|
|
@ -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();
|
||||
|
|
33
morphic.txt
33
morphic.txt
|
@ -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
|
||||
|
|
Plik diff jest za duży
Load Diff
2
paint.js
2
paint.js
|
@ -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,
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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) : ''
|
||||
);
|
||||
};
|
||||
|
|
Plik diff jest za duży
Load Diff
|
@ -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();
|
||||
|
|
|
@ -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 + ']';
|
||||
};
|
||||
|
||||
|
|
398
widgets.js
398
widgets.js
|
@ -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
4
ypr.js
|
@ -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');
|
||||
|
|
Ładowanie…
Reference in New Issue