diff --git a/README.md b/README.md
index f1d34f86..02810db7 100644
--- a/README.md
+++ b/README.md
@@ -26,7 +26,7 @@ Snap! Build Your Own Blocks [http://snap.berkeley.edu] is a
visual, blocks based programming language inspired by Scratch
-Copyright (C) 2017 by Jens Mönig and Brian Harvey
+Copyright (C) 2018 by Jens Mönig and Brian Harvey
Snap! is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
diff --git a/blocks.js b/blocks.js
index b83d1dbf..7e8c6771 100644
--- a/blocks.js
+++ b/blocks.js
@@ -9,7 +9,7 @@
written by Jens Mönig
jens@moenig.org
- Copyright (C) 2017 by Jens Mönig
+ Copyright (C) 2018 by Jens Mönig
This file is part of Snap!.
@@ -144,11 +144,11 @@ fontHeight, TableFrameMorph, SpriteMorph, Context, ListWatcherMorph,
CellMorph, DialogBoxMorph, BlockInputFragmentMorph, PrototypeHatBlockMorph,
Costume, IDE_Morph, BlockDialogMorph, BlockEditorMorph, localize, isNil,
isSnapObject, PushButtonMorph, SpriteIconMorph, Process, AlignmentMorph,
-CustomCommandBlockMorph, SymbolMorph, ToggleButtonMorph*/
+CustomCommandBlockMorph, SymbolMorph, ToggleButtonMorph, DialMorph*/
// Global stuff ////////////////////////////////////////////////////////
-modules.blocks = '2017-October-17';
+modules.blocks = '2018-June-08';
var SyntaxElementMorph;
var BlockMorph;
@@ -613,6 +613,18 @@ SyntaxElementMorph.prototype.getVarNamesDict = function () {
tempVars.forEach(function (name) {
dict[name] = name;
});
+ if (block.selector === 'doSetVar') {
+ // add settable object attributes
+ dict['~'] = null;
+ dict.my = {
+ 'anchor' : ['anchor'],
+ 'parent' : ['parent'],
+ // 'temporary?' : ['temporary?'],
+ 'dangling?' : ['dangling?'],
+ 'rotation x' : ['rotation x'],
+ 'rotation y' : ['rotation y']
+ };
+ }
return dict;
}
return {};
@@ -647,7 +659,7 @@ SyntaxElementMorph.prototype.refactorVarInStack = function (
if (this instanceof CustomCommandBlockMorph
&& this.definition.body
- && isNil(this.definition.declarations[oldName])
+ && isNil(this.definition.declarations.get(oldName))
&& !contains(this.definition.variableNames, oldName)) {
this.definition.body.expression.refactorVarInStack(oldName, newName);
}
@@ -737,7 +749,8 @@ SyntaxElementMorph.prototype.setColor = function (aColor, silently) {
this.color = aColor;
if (!silently) {this.drawNew(); }
this.children.forEach(function (child) {
- if (!silently || child instanceof TemplateSlotMorph) {
+ if ((!silently || child instanceof TemplateSlotMorph) &&
+ !(child instanceof BlockHighlightMorph)) {
child.drawNew();
child.changed();
}
@@ -933,10 +946,12 @@ SyntaxElementMorph.prototype.labelPart = function (spec) {
null,
true,
{
+ '§_dir': null,
'(90) right' : 90,
'(-90) left' : -90,
'(0) up' : '0',
- '(180) down' : 180
+ '(180) down' : 180,
+ 'random' : ['random']
}
);
part.setContents(90);
@@ -992,7 +1007,10 @@ SyntaxElementMorph.prototype.labelPart = function (spec) {
'pressed' : ['pressed'],
'dropped' : ['dropped'],
'mouse-entered' : ['mouse-entered'],
- 'mouse-departed' : ['mouse-departed']
+ 'mouse-departed' : ['mouse-departed'],
+ 'scrolled-up' : ['scrolled-up'],
+ 'scrolled-down' : ['scrolled-down'],
+ 'stopped' : ['stopped'] // experimental
},
true // read-only
);
@@ -1056,6 +1074,17 @@ SyntaxElementMorph.prototype.labelPart = function (spec) {
);
part.setContents(1);
break;
+ case '%rel':
+ part = new InputSlotMorph(
+ null, // text
+ false, // numeric?
+ {
+ 'distance' : ['distance'],
+ 'direction' : ['direction']
+ },
+ true // read-only
+ );
+ break;
case '%spr':
part = new InputSlotMorph(
null,
@@ -1591,12 +1620,12 @@ SyntaxElementMorph.prototype.fixLayout = function (silently) {
lines = [],
space = this.isPrototype ?
1 : Math.floor(fontHeight(this.fontSize) / 3),
- ico = this.isCustomBlock && !this.isGlobal ?
- this.methodIconExtent().x + space : 0,
+ ico = this instanceof BlockMorph && this.hasLocationPin() ?
+ this.methodIconExtent().x + space : 0,
bottomCorrection,
initialExtent = this.extent();
- if ((this instanceof MultiArgMorph) && (this.slotSpec !== '%c')) {
+ if ((this instanceof MultiArgMorph) && (this.slotSpec !== '%cs')) {
blockWidth += this.arrows().width();
} else if (this instanceof ReporterBlockMorph) {
blockWidth += (this.rounding * 2) + (this.edge * 2);
@@ -1614,7 +1643,7 @@ SyntaxElementMorph.prototype.fixLayout = function (silently) {
// determine lines
parts.forEach(function (part) {
if ((part instanceof CSlotMorph)
- || (part.slotSpec === '%c')) {
+ || (part.slotSpec === '%cs')) {
if (l.length > 0) {
lines.push(l);
lines.push([part]);
@@ -1659,6 +1688,9 @@ SyntaxElementMorph.prototype.fixLayout = function (silently) {
} else if (this instanceof MultiArgMorph
|| this instanceof ArgLabelMorph) {
y = this.top();
+ if (this.slotSpec === '%cs' && this.inputs().length > 0) {
+ y -= this.rounding;
+ }
}
lines.forEach(function (line) {
x = myself.left() + ico + myself.edge + myself.labelPadding;
@@ -1681,6 +1713,13 @@ SyntaxElementMorph.prototype.fixLayout = function (silently) {
part.setColor(myself.color);
part.setPosition(new Point(x, y));
lineHeight = part.height();
+ } else if (part instanceof MultiArgMorph &&
+ (part.slotSpec === '%cs')) {
+ if (myself.isPredicate) {
+ x += myself.corner;
+ }
+ part.setPosition(new Point(x, y));
+ lineHeight = part.height();
} else {
part.setPosition(new Point(x, y));
if (!part.isBlockLabelBreak) {
@@ -1737,7 +1776,7 @@ SyntaxElementMorph.prototype.fixLayout = function (silently) {
blockWidth,
maxX - this.left() + this.rounding
);
- } else if (this instanceof MultiArgMorph
+ } else if ((this instanceof MultiArgMorph && this.slotSpec !== '%cs')
|| this instanceof ArgLabelMorph) {
blockWidth = Math.max(
blockWidth,
@@ -1764,13 +1803,22 @@ SyntaxElementMorph.prototype.fixLayout = function (silently) {
// adjust CSlots
parts.forEach(function (part) {
- if (part instanceof CSlotMorph) {
+ var adjustMultiWidth = 0;
+ if (part instanceof CSlotMorph || (part.slotSpec === '%cs')) {
if (myself.isPredicate) {
- part.setWidth(blockWidth - ico - myself.rounding * 2);
+ part.setWidth(
+ blockWidth - ico - myself.rounding * 2 - myself.corner
+ );
} else {
part.setWidth(blockWidth - myself.edge - ico);
+ adjustMultiWidth = myself.corner + myself.edge;
}
}
+ if (part.slotSpec === '%cs') {
+ part.inputs().forEach(function (slot) {
+ slot.setWidth(part.right() - slot.left() - adjustMultiWidth);
+ });
+ }
});
// redraw in order to erase CSlot backgrounds
@@ -1826,8 +1874,8 @@ SyntaxElementMorph.prototype.fixHighlight = function () {
SyntaxElementMorph.prototype.methodIconExtent = function () {
// answer the span of the icon for the "local method" indicator
var ico = this.fontSize * 1.2;
- return this.isCustomBlock && !this.isGlobal ?
- new Point(ico * 0.66, ico) : new Point(0, 0);
+ return this.hasLocationPin() ? new Point(ico * 0.66, ico)
+ : new Point(0, 0);
};
// SyntaxElementMorph evaluating:
@@ -1938,7 +1986,7 @@ SyntaxElementMorph.prototype.showBubble = function (value, exportPic, target) {
if (ide && (ide.currentSprite !== target)) {
if (target instanceof StageMorph) {
anchor = ide.corral.stageIcon;
- } else {
+ } else if (target) {
if (target.isTemporary) {
target = detect(
target.allExemplars(),
@@ -1949,6 +1997,8 @@ SyntaxElementMorph.prototype.showBubble = function (value, exportPic, target) {
ide.corral.frame.contents.children,
function (icon) {return icon.object === target; }
);
+ } else {
+ target = ide;
}
pos = anchor.center();
}
@@ -2074,6 +2124,7 @@ SyntaxElementMorph.prototype.endLayout = function () {
%ida - white roundish type-in slot with drop-down for list indices
%idx - white roundish type-in slot for indices incl. "any"
%obj - specially drawn slot for object reporters
+ %rel - chameleon colored rectangular drop-down for relation options
%spr - chameleon colored rectangular drop-down for object-names
%col - chameleon colored rectangular drop-down for collidables
%dst - chameleon colored rectangular drop-down for distances
@@ -3461,10 +3512,12 @@ BlockMorph.prototype.doRefactorGlobalVar = function (
true
);
stage.globalBlocks.forEach(function (eachBlock) {
- eachBlock.body.expression.refactorVarInStack(
- oldName,
- newName
- );
+ if (eachBlock.body) {
+ eachBlock.body.expression.refactorVarInStack(
+ oldName,
+ newName
+ );
+ }
});
stage.forAllChildren(function (child) {
if (child instanceof SpriteMorph) {
@@ -3499,10 +3552,17 @@ BlockMorph.prototype.eraseHoles = function (context) {
shift = this.edge * 0.5,
gradient,
rightX,
- holes = this.parts().filter(function (part) {
- return part.isHole;
- });
-
+ holes = [];
+
+ this.parts().forEach(function (part) {
+ if (part.isHole) {
+ holes.push(part);
+ } else if (part instanceof MultiArgMorph) {
+ holes.push.apply(holes, part.inputs().filter(function (inp) {
+ return inp.isHole;
+ }));
+ }
+ });
if (this.isPredicate && (holes.length > 0)) {
rightX = this.width() - this.rounding;
context.clearRect(
@@ -3543,6 +3603,10 @@ BlockMorph.prototype.eraseHoles = function (context) {
};
+BlockMorph.prototype.hasLocationPin = function () {
+ return (this.isCustomBlock && !this.isGlobal) || this.isLocalVarTemplate;
+};
+
// BlockMorph highlighting
BlockMorph.prototype.addHighlight = function (oldHighlight) {
@@ -3834,6 +3898,11 @@ BlockMorph.prototype.fullCopy = function () {
};
BlockMorph.prototype.reactToTemplateCopy = function () {
+ if (this.isLocalVarTemplate) {
+ this.isLocalVarTemplate = null;
+ this.drawNew();
+ this.fixLayout();
+ }
this.forceNormalColoring();
};
@@ -3856,7 +3925,7 @@ BlockMorph.prototype.mouseClickLeft = function () {
return this.selectForEdit().focus(); // enable coopy-on-edit
}
if (top instanceof PrototypeHatBlockMorph) {
- return top.mouseClickLeft();
+ return; // top.mouseClickLeft();
}
if (receiver) {
stage = receiver.parentThatIsA(StageMorph);
@@ -4041,6 +4110,9 @@ BlockMorph.prototype.situation = function () {
BlockMorph.prototype.prepareToBeGrabbed = function (hand) {
var myself = this;
+ this.allInputs().forEach(function (input) {
+ delete input.bindingID;
+ });
this.allComments().forEach(function (comment) {
comment.startFollowing(myself, hand.world);
});
@@ -4480,9 +4552,6 @@ CommandBlockMorph.prototype.snap = function (hand) {
CommandBlockMorph.prototype.isStop = function () {
return ([
'doStopThis',
- 'doStop',
- 'doStopBlock',
- 'doStopAll',
'doForever',
'doReport',
'removeClone'
@@ -4612,8 +4681,8 @@ CommandBlockMorph.prototype.drawNew = function () {
*/
}
- // draw method icon if applicable
- if (this.isCustomBlock && !this.isGlobal) {
+ // draw location pin icon if applicable
+ if (this.hasLocationPin()) {
this.drawMethodIcon(context);
}
@@ -5167,6 +5236,7 @@ ReporterBlockMorph.prototype.init = function (isPredicate, silently) {
this.isPredicate = isPredicate || false;
this.setExtent(new Point(200, 80), silently);
this.cachedSlotSpec = null; // don't serialize
+ this.isLocalVarTemplate = null; // don't serialize
};
// ReporterBlockMorph drag & drop:
@@ -5388,10 +5458,11 @@ ReporterBlockMorph.prototype.drawNew = function () {
this.drawRounded(context);
}
- // draw method icon if applicable
- if (this.isCustomBlock && !this.isGlobal) {
+ // draw location pin icon if applicable
+ if (this.hasLocationPin()) {
this.drawMethodIcon(context);
}
+
// erase CommandSlots
this.eraseHoles(context);
};
@@ -5837,7 +5908,10 @@ RingMorph.prototype.vanishForSimilar = function () {
return null;
}
if (block.selector === 'reportGetVar' ||
+ // block.selector === 'reportListItem' ||
block.selector === 'reportJSFunction' ||
+ block.selector === 'reportAttributeOf' ||
+ block.selector === 'reportCompiled' ||
(block instanceof RingMorph)
) {
this.parent.silentReplaceInput(this, block);
@@ -8115,6 +8189,8 @@ InputSlotMorph.prototype.init = function (
contents.isShowingBlanks = true;
contents.drawNew();
+ this.selectedBlock = null;
+
this.isUnevaluated = false;
this.choices = choiceDict || null; // object, function or selector
this.oldContentsExtent = contents.extent();
@@ -8160,13 +8236,22 @@ InputSlotMorph.prototype.arrow = function () {
);
};
-InputSlotMorph.prototype.setContents = function (aStringOrFloat) {
+InputSlotMorph.prototype.setContents = function (data) {
+ // data can be a String, Float, or "wish" Block
var cnts = this.contents(),
- dta = aStringOrFloat,
+ dta = data,
isConstant = dta instanceof Array;
+
+ if (this.selectedBlock) {
+ this.selectedBlock = null;
+ }
+
if (isConstant) {
dta = localize(dta[0]);
cnts.isItalic = !this.isReadOnly;
+ } else if (dta instanceof BlockMorph) {
+ this.selectedBlock = dta;
+ dta = ''; // make sure the contents text emptied
} else { // assume dta is a localizable choice if it's a key in my choices
cnts.isItalic = false;
if (!isNil(this.choices) && this.choices[dta] instanceof Array) {
@@ -8187,7 +8272,7 @@ InputSlotMorph.prototype.setContents = function (aStringOrFloat) {
}
// remember the constant, if any
- this.constant = isConstant ? aStringOrFloat : null;
+ this.constant = isConstant ? data : null;
};
InputSlotMorph.prototype.userSetContents = function (aStringOrFloat) {
@@ -8217,7 +8302,8 @@ InputSlotMorph.prototype.menuFromDict = function (
noEmptyOption,
enableKeyboard)
{
- var key,
+ var key, dial,
+ myself = this,
menu = new MenuMorph(
this.userSetContents,
null,
@@ -8225,6 +8311,11 @@ InputSlotMorph.prototype.menuFromDict = function (
this.fontSize
);
+ function update (num) {
+ myself.setContents(num);
+ myself.reactToSliderEdit();
+ }
+
if (choices instanceof Function) {
choices = choices.call(this);
} else if (isString(choices)) {
@@ -8240,8 +8331,19 @@ InputSlotMorph.prototype.menuFromDict = function (
if (Object.prototype.hasOwnProperty.call(choices, key)) {
if (key[0] === '~') {
menu.addLine();
- // } else if (key.indexOf('§_def') === 0) {
- // menu.addItem(choices[key].blockInstance(), choices[key]);
+ } else if (key.indexOf('§_def') === 0) {
+ menu.addItem(choices[key], choices[key]);
+ } else if (key.indexOf('§_dir') === 0) {
+ dial = new DialMorph();
+ dial.rootForGrab = function () {return this; };
+ dial.target = this;
+ dial.action = update;
+ dial.fillColor = this.parent.color;
+ dial.setRadius(this.fontSize * 3);
+ dial.setValue(this.evaluate(), false, true);
+ menu.addLine();
+ menu.items.push(dial);
+ menu.addLine();
} else if (choices[key] instanceof Object &&
!(choices[key] instanceof Array) &&
(typeof choices[key] !== 'function')) {
@@ -8345,13 +8447,18 @@ InputSlotMorph.prototype.collidablesMenu = function () {
};
InputSlotMorph.prototype.distancesMenu = function () {
- var dict = {
- 'mouse-pointer' : ['mouse-pointer']
- },
+ var block = this.parentThatIsA(BlockMorph),
+ dict = {},
rcvr = this.parentThatIsA(BlockMorph).scriptTarget(),
stage = rcvr.parentThatIsA(StageMorph),
allNames = [];
+ if (block && (block.selector !== 'reportRelationTo')) {
+ dict['random position'] = ['random position'];
+ }
+ dict['mouse-pointer'] = ['mouse-pointer'];
+ dict.center = ['center'];
+
stage.children.forEach(function (morph) {
if (morph instanceof SpriteMorph && !morph.isTemporary) {
if (morph.name !== rcvr.name) {
@@ -8505,11 +8612,9 @@ InputSlotMorph.prototype.attributesMenu = function () {
dict[name] = name;
});
}
- /*
- obj.customBlocks.forEach(function (def, i) {
- dict['§_def' + i] = def
+ obj.allBlocks(true).forEach(function (def, i) {
+ dict['§_def' + i] = def.blockInstance(true); // include translations
});
- */
return dict;
};
@@ -8655,26 +8760,36 @@ InputSlotMorph.prototype.fixLayout = function () {
}
arrowWidth = arrow.isVisible ? arrow.width() : 0;
- height = contents.height() + this.edge * 2; // + this.typeInPadding * 2
- if (this.isNumeric) {
- width = contents.width()
- + Math.floor(arrowWidth * 0.5)
- + height
+ // determine slot dimensions
+ if (this.selectedBlock) { // a "wish" in the OF-block's left slot
+ height = this.selectedBlock.height() + this.edge * 2;
+ width = this.selectedBlock.width()
+ + arrowWidth
+ + this.edge * 2
+ this.typeInPadding * 2;
- } else {
- width = Math.max(
- contents.width()
- + arrowWidth
- + this.edge * 2
- + this.typeInPadding * 2,
- contents.rawHeight ? // single vs. multi-line contents
- contents.rawHeight() + arrowWidth
- : fontHeight(contents.fontSize) / 1.3
- + arrowWidth,
- this.minWidth // for text-type slots
- );
+ } else {
+ height = contents.height() + this.edge * 2; // + this.typeInPadding * 2
+ if (this.isNumeric) {
+ width = contents.width()
+ + Math.floor(arrowWidth * 0.5)
+ + height
+ + this.typeInPadding * 2;
+ } else {
+ width = Math.max(
+ contents.width()
+ + arrowWidth
+ + this.edge * 2
+ + this.typeInPadding * 2,
+ contents.rawHeight ? // single vs. multi-line contents
+ contents.rawHeight() + arrowWidth
+ : fontHeight(contents.fontSize) / 1.3
+ + arrowWidth,
+ this.minWidth // for text-type slots
+ );
+ }
}
this.setExtent(new Point(width, height));
+
if (this.isNumeric) {
contents.setPosition(new Point(
Math.floor(height / 2),
@@ -8836,16 +8951,21 @@ InputSlotMorph.prototype.mappedCode = function () {
InputSlotMorph.prototype.evaluate = function () {
/*
- answer my content's text string. If I am numerical convert that
- string to a number. If the conversion fails answer the string
+ answer my contents, which can be a "wish", i.e. a block that refers to
+ another sprite's local method, or a text string. If I am numerical convert
+ that string to a number. If the conversion fails answer the string
(e.g. for special choices like 'any', 'all' or 'last') otherwise
the numerical value.
*/
- var num,
- contents = this.contents();
+ var num, contents;
+
+ if (this.selectedBlock) {
+ return this.selectedBlock;
+ }
if (this.constant) {
return this.constant;
}
+ contents = this.contents();
if (this.isNumeric) {
num = parseFloat(contents.text || '0');
if (!isNaN(num)) {
@@ -8943,6 +9063,16 @@ InputSlotMorph.prototype.drawNew = function () {
this.drawRoundBorder(context);
}
}
+
+ // draw my "wish" block, if any
+ if (this.selectedBlock) {
+ context.drawImage(
+ this.selectedBlock.fullImageClassic(),
+ this.edge + this.typeInPadding,
+ this.edge
+ );
+ }
+
};
InputSlotMorph.prototype.drawRectBorder = function (context) {
@@ -9136,10 +9266,6 @@ InputSlotMorph.prototype.drawRoundBorder = function (context) {
context.stroke();
};
-
-
-
-
// TemplateSlotMorph ///////////////////////////////////////////////////
/*
@@ -10470,7 +10596,8 @@ MultiArgMorph.prototype.removeInput = function () {
if (this.children.length > 1) {
oldPart = this.children[this.children.length - 2];
this.removeChild(oldPart);
- if (oldPart instanceof BlockMorph) {
+ if (oldPart instanceof BlockMorph &&
+ !(oldPart instanceof RingMorph && !oldPart.contents())) {
scripts = this.parentThatIsA(ScriptsMorph);
if (scripts) {
scripts.add(oldPart);
diff --git a/byob.js b/byob.js
index 9c7d1b8c..773f71ed 100644
--- a/byob.js
+++ b/byob.js
@@ -9,7 +9,7 @@
written by Jens Mönig
jens@moenig.org
- Copyright (C) 2017 by Jens Mönig
+ Copyright (C) 2018 by Jens Mönig
This file is part of Snap!.
@@ -95,7 +95,7 @@
*/
-/*global modules, CommandBlockMorph, SpriteMorph, TemplateSlotMorph,
+/*global modules, CommandBlockMorph, SpriteMorph, TemplateSlotMorph, Map,
StringMorph, Color, DialogBoxMorph, ScriptsMorph, ScrollFrameMorph,
Point, HandleMorph, HatBlockMorph, BlockMorph, detect, List, Process,
AlignmentMorph, ToggleMorph, InputFieldMorph, ReporterBlockMorph,
@@ -104,11 +104,11 @@ contains, InputSlotMorph, ToggleButtonMorph, IDE_Morph, MenuMorph, copy,
ToggleElementMorph, Morph, fontHeight, StageMorph, SyntaxElementMorph,
SnapSerializer, CommentMorph, localize, CSlotMorph, MorphicPreferences,
SymbolMorph, isNil, CursorMorph, VariableFrame, WatcherMorph, Variable,
-BooleanSlotMorph, XML_Serializer*/
+BooleanSlotMorph, XML_Serializer, SnapTranslator*/
// Global stuff ////////////////////////////////////////////////////////
-modules.byob = '2017-October-09';
+modules.byob = '2018-June-15';
// Declarations
@@ -140,22 +140,28 @@ function CustomBlockDefinition(spec, receiver) {
this.isGlobal = false;
this.type = 'command';
this.spec = spec || '';
- // format: {'inputName' : [type, default, options, readonly]}
- this.declarations = {};
+ this.declarations = new Map();
+ // key: inputName
+ // value: [type, default, options, isReadOnly]
this.variableNames = [];
this.comment = null;
this.codeMapping = null; // experimental, generate text code
this.codeHeader = null; // experimental, generate text code
+ this.translations = {}; // experimental, format: {lang : spec}
// don't serialize (not needed for functionality):
this.receiver = receiver || null; // for serialization only (pointer)
this.editorDimensions = null; // a rectangle, last bounds of the editor
this.cachedIsRecursive = null; // for automatic yielding
+ this.cachedTranslation = null; // for localized block specs
+
+ // transient - for "wishes"
+ this.storedSemanticSpec = null;
}
// CustomBlockDefinition instantiating blocks
-CustomBlockDefinition.prototype.blockInstance = function () {
+CustomBlockDefinition.prototype.blockInstance = function (storeTranslations) {
var block;
if (this.type === 'command') {
block = new CustomCommandBlockMorph(this);
@@ -166,6 +172,9 @@ CustomBlockDefinition.prototype.blockInstance = function () {
);
}
block.isDraggable = true;
+ if (storeTranslations) { // only for "wishes"
+ block.storedTranslations = this.translationsAsText();
+ }
return block;
};
@@ -195,7 +204,7 @@ CustomBlockDefinition.prototype.prototypeInstance = function () {
// assign slot declarations to prototype inputs
block.parts().forEach(function (part) {
if (part instanceof BlockInputFragmentMorph) {
- slot = myself.declarations[part.fragment.labelString];
+ slot = myself.declarations.get(part.fragment.labelString);
if (slot) {
part.fragment.type = slot[0];
part.fragment.defaultValue = slot[1];
@@ -215,7 +224,13 @@ CustomBlockDefinition.prototype.copyAndBindTo = function (sprite, headerOnly) {
delete c[XML_Serializer.prototype.idProperty];
c.receiver = sprite; // only for (kludgy) serialization
- c.declarations = copy(this.declarations); // might have to go deeper
+
+ // copy declarations
+ c.declarations = new Map();
+ for (var [key, val] of this.declarations) {
+ c.declarations.set(key, val);
+ }
+
if (headerOnly) { // for serializing inherited method signatures
c.body = null;
return c;
@@ -234,6 +249,10 @@ CustomBlockDefinition.prototype.copyAndBindTo = function (sprite, headerOnly) {
// CustomBlockDefinition accessing
CustomBlockDefinition.prototype.blockSpec = function () {
+ if (this.storedSemanticSpec) {
+ return this.storedSemanticSpec; // for "wishes"
+ }
+
var myself = this,
ans = [],
parts = this.parseSpec(this.spec),
@@ -264,15 +283,15 @@ CustomBlockDefinition.prototype.helpSpec = function () {
};
CustomBlockDefinition.prototype.typeOf = function (inputName) {
- if (this.declarations[inputName]) {
- return this.declarations[inputName][0];
+ if (this.declarations.has(inputName)) {
+ return this.declarations.get(inputName)[0];
}
return '%s';
};
CustomBlockDefinition.prototype.defaultValueOf = function (inputName) {
- if (this.declarations[inputName]) {
- return this.declarations[inputName][1];
+ if (this.declarations.has(inputName)) {
+ return this.declarations.get(inputName)[1];
}
return '';
};
@@ -298,8 +317,9 @@ CustomBlockDefinition.prototype.inputOptionsOfIdx = function (idx) {
};
CustomBlockDefinition.prototype.dropDownMenuOf = function (inputName) {
- if (this.declarations[inputName] && this.declarations[inputName][2]) {
- return this.parseChoices(this.declarations[inputName][2]);
+ if (this.declarations.has(inputName) &&
+ this.declarations.get(inputName)[2]) {
+ return this.parseChoices(this.declarations.get(inputName)[2]);
}
return null;
};
@@ -324,8 +344,8 @@ CustomBlockDefinition.prototype.parseChoices = function (string) {
};
CustomBlockDefinition.prototype.isReadOnlyInput = function (inputName) {
- return this.declarations[inputName] &&
- this.declarations[inputName][3] === true;
+ return this.declarations.has(inputName) &&
+ this.declarations.get(inputName)[3] === true;
};
CustomBlockDefinition.prototype.inputOptionsOf = function (inputName) {
@@ -383,6 +403,82 @@ CustomBlockDefinition.prototype.isDirectlyRecursive = function () {
return this.cachedIsRecursive;
};
+// CustomBlockDefinition localizing, highly experimental
+
+CustomBlockDefinition.prototype.localizedSpec = function () {
+ if (this.cachedTranslation) {return this.cachedTranslation; }
+
+ var loc = this.translations[SnapTranslator.language],
+ sem = this.blockSpec(),
+ locParts,
+ inputs,
+ i = -1;
+
+ function isInput(str) {
+ return (str.length > 1) && (str[0] === '%');
+ }
+
+ if (isNil(loc)) {return sem; }
+ inputs = BlockMorph.prototype.parseSpec(sem).filter(function (str) {
+ return (isInput(str));
+ });
+ locParts = BlockMorph.prototype.parseSpec(loc);
+
+ // perform a bunch of sanity checks on the localized spec
+ if (locParts.some(function (str) {return isInput(str); }) ||
+ (locParts.filter(function (str) {return str === '_'; }).length !==
+ inputs.length)
+ ) {
+ this.cachedTranslation = sem;
+ } else {
+ // substitute each input place holder with its semantic spec part
+ locParts = locParts.map(function (str) {
+ if (str === '_') {
+ i += 1;
+ return inputs[i];
+ }
+ return str;
+ });
+ this.cachedTranslation = locParts.join(' ');
+ }
+ return this.cachedTranslation;
+};
+
+CustomBlockDefinition.prototype.abstractBlockSpec = function () {
+ // answer the semantic block spec substituting each input
+ // with an underscore
+ return BlockMorph.prototype.parseSpec(this.blockSpec()).map(
+ function (str) {
+ return (str.length > 1 && (str[0]) === '%') ? '_' : str;
+ }
+ ).join(' ');
+};
+
+CustomBlockDefinition.prototype.translationsAsText = function () {
+ var myself = this,
+ txt = '';
+ Object.keys(this.translations).forEach(function (lang) {
+ txt += (lang + ':' + myself.translations[lang] + '\n');
+ });
+ return txt;
+};
+
+CustomBlockDefinition.prototype.updateTranslations = function (text) {
+ var myself = this,
+ lines = text.split('\n').filter(function (txt) {
+ return txt.length;
+ });
+ this.translations = {};
+ lines.forEach(function (txt) {
+ var idx = txt.indexOf(':'),
+ key = txt.slice(0, idx).trim(),
+ val = txt.slice(idx + 1).trim();
+ if (idx) {
+ myself.translations[key] = val;
+ }
+ });
+};
+
// CustomBlockDefinition picturing
CustomBlockDefinition.prototype.scriptsPicture = function () {
@@ -465,12 +561,14 @@ function CustomCommandBlockMorph(definition, isProto) {
CustomCommandBlockMorph.prototype.init = function (definition, isProto) {
this.definition = definition; // mandatory
+ this.semanticSpec = '';
this.isGlobal = definition ? definition.isGlobal : false;
this.isPrototype = isProto || false; // optional
CustomCommandBlockMorph.uber.init.call(this, true); // silently
this.category = definition.category;
this.selector = 'evaluateCustomBlock';
this.variables = null;
+ this.storedTranslations = null; // transient - only for "wishes"
this.initializeVariables();
if (definition) { // needed for de-serializing
this.refresh();
@@ -495,9 +593,11 @@ CustomCommandBlockMorph.prototype.initializeVariables = function (oldVars) {
CustomCommandBlockMorph.prototype.refresh = function (aDefinition, silently) {
var def = aDefinition || this.definition,
newSpec = this.isPrototype ?
- def.spec : def.blockSpec(),
+ def.spec : def.localizedSpec(),
oldInputs;
+ this.semanticSpec = def.blockSpec();
+
// make sure local custom blocks don't hold on to a method.
// future performance optimization plan:
// null out the definition for local blocks here,
@@ -738,17 +838,22 @@ CustomCommandBlockMorph.prototype.blockSpecFromFragments = function () {
};
CustomCommandBlockMorph.prototype.declarationsFromFragments = function () {
- // format for type declarations: {inputName : [type, default]}
- var ans = {};
+ // returns a Map object for type declarations:
+ // key: inputName
+ // value: [type, default, options, isReadOnly]
+ var ans = new Map();
this.parts().forEach(function (part) {
if (part instanceof BlockInputFragmentMorph) {
- ans[part.fragment.labelString] = [
- part.fragment.type,
- part.fragment.defaultValue,
- part.fragment.options,
- part.fragment.isReadOnly
- ];
+ ans.set(
+ part.fragment.labelString,
+ [
+ part.fragment.type,
+ part.fragment.defaultValue,
+ part.fragment.options,
+ part.fragment.isReadOnly
+ ]
+ );
}
});
return ans;
@@ -809,7 +914,7 @@ CustomCommandBlockMorph.prototype.edit = function () {
this.duplicateBlockDefinition();
return;
}
- def = rcvr.getMethod(this.blockSpec);
+ def = rcvr.getMethod(this.semanticSpec);
}
Morph.prototype.trackChanges = false;
editor = new BlockEditorMorph(def, rcvr);
@@ -946,6 +1051,13 @@ CustomCommandBlockMorph.prototype.userMenu = function () {
},
'open a new window\nwith a picture of this script'
);
+ menu.addItem(
+ "translations...",
+ function () {
+ hat.parentThatIsA(BlockEditorMorph).editTranslations();
+ },
+ 'experimental -\nunder construction'
+ );
if (this.isGlobal) {
if (hat.inputs().length < 2) {
menu.addItem(
@@ -1193,10 +1305,12 @@ CustomReporterBlockMorph.prototype.init = function (
isProto
) {
this.definition = definition; // mandatory
+ this.semanticSpec = ''; // used for translations
this.isGlobal = definition ? definition.isGlobal : false;
this.isPrototype = isProto || false; // optional
CustomReporterBlockMorph.uber.init.call(this, isPredicate, true); // sil.
this.category = definition.category;
+ this.storedTranslations = null; // transient - only for "wishes"
this.variables = new VariableFrame();
this.initializeVariables();
this.selector = 'evaluateCustomBlock';
@@ -1942,6 +2056,7 @@ BlockEditorMorph.prototype.init = function (definition, target) {
// additional properties:
this.definition = definition;
+ this.translations = definition.translationsAsText();
this.handle = null;
// initialize inherited properties:
@@ -2086,11 +2201,13 @@ BlockEditorMorph.prototype.close = function () {
block = detect(
this.body.contents.allChildren(),
function (morph) {
- return morph.definition && !morph.definition.isGlobal;
+ return morph.isCustomBlock && !morph.isGlobal;
}
);
if (block) {
- block = block.definition.blockInstance();
+ block = block.scriptTarget()
+ .getMethod(block.semanticSpec)
+ .blockInstance();
block.addShadow();
new DialogBoxMorph().inform(
'Local Block(s) in Global Definition',
@@ -2161,6 +2278,8 @@ BlockEditorMorph.prototype.updateDefinition = function () {
this.definition.declarations = this.prototypeSlots();
this.definition.variableNames = this.variableNames();
this.definition.scripts = [];
+ this.definition.updateTranslations(this.translations);
+ this.definition.cachedTranslation = null;
this.definition.editorDimensions = this.bounds.copy();
this.definition.cachedIsRecursive = null; // flush the cache, don't update
@@ -2247,6 +2366,32 @@ BlockEditorMorph.prototype.variableNames = function () {
).variableNames();
};
+// BlockEditorMorph translation
+
+BlockEditorMorph.prototype.editTranslations = function () {
+ var myself = this,
+ block = this.definition.blockInstance();
+ block.addShadow(new Point(3, 3));
+ new DialogBoxMorph(
+ myself,
+ function (text) {
+ myself.translations = text;
+ },
+ myself
+ ).promptCode(
+ 'Custom Block Translations',
+ myself.translations,
+ myself.world(),
+ block.fullImage(),
+ myself.definition.abstractBlockSpec() +
+ '\n\n' +
+ localize('Enter one translation per line. ' +
+ 'use colon (":") as lang/spec delimiter\n' +
+ 'and underscore ("_") as placeholder for an input, ' +
+ 'e.g.:\n\nen:say _ for _ secs')
+ );
+};
+
// BlockEditorMorph layout
BlockEditorMorph.prototype.setInitialDimensions = function () {
diff --git a/cloud.js b/cloud.js
old mode 100644
new mode 100755
index 28b2a6c5..1c1fcd93
--- a/cloud.js
+++ b/cloud.js
@@ -1,658 +1,745 @@
-/*
-
- cloud.js
-
- a backend API for SNAP!
-
- written by Jens Mönig
-
- Copyright (C) 2015 by Jens Mönig
-
- This file is part of Snap!.
-
- Snap! is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as
- published by the Free Software Foundation, either version 3 of
- the License, or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see .
-
-*/
-
-// Global settings /////////////////////////////////////////////////////
-
-/*global modules, IDE_Morph, SnapSerializer, hex_sha512, alert, nop,
-localize*/
-
-modules.cloud = '2015-December-15';
-
-// Global stuff
-
-var Cloud;
-var SnapCloud = new Cloud(
- 'https://snap.apps.miosoft.com/SnapCloud'
-);
-
-// Cloud /////////////////////////////////////////////////////////////
-
-function Cloud(url) {
- this.username = null;
- this.password = null; // hex_sha512 hashed
- this.url = url;
- this.session = null;
- this.limo = null;
- this.route = null;
- this.api = {};
-}
-
-Cloud.prototype.clear = function () {
- this.username = null;
- this.password = null;
- this.session = null;
- this.limo = null;
- this.route = null;
- this.api = {};
-};
-
-Cloud.prototype.hasProtocol = function () {
- return this.url.toLowerCase().indexOf('http') === 0;
-};
-
-Cloud.prototype.setRoute = function (username) {
- var routes = 20,
- userNum = 0,
- i;
-
- for (i = 0; i < username.length; i += 1) {
- userNum += username.charCodeAt(i);
- }
- userNum = userNum % routes + 1;
- this.route = '.sc1m' +
- (userNum < 10 ? '0' : '') +
- userNum;
-};
-
-// Cloud: Snap! API
-
-Cloud.prototype.signup = function (
- username,
- email,
- callBack,
- errorCall
-) {
- // both callBack and errorCall are two-argument functions
- var request = new XMLHttpRequest(),
- myself = this;
- try {
- request.open(
- "GET",
- (this.hasProtocol() ? '' : 'http://')
- + this.url + 'SignUp'
- + '?Username='
- + encodeURIComponent(username)
- + '&Email='
- + encodeURIComponent(email),
- true
- );
- request.setRequestHeader(
- "Content-Type",
- "application/x-www-form-urlencoded"
- );
- request.withCredentials = true;
- request.onreadystatechange = function () {
- if (request.readyState === 4) {
- if (request.responseText) {
- if (request.responseText.indexOf('ERROR') === 0) {
- errorCall.call(
- this,
- request.responseText,
- 'Signup'
- );
- } else {
- callBack.call(
- null,
- request.responseText,
- 'Signup'
- );
- }
- } else {
- errorCall.call(
- null,
- myself.url + 'SignUp',
- localize('could not connect to:')
- );
- }
- }
- };
- request.send(null);
- } catch (err) {
- errorCall.call(this, err.toString(), 'Snap!Cloud');
- }
-};
-
-Cloud.prototype.getPublicProject = function (
- id,
- callBack,
- errorCall
-) {
- // id is Username=username&projectName=projectname,
- // where the values are url-component encoded
- // callBack is a single argument function, errorCall take two args
- var request = new XMLHttpRequest(),
- myself = this;
- try {
- request.open(
- "GET",
- (this.hasProtocol() ? '' : 'http://')
- + this.url + 'RawPublic'
- + '?'
- + id,
- true
- );
- request.setRequestHeader(
- "Content-Type",
- "application/x-www-form-urlencoded"
- );
- request.withCredentials = true;
- request.onreadystatechange = function () {
- if (request.readyState === 4) {
- if (request.responseText) {
- if (request.responseText.indexOf('ERROR') === 0) {
- errorCall.call(
- this,
- request.responseText
- );
- } else {
- callBack.call(
- null,
- request.responseText
- );
- }
- } else {
- errorCall.call(
- null,
- myself.url + 'Public',
- localize('could not connect to:')
- );
- }
- }
- };
- request.send(null);
- } catch (err) {
- errorCall.call(this, err.toString(), 'Snap!Cloud');
- }
-};
-
-Cloud.prototype.resetPassword = function (
- username,
- callBack,
- errorCall
-) {
- // both callBack and errorCall are two-argument functions
- var request = new XMLHttpRequest(),
- myself = this;
- try {
- request.open(
- "GET",
- (this.hasProtocol() ? '' : 'http://')
- + this.url + 'ResetPW'
- + '?Username='
- + encodeURIComponent(username),
- true
- );
- request.setRequestHeader(
- "Content-Type",
- "application/x-www-form-urlencoded"
- );
- request.withCredentials = true;
- request.onreadystatechange = function () {
- if (request.readyState === 4) {
- if (request.responseText) {
- if (request.responseText.indexOf('ERROR') === 0) {
- errorCall.call(
- this,
- request.responseText,
- 'Reset Password'
- );
- } else {
- callBack.call(
- null,
- request.responseText,
- 'Reset Password'
- );
- }
- } else {
- errorCall.call(
- null,
- myself.url + 'ResetPW',
- localize('could not connect to:')
- );
- }
- }
- };
- request.send(null);
- } catch (err) {
- errorCall.call(this, err.toString(), 'Snap!Cloud');
- }
-};
-
-Cloud.prototype.login = function (
- username,
- password,
- callBack,
- errorCall
-) {
- // both callBack and errorCall are two-argument functions
- var request = new XMLHttpRequest(),
- usr = JSON.stringify({'__h': password, '__u': username}),
- myself = this;
- this.setRoute(username);
- try {
- request.open(
- "POST",
- (this.hasProtocol() ? '' : 'http://') +
- this.url +
- '?SESSIONGLUE=' +
- this.route,
- true
- );
- request.setRequestHeader(
- "Content-Type",
- "application/json; charset=utf-8"
- );
- // glue this session to a route:
- request.setRequestHeader('SESSIONGLUE', this.route);
- request.withCredentials = true;
- request.onreadystatechange = function () {
- if (request.readyState === 4) {
- if (request.responseText) {
- myself.api = myself.parseAPI(request.responseText);
- myself.session = request.getResponseHeader('MioCracker')
- .split(';')[0];
- // set the cookie identifier:
- myself.limo = this.getResponseHeader("miocracker")
- .substring(
- 9,
- this.getResponseHeader("miocracker").indexOf("=")
- );
- if (myself.api.logout) {
- myself.username = username;
- myself.password = password;
- callBack.call(null, myself.api, 'Snap!Cloud');
- } else {
- errorCall.call(
- null,
- request.responseText,
- 'connection failed'
- );
- }
- } else {
- errorCall.call(
- null,
- myself.url,
- localize('could not connect to:')
- );
- }
- }
- };
- request.send(usr);
- } catch (err) {
- errorCall.call(this, err.toString(), 'Snap!Cloud');
- }
-};
-
-Cloud.prototype.reconnect = function (
- callBack,
- errorCall
-) {
- if (!(this.username && this.password)) {
- this.message('You are not logged in');
- return;
- }
- this.login(
- this.username,
- this.password,
- callBack,
- errorCall
- );
-};
-
-Cloud.prototype.saveProject = function (ide, callBack, errorCall) {
- var myself = this,
- pdata,
- media,
- size,
- mediaSize;
-
- ide.serializer.isCollectingMedia = true;
- pdata = ide.serializer.serialize(ide.stage);
- media = ide.hasChangedMedia ?
- ide.serializer.mediaXML(ide.projectName) : null;
- ide.serializer.isCollectingMedia = false;
- ide.serializer.flushMedia();
-
- mediaSize = media ? media.length : 0;
- size = pdata.length + mediaSize;
- if (mediaSize > 10485760) {
- new DialogBoxMorph().inform(
- 'Snap!Cloud - Cannot Save Project',
- 'The media inside this project exceeds 10 MB.\n' +
- 'Please reduce the size of costumes or sounds.\n',
- ide.world(),
- ide.cloudIcon(null, new Color(180, 0, 0))
- );
- throw new Error('Project media exceeds 10 MB size limit');
- }
-
- // check if serialized data can be parsed back again
- try {
- ide.serializer.parse(pdata);
- } catch (err) {
- ide.showMessage('Serialization of program data failed:\n' + err);
- throw new Error('Serialization of program data failed:\n' + err);
- }
- if (media !== null) {
- try {
- ide.serializer.parse(media);
- } catch (err) {
- ide.showMessage('Serialization of media failed:\n' + err);
- throw new Error('Serialization of media failed:\n' + err);
- }
- }
- ide.serializer.isCollectingMedia = false;
- ide.serializer.flushMedia();
-
- ide.showMessage('Uploading ' + Math.round(size / 1024) + ' KB...');
- myself.reconnect(
- function () {
- myself.callService(
- 'saveProject',
- function (response, url) {
- callBack.call(null, response, url);
- myself.disconnect();
- ide.hasChangedMedia = false;
- },
- errorCall,
- [
- ide.projectName,
- pdata,
- media,
- pdata.length,
- media ? media.length : 0
- ]
- );
- },
- errorCall
- );
-};
-
-Cloud.prototype.getProjectList = function (callBack, errorCall) {
- var myself = this;
- this.reconnect(
- function () {
- myself.callService(
- 'getProjectList',
- function (response, url) {
- callBack.call(null, response, url);
- myself.disconnect();
- },
- errorCall
- );
- },
- errorCall
- );
-};
-
-Cloud.prototype.changePassword = function (
- oldPW,
- newPW,
- callBack,
- errorCall
-) {
- var myself = this;
- this.reconnect(
- function () {
- myself.callService(
- 'changePassword',
- function (response, url) {
- callBack.call(null, response, url);
- myself.disconnect();
- },
- errorCall,
- [hex_sha512(oldPW), hex_sha512(newPW)]
- );
- },
- errorCall
- );
-};
-
-Cloud.prototype.logout = function (callBack, errorCall) {
- this.clear();
- this.callService(
- 'logout',
- callBack,
- errorCall
- );
-};
-
-Cloud.prototype.disconnect = function () {
- this.callService(
- 'logout',
- nop,
- nop
- );
-};
-
-// Cloud: backend communication
-
-Cloud.prototype.callURL = function (url, callBack, errorCall) {
- // both callBack and errorCall are optional two-argument functions
- var request = new XMLHttpRequest(),
- stickyUrl,
- myself = this;
- try {
- // set the Limo. Also set the glue as a query paramter for backup.
- stickyUrl = url +
- '&SESSIONGLUE=' +
- this.route +
- '&_Limo=' +
- this.limo;
- request.open('GET', stickyUrl, true);
- request.withCredentials = true;
- request.setRequestHeader(
- "Content-Type",
- "application/x-www-form-urlencoded"
- );
- request.setRequestHeader('MioCracker', this.session);
- // Set the glue as a request header.
- request.setRequestHeader('SESSIONGLUE', this.route);
- request.onreadystatechange = function () {
- if (request.readyState === 4) {
- if (request.responseText) {
- var responseList = myself.parseResponse(
- request.responseText
- );
- callBack.call(null, responseList, url);
- } else {
- errorCall.call(
- null,
- url,
- 'no response from:'
- );
- }
- }
- };
- request.send(null);
- } catch (err) {
- errorCall.call(this, err.toString(), url);
- }
-};
-
-Cloud.prototype.callService = function (
- serviceName,
- callBack,
- errorCall,
- args
-) {
- // both callBack and errorCall are optional two-argument functions
- var request = new XMLHttpRequest(),
- service = this.api[serviceName],
- myself = this,
- stickyUrl,
- postDict;
-
- if (!this.session) {
- errorCall.call(null, 'You are not connected', 'Cloud');
- return;
- }
- if (!service) {
- errorCall.call(
- null,
- 'service ' + serviceName + ' is not available',
- 'API'
- );
- return;
- }
- if (args && args.length > 0) {
- postDict = {};
- service.parameters.forEach(function (parm, idx) {
- postDict[parm] = args[idx];
- });
- }
- try {
- stickyUrl = this.url +
- '/' +
- service.url +
- '&SESSIONGLUE=' +
- this.route +
- '&_Limo=' +
- this.limo;
- request.open(service.method, stickyUrl, true);
- request.withCredentials = true;
- request.setRequestHeader(
- "Content-Type",
- "application/x-www-form-urlencoded"
- );
- request.setRequestHeader('MioCracker', this.session);
- request.setRequestHeader('SESSIONGLUE', this.route);
- request.onreadystatechange = function () {
- if (request.readyState === 4) {
- var responseList = [];
- if (request.responseText &&
- request.responseText.indexOf('ERROR') === 0) {
- errorCall.call(
- this,
- request.responseText,
- localize('Service:') + ' ' + localize(serviceName)
- );
- return;
- }
- if (serviceName === 'login') {
- myself.api = myself.parseAPI(request.responseText);
- }
- if (serviceName === 'getRawProject') {
- responseList = request.responseText;
- } else {
- responseList = myself.parseResponse(
- request.responseText
- );
- }
- callBack.call(null, responseList, service.url);
- }
- };
- request.send(this.encodeDict(postDict));
- } catch (err) {
- errorCall.call(this, err.toString(), service.url);
- }
-};
-
-// Cloud: payload transformation
-
-Cloud.prototype.parseAPI = function (src) {
- var api = {},
- services;
- services = src.split(" ");
- services.forEach(function (service) {
- var entries = service.split("&"),
- serviceDescription = {},
- parms;
- entries.forEach(function (entry) {
- var pair = entry.split("="),
- key = decodeURIComponent(pair[0]).toLowerCase(),
- val = decodeURIComponent(pair[1]);
- if (key === "service") {
- api[val] = serviceDescription;
- } else if (key === "parameters") {
- parms = val.split(",");
- if (!(parms.length === 1 && !parms[0])) {
- serviceDescription.parameters = parms;
- }
- } else {
- serviceDescription[key] = val;
- }
- });
- });
- return api;
-};
-
-Cloud.prototype.parseResponse = function (src) {
- var ans = [],
- lines;
- if (!src) {return ans; }
- lines = src.split(" ");
- lines.forEach(function (service) {
- var entries = service.split("&"),
- dict = {};
- entries.forEach(function (entry) {
- var pair = entry.split("="),
- key = decodeURIComponent(pair[0]),
- val = decodeURIComponent(pair[1]);
- dict[key] = val;
- });
- ans.push(dict);
- });
- return ans;
-};
-
-Cloud.prototype.parseDict = function (src) {
- var dict = {};
- if (!src) {return dict; }
- src.split("&").forEach(function (entry) {
- var pair = entry.split("="),
- key = decodeURIComponent(pair[0]),
- val = decodeURIComponent(pair[1]);
- dict[key] = val;
- });
- return dict;
-};
-
-Cloud.prototype.encodeDict = function (dict) {
- var str = '',
- pair,
- key;
- if (!dict) {return null; }
- for (key in dict) {
- if (dict.hasOwnProperty(key)) {
- pair = encodeURIComponent(key)
- + '='
- + encodeURIComponent(dict[key]);
- if (str.length > 0) {
- str += '&';
- }
- str += pair;
- }
- }
- return str;
-};
-
-// Cloud: user messages (to be overridden)
-
-Cloud.prototype.message = function (string) {
- alert(string);
-};
+/*
+
+ cloud.js
+
+ a backend API for SNAP!
+
+ written by Bernat Romagosa
+ inspired by the original cloud API by Jens Mönig
+
+ Copyright (C) 2018 by Bernat Romagosa
+ Copyright (C) 2015 by Jens Mönig
+
+ This file is part of Snap!.
+
+ Snap! is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as
+ published by the Free Software Foundation, either version 3 of
+ the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see .
+
+*/
+
+// Global settings /////////////////////////////////////////////////////
+
+/*global modules, SnapSerializer, nop, hex_sha512, DialogBoxMorph, Color,
+normalizeCanvas*/
+
+modules.cloud = '2018-June-15';
+
+// Global stuff
+
+var Cloud;
+
+// Cloud /////////////////////////////////////////////////////////////
+
+function Cloud() {
+ this.init();
+}
+
+Cloud.prototype.init = function () {
+ this.url = this.determineCloudDomain();
+ this.username = null;
+};
+
+Cloud.prototype.knownDomains = {
+ 'Snap!Cloud' : 'https://cloud.snap.berkeley.edu',
+ 'Snap!Cloud (cs10)' : 'https://snap-cloud.cs10.org',
+ 'Snap!Cloud (staging)': 'https://snap-staging.cs10.org',
+ 'localhost': 'http://localhost:8080',
+ 'localhost (secure)': 'https://localhost:4431'
+};
+
+Cloud.prototype.defaultDomain = Cloud.prototype.knownDomains['Snap!Cloud'];
+
+Cloud.prototype.determineCloudDomain = function () {
+ // We dynamically determine the domain of the cloud server.
+ // Thise allows for easy mirrors and development servers.
+ // The domain is determined by:
+ // 1. in snap.html.
+ // 2. The current page's domain
+ var currentDomain = window.location.host, // host includes the port.
+ metaTag = document.head.querySelector("[name='snap-cloud-domain']"),
+ cloudDomain = this.defaultDomain;
+
+ if (metaTag) { return metaTag.getAttribute('location'); }
+
+ Object.values(this.knownDomains).some(function (server) {
+ if (Cloud.isMatchingDomain(currentDomain, server)) {
+ cloudDomain = server;
+ return true;
+ }
+ return false;
+ });
+
+ return cloudDomain;
+};
+
+Cloud.isMatchingDomain = function (client, server) {
+ // A matching domain means that the client-server are not subject to
+ // 3rd party cookie restrictions.
+ // see https://tools.ietf.org/html/rfc6265#section-5.1.3
+ // This matches a domain at end of a subdomain URL.
+ var position = server.indexOf(client);
+ switch (position) {
+ case -1:
+ return false;
+ case 0:
+ return client === server;
+ default:
+ return /[\.\/]/.test(server[position - 1]) &&
+ server.length === position + client.length;
+ }
+};
+
+// Dictionary handling
+
+Cloud.prototype.parseDict = function (src) {
+ var dict = {};
+ if (!src) {return dict; }
+ src.split("&").forEach(function (entry) {
+ var pair = entry.split("="),
+ key = decodeURIComponent(pair[0]),
+ val = decodeURIComponent(pair[1]);
+ dict[key] = val;
+ });
+ return dict;
+};
+
+Cloud.prototype.encodeDict = function (dict) {
+ var str = '',
+ pair,
+ key;
+ if (!dict) {return null; }
+ for (key in dict) {
+ if (dict.hasOwnProperty(key)) {
+ pair = encodeURIComponent(key)
+ + '='
+ + encodeURIComponent(dict[key]);
+ if (str.length > 0) {
+ str += '&';
+ }
+ str += pair;
+ }
+ }
+ return str;
+};
+
+// Error handling
+
+Cloud.genericErrorMessage =
+ 'There was an error while trying to access\n' +
+ 'a Snap!Cloud service. Please try again later.';
+
+Cloud.prototype.genericError = function () {
+ throw new Error(Cloud.genericErrorMessage);
+};
+
+// Low level functionality
+
+Cloud.prototype.request = function (
+ method,
+ path,
+ onSuccess,
+ onError,
+ errorMsg,
+ wantsRawResponse,
+ body) {
+
+ var request = new XMLHttpRequest(),
+ myself = this;
+
+ try {
+ request.open(
+ method,
+ this.url + path,
+ true
+ );
+ request.setRequestHeader(
+ 'Content-Type',
+ 'application/json; charset=utf-8'
+ );
+ request.withCredentials = true;
+ request.onreadystatechange = function () {
+ if (request.readyState === 4) {
+ if (request.responseText) {
+ var response =
+ (!wantsRawResponse ||
+ (request.responseText.indexOf('{"errors"') === 0)) ?
+ JSON.parse(request.responseText) :
+ request.responseText;
+
+ if (response.errors) {
+ onError.call(
+ null,
+ response.errors[0],
+ errorMsg
+ );
+ } else {
+ if (onSuccess) {
+ onSuccess.call(null, response.message || response);
+ }
+ }
+ } else {
+ if (onError) {
+ onError.call(
+ null,
+ errorMsg || Cloud.genericErrorMessage,
+ myself.url
+ );
+ } else {
+ myself.genericError();
+ }
+ }
+ }
+ };
+ request.send(body);
+ } catch (err) {
+ onError.call(this, err.toString(), 'Cloud Error');
+ }
+};
+
+Cloud.prototype.withCredentialsRequest = function (
+ method,
+ path,
+ onSuccess,
+ onError,
+ errorMsg,
+ wantsRawResponse,
+ body) {
+
+ var myself = this;
+ this.checkCredentials(
+ function (username) {
+ if (username) {
+ myself.request(
+ method,
+ // %username is replaced by the actual username
+ path.replace('%username', encodeURIComponent(username)),
+ onSuccess,
+ onError,
+ errorMsg,
+ wantsRawResponse,
+ body);
+ } else {
+ onError.call(this, 'You are not logged in', 'Snap!Cloud');
+ }
+ }
+ );
+};
+
+// Credentials management
+
+Cloud.prototype.initSession = function (onSuccess) {
+ var myself = this;
+ this.request(
+ 'POST',
+ '/init',
+ function () { myself.checkCredentials(onSuccess); },
+ nop,
+ null,
+ true
+ );
+};
+
+Cloud.prototype.checkCredentials = function (onSuccess, onError, response) {
+ var myself = this;
+ this.getCurrentUser(
+ function (user) {
+ if (user.username) {
+ myself.username = user.username;
+ myself.verified = user.verified;
+ }
+ if (onSuccess) {
+ onSuccess.call(
+ null,
+ user.username,
+ user.isadmin,
+ response ? JSON.parse(response) : null
+ );
+ }
+ },
+ onError
+ );
+};
+
+Cloud.prototype.getCurrentUser = function (onSuccess, onError) {
+ this.request(
+ 'GET',
+ '/users/c',
+ onSuccess,
+ onError,
+ 'Could not retrieve current user'
+ );
+};
+
+Cloud.prototype.getUser = function (username, onSuccess, onError) {
+ this.request(
+ 'GET',
+ '/users/' + encodeURIComponent(username),
+ onSuccess,
+ onError,
+ 'Could not retrieve user'
+ );
+};
+
+Cloud.prototype.logout = function (onSuccess, onError) {
+ this.username = null;
+ this.request(
+ 'POST',
+ '/logout',
+ onSuccess,
+ onError,
+ 'logout failed'
+ );
+};
+
+Cloud.prototype.login = function (
+ username,
+ password,
+ persist,
+ onSuccess,
+ onError
+) {
+ var myself = this;
+ this.request(
+ 'POST',
+ '/users/' + encodeURIComponent(username) + '/login?' +
+ this.encodeDict({
+ persist: persist
+ }),
+ function (response) {
+ myself.checkCredentials(onSuccess, onError, response);
+ },
+ onError,
+ 'login failed',
+ 'false', // wants raw response
+ hex_sha512(password) // password travels inside the body
+ );
+};
+
+Cloud.prototype.signup = function (
+ username,
+ password,
+ passwordRepeat,
+ email,
+ onSuccess,
+ onError
+) {
+ this.request(
+ 'POST',
+ '/users/' + encodeURIComponent(username) + '?' + this.encodeDict({
+ email: email,
+ password: hex_sha512(password),
+ password_repeat: hex_sha512(passwordRepeat)
+ }),
+ onSuccess,
+ onError,
+ 'signup failed');
+};
+
+Cloud.prototype.changePassword = function (
+ password,
+ newPassword,
+ passwordRepeat,
+ onSuccess,
+ onError
+) {
+ this.withCredentialsRequest(
+ 'POST',
+ '/users/%username/newpassword?' + this.encodeDict({
+ oldpassword: hex_sha512(password),
+ password_repeat: hex_sha512(passwordRepeat),
+ newpassword: hex_sha512(newPassword)
+ }),
+ onSuccess,
+ onError,
+ 'Could not change password'
+ );
+
+};
+
+Cloud.prototype.resetPassword = function (username, onSuccess, onError) {
+ this.request(
+ 'POST',
+ '/users/' + encodeURIComponent(username) + '/password_reset',
+ onSuccess,
+ onError,
+ 'Password reset request failed'
+ );
+};
+
+Cloud.prototype.resendVerification = function (username, onSuccess, onError) {
+ this.request(
+ 'POST',
+ '/users/' + encodeURIComponent(username) + '/resendverification',
+ onSuccess,
+ onError,
+ 'Could not send verification email'
+ );
+};
+
+
+// Projects
+
+Cloud.prototype.saveProject = function (ide, onSuccess, onError) {
+ var myself = this;
+ this.checkCredentials(
+ function (username) {
+ if (username) {
+ var xml = ide.serializer.serialize(ide.stage),
+ thumbnail = normalizeCanvas(
+ ide.stage.thumbnail(
+ SnapSerializer.prototype.thumbnailSize
+ )).toDataURL(),
+ body, mediaSize, size;
+
+ ide.serializer.isCollectingMedia = true;
+ body = {
+ notes: ide.projectNotes,
+ xml: xml,
+ media: ide.hasChangedMedia ?
+ ide.serializer.mediaXML(ide.projectName) : null,
+ thumbnail: thumbnail
+ };
+ ide.serializer.isCollectingMedia = false;
+ ide.serializer.flushMedia();
+
+ mediaSize = body.media ? body.media.length : 0;
+ size = body.xml.length + mediaSize;
+ if (mediaSize > 10485760) {
+ new DialogBoxMorph().inform(
+ 'Snap!Cloud - Cannot Save Project',
+ 'The media inside this project exceeds 10 MB.\n' +
+ 'Please reduce the size of costumes or sounds.\n',
+ ide.world(),
+ ide.cloudIcon(null, new Color(180, 0, 0))
+ );
+ throw new Error('Project media exceeds 10 MB size limit');
+ }
+
+ // check if serialized data can be parsed back again
+ try {
+ ide.serializer.parse(body.xml);
+ } catch (err) {
+ ide.showMessage(
+ 'Serialization of program data failed:\n' + err
+ );
+ throw new Error(
+ 'Serialization of program data failed:\n' + err
+ );
+ }
+ if (body.media !== null) {
+ try {
+ ide.serializer.parse(body.media);
+ } catch (err) {
+ ide.showMessage(
+ 'Serialization of media failed:\n' + err
+ );
+ throw new Error(
+ 'Serialization of media failed:\n' + err
+ );
+ }
+ }
+ ide.serializer.isCollectingMedia = false;
+ ide.serializer.flushMedia();
+
+ ide.showMessage(
+ 'Uploading ' + Math.round(size / 1024) + ' KB...'
+ );
+
+ myself.request(
+ 'POST',
+ '/projects/' +
+ encodeURIComponent(username) +
+ '/' +
+ encodeURIComponent(ide.projectName),
+ onSuccess,
+ onError,
+ 'Project could not be saved',
+ false,
+ JSON.stringify(body) // POST body
+ );
+ } else {
+ onError.call(this, 'You are not logged in', 'Snap!Cloud');
+ }
+ }
+ );
+};
+
+Cloud.prototype.getProjectList = function (onSuccess, onError, withThumbnail) {
+ var path = '/projects/%username?updatingnotes=true';
+
+ if (withThumbnail) {
+ path += '&withthumbnail=true';
+ }
+
+ this.withCredentialsRequest(
+ 'GET',
+ path,
+ onSuccess,
+ onError,
+ 'Could not fetch projects'
+ );
+};
+
+Cloud.prototype.getPublishedProjectList = function (
+ username,
+ page,
+ pageSize,
+ searchTerm,
+ onSuccess,
+ onError,
+ withThumbnail
+) {
+ var path = '/projects' +
+ (username ? '/' + encodeURIComponent(username) : '') +
+ '?ispublished=true';
+
+ if (withThumbnail) {
+ path += '&withthumbnail=true';
+ }
+
+ if (page) {
+ path += '&page=' + page + '&pagesize=' + (pageSize || 16);
+ }
+
+ if (searchTerm) {
+ path += '&matchtext=' + encodeURIComponent(searchTerm);
+ }
+
+ this.request(
+ 'GET',
+ path,
+ onSuccess,
+ onError,
+ 'Could not fetch projects'
+ );
+};
+
+Cloud.prototype.getThumbnail = function (
+ username,
+ projectName,
+ onSuccess,
+ onError
+) {
+ this[username ? 'request' : 'withCredentialsRequest'](
+ 'GET',
+ '/projects/' +
+ (username ? encodeURIComponent(username) : '%username') +
+ '/' +
+ encodeURIComponent(projectName) +
+ '/thumbnail',
+ onSuccess,
+ onError,
+ 'Could not fetch thumbnail',
+ true
+ );
+};
+
+Cloud.prototype.getProject = function (projectName, delta, onSuccess, onError) {
+ this.withCredentialsRequest(
+ 'GET',
+ '/projects/%username/' +
+ encodeURIComponent(projectName) +
+ (delta ? '?delta=' + delta : ''),
+ onSuccess,
+ onError,
+ 'Could not fetch project ' + projectName,
+ true
+ );
+};
+
+Cloud.prototype.getPublicProject = function (
+ projectName,
+ username,
+ onSuccess,
+ onError
+) {
+ this.request(
+ 'GET',
+ '/projects/' +
+ encodeURIComponent(username) +
+ '/' +
+ encodeURIComponent(projectName),
+ onSuccess,
+ onError,
+ 'Could not fetch project ' + projectName,
+ true
+ );
+};
+
+Cloud.prototype.getProjectMetadata = function (
+ projectName,
+ username,
+ onSuccess,
+ onError
+) {
+ this.request(
+ 'GET',
+ '/projects/' +
+ encodeURIComponent(username) +
+ '/' +
+ encodeURIComponent(projectName) +
+ '/metadata',
+ onSuccess,
+ onError,
+ 'Could not fetch metadata for ' + projectName
+ );
+};
+
+Cloud.prototype.getProjectVersionMetadata = function (
+ projectName,
+ onSuccess,
+ onError
+) {
+ this.withCredentialsRequest(
+ 'GET',
+ '/projects/%username/' +
+ encodeURIComponent(projectName) +
+ '/versions',
+ onSuccess,
+ onError,
+ 'Could not fetch versions for project ' + projectName
+ );
+};
+
+Cloud.prototype.deleteProject = function (
+ projectName,
+ username,
+ onSuccess,
+ onError
+) {
+ this[username ? 'request' : 'withCredentialsRequest'](
+ 'DELETE',
+ '/projects/' +
+ (username ? encodeURIComponent(username) : '%username') +
+ '/' +
+ encodeURIComponent(projectName),
+ onSuccess,
+ onError,
+ 'Could not delete project'
+ );
+};
+
+Cloud.prototype.shareProject = function (
+ projectName,
+ username,
+ onSuccess,
+ onError
+) {
+ this[username ? 'request' : 'withCredentialsRequest'](
+ 'POST',
+ '/projects/' +
+ (username ? encodeURIComponent(username) : '%username') +
+ '/' +
+ encodeURIComponent(projectName) +
+ '/metadata?ispublic=true',
+ onSuccess,
+ onError,
+ 'Could not share project'
+ );
+};
+
+Cloud.prototype.unshareProject = function (
+ projectName,
+ username,
+ onSuccess,
+ onError
+) {
+ this[username ? 'request' : 'withCredentialsRequest'](
+ 'POST',
+ '/projects/' +
+ (username ? encodeURIComponent(username) : '%username') +
+ '/' +
+ encodeURIComponent(projectName) +
+ '/metadata?ispublic=false&ispublished=false',
+ onSuccess,
+ onError,
+ 'Could not unshare project'
+ );
+};
+
+Cloud.prototype.publishProject = function (
+ projectName,
+ username,
+ onSuccess,
+ onError
+) {
+ this[username ? 'request' : 'withCredentialsRequest'](
+ 'POST',
+ '/projects/' +
+ (username ? encodeURIComponent(username) : '%username') +
+ '/' +
+ encodeURIComponent(projectName) +
+ '/metadata?ispublished=true',
+ onSuccess,
+ onError,
+ 'Could not publish project'
+ );
+};
+
+Cloud.prototype.unpublishProject = function (
+ projectName,
+ username,
+ onSuccess,
+ onError
+) {
+ this[username ? 'request' : 'withCredentialsRequest'](
+ 'POST',
+ '/projects/' +
+ (username ? encodeURIComponent(username) : '%username') +
+ '/' +
+ encodeURIComponent(projectName) +
+ '/metadata?ispublished=false',
+ onSuccess,
+ onError,
+ 'Could not unpublish project'
+ );
+};
+
+Cloud.prototype.remixProject = function (
+ projectName,
+ username,
+ onSuccess,
+ onError
+) {
+ this.withCredentialsRequest(
+ 'POST',
+ '/projects/' +
+ encodeURIComponent(username) +
+ '/' +
+ encodeURIComponent(projectName) +
+ '/remix',
+ onSuccess,
+ onError,
+ 'Could not remix project'
+ );
+};
+
+Cloud.prototype.updateNotes = function (
+ projectName,
+ notes,
+ onSuccess,
+ onError
+) {
+ this.withCredentialsRequest(
+ 'POST',
+ '/projects/%username/' +
+ encodeURIComponent(projectName) +
+ '/metadata',
+ onSuccess,
+ onError,
+ 'Could not update project notes',
+ false, // wants raw response
+ JSON.stringify({ notes: notes })
+ );
+};
+
diff --git a/gui.js b/gui.js
index 6c08e07e..8c59fe98 100644
--- a/gui.js
+++ b/gui.js
@@ -9,7 +9,7 @@
written by Jens Mönig
jens@moenig.org
- Copyright (C) 2017 by Jens Mönig
+ Copyright (C) 2018 by Jens Mönig
This file is part of Snap!.
@@ -29,7 +29,7 @@
prerequisites:
--------------
- needs blocks.js, threads.js, objects.js and morphic.js
+ needs blocks.js, threads.js, objects.js, cloud.jus and morphic.js
toc
@@ -58,7 +58,7 @@
*/
-/*global modules, Morph, SpriteMorph, SyntaxElementMorph, Color,
+/*global modules, Morph, SpriteMorph, SyntaxElementMorph, Color, Cloud,
ListWatcherMorph, TextMorph, newCanvas, useBlurredShadows, VariableFrame,
StringMorph, Point, MenuMorph, morphicVersion, DialogBoxMorph,
ToggleButtonMorph, contains, ScrollFrameMorph, StageMorph, PushButtonMorph,
@@ -66,16 +66,16 @@ InputFieldMorph, FrameMorph, Process, nop, SnapSerializer, ListMorph, detect,
AlignmentMorph, TabMorph, Costume, MorphicPreferences, Sound, BlockMorph,
ToggleMorph, InputSlotDialogMorph, ScriptsMorph, isNil, SymbolMorph,
BlockExportDialogMorph, BlockImportDialogMorph, SnapTranslator, localize,
-List, ArgMorph, SnapCloud, Uint8Array, HandleMorph, SVG_Costume,
-fontHeight, hex_sha512, sb, CommentMorph, CommandBlockMorph, BooleanSlotMorph,
+List, ArgMorph, Uint8Array, HandleMorph, SVG_Costume,
+fontHeight, sb, CommentMorph, CommandBlockMorph, BooleanSlotMorph,
BlockLabelPlaceHolderMorph, Audio, SpeechBubbleMorph, ScriptFocusMorph,
XML_Element, WatcherMorph, BlockRemovalDialogMorph, saveAs, TableMorph,
isSnapObject, isRetinaEnabled, disableRetinaSupport, enableRetinaSupport,
-isRetinaSupported, SliderMorph, Animation*/
+isRetinaSupported, SliderMorph, Animation, BoxMorph, MediaRecorder*/
// Global stuff ////////////////////////////////////////////////////////
-modules.gui = '2017-October-28';
+modules.gui = '2018-July-09';
// Declarations
@@ -216,6 +216,7 @@ IDE_Morph.prototype.init = function (isAutoFill) {
this.applySavedSettings();
// additional properties:
+ this.cloud = new Cloud();
this.cloudMsg = null;
this.source = 'local';
this.serializer = new SnapSerializer();
@@ -241,6 +242,10 @@ IDE_Morph.prototype.init = function (isAutoFill) {
this.corralBar = null;
this.corral = null;
+ this.embedPlayButton = null;
+ this.embedOverlay = null;
+ this.isEmbedMode = false;
+
this.isAutoFill = isAutoFill === undefined ? true : isAutoFill;
this.isAppMode = false;
this.isSmallStage = false;
@@ -266,29 +271,38 @@ IDE_Morph.prototype.init = function (isAutoFill) {
};
IDE_Morph.prototype.openIn = function (world) {
- var hash, usr, myself = this, urlLanguage = null;
+ var hash, myself = this, urlLanguage = null;
- // get persistent user data, if any
- if (this.hasLocalStorage()) {
- usr = localStorage['-snap-user'];
- if (usr) {
- usr = SnapCloud.parseResponse(usr)[0];
- if (usr) {
- SnapCloud.username = usr.username || null;
- SnapCloud.password = usr.password || null;
- if (SnapCloud.username) {
- this.source = 'cloud';
+ this.cloud.initSession(
+ function (username) {
+ if (username) {
+ myself.source = 'cloud';
+ if (!myself.cloud.verified) {
+ new DialogBoxMorph().inform(
+ 'Unverified account',
+ 'Your account is still unverified.\n' +
+ 'Please use the verification link that\n' +
+ 'was sent to your email address when you\n' +
+ 'signed up.\n\n' +
+ 'If you cannot find that email, please\n' +
+ 'check your spam folder. If you still\n' +
+ 'cannot find it, please use the "Resend\n' +
+ 'Verification Email..." option in the cloud\n' +
+ 'menu.',
+ world,
+ myself.cloudIcon(null, new Color(0, 180, 0))
+ );
}
}
}
- }
+ );
this.buildPanes();
world.add(this);
world.userMenu = this.userMenu;
// override SnapCloud's user message with Morphic
- SnapCloud.message = function (string) {
+ this.cloud.message = function (string) {
var m = new MenuMorph(null, string),
intervalHandle;
m.popUpCenteredInWorld(world);
@@ -301,7 +315,8 @@ IDE_Morph.prototype.openIn = function (world) {
// prevent non-DialogBoxMorphs from being dropped
// onto the World in user-mode
world.reactToDropOf = function (morph) {
- if (!(morph instanceof DialogBoxMorph)) {
+ if (!(morph instanceof DialogBoxMorph ||
+ (morph instanceof MenuMorph))) {
if (world.hand.grabOrigin) {
morph.slideBackTo(world.hand.grabOrigin);
} else {
@@ -327,7 +342,10 @@ IDE_Morph.prototype.openIn = function (world) {
}
}
- function applyFlags(dict) {
+ function applyFlags(dict) {
+ if (dict.embedMode) {
+ myself.setEmbedMode();
+ }
if (dict.editMode) {
myself.toggleAppMode(false);
} else {
@@ -343,7 +361,7 @@ IDE_Morph.prototype.openIn = function (world) {
if (dict.noExitWarning) {
window.onbeforeunload = nop;
}
- }
+ }
// dynamic notifications from non-source text files
// has some issues, commented out for now
@@ -391,7 +409,7 @@ IDE_Morph.prototype.openIn = function (world) {
} else {
this.rawOpenProjectString(getURL(hash));
}
- applyFlags(SnapCloud.parseDict(location.hash.substr(5)));
+ applyFlags(myself.cloud.parseDict(location.hash.substr(5)));
} else if (location.hash.substr(0, 9) === '#present:') {
this.shield = new Morph();
this.shield.color = this.color;
@@ -400,11 +418,12 @@ IDE_Morph.prototype.openIn = function (world) {
myself.showMessage('Fetching project\nfrom the cloud...');
// make sure to lowercase the username
- dict = SnapCloud.parseDict(location.hash.substr(9));
+ dict = myself.cloud.parseDict(location.hash.substr(9));
dict.Username = dict.Username.toLowerCase();
- SnapCloud.getPublicProject(
- SnapCloud.encodeDict(dict),
+ myself.cloud.getPublicProject(
+ dict.ProjectName,
+ dict.Username,
function (projectData) {
var msg;
myself.nextSteps([
@@ -440,11 +459,11 @@ IDE_Morph.prototype.openIn = function (world) {
myself.showMessage('Fetching project\nfrom the cloud...');
// make sure to lowercase the username
- dict = SnapCloud.parseDict(location.hash.substr(7));
- dict.Username = dict.Username.toLowerCase();
+ dict = myself.cloud.parseDict(location.hash.substr(7));
- SnapCloud.getPublicProject(
- SnapCloud.encodeDict(dict),
+ myself.cloud.getPublicProject(
+ dict.ProjectName,
+ dict.Username,
function (projectData) {
var msg;
myself.nextSteps([
@@ -476,11 +495,12 @@ IDE_Morph.prototype.openIn = function (world) {
myself.showMessage('Fetching project\nfrom the cloud...');
// make sure to lowercase the username
- dict = SnapCloud.parseDict(location.hash.substr(4));
+ dict = myself.cloud.parseDict(location.hash.substr(4));
dict.Username = dict.Username.toLowerCase();
- SnapCloud.getPublicProject(
- SnapCloud.encodeDict(dict),
+ myself.cloud.getPublicProject(
+ dict.ProjectName,
+ dict.Username,
function (projectData) {
myself.saveXMLAs(projectData, dict.ProjectName);
myself.showMessage(
@@ -1565,7 +1585,8 @@ IDE_Morph.prototype.createCorralBar = function () {
cambutton.labelColor = this.buttonLabelColor;
cambutton.contrast = this.buttonContrast;
cambutton.drawNew();
- cambutton.hint = "take a camera snapshot and\nimport it as a new sprite";
+ cambutton.hint = "take a camera snapshot and\n" +
+ "import it as a new sprite";
cambutton.fixLayout();
cambutton.setCenter(this.corralBar.center());
cambutton.setLeft(
@@ -1723,7 +1744,24 @@ IDE_Morph.prototype.fixLayout = function (situation) {
if (situation !== 'refreshPalette') {
// stage
- if (this.isAppMode) {
+ if (this.isEmbedMode) {
+ this.stage.setScale(Math.floor(Math.min(
+ this.width() / this.stage.dimensions.x,
+ this.height() / this.stage.dimensions.y
+ ) * 10) / 10);
+
+ this.embedPlayButton.size = Math.floor(Math.min(
+ this.width(), this.height())) / 3;
+ this.embedPlayButton.setWidth(this.embedPlayButton.size);
+ this.embedPlayButton.setHeight(this.embedPlayButton.size);
+
+ if (this.embedOverlay) {
+ this.embedOverlay.setExtent(this.extent());
+ }
+
+ this.stage.setCenter(this.center());
+ this.embedPlayButton.setCenter(this.center());
+ } else if (this.isAppMode) {
this.stage.setScale(Math.floor(Math.min(
(this.width() - padding * 2) / this.stage.dimensions.x,
(this.height() - this.controlBar.height() * 2 - padding * 2)
@@ -1884,9 +1922,29 @@ IDE_Morph.prototype.droppedSVG = function (anImage, name) {
};
IDE_Morph.prototype.droppedAudio = function (anAudio, name) {
- this.currentSprite.addSound(anAudio, name.split('.')[0]); // up to period
- this.spriteBar.tabBar.tabTo('sounds');
- this.hasChangedMedia = true;
+ var myself = this;
+ if (anAudio.src.indexOf('data:audio') !== 0) {
+ // fetch and base 64 encode samples using FileReader
+ this.getURL(
+ anAudio.src,
+ function (blob) {
+ var reader = new window.FileReader();
+ reader.readAsDataURL(blob);
+ reader.onloadend = function() {
+ var base64 = reader.result;
+ base64 = 'data:audio/ogg;base64,' +
+ base64.split(',')[1];
+ anAudio.src = base64;
+ myself.droppedAudio(anAudio, name);
+ };
+ },
+ 'blob'
+ );
+ } else {
+ this.currentSprite.addSound(anAudio, name.split('.')[0]); // up to '.'
+ this.spriteBar.tabBar.tabTo('sounds');
+ this.hasChangedMedia = true;
+ }
};
IDE_Morph.prototype.droppedText = function (aString, name) {
@@ -2238,10 +2296,13 @@ IDE_Morph.prototype.addNewSprite = function () {
// randomize sprite properties
sprite.setHue(rnd.call(this, 0, 100));
sprite.setBrightness(rnd.call(this, 50, 100));
- sprite.turn(rnd.call(this, 1, 360));
sprite.setXPosition(rnd.call(this, -220, 220));
sprite.setYPosition(rnd.call(this, -160, 160));
+ if (this.world().currentKey === 16) { // shift-click
+ sprite.turn(rnd.call(this, 1, 360));
+ }
+
this.sprites.add(sprite);
this.corral.addSprite(sprite);
this.selectSprite(sprite);
@@ -2295,6 +2356,26 @@ IDE_Morph.prototype.newCamSprite = function () {
camDialog.popUp(this.world());
};
+IDE_Morph.prototype.recordNewSound = function () {
+ var soundRecorder,
+ myself = this;
+
+ soundRecorder = new SoundRecorderDialogMorph(
+ function (sound) {
+ if (sound) {
+ myself.currentSprite.addSound(
+ sound,
+ myself.newSoundName('recording')
+ );
+ myself.spriteBar.tabBar.tabTo('sounds');
+ myself.hasChangedMedia = true;
+ }
+ });
+
+ soundRecorder.key = 'microphone';
+ soundRecorder.popUp(this.world());
+};
+
IDE_Morph.prototype.duplicateSprite = function (sprite) {
var duplicate = sprite.fullCopy();
duplicate.setPosition(this.world().hand.position());
@@ -2318,7 +2399,9 @@ IDE_Morph.prototype.instantiateSprite = function (sprite) {
IDE_Morph.prototype.removeSprite = function (sprite) {
var idx, myself = this;
- sprite.parts.forEach(function (part) {myself.removeSprite(part); });
+ sprite.parts.slice().forEach(function (part) {
+ myself.removeSprite(part);
+ });
idx = this.sprites.asArray().indexOf(sprite) + 1;
this.stage.threads.stopAllForReceiver(sprite);
sprite.corpsify();
@@ -2343,17 +2426,37 @@ IDE_Morph.prototype.removeSprite = function (sprite) {
this.selectSprite(this.currentSprite);
};
+IDE_Morph.prototype.newSoundName = function (name) {
+ var lastSound =
+ this.currentSprite.sounds.at(
+ this.currentSprite.sounds.length());
+
+ return this.newName(
+ name || lastSound.name,
+ this.currentSprite.sounds.asArray().map(
+ function (eachSound) {
+ return eachSound.name;
+ }
+ )
+ );
+};
+
IDE_Morph.prototype.newSpriteName = function (name, ignoredSprite) {
- var ix = name.indexOf('('),
- stem = (ix < 0) ? name : name.substring(0, ix),
- count = 1,
- newName = stem,
- all = this.sprites.asArray().concat(this.stage).filter(
+ var all = this.sprites.asArray().concat(this.stage).filter(
function (each) {return each !== ignoredSprite; }
).map(
function (each) {return each.name; }
);
- while (contains(all, newName)) {
+ return this.newName(name, all);
+};
+
+IDE_Morph.prototype.newName = function (name, elements) {
+ var ix = name.indexOf('('),
+ stem = (ix < 0) ? name : name.substring(0, ix),
+ count = 1,
+ newName = stem;
+
+ while (contains(elements, newName)) {
count += 1;
newName = stem + '(' + count + ')';
}
@@ -2444,7 +2547,7 @@ IDE_Morph.prototype.cloudMenu = function () {
);
menu.addLine();
}
- if (!SnapCloud.username) {
+ if (!this.cloud.username) {
menu.addItem(
'Login...',
'initializeCloud'
@@ -2457,9 +2560,13 @@ IDE_Morph.prototype.cloudMenu = function () {
'Reset Password...',
'resetCloudPassword'
);
+ menu.addItem(
+ 'Resend Verification Email...',
+ 'resendVerification'
+ );
} else {
menu.addItem(
- localize('Logout') + ' ' + SnapCloud.username,
+ localize('Logout') + ' ' + this.cloud.username,
'logout'
);
menu.addItem(
@@ -2517,15 +2624,12 @@ IDE_Morph.prototype.cloudMenu = function () {
function () {
myself.prompt('Author name…', function (usr) {
myself.prompt('Project name...', function (prj) {
- var id = 'Username=' +
- encodeURIComponent(usr.toLowerCase()) +
- '&ProjectName=' +
- encodeURIComponent(prj);
myself.showMessage(
'Fetching project\nfrom the cloud...'
);
- SnapCloud.getPublicProject(
- id,
+ myself.cloud.getPublicProject(
+ prj,
+ usr.toLowerCase(),
function (projectData) {
var msg;
if (!Process.prototype.isCatchingErrors) {
@@ -2907,6 +3011,20 @@ IDE_Morph.prototype.settingsMenu = function () {
'EXPERIMENTAL! check to enable\n live custom control structures',
true
);
+ addPreference(
+ 'JIT compiler support',
+ function () {
+ Process.prototype.enableCompiling =
+ !Process.prototype.enableCompiling;
+ myself.currentSprite.blocksCache.operators = null;
+ myself.currentSprite.paletteCache.operators = null;
+ myself.refreshPalette();
+ },
+ Process.prototype.enableCompiling,
+ 'EXPERIMENTAL! uncheck to disable live\nsupport for compiling',
+ 'EXPERIMENTAL! check to enable\nsupport for compiling',
+ true
+ );
menu.addLine(); // everything below this line is stored in the project
addPreference(
'Thread safe scripts',
@@ -3386,8 +3504,8 @@ IDE_Morph.prototype.aboutSnap = function () {
module, btn1, btn2, btn3, btn4, licenseBtn, translatorsBtn,
world = this.world();
- aboutTxt = 'Snap! 4.1.0.2\nBuild Your Own Blocks\n\n'
- + 'Copyright \u24B8 2017 Jens M\u00F6nig and '
+ aboutTxt = 'Snap! 4.2.1\nBuild Your Own Blocks\n\n'
+ + 'Copyright \u24B8 2018 Jens M\u00F6nig and '
+ 'Brian Harvey\n'
+ 'jens@moenig.org, bh@cs.berkeley.edu\n\n'
@@ -3430,6 +3548,7 @@ IDE_Morph.prototype.aboutSnap = function () {
+ '\ncountless bugfixes and optimizations'
+ '\nBartosz Leper: Retina Display Support'
+ '\nBernat Romagosa: Countless contributions'
+ + '\nCarles Paredes: Initial Vector Paint Editor'
+ '\n"Ava" Yuan Yuan, Dylan Servilla: Graphic Effects'
+ '\nKyle Hotchkiss: Block search design'
+ '\nBrian Broll: Many bugfixes and optimizations'
@@ -3597,7 +3716,7 @@ IDE_Morph.prototype.editProjectNotes = function () {
};
IDE_Morph.prototype.newProject = function () {
- this.source = SnapCloud.username ? 'cloud' : 'local';
+ this.source = this.cloud.username ? 'cloud' : 'local';
if (this.stage) {
this.stage.destroy();
}
@@ -3957,8 +4076,8 @@ IDE_Morph.prototype.exportProjectSummary = function (useDropShadows) {
add(pname, 'h1');
/*
- if (SnapCloud.username) {
- add(localize('by ') + SnapCloud.username);
+ if (this.cloud.username) {
+ add(localize('by ') + this.cloud.username);
}
*/
if (location.hash.indexOf('#present:') === 0) {
@@ -4347,9 +4466,7 @@ IDE_Morph.prototype.openProject = function (name) {
IDE_Morph.prototype.setURL = function (str) {
// Set the URL to a project's XML contents
- if (this.projectsInURLs) {
- location.hash = str;
- }
+ location.hash = this.projectsInURLs ? str : '';
};
IDE_Morph.prototype.saveFileAs = function (
@@ -4384,6 +4501,14 @@ IDE_Morph.prototype.saveFileAs = function (
while (i--) {
data[i] = text.charCodeAt(i);
}
+ } else if (hasTypeStr) {
+ // not base64 encoded
+ text = text.replace(/^data:image\/.*?, */, '');
+ data = new Uint8Array(text.length);
+ i = text.length;
+ while (i--) {
+ data[i] = text.charCodeAt(i);
+ }
}
return new Blob([data], {type: mimeType });
}
@@ -4454,7 +4579,8 @@ IDE_Morph.prototype.switchToUserMode = function () {
// prevent non-DialogBoxMorphs from being dropped
// onto the World in user-mode
world.reactToDropOf = function (morph) {
- if (!(morph instanceof DialogBoxMorph)) {
+ if (!(morph instanceof DialogBoxMorph ||
+ (morph instanceof MenuMorph))) {
if (world.hand.grabOrigin) {
morph.slideBackTo(world.hand.grabOrigin);
} else {
@@ -4618,6 +4744,28 @@ IDE_Morph.prototype.toggleSliderExecute = function () {
!ArgMorph.prototype.executeOnSliderEdit;
};
+IDE_Morph.prototype.setEmbedMode = function () {
+ var myself = this;
+ this.embedOverlay = new Morph();
+ this.embedOverlay.color = new Color(128, 128, 128);
+ this.embedOverlay.alpha = 0.5;
+
+ this.embedPlayButton = new SymbolMorph('pointRight');
+ this.embedPlayButton.color = new Color(128, 255, 128);
+
+ this.embedPlayButton.mouseClickLeft = function () {
+ myself.runScripts();
+ myself.embedOverlay.destroy();
+ this.destroy();
+ };
+
+ this.isEmbedMode = true;
+ this.controlBar.hide();
+ this.add(this.embedOverlay);
+ this.add(this.embedPlayButton);
+ this.fixLayout();
+};
+
IDE_Morph.prototype.toggleAppMode = function (appMode) {
var world = this.world(),
elements = [
@@ -4987,7 +5135,7 @@ IDE_Morph.prototype.userSetStageSize = function () {
IDE_Morph.prototype.setStageExtent = function (aPoint) {
var myself = this,
world = this.world(),
- ext = aPoint.max(new Point(480, 180));
+ ext = aPoint.max(new Point(240, 180));
function zoom() {
myself.step = function () {
@@ -5054,23 +5202,34 @@ IDE_Morph.prototype.initializeCloud = function () {
new DialogBoxMorph(
null,
function (user) {
- var pwh = hex_sha512(user.password),
- str;
- SnapCloud.login(
- user.username,
- pwh,
- function () {
- if (user.choice) {
- str = SnapCloud.encodeDict(
- {
- username: user.username,
- password: pwh
- }
- );
- localStorage['-snap-user'] = str;
- }
+ myself.cloud.login(
+ user.username.toLowerCase(),
+ user.password,
+ user.choice,
+ function (username, isadmin, response) {
myself.source = 'cloud';
- myself.showMessage('now connected.', 2);
+ if (!isNil(response.days_left)) {
+ new DialogBoxMorph().inform(
+ 'Unverified account: ' +
+ response.days_left +
+ ' days left',
+ 'You are now logged in, and your account\n' +
+ 'is enabled for three days.\n' +
+ 'Please use the verification link that\n' +
+ 'was sent to your email address when you\n' +
+ 'signed up.\n\n' +
+ 'If you cannot find that email, please\n' +
+ 'check your spam folder. If you still\n' +
+ 'cannot find it, please use the "Resend\n' +
+ 'Verification Email..." option in the cloud\n' +
+ 'menu.\n\n' +
+ 'You have ' + response.days_left + ' days left.',
+ world,
+ myself.cloudIcon(null, new Color(0, 180, 0))
+ );
+ } else {
+ myself.showMessage(response.message, 2);
+ }
},
myself.cloudError()
);
@@ -5092,23 +5251,20 @@ IDE_Morph.prototype.initializeCloud = function () {
IDE_Morph.prototype.createCloudAccount = function () {
var myself = this,
world = this.world();
-/*
- // force-logout, commented out for now:
- delete localStorage['-snap-user'];
- SnapCloud.clear();
-*/
+
new DialogBoxMorph(
null,
function (user) {
- SnapCloud.signup(
+ myself.cloud.signup(
user.username,
+ user.password,
+ user.passwordRepeat,
user.email,
function (txt, title) {
new DialogBoxMorph().inform(
title,
txt +
- '.\n\nAn e-mail with your password\n' +
- 'has been sent to the address provided',
+ '.\n\nYou can now log in.',
world,
myself.cloudIcon(null, new Color(0, 180, 0))
);
@@ -5133,21 +5289,17 @@ IDE_Morph.prototype.createCloudAccount = function () {
IDE_Morph.prototype.resetCloudPassword = function () {
var myself = this,
world = this.world();
-/*
- // force-logout, commented out for now:
- delete localStorage['-snap-user'];
- SnapCloud.clear();
-*/
+
new DialogBoxMorph(
null,
function (user) {
- SnapCloud.resetPassword(
+ myself.cloud.resetPassword(
user.username,
function (txt, title) {
new DialogBoxMorph().inform(
title,
txt +
- '.\n\nAn e-mail with a link to\n' +
+ '\n\nAn e-mail with a link to\n' +
'reset your password\n' +
'has been sent to the address provided',
world,
@@ -5171,17 +5323,51 @@ IDE_Morph.prototype.resetCloudPassword = function () {
);
};
+IDE_Morph.prototype.resendVerification = function () {
+ var myself = this,
+ world = this.world();
+
+ new DialogBoxMorph(
+ null,
+ function (user) {
+ myself.cloud.resendVerification(
+ user.username,
+ function (txt, title) {
+ new DialogBoxMorph().inform(
+ title,
+ txt,
+ world,
+ myself.cloudIcon(null, new Color(0, 180, 0))
+ );
+ },
+ myself.cloudError()
+ );
+ }
+ ).withKey('cloudresendverification').promptCredentials(
+ 'Resend verification email',
+ 'resendVerification',
+ null,
+ null,
+ null,
+ null,
+ null,
+ world,
+ myself.cloudIcon(),
+ myself.cloudMsg
+ );
+};
+
IDE_Morph.prototype.changeCloudPassword = function () {
var myself = this,
world = this.world();
new DialogBoxMorph(
null,
function (user) {
- SnapCloud.changePassword(
+ myself.cloud.changePassword(
user.oldpassword,
user.password,
+ user.passwordRepeat,
function () {
- myself.logout();
myself.showMessage('password has been changed.', 2);
},
myself.cloudError()
@@ -5203,14 +5389,11 @@ IDE_Morph.prototype.changeCloudPassword = function () {
IDE_Morph.prototype.logout = function () {
var myself = this;
- delete localStorage['-snap-user'];
- SnapCloud.logout(
+ this.cloud.logout(
function () {
- SnapCloud.clear();
myself.showMessage('disconnected.', 2);
},
function () {
- SnapCloud.clear();
myself.showMessage('disconnected.', 2);
}
);
@@ -5221,7 +5404,7 @@ IDE_Morph.prototype.saveProjectToCloud = function (name) {
if (name) {
this.showMessage('Saving project\nto the cloud...');
this.setProjectName(name);
- SnapCloud.saveProject(
+ this.cloud.saveProject(
this,
function () {myself.showMessage('saved.', 2); },
this.cloudError()
@@ -5385,9 +5568,6 @@ IDE_Morph.prototype.cloudError = function () {
myself.showMessage(explanation);
return;
}
- if (response.length > 50) {
- response = response.substring(0, 50) + '...';
- }
new DialogBoxMorph().inform(
'Snap!Cloud',
(url ? url + '\n' : '')
@@ -5415,41 +5595,45 @@ IDE_Morph.prototype.cloudIcon = function (height, color) {
};
IDE_Morph.prototype.setCloudURL = function () {
+ var myself = this;
new DialogBoxMorph(
null,
function (url) {
- SnapCloud.url = url;
+ myself.cloud.url = url;
}
).withKey('cloudURL').prompt(
'Cloud URL',
- SnapCloud.url,
+ this.cloud.url,
this.world(),
null,
- {
- 'Snap!Cloud' :
- 'https://snap.apps.miosoft.com/SnapCloud'
- }
+ this.cloud.knownDomains
);
};
// IDE_Morph HTTP data fetching
-IDE_Morph.prototype.getURL = function (url, callback) {
+IDE_Morph.prototype.getURL = function (url, callback, responseType) {
// fetch the contents of a url and pass it into the specified callback.
// If no callback is specified synchronously fetch and return it
// Note: Synchronous fetching has been deprecated and should be switched
var request = new XMLHttpRequest(),
async = callback instanceof Function,
- myself = this;
+ myself = this,
+ rsp;
+ if (async) {
+ request.responseType = responseType || 'text';
+ }
+ rsp = (!async || request.responseType === 'text') ? 'responseText'
+ : 'response';
try {
request.open('GET', url, async);
if (async) {
request.onreadystatechange = function () {
if (request.readyState === 4) {
- if (request.responseText) {
+ if (request[rsp]) {
callback.call(
myself,
- request.responseText
+ request[rsp]
);
} else {
throw new Error('unable to retrieve ' + url);
@@ -5457,10 +5641,11 @@ IDE_Morph.prototype.getURL = function (url, callback) {
}
};
}
+ request.setRequestHeader('Cache-Control', 'max-age=0');
request.send();
if (!async) {
if (request.status === 200) {
- return request.responseText;
+ return request[rsp];
}
throw new Error('unable to retrieve ' + url);
}
@@ -5469,7 +5654,7 @@ IDE_Morph.prototype.getURL = function (url, callback) {
if (async) {
callback.call(this);
} else {
- return request.responseText;
+ return request[rsp];
}
}
};
@@ -5550,6 +5735,7 @@ ProjectDialogMorph.prototype.init = function (ide, task) {
this.deleteButton = null;
this.shareButton = null;
this.unshareButton = null;
+ this.recoverButton = null;
// initialize inherited properties:
ProjectDialogMorph.uber.init.call(
@@ -5686,6 +5872,8 @@ ProjectDialogMorph.prototype.buildContents = function () {
if (this.task === 'open') {
this.addButton('openProject', 'Open');
this.action = 'openProject';
+ this.recoverButton = this.addButton('recoveryDialog', 'Recover');
+ this.recoverButton.hide();
} else { // 'save'
this.addButton('saveProject', 'Save');
this.action = 'saveProject';
@@ -5694,6 +5882,12 @@ ProjectDialogMorph.prototype.buildContents = function () {
this.unshareButton = this.addButton('unshareProject', 'Unshare');
this.shareButton.hide();
this.unshareButton.hide();
+ /*
+ this.publishButton = this.addButton('publishProject', 'Publish');
+ this.unpublishButton = this.addButton('unpublishProject', 'Unpublish');
+ this.publishButton.hide();
+ this.unpublishButton.hide();
+ */
this.deleteButton = this.addButton('deleteProject', 'Delete');
this.addButton('cancel', 'Cancel');
@@ -5841,16 +6035,8 @@ ProjectDialogMorph.prototype.buildFilterField = function () {
myself.listField.elements =
myself.projectList.filter(function (aProject) {
- var name,
- notes;
-
- if (aProject.ProjectName) { // cloud
- name = aProject.ProjectName;
- notes = aProject.Notes;
- } else { // local or examples
- name = aProject.name;
+ var name = aProject.projectname || aProject.name,
notes = aProject.notes || '';
- }
return name.toLowerCase().indexOf(text.toLowerCase()) > -1 ||
notes.toLowerCase().indexOf(text.toLowerCase()) > -1;
@@ -5883,11 +6069,11 @@ ProjectDialogMorph.prototype.setSource = function (source) {
case 'cloud':
msg = myself.ide.showMessage('Updating\nproject list...');
this.projectList = [];
- SnapCloud.getProjectList(
- function (projectList) {
+ myself.ide.cloud.getProjectList(
+ function (response) {
// Don't show cloud projects if user has since switch panes.
if (myself.source === 'cloud') {
- myself.installCloudProjectList(projectList);
+ myself.installCloudProjectList(response.projects);
}
msg.destroy();
},
@@ -5978,6 +6164,15 @@ ProjectDialogMorph.prototype.setSource = function (source) {
this.body.add(this.listField);
this.shareButton.hide();
this.unshareButton.hide();
+
+ if (this.task === 'open') {
+ this.recoverButton.hide();
+ }
+
+ /*
+ this.publishButton.hide();
+ this.unpublishButton.hide();
+ */
if (this.source === 'local') {
this.deleteButton.show();
} else { // examples
@@ -6017,9 +6212,9 @@ ProjectDialogMorph.prototype.getExamplesProjectList = function () {
ProjectDialogMorph.prototype.installCloudProjectList = function (pl) {
var myself = this;
- this.projectList = pl || [];
+ this.projectList = pl[0] ? pl : [];
this.projectList.sort(function (x, y) {
- return x.ProjectName.toLowerCase() < y.ProjectName.toLowerCase() ?
+ return x.projectname.toLowerCase() < y.projectname.toLowerCase() ?
-1 : 1;
});
@@ -6028,15 +6223,19 @@ ProjectDialogMorph.prototype.installCloudProjectList = function (pl) {
this.projectList,
this.projectList.length > 0 ?
function (element) {
- return element.ProjectName || element;
+ return element.projectname || element;
} : null,
[ // format: display shared project names bold
[
'bold',
- function (proj) {return proj.Public === 'true'; }
+ function (proj) { return proj.ispublic; }
+ ],
+ [
+ 'italic',
+ function (proj) { return proj.ispublished; }
]
],
- function () {myself.ok(); }
+ function () { myself.ok(); }
);
this.fixListFieldItemColors();
this.listField.fixLayout = nop;
@@ -6050,17 +6249,25 @@ ProjectDialogMorph.prototype.installCloudProjectList = function (pl) {
this.listField.action = function (item) {
if (item === undefined) {return; }
if (myself.nameField) {
- myself.nameField.setContents(item.ProjectName || '');
+ myself.nameField.setContents(item.projectname || '');
}
if (myself.task === 'open') {
- myself.notesText.text = item.Notes || '';
+ myself.notesText.text = item.notes || '';
myself.notesText.drawNew();
myself.notesField.contents.adjustBounds();
- myself.preview.texture = item.Thumbnail || null;
- myself.preview.cachedTexture = null;
+ myself.preview.texture = '';
myself.preview.drawNew();
+ // we ask for the thumbnail when selecting a project
+ myself.ide.cloud.getThumbnail(
+ null, // username is implicit
+ item.projectname,
+ function (thumbnail) {
+ myself.preview.texture = thumbnail;
+ myself.preview.cachedTexture = null;
+ myself.preview.drawNew();
+ });
(new SpeechBubbleMorph(new TextMorph(
- localize('last changed') + '\n' + item.Updated,
+ localize('last changed') + '\n' + item.lastupdated,
null,
null,
null,
@@ -6071,18 +6278,34 @@ ProjectDialogMorph.prototype.installCloudProjectList = function (pl) {
myself.preview.rightCenter().add(new Point(2, 0))
);
}
- if (item.Public === 'true') {
+ if (item.ispublic) {
myself.shareButton.hide();
myself.unshareButton.show();
+ /*
+ if (item.ispublished) {
+ myself.publishButton.hide();
+ myself.unpublishButton.show();
+ } else {
+ myself.publishButton.show();
+ myself.unpublishButton.hide();
+ }
+ */
} else {
myself.unshareButton.hide();
myself.shareButton.show();
+ /*
+ myself.publishButton.hide();
+ myself.unpublishButton.hide();
+ */
}
myself.buttons.fixLayout();
myself.fixLayout();
myself.edit();
};
this.body.add(this.listField);
+ if (this.task === 'open') {
+ this.recoverButton.show();
+ }
this.shareButton.show();
this.unshareButton.hide();
this.deleteButton.show();
@@ -6102,6 +6325,13 @@ ProjectDialogMorph.prototype.clearDetails = function () {
this.preview.drawNew();
};
+ProjectDialogMorph.prototype.recoveryDialog = function () {
+ var proj = this.listField.selected;
+ if (!proj) {return; }
+ new ProjectRecoveryDialogMorph(this.ide, proj.projectname, this).popUp();
+ this.hide();
+};
+
ProjectDialogMorph.prototype.openProject = function () {
var proj = this.listField.selected,
src;
@@ -6120,44 +6350,37 @@ ProjectDialogMorph.prototype.openProject = function () {
}
};
-ProjectDialogMorph.prototype.openCloudProject = function (project) {
+ProjectDialogMorph.prototype.openCloudProject = function (project, delta) {
var myself = this;
myself.ide.nextSteps([
function () {
myself.ide.showMessage('Fetching project\nfrom the cloud...');
},
function () {
- myself.rawOpenCloudProject(project);
+ myself.rawOpenCloudProject(project, delta);
}
]);
};
-ProjectDialogMorph.prototype.rawOpenCloudProject = function (proj) {
+ProjectDialogMorph.prototype.rawOpenCloudProject = function (proj, delta) {
var myself = this;
- SnapCloud.reconnect(
- function () {
- SnapCloud.callService(
- 'getRawProject',
- function (response) {
- SnapCloud.disconnect();
- /*
- if (myself.world().currentKey === 16) {
- myself.ide.download(response);
- return;
- }
- */
- myself.ide.source = 'cloud';
- myself.ide.droppedText(response);
- if (proj.Public === 'true') {
- location.hash = '#present:Username=' +
- encodeURIComponent(SnapCloud.username) +
- '&ProjectName=' +
- encodeURIComponent(proj.ProjectName);
- }
- },
- myself.ide.cloudError(),
- [proj.ProjectName]
- );
+ this.ide.cloud.getProject(
+ proj.projectname,
+ delta,
+ function (clouddata) {
+ myself.ide.source = 'cloud';
+ myself.ide.nextSteps([
+ function () {
+ myself.ide.openCloudDataString(clouddata);
+ }
+ ]);
+ location.hash = '';
+ if (proj.ispublic) {
+ location.hash = '#present:Username=' +
+ encodeURIComponent(myself.ide.cloud.username) +
+ '&ProjectName=' +
+ encodeURIComponent(proj.projectname);
+ }
},
myself.ide.cloudError()
);
@@ -6174,7 +6397,7 @@ ProjectDialogMorph.prototype.saveProject = function () {
if (this.source === 'cloud') {
if (detect(
this.projectList,
- function (item) {return item.ProjectName === name; }
+ function (item) {return item.projectname === name; }
)) {
this.ide.confirm(
localize(
@@ -6220,7 +6443,7 @@ ProjectDialogMorph.prototype.saveProject = function () {
ProjectDialogMorph.prototype.saveCloudProject = function () {
var myself = this;
this.ide.showMessage('Saving project\nto the cloud...');
- SnapCloud.saveProject(
+ this.ide.cloud.saveProject(
this.ide,
function () {
myself.ide.source = 'cloud';
@@ -6243,25 +6466,19 @@ ProjectDialogMorph.prototype.deleteProject = function () {
this.ide.confirm(
localize(
'Are you sure you want to delete'
- ) + '\n"' + proj.ProjectName + '"?',
+ ) + '\n"' + proj.projectname + '"?',
'Delete Project',
function () {
- SnapCloud.reconnect(
+ myself.ide.cloud.deleteProject(
+ proj.projectname,
+ null, // username is implicit
function () {
- SnapCloud.callService(
- 'deleteProject',
- function () {
- SnapCloud.disconnect();
- myself.ide.hasChangedMedia = true;
- idx = myself.projectList.indexOf(proj);
- myself.projectList.splice(idx, 1);
- myself.installCloudProjectList(
- myself.projectList
- ); // refresh list
- },
- myself.ide.cloudError(),
- [proj.ProjectName]
- );
+ myself.ide.hasChangedMedia = true;
+ idx = myself.projectList.indexOf(proj);
+ myself.projectList.splice(idx, 1);
+ myself.installCloudProjectList(
+ myself.projectList
+ ); // refresh list
},
myself.ide.cloudError()
);
@@ -6294,37 +6511,36 @@ ProjectDialogMorph.prototype.shareProject = function () {
if (proj) {
this.ide.confirm(
localize(
- 'Are you sure you want to publish'
- ) + '\n"' + proj.ProjectName + '"?',
+ 'Are you sure you want to share'
+ ) + '\n"' + proj.projectname + '"?',
'Share Project',
function () {
- myself.ide.showMessage('sharing\nproject...');
- SnapCloud.reconnect(
+ ide.showMessage('sharing\nproject...');
+ ide.cloud.shareProject(
+ proj.projectname,
+ null, // username is implicit
function () {
- SnapCloud.callService(
- 'publishProject',
- function () {
- SnapCloud.disconnect();
- proj.Public = 'true';
- myself.unshareButton.show();
- myself.shareButton.hide();
- entry.label.isBold = true;
- entry.label.drawNew();
- entry.label.changed();
- myself.buttons.fixLayout();
- myself.drawNew();
- myself.ide.showMessage('shared.', 2);
- },
- myself.ide.cloudError(),
- [proj.ProjectName]
- );
+ proj.ispublic = true;
+ myself.unshareButton.show();
+ myself.shareButton.hide();
+ /*
+ myself.publishButton.show();
+ myself.unpublishButton.hide();
+ */
+ entry.label.isBold = true;
+ entry.label.drawNew();
+ entry.label.changed();
+ myself.buttons.fixLayout();
+ myself.drawNew();
+ myself.ide.showMessage('shared.', 2);
+
// Set the Shared URL if the project is currently open
- if (proj.ProjectName === ide.projectName) {
- var usr = SnapCloud.username,
+ if (proj.projectname === ide.projectName) {
+ var usr = ide.cloud.username,
projectId = 'Username=' +
encodeURIComponent(usr.toLowerCase()) +
'&ProjectName=' +
- encodeURIComponent(proj.ProjectName);
+ encodeURIComponent(proj.projectname);
location.hash = 'present:' + projectId;
}
},
@@ -6341,38 +6557,118 @@ ProjectDialogMorph.prototype.unshareProject = function () {
proj = this.listField.selected,
entry = this.listField.active;
+ if (proj) {
+ this.ide.confirm(
+ localize(
+ 'Are you sure you want to unshare'
+ ) + '\n"' + proj.projectname + '"?',
+ 'Unshare Project',
+ function () {
+ ide.showMessage('unsharing\nproject...');
+ ide.cloud.unshareProject(
+ proj.projectname,
+ null, // username is implicit
+ function () {
+ proj.ispublic = false;
+ myself.shareButton.show();
+ myself.unshareButton.hide();
+ /*
+ myself.publishButton.hide();
+ myself.unpublishButton.hide();
+ */
+ entry.label.isBold = false;
+ entry.label.isItalic = false;
+ entry.label.drawNew();
+ entry.label.changed();
+ myself.buttons.fixLayout();
+ myself.drawNew();
+ myself.ide.showMessage('unshared.', 2);
+ if (proj.projectname === ide.projectName) {
+ location.hash = '';
+ }
+ },
+ myself.ide.cloudError()
+ );
+ }
+ );
+ }
+};
+
+ProjectDialogMorph.prototype.publishProject = function () {
+ var myself = this,
+ ide = this.ide,
+ proj = this.listField.selected,
+ entry = this.listField.active;
+
+ if (proj) {
+ this.ide.confirm(
+ localize(
+ 'Are you sure you want to publish'
+ ) + '\n"' + proj.projectname + '"?',
+ 'Publish Project',
+ function () {
+ ide.showMessage('publishing\nproject...');
+ ide.cloud.publishProject(
+ proj.projectname,
+ null, // username is implicit
+ function () {
+ proj.ispublished = true;
+ myself.unshareButton.show();
+ myself.shareButton.hide();
+ myself.publishButton.hide();
+ myself.unpublishButton.show();
+ entry.label.isItalic = true;
+ entry.label.drawNew();
+ entry.label.changed();
+ myself.buttons.fixLayout();
+ myself.drawNew();
+ myself.ide.showMessage('published.', 2);
+
+ // Set the Shared URL if the project is currently open
+ if (proj.projectname === ide.projectName) {
+ var usr = ide.cloud.username,
+ projectId = 'Username=' +
+ encodeURIComponent(usr.toLowerCase()) +
+ '&ProjectName=' +
+ encodeURIComponent(proj.projectname);
+ location.hash = 'present:' + projectId;
+ }
+ },
+ myself.ide.cloudError()
+ );
+ }
+ );
+ }
+};
+
+ProjectDialogMorph.prototype.unpublishProject = function () {
+ var myself = this,
+ proj = this.listField.selected,
+ entry = this.listField.active;
if (proj) {
this.ide.confirm(
localize(
'Are you sure you want to unpublish'
- ) + '\n"' + proj.ProjectName + '"?',
- 'Unshare Project',
+ ) + '\n"' + proj.projectname + '"?',
+ 'Unpublish Project',
function () {
- myself.ide.showMessage('unsharing\nproject...');
- SnapCloud.reconnect(
+ myself.ide.showMessage('unpublishing\nproject...');
+ myself.ide.cloud.unpublishProject(
+ proj.projectname,
+ null, // username is implicit
function () {
- SnapCloud.callService(
- 'unpublishProject',
- function () {
- SnapCloud.disconnect();
- proj.Public = 'false';
- myself.shareButton.show();
- myself.unshareButton.hide();
- entry.label.isBold = false;
- entry.label.drawNew();
- entry.label.changed();
- myself.buttons.fixLayout();
- myself.drawNew();
- myself.ide.showMessage('unshared.', 2);
- },
- myself.ide.cloudError(),
- [proj.ProjectName]
- );
- // Remove the shared URL if the project is open.
- if (proj.ProjectName === ide.projectName) {
- location.hash = '';
- }
+ proj.ispublished = false;
+ myself.unshareButton.show();
+ myself.shareButton.hide();
+ myself.publishButton.show();
+ myself.unpublishButton.hide();
+ entry.label.isItalic = false;
+ entry.label.drawNew();
+ entry.label.changed();
+ myself.buttons.fixLayout();
+ myself.drawNew();
+ myself.ide.showMessage('unpublished.', 2);
},
myself.ide.cloudError()
);
@@ -6465,6 +6761,285 @@ ProjectDialogMorph.prototype.fixLayout = function () {
this.changed();
};
+// ProjectRecoveryDialogMorph /////////////////////////////////////////
+// I show previous versions for a particular project and
+// let users recover them.
+
+ProjectRecoveryDialogMorph.prototype = new DialogBoxMorph();
+ProjectRecoveryDialogMorph.prototype.constructor = ProjectRecoveryDialogMorph;
+ProjectRecoveryDialogMorph.uber = DialogBoxMorph.prototype;
+
+// ProjectRecoveryDialogMorph instance creation:
+
+function ProjectRecoveryDialogMorph(ide, project, browser) {
+ this.init(ide, project, browser);
+}
+
+ProjectRecoveryDialogMorph.prototype.init = function (
+ ide,
+ projectName,
+ browser
+) {
+ // initialize inherited properties:
+ ProjectRecoveryDialogMorph.uber.init.call(
+ this,
+ this, // target
+ null, // function
+ null // environment
+ );
+
+ this.ide = ide;
+ this.browser = browser;
+ this.key = 'recoverProject';
+ this.projectName = projectName;
+
+ this.versions = null;
+
+ this.handle = null;
+ this.listField = null;
+ this.preview = null;
+ this.notesText = null;
+ this.notesField = null;
+
+ this.labelString = 'Recover project';
+ this.createLabel();
+
+ this.buildContents();
+};
+
+ProjectRecoveryDialogMorph.prototype.buildContents = function () {
+ this.addBody(new Morph());
+ this.body.color = this.color;
+
+ this.buildListField();
+
+ this.preview = new Morph();
+ this.preview.fixLayout = nop;
+ this.preview.edge = InputFieldMorph.prototype.edge;
+ this.preview.fontSize = InputFieldMorph.prototype.fontSize;
+ this.preview.typeInPadding = InputFieldMorph.prototype.typeInPadding;
+ this.preview.contrast = InputFieldMorph.prototype.contrast;
+ this.preview.drawNew = function () {
+ InputFieldMorph.prototype.drawNew.call(this);
+ if (this.texture) {
+ this.drawTexture(this.texture);
+ }
+ };
+ this.preview.drawCachedTexture = function () {
+ var context = this.image.getContext('2d');
+ context.drawImage(this.cachedTexture, this.edge, this.edge);
+ this.changed();
+ };
+ this.preview.drawRectBorder = InputFieldMorph.prototype.drawRectBorder;
+ this.preview.setExtent(
+ this.ide.serializer.thumbnailSize.add(this.preview.edge * 2)
+ );
+
+ this.body.add(this.preview);
+ this.preview.drawNew();
+
+ this.notesField = new ScrollFrameMorph();
+ this.notesField.fixLayout = nop;
+
+ this.notesField.edge = InputFieldMorph.prototype.edge;
+ this.notesField.fontSize = InputFieldMorph.prototype.fontSize;
+ this.notesField.typeInPadding = InputFieldMorph.prototype.typeInPadding;
+ this.notesField.contrast = InputFieldMorph.prototype.contrast;
+ this.notesField.drawNew = InputFieldMorph.prototype.drawNew;
+ this.notesField.drawRectBorder = InputFieldMorph.prototype.drawRectBorder;
+
+ this.notesField.acceptsDrops = false;
+ this.notesField.contents.acceptsDrops = false;
+
+ this.notesText = new TextMorph('');
+
+ this.notesField.isTextLineWrapping = true;
+ this.notesField.padding = 3;
+ this.notesField.setContents(this.notesText);
+ this.notesField.setWidth(this.preview.width());
+
+ this.body.add(this.notesField);
+
+ this.addButton('recoverProject', 'Recover');
+ this.addButton('cancel', 'Cancel');
+
+ this.setExtent(new Point(360, 300));
+ this.fixLayout();
+};
+
+ProjectRecoveryDialogMorph.prototype.buildListField = function () {
+ var myself = this;
+
+ this.listField = new ListMorph([]);
+ this.fixListFieldItemColors();
+ this.listField.fixLayout = nop;
+ this.listField.edge = InputFieldMorph.prototype.edge;
+ this.listField.fontSize = InputFieldMorph.prototype.fontSize;
+ this.listField.typeInPadding = InputFieldMorph.prototype.typeInPadding;
+ this.listField.contrast = InputFieldMorph.prototype.contrast;
+ this.listField.drawNew = InputFieldMorph.prototype.drawNew;
+ this.listField.drawRectBorder = InputFieldMorph.prototype.drawRectBorder;
+
+ this.listField.action = function (item) {
+ var version;
+ if (item === undefined) { return; }
+ version = detect(
+ myself.versions,
+ function (version) {
+ return version.lastupdated === item;
+ });
+ myself.notesText.text = version.notes || '';
+ myself.notesText.drawNew();
+ myself.notesField.contents.adjustBounds();
+ myself.preview.texture = version.thumbnail;
+ myself.preview.cachedTexture = null;
+ myself.preview.drawNew();
+ };
+
+ this.ide.cloud.getProjectVersionMetadata(
+ this.projectName,
+ function (versions) {
+ var today = new Date(),
+ yesterday = new Date();
+ yesterday.setDate(today.getDate() - 1);
+ myself.versions = versions;
+ myself.versions.forEach(function (version) {
+ var date = new Date(
+ new Date().getTime() - version.lastupdated * 1000
+ );
+ if (date.toDateString() === today.toDateString()) {
+ version.lastupdated = localize('Today, ') +
+ date.toLocaleTimeString();
+ } else if (date.toDateString() === yesterday.toDateString()) {
+ version.lastupdated = localize('Yesterday, ') +
+ date.toLocaleTimeString();
+ } else {
+ version.lastupdated = date.toLocaleString();
+ }
+ });
+ myself.listField.elements =
+ myself.versions.map(function (version) {
+ return version.lastupdated;
+ });
+ myself.clearDetails();
+ myself.listField.buildListContents();
+ myself.fixListFieldItemColors();
+ myself.listField.adjustScrollBars();
+ myself.listField.scrollY(myself.listField.top());
+ myself.fixLayout();
+ },
+ this.ide.cloudError()
+ );
+
+ this.body.add(this.listField);
+};
+
+ProjectRecoveryDialogMorph.prototype.cancel = function () {
+ var myself = this;
+ this.browser.show();
+ this.browser.listField.select(
+ detect(
+ this.browser.projectList,
+ function (item) {
+ return item.projectname === myself.projectName;
+ }
+ )
+ );
+ ProjectRecoveryDialogMorph.uber.cancel.call(this);
+};
+
+ProjectRecoveryDialogMorph.prototype.recoverProject = function () {
+ var lastupdated = this.listField.selected,
+ version = detect(
+ this.versions,
+ function (version) {
+ return version.lastupdated === lastupdated;
+ });
+
+ this.browser.openCloudProject(
+ {projectname: this.projectName},
+ version.delta
+ );
+ this.destroy();
+};
+
+ProjectRecoveryDialogMorph.prototype.popUp = function () {
+ var world = this.ide.world();
+ if (world) {
+ ProjectRecoveryDialogMorph.uber.popUp.call(this, world);
+ this.handle = new HandleMorph(
+ this,
+ 300,
+ 300,
+ this.corner,
+ this.corner
+ );
+ }
+};
+
+ProjectRecoveryDialogMorph.prototype.fixListFieldItemColors =
+ ProjectDialogMorph.prototype.fixListFieldItemColors;
+
+ProjectRecoveryDialogMorph.prototype.clearDetails =
+ ProjectDialogMorph.prototype.clearDetails;
+
+ProjectRecoveryDialogMorph.prototype.fixLayout = function () {
+ var titleHeight = fontHeight(this.titleFontSize) + this.titlePadding * 2,
+ thin = this.padding / 2,
+ oldFlag = Morph.prototype.trackChanges;
+
+ Morph.prototype.trackChanges = false;
+
+ if (this.body) {
+ this.body.setPosition(this.position().add(new Point(
+ this.padding,
+ titleHeight + this.padding
+ )));
+ this.body.setExtent(new Point(
+ this.width() - this.padding * 2,
+ this.height()
+ - this.padding * 3 // top, bottom and button padding.
+ - titleHeight
+ - this.buttons.height()
+ ));
+
+ this.listField.setWidth(
+ this.body.width()
+ - this.preview.width()
+ - this.padding
+ );
+ this.listField.contents.children[0].adjustWidths();
+
+ this.listField.setPosition(this.body.position());
+ this.listField.setHeight(this.body.height());
+
+ this.preview.setRight(this.body.right());
+ this.preview.setTop(this.listField.top());
+
+ this.notesField.setTop(this.preview.bottom() + thin);
+ this.notesField.setLeft(this.preview.left());
+ this.notesField.setHeight(
+ this.body.bottom() - this.preview.bottom() - thin
+ );
+ }
+
+ if (this.label) {
+ this.label.setCenter(this.center());
+ this.label.setTop(
+ this.top() + (titleHeight - this.label.height()) / 2
+ );
+ }
+
+ if (this.buttons) {
+ this.buttons.fixLayout();
+ this.buttons.setCenter(this.center());
+ this.buttons.setBottom(this.bottom() - this.padding);
+ }
+
+ Morph.prototype.trackChanges = oldFlag;
+ this.changed();
+};
+
// LibraryImportDialogMorph ///////////////////////////////////////////
// I am preview dialog shown before importing a library.
// I inherit from a DialogMorph but look similar to
@@ -6594,7 +7169,7 @@ LibraryImportDialogMorph.prototype.installLibrariesList = function () {
this.listField.action = function (item) {
if (isNil(item)) {return; }
- myself.notesText.text = item.description || '';
+ myself.notesText.text = localize(item.description || '');
myself.notesText.drawNew();
myself.notesField.contents.adjustBounds();
@@ -7203,22 +7778,26 @@ SpriteIconMorph.prototype.reactToDropOf = function (morph, hand) {
};
SpriteIconMorph.prototype.copyStack = function (block) {
- var dup = block.fullCopy(),
- y = Math.max(this.object.scripts.children.map(function (stack) {
+ var sprite = this.object,
+ dup = block.fullCopy(),
+ y = Math.max(sprite.scripts.children.map(function (stack) {
return stack.fullBounds().bottom();
- }).concat([this.object.scripts.top()]));
+ }).concat([sprite.scripts.top()]));
- dup.setPosition(new Point(this.object.scripts.left() + 20, y + 20));
- this.object.scripts.add(dup);
+ dup.setPosition(new Point(sprite.scripts.left() + 20, y + 20));
+ sprite.scripts.add(dup);
dup.allComments().forEach(function (comment) {
comment.align(dup);
});
- this.object.scripts.adjustBounds();
+ sprite.scripts.adjustBounds();
- // delete all custom blocks pointing to local definitions
- // under construction...
+ // delete all local custom blocks (methods) that the receiver
+ // doesn't understand
dup.allChildren().forEach(function (morph) {
- if (morph.definition && !morph.definition.isGlobal) {
+ if (morph.isCustomBlock &&
+ !morph.isGlobal &&
+ !sprite.getMethod(morph.blockSpec)
+ ) {
morph.deleteBlock();
}
});
@@ -7386,15 +7965,21 @@ CostumeIconMorph.prototype.userMenu = function () {
CostumeIconMorph.prototype.editCostume = function () {
this.disinherit();
- if (this.object instanceof SVG_Costume) {
- this.object.editRotationPointOnly(this.world());
- } else {
- this.object.edit(
- this.world(),
- this.parentThatIsA(IDE_Morph),
- false // not a new costume, retain existing rotation center
- );
+
+ if (this.object instanceof SVG_Costume && this.object.shapes.length === 0) {
+ try {
+ this.object.parseShapes();
+ } catch (e) {
+ this.editRotationPointOnly();
+ return;
+ }
}
+
+ this.object.edit(
+ this.world(),
+ this.parentThatIsA(IDE_Morph),
+ false // not a new costume, retain existing rotation center
+ );
};
CostumeIconMorph.prototype.editRotationPointOnly = function () {
@@ -7790,7 +8375,8 @@ WardrobeMorph.prototype.updateList = function () {
if (!CamSnapshotDialogMorph.prototype.enabled) {
cambutton.disable();
- cambutton.hint = CamSnapshotDialogMorph.prototype.notSupportedMessage;
+ cambutton.hint =
+ CamSnapshotDialogMorph.prototype.notSupportedMessage;
}
document.addEventListener(
@@ -7888,6 +8474,7 @@ WardrobeMorph.prototype.newFromCam = function () {
myself.updateList();
});
+ camDialog.key = 'camera';
camDialog.popUp(this.world());
};
@@ -8108,7 +8695,7 @@ SoundIconMorph.prototype.renameSound = function () {
SoundIconMorph.prototype.removeSound = function () {
var jukebox = this.parentThatIsA(JukeboxMorph),
- idx = this.parent.children.indexOf(this);
+ idx = this.parent.children.indexOf(this) - 1;
jukebox.removeSound(idx);
};
@@ -8177,7 +8764,9 @@ JukeboxMorph.prototype.updateList = function () {
oldFlag = Morph.prototype.trackChanges,
icon,
template,
- txt;
+ txt,
+ ide = this.sprite.parentThatIsA(IDE_Morph),
+ recordButton;
this.changed();
oldFlag = Morph.prototype.trackChanges;
@@ -8198,7 +8787,31 @@ JukeboxMorph.prototype.updateList = function () {
txt.setColor(SpriteMorph.prototype.paletteTextColor);
txt.setPosition(new Point(x, y));
this.addContents(txt);
- y = txt.bottom() + padding;
+
+ recordButton = new PushButtonMorph(
+ ide,
+ 'recordNewSound',
+ new SymbolMorph('circleSolid', 15)
+ );
+ recordButton.padding = 0;
+ recordButton.corner = 12;
+ recordButton.color = IDE_Morph.prototype.groupColor;
+ recordButton.highlightColor = IDE_Morph.prototype.frameColor.darker(50);
+ recordButton.pressColor = recordButton.highlightColor;
+ recordButton.labelMinExtent = new Point(36, 18);
+ recordButton.labelShadowOffset = new Point(-1, -1);
+ recordButton.labelShadowColor = recordButton.highlightColor;
+ recordButton.labelColor = TurtleIconMorph.prototype.labelColor;
+ recordButton.contrast = this.buttonContrast;
+ recordButton.drawNew();
+ recordButton.hint = 'Record a new sound';
+ recordButton.fixLayout();
+ recordButton.label.setColor(new Color(255, 20, 20));
+ recordButton.setPosition(txt.bottomLeft().add(new Point(0, padding * 2)));
+
+ this.addContents(recordButton);
+
+ y = recordButton.bottom() + padding;
this.sprite.sounds.asArray().forEach(function (sound) {
template = icon = new SoundIconMorph(sound, template);
@@ -8206,7 +8819,7 @@ JukeboxMorph.prototype.updateList = function () {
myself.addContents(icon);
y = icon.bottom() + padding;
});
- this.soundsVersion = this.sprite.costumes.lastChanged;
+ this.soundsVersion = this.sprite.sounds.lastChanged;
Morph.prototype.trackChanges = oldFlag;
this.changed();
@@ -8668,3 +9281,215 @@ CamSnapshotDialogMorph.prototype.close = function () {
}
CamSnapshotDialogMorph.uber.destroy.call(this);
};
+
+// SoundRecorderDialogMorph ////////////////////////////////////////////////////
+
+/*
+ I am a dialog morph that lets users record sound snippets for their
+ sprites or Stage.
+*/
+
+// SoundRecorderDialogMorph inherits from DialogBoxMorph:
+
+SoundRecorderDialogMorph.prototype = new DialogBoxMorph();
+SoundRecorderDialogMorph.prototype.constructor = SoundRecorderDialogMorph;
+SoundRecorderDialogMorph.uber = DialogBoxMorph.prototype;
+
+// SoundRecorderDialogMorph instance creation
+
+function SoundRecorderDialogMorph(onAccept) {
+ this.init(onAccept);
+}
+
+SoundRecorderDialogMorph.prototype.init = function (onAccept) {
+ var myself = this;
+ this.padding = 10;
+ this.accept = onAccept;
+
+ this.mediaRecorder = null; // an HTML5 MediaRecorder object
+ this.audioElement = document.createElement('audio');
+ this.audioElement.hidden = true;
+ this.audioElement.onended = function (event) {
+ myself.stop();
+ };
+ document.body.appendChild(this.audioElement);
+
+ this.recordButton = null;
+ this.stopButton = null;
+ this.playButton = null;
+ this.progressBar = new BoxMorph();
+
+ SoundRecorderDialogMorph.uber.init.call(this);
+ this.labelString = 'Sound Recorder';
+ this.createLabel();
+ this.buildContents();
+};
+
+SoundRecorderDialogMorph.prototype.buildContents = function () {
+ var myself = this,
+ audioChunks = [];
+
+ this.recordButton = new PushButtonMorph(
+ this,
+ 'record',
+ new SymbolMorph('circleSolid', 10)
+ );
+ this.recordButton.drawNew();
+ this.recordButton.fixLayout();
+
+ this.stopButton = new PushButtonMorph(
+ this,
+ 'stop',
+ new SymbolMorph('rectangleSolid', 10)
+ );
+ this.stopButton.drawNew();
+ this.stopButton.fixLayout();
+
+ this.playButton = new PushButtonMorph(
+ this,
+ 'play',
+ new SymbolMorph('pointRight', 10)
+ );
+ this.playButton.drawNew();
+ this.playButton.fixLayout();
+
+ this.buildProgressBar();
+
+ this.addBody(new AlignmentMorph('row', this.padding));
+ this.body.add(this.recordButton);
+ this.body.add(this.stopButton);
+ this.body.add(this.playButton);
+ this.body.add(this.progressBar);
+
+ this.body.fixLayout();
+
+ if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
+ navigator.mediaDevices.getUserMedia({ audio: true })
+ .then(function (stream) {
+ myself.mediaRecorder = new MediaRecorder(stream);
+ myself.mediaRecorder.ondataavailable = function (event) {
+ audioChunks.push(event.data);
+ };
+ myself.mediaRecorder.onstop = function (event) {
+ var buffer = new Blob(audioChunks),
+ reader = new window.FileReader();
+ reader.readAsDataURL(buffer);
+ reader.onloadend = function() {
+ var base64 = reader.result;
+ base64 = 'data:audio/ogg;base64,' +
+ base64.split(',')[1];
+ myself.audioElement.src = base64;
+ myself.audioElement.load();
+ audioChunks = [];
+ };
+ };
+ });
+ }
+
+ this.addButton('ok', 'Save');
+ this.addButton('cancel', 'Cancel');
+
+ this.fixLayout();
+ this.drawNew();
+};
+
+SoundRecorderDialogMorph.prototype.buildProgressBar = function () {
+ var line = new Morph(),
+ myself = this;
+
+ this.progressBar.setExtent(new Point(150, 20));
+ this.progressBar.setColor(new Color(200, 200, 200));
+ this.progressBar.setBorderWidth(1);
+ this.progressBar.setBorderColor(new Color(150, 150, 150));
+
+ line.setExtent(new Point(130, 2));
+ line.setColor(new Color(50, 50, 50));
+ line.setCenter(this.progressBar.center());
+ this.progressBar.add(line);
+
+ this.progressBar.indicator = new Morph();
+ this.progressBar.indicator.setExtent(new Point(5, 15));
+ this.progressBar.indicator.setColor(new Color(50, 200, 50));
+ this.progressBar.indicator.setCenter(line.leftCenter());
+
+ this.progressBar.add(this.progressBar.indicator);
+
+ this.progressBar.setPercentage = function (percentage) {
+ this.indicator.setLeft(
+ line.left() +
+ (line.width() / 100 * percentage) -
+ this.indicator.width() / 2
+ );
+ };
+
+ this.progressBar.step = function () {
+ if (myself.audioElement.duration) {
+ this.setPercentage(
+ myself.audioElement.currentTime /
+ myself.audioElement.duration * 100);
+ } else {
+ this.setPercentage(0);
+ }
+ };
+};
+
+SoundRecorderDialogMorph.prototype.record = function () {
+ if (this.mediaRecorder && this.mediaRecorder.state !== 'inactive') {
+ this.stop();
+ return;
+ }
+
+ this.mediaRecorder.start();
+ this.recordButton.label.setColor(new Color(255, 0, 0));
+ this.playButton.label.setColor(new Color(0, 0, 0));
+};
+
+SoundRecorderDialogMorph.prototype.stop = function () {
+ if (this.mediaRecorder && this.mediaRecorder.state !== 'inactive') {
+ this.mediaRecorder.stop();
+ }
+
+ this.audioElement.pause();
+ this.audioElement.currentTime = 0;
+
+ this.recordButton.label.setColor(new Color(0, 0, 0));
+ this.playButton.label.setColor(new Color(0, 0, 0));
+};
+
+SoundRecorderDialogMorph.prototype.play = function () {
+ this.stop();
+ this.audioElement.oncanplaythrough = function () {
+ this.play();
+ this.oncanplaythrough = nop;
+ };
+ this.playButton.label.setColor(new Color(0, 255, 0));
+};
+
+SoundRecorderDialogMorph.prototype.ok = function () {
+ var myself = this;
+ this.stop();
+ this.audioElement.oncanplaythrough = function () {
+ if (this.duration && this.duration !== Infinity) {
+ myself.accept(this);
+ this.oncanplaythrough = nop;
+ myself.destroy();
+ } else {
+ // For some reason, we need to play the sound
+ // at least once to get its duration.
+ myself.buttons.children.forEach(function (button) {
+ button.disable();
+ });
+ this.play();
+ }
+ };
+
+};
+
+SoundRecorderDialogMorph.prototype.destroy = function () {
+ this.stop();
+ this.audioElement.remove();
+ if (this.mediaRecorder) {
+ this.mediaRecorder.stream.getTracks()[0].stop();
+ }
+ SoundRecorderDialogMorph.uber.destroy.call(this);
+};
diff --git a/help/reportDistanceTo.png b/help/reportRelationTo.png
similarity index 100%
rename from help/reportDistanceTo.png
rename to help/reportRelationTo.png
diff --git a/history.txt b/history.txt
index c1b28eb0..4ae82c9f 100755
--- a/history.txt
+++ b/history.txt
@@ -1,4 +1,4 @@
-BYOB4 (Snap) history
+BYOB4 (Snap!) history
---------------------
110511
------
@@ -1626,7 +1626,7 @@ ______
130415
------
-* Blocks: place sticky comments on World layer on dragging their anchor block
+* Blocks: place sticky comments on World layer on dragging their anchor block
130416
------
@@ -1704,7 +1704,7 @@ ______
130514
------
* paint.js: Paint editor, first version, contributed by Kartik Chandra, Yay!!
-* Threads, Objects, Blocks: Broadcast & message enhancements: When I receive , and getLastMessage reporter + watcher
+* Threads, Objects, Blocks: Broadcast & message enhancements: When I receive , and getLastMessage reporter + watcher
130515
------
@@ -1726,7 +1726,7 @@ ______
130605
------
-* Objects: fix for hiding 'getLastAnswer' and 'getTimer' primitives
+* Objects: fix for hiding 'getLastAnswer' and 'getTimer' primitives
130606
------
@@ -1837,7 +1837,7 @@ ______
130801
------
* Blocks, Threads: "whitespace" & other options in SPLIT reporter's dropdown
-* Blocks: Italicize editable input options (e.g. for the SPLT block)
+* Blocks: Italicize editable input options (e.g. for the SPLT block)
* Blocks: Undrop Reporters feature (in script areas' context menus)
130802
@@ -2245,7 +2245,7 @@ ______
* Objects, GUI: duplicate and clone nested sprites
* GUI, Store: export and import nested sprites
* Objects: double clicking on a sprite in the stage selects it in the IDE
-* Objects: added ‘move’ option to the sprite context menu, lets the user move (nested) sprites in edit mode without changing their layering, and also sprites marked “undraggable”
+* Objects: added ‘move’ option to the sprite context menu, lets the user move (nested) sprites in edit mode without changing their layering, and also sprites marked “undraggable”
* updated Portuguese translation, thanks, Manuel!
* updated German translation
* Morphic: fixed #497 (prevent bubble shadows from getting cut-off)
@@ -2285,7 +2285,7 @@ ______
140930
------
-* Objects: fixed #593 match broadcast numbers with event hat blocks containing strings that can be parsed as numbers
+* Objects: fixed #593 match broadcast numbers with event hat blocks containing strings that can be parsed as numbers
* BYOB: allow percent symbols in custom block texts (fix #361), thanks, @Gubolin!!
* Morphic: allow negative min/max values for sliders (fix #285), thanks, @Gubolin!!
* Objects: fixed #378 (disable context menus for boolean representations)
@@ -2927,7 +2927,7 @@ http://snap.berkeley.edu/run#cloud:Username=jens&ProjectName=rotation
* Updated Simplified Chinese translation, thanks to @ubertao!
* Media import dialog with thumbnail, thanks to @ubertao!
-== v4.0.7.2 ====
+== v4.0.7.2 ====
160714
------
@@ -3050,7 +3050,7 @@ http://snap.berkeley.edu/run#cloud:Username=jens&ProjectName=rotation
160924
------
-* don’t update the recursion cache when updating a custom block definition
+* don’t update the recursion cache when updating a custom block definition
160929
------
@@ -3167,7 +3167,7 @@ http://snap.berkeley.edu/run#cloud:Username=jens&ProjectName=rotation
161206
------
-* GUI: Switch to asynchronous loading of resources (costumes, sounds, libraries etc.)
+* GUI: Switch to asynchronous loading of resources (costumes, sounds, libraries etc.)
* Morphic: Added support for dropping links to SVGs from other web pages onto the World
* GUI: Support importing unrasterized SVG_Costumes from the “Costumes” and “Backgrounds” dialog
@@ -3348,7 +3348,7 @@ Fixes:
170201
------
-* GUI: let costume icons indicate svg costumes
+* GUI: let costume icons indicate svg costumes
170202
------
@@ -3508,7 +3508,7 @@ Fixes:
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
+* 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
@@ -3518,7 +3518,7 @@ Fixes:
170709
------
-* Objects, Threads: added experimental (only shown in dev mode) “tell ... to ..." and “ask ... for ...” primitives
+* Objects, Threads: added experimental (only shown in dev mode) “tell ... to ..." and “ask ... for ...” primitives
170711
------
@@ -3759,14 +3759,14 @@ v4.1 Features:
Fixes:
* changed keyboard shortcut indicator for “find blocks” to “^”
-* prevent Snap from “hanging” when encountering certain errors in visible stepping
+* 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 occasional empty drop-down menu items named “close”
+* fixed occasional empty drop-down menu items named “close”
* fixed some typos
* limited sprites' direction and coordinates to finite numbers
* made block vars transient for block libraries
@@ -3787,3 +3787,469 @@ Fixes:
=== v4.1.0.2 maintenance release ===
+171115
+------
+* Portuguese & Polish translation updates, thanks, Witek and Manuel!!
+* escape xml attribute contents, thanks, Brian Broll!
+* changed minimum stage width to 240
+* new Audio Comp library for Guzdial-style sound samples fun
+
+=== v4.1.0.3 maintenance release ===
+
+171116
+------
+* Threads: suppress "exit" context when forking a process while single-stepping, this avoids a false "reporter didn't report" error message
+* Blocks: avoid coloring the block-highlight when re-coloring a syntax element, this prevents highlighted blocks inside LAUNCH statements to expand when repeatedly single-stepped.
+
+=== v4.1.0.4 maintenance release ===
+
+171126
+------
+* GUI: fixed #1933 - avoid creating "obsolete" blocks by not copying method blocks into sprites that don't understand them
+* Store: fixed #1937 - allow stage width to be a minimum of 240 pixels
+
+=== v4.1.0.5 maintenance release ===
+
+171201
+------
+* GUI: started development on v 4.1.1
+* BYOB, Store, Threads: Localization support for custom blocks (experimental)
+* Tools: German translation of tools (experimental)
+
+171212
+------
+* fixed #1963
+
+180102
+------
+* new "direction to..." primitive as variant of "distance to..." in "Sensing"
+
+180104
+------
+* Morphic: scroll menus if they are taller than the world
+* Morphic: added keyboard navigation for menus that scroll
+* added "width" and "height" selectors to Pixels library
+
+180104
+------
+* Objects: fixed #1979 - make sure to always re-focus the world-canvas
+
+180117
+------
+* Objects: made keyboard events always be thread safe (same as in Scratch nowadays)
+
+180118
+------
+* Blocks, Threads, BYOB, Store: included local methods in the OF-block's left drop-down menu
+
+180119
+------
+* merged a bunch of pull requests (unicode support for emojis, translation updates)
+
+180121
+------
+* Threads: fixed a scope-glitch in the new OF-block's drop-down mechanism
+* Blocks: made the OF-block auto-unringify when dropped on ring-slots
+* Blocks: disabled firing the Custom-Block-Dialog when accidentall clicking on a custom block definition script
+
+180122
+------
+* Morphic: fixed occasional stuck cursors, thanks, Bernat!
+* Paint: fixed a flood-fill alpha issue, thanks, Bernat!I
+* Blocks, GUI: minor fixes, contributed by the community
+* various Translation updates, contributed by the community
+* Blocks, Objects, Threads: separated global and local variables in the palette, marked local ones with location pin
+* Blocks, Objects: added scroll events, thanks, Bernat!
+
+180123
+------
+* fixed #1972, thanks, Joan!
+* Objects, GUI: When deleting a temporary clone, detach all its parts and delete the temporary ones
+
+180125
+------
+* Morphic: new DialMorph widget
+* Blocks: added dial widget to POINT IN DIRECTION's drop-down menu
+* Objects: added "rotate" option to Sprite context menu
+* Threads, Blocks: fixed Joan's fix for #1972, because it broke HOFs
+* new Sound Recorder, yay!! Thanks, Bernat!
+* Blocks: fixed a glitch in the error-bubble handling mechanism
+
+180201
+------
+* GUI: encode recorded sounds to base64
+* snap.html: added version queries to script urls
+
+180202
+------
+* Libraries: Crayons library, thanks, Brian!
+
+180205
+------
+* Russian translation update, thanks, temap!
+* release
+
+=== v4.1.1 minor release ===
+
+v4.1.1 New Features:
+* translation support for custom blocks
+* new "direction to..." primitive as variant of "distance to..." in "Sensing"
+* included local methods in the OF-block's left drop-down menu
+* added "width" and "height" selectors to Pixels library
+* added scroll events, thanks, Bernat!
+* new dial widget POINT IN DIRECTION's drop-down menu
+* new "rotate" option for sprite context menu
+* new sound recorder, thanks, Bernat!
+* new "Crayons" library, thanks, Brian!
+
+Notable Changes:
+* global and local variables are now separat in the palette, each sorted alphabetically, local vars marked with location pin (only in palette)
+* keyboard events are now always thread safe (the same as in Scratch nowadays)
+* the OF-block auto-unringifies when being dropped on ring-slots, such as in CALL
+* accidentally clicking on a custom block definition no longer fires up the Block Dialog
+
+Notable Fixes:
+* scroll menus if they are taller than the world
+* enabled color picker for pen trails on stage
+* track keyboard events after accepting ASK using the keyboard
+* improved support for emojis, thanks, Michael!
+* avoid occasional stuck text cursors, thanks, Bernat!
+* paint editor flood fill alpha issue, thanks, Bernat!
+* implicit parameter binding in visible stepping, thanks, Joan!
+* when deleting a temporary clone, detach all its parts and delete the temporary ones
+* new release protocol to avoid browser caching related version conflicts
+
+Translation Updates:
+* German
+* Greek
+* Turkish
+* Chinese
+* Spanish
+* Russian
+
+180206
+------
+* GUI: start developing v4.1.2
+* Morphic: roll back temporary rectangle filling workaround for a bug in Chrome v57
+
+180208
+------
+* Cloud, GUI, Widgets: New Cloud API, thanks, Bernat!
+* GUI: fixed a url-bar refresh bug introduced by the new cloud mechanism
+* GUI: made sure user names are lower case when sent to the cloud
+* Cloud: made sure project thumbnails are normalized when saved
+* Cloud: warn user if overwriting an existing project with another one
+
+180209
+------
+* Store, GUI: small tweaks
+* new Valencian Catalan translation, thanks, Jose A. Múrcia!!
+
+180212
+------
+* Threads: Allow JS-functions for invoke()
+* Threads, Objects: Small compilation experiment
+
+180215
+------
+* Threads, Blocks, Objects: experimental JIT compiler
+
+180217
+------
+release
+
+=== v4.1.2 minor release ===
+
+v4.1.2 Notable Changes:
+* new cloud backend
+
+New Features:
+* experimental JIT compiler (in progress)
+
+Translation Updates:
+* new Catalan-Valencia translation
+* Catalan
+* German
+
+180219
+------
+* GUI, snap.html: started v4.1.2.1 development
+* Threads: optimized "broadcast and wait" for atomic subroutines
+* Spanish translation update
+
+180220
+------
+* Libraries: Changed LeapMotion library source to https
+* account verification
+* release
+
+=== v4.1.2.1 maintenance release ===
+
+v4.1.2.1 Notable Changes:
+* account verification
+* optimized "broadcast and wait" for atomic subroutines
+* changed leap motion library to https
+
+Translation Updates:
+* Spanish
+
+180222
+------
+* crayons library: fixed "nearest crayon to" reporter
+* release
+
+=== v4.1.2.2 maintenance release ===
+
+180305
+------
+* cloud tweaks, thanks, Bernat and Michael!
+* fixed "join words" in the tools, library, thanks, Brian, for reporting the bug!
+* added new "text to speech" library
+* made sure sound data is always stored in the project (not referenced)
+* added capability to compile input slot options to experimental JIT
+* Spanish and German translation updates
+
+=== v4.1.2.3 maintenance release ===
+
+180308
+------
+* Objects: fixed #2053
+* GUI: fixed #2052
+
+180309
+------
+* Blocks, Objects, Threads: added "random" option for "go to", "point towards" and "point in direction" primitives
+
+=== v4.1.2.4 maintenance release ===
+
+180313
+------
+* Objects: draw a "dot" pentrail when moving zero steps while the pen is down
+
+=== v4.1.2.5 maintenance release ===
+
+180314
+------
+* Threads: changed testing order for type inferral, speeds up list operations significantly
+* Cloud: remix project method, thanks, Bernat!
+
+=== v4.1.2.6 maintenance release ===
+
+180316
+------
+* Threads: experimental JIT compiler support for multi-word formal parameters and a single implicit formal parameter mapped to all empty input slots
+
+180319
+------
+* Threads: initialize Process>>gensyms with null (because it's hardly ever needed)
+* Objects: remove obsolete STOP primitive from the stage's palette
+
+=== v4.1.2.7 maintenance release ===
+
+180319
+------
+* new Vector Paint Editor, thanks, Carles Paredes and Bernat Romagosa!
+
+180320
+------
+* Threads: refactored experimental JS-compiler
+* Threads: enabled variables access for experimental JS-compiler
+
+180322
+------
+* Threads: extended implicit parameters handling for experimental JS-Compiler
+* Threads: new experimental atomic HOFs utilizing JIT compilation (MAP, KEEP, SORT)
+* new experimental "Big Data" library using JIT compiler
+
+180323
+------
+* Threads: new experimental atomic COMBINE utilizing JIT compiler
+* added atomic COMBINE to new experimental "Bigger Data" library
+* removed unused blocks from the audio comp library
+* added and removed atomic FOR EACH to new experimental "Bigger Data" library
+
+180412
+------
+* Threads: disable detecting collision with hidden sprites
+
+180413
+------
+* Objects: added implicit parameter count to experimental JIT compile reporter
+
+180416
+------
+* Blocks: only preserve filled rings when collapsing ring-typed multi-arg-slots
+* Blocks: minor tweaks
+
+180424
+------
+* added 'name' selector to pixel library
+
+180425
+------
+* GUI: fixed rearranging sound icons in the jukebox
+* GUI: fixed scrolling for the jukebox (updating the sounds list version)
+* GUI: only randomize position when shift-clicking on new turtle-sprite button
+
+180427
+------
+* GUI: when creating a new sprite only randomize color and direction when shift-clicking
+
+180502
+------
+* Blocks, Threads: added "center" to drop-down options of location blocks (GO TO, POINT TOWARDS, DISTANCE TO and DIRECTION TO)
+* updated German translation
+* disabled keyboard shortcuts for green-flag (cmd-enter) and stop (esc) in presentation mode
+* Blocks, Threads: added options for sprite attributes to the SET block
+
+180503
+------
+* GUI: (again) randomize pen color when creating a new sprite
+
+180508
+------
+* Threads: tweaked JS-Compiler to better handle process related ops
+
+180524
+------
+* Blocks: fixed rendering and layout of variadic C-shaped input slots
+
+180604
+------
+* Blocks: tweaked layout of variadic C-shaped input slots
+
+180605
+------
+* VectorPaint: fixed rotation center editing for existing costumes
+* VectorPaint: fixed initial rendering, so costumes can be re-opened after saving
+* Symbols: fixed 'polygon' symbol rendering
+
+180606
+------
+* updated German translation, thanks, Jadga!
+* updated Portuguese translation, thanks, Manuel!
+* new Project Cloud Backups feature, thanks, Bernat!
+* BYOB, Blocks, Threads, Store: fixed support for numerical custom block input names
+
+180608
+------
+* Blocks, Objects: new experimental "When I am stopped" event option
+* Threads: Prevent terminated threads from forking new ones and from cloning
+
+180609
+------
+* Objects, Threads: Also trigger "When I am stopped" when programmatically calling "stop all"
+
+180611
+------
+* Objects, Threads: fixed #2108 (added drop-down menu to "letter _ of _ ")
+* German translation update
+
+180612
+------
+* Renamed vectorPaint.js to sketch.js
+* GUI: updated credits for Carles Paredes
+* "Pixels" library: Enabled multiple references to the same pixel (variable)
+
+180614
+------
+* Threads: Prevent terminated threads from launching new ones
+* prepared v4.2 rc
+
+180615
+------
+* BYOB: fixed #2043 (regression)
+
+180617
+------
+* GUI: fixed cloud scope issues
+
+180618
+------
+* Threads: added capability to JIT-compile command scripts to JS
+
+180620
+------
+* Sketch: enable right-click to select secondary color in vector paint editor
+* GUI: allow only one instance of Camera and Sound Recorder to open
+* new "webcam snap" reporter in the "Pixels" library
+* new "record" reporter in the "Audio Comp" library
+
+180621
+------
+* Threads, Objects: made "When I am stopped" scripts atomic, so you can use loops
+
+=== v4.2 major release ===
+
+v4.2 New Features:
+* "recover project" feature, (cloud backups), thanks, Bernat Romagosa!
+* vector paint editor, thanks, Carles Paredes and Bernat Romagosa!
+* "When I am stopped" event option, runs one atomic frame before terminating, use-case: stop robots when a user hits the stop button
+* experimental JIT compiler for atomic HOFs, used in new "Bigger Data" library
+* new library for programmatically creating variables, thanks, Brian Harvey!
+* added options for sprite attributes to the SET block
+* new "webcam snap" reporter in the "Pixels" library
+* new "record" reporter in the "Audio Comp" library
+* added "name" selector to the "Pixels" library
+* added drop-down menu to "letter _ of _ ", adjusted all translations (thanks, Joan!)
+
+Notable Changes:
+* hidden sprites can no longer be collision detected (but can test for other sprites)
+* new sprites created by pressing the arrow button no point in random directions (unless you hold down the shift-key)
+* new "center" option for location blocks (GO TO, POINT TOWARDS, DISTANCE TO and DIRECTION TO)
+* disabled keyboard shortcuts for green-flag (cmd-enter) and stop (esc) in presentation mode
+
+Notable Fixes:
+* rearranging and scrolling sound icons
+* rendering and layout of variadic C-shaped input slots
+* when collapsing ring-typed multi-arg slots only filled rings are preserved
+* support for numerical custom block input names
+* no more "leftover" clones when pressing the stop button or executing the STOP block
+
+Translation Updates:
+* German, thanks, Jadga!
+* Portuguese, thanks, Manuel!
+* Catalan, thanks, Joan!
+
+=== in development ===
+
+180703
+------
+* speed up HTTP based hardware APIs (by not waiting for the result if the URL reporter is used inside a REPORT block within a custom COMMAND block definition)
+
+180705
+------
+* Threads: added JIT compiler support for "change variable" primitive
+* Threads: optimized RUN with reportURL (fire-and-forget)
+
+180706
+------
+* Objects: fixed #2142 - search and keyboard entry support for custom block translations
+
+180709
+------
+* Portuguese translation update, thanks, Manuel!
+* New Basque translation, thanks, Asier Iturralde Sarasola!
+* French translation update, thanks, Nathalie and Joan!
+* Spanish, Catalan and French translations of the tools library, thanks, Joan!
+* New JSON library, thanks, Bernat!
+* URL cache issue fix, thanks, Joan!
+
+=== v4.2.1 minor release ===
+
+v4.2.1 New Features:
+* new libraries for parallelization and JSON support
+* new "loudness" reporter in audio comp library, thanks, Bernat!
+
+Notable Changes:
+* significant speed-up for HTTP based robot APIs such as the Hummingbird kit
+
+Notable Fixes:
+* "When I am stopped" hat block now also works for stacks of HTTP based robot commands
+* resolved name conflicts in pixels and audio comp libraries
+
+Translation Updates:
+* New Basque translation, thanks, Asier Iturralde Sarasola!
+* Portuguese, thanks, Manuel!
+* French, thanks, Nathalie and Joan!
+* Spanish, Catalan and French translations of the tools library, thanks, Nathalie and Joan!
diff --git a/lang-ar.js b/lang-ar.js
index 62fbb749..fabac064 100755
--- a/lang-ar.js
+++ b/lang-ar.js
@@ -565,8 +565,8 @@ SnapTranslator.dict.ar = {
'مرحبا',
'world':
'ايها العالم',
- 'letter %n of %s':
- '%n الحرف أوجد %s العبارة من',
+ 'letter %idx of %s':
+ '%idx الحرف أوجد %s العبارة من',
'length of %s':
'%s أحرف عدد',
'unicode of %s':
diff --git a/lang-bg.js b/lang-bg.js
index c8c0372b..0ac556ac 100644
--- a/lang-bg.js
+++ b/lang-bg.js
@@ -519,8 +519,8 @@ SnapTranslator.dict.bg = {
'здравейте',
'world':
'хора',
- 'letter %n of %s':
- 'буква %n от %s',
+ 'letter %idx of %s':
+ 'буква %idx от %s',
'length of %s':
'дължина на %s',
'unicode of %s':
diff --git a/lang-bn.js b/lang-bn.js
index 2d734d2a..6a359ce7 100644
--- a/lang-bn.js
+++ b/lang-bn.js
@@ -552,8 +552,8 @@ SnapTranslator.dict.bn = {
'হ্যালো',
'world':
'পৃথিবী',
- 'letter %n of %s':
- '%n -তম বর্ণ %s এর',
+ 'letter %idx of %s':
+ '%idx -তম বর্ণ %s এর',
'length of %s':
'%s এর বর্ণদৈর্ঘ্য ',
'unicode of %s':
diff --git a/lang-ca.js b/lang-ca.js
index dac1dd3f..de52ab2c 100644
--- a/lang-ca.js
+++ b/lang-ca.js
@@ -183,9 +183,9 @@ SnapTranslator.dict.ca = {
'language_translator':
'Bernat Romagosa Carrasquer, Joan Guillén i Pelegay', // your name for the Translators tab
'translator_e-mail':
- 'bernat@arduino.org, jguille2@xtec.cat', // optional
+ 'bernat@snap4arduino.rocks, jguille2@xtec.cat', // optional
'last_changed':
- '2017-01-09', // this, too, will appear in the Translators tab
+ '2018-06-16', // this, too, will appear in the Translators tab
// GUI
// control bar:
@@ -590,8 +590,8 @@ SnapTranslator.dict.ca = {
'hola',
'world':
'món',
- 'letter %n of %s':
- 'lletra %n de %s',
+ 'letter %idx of %s':
+ 'lletra %idx de %s',
'length of %s':
'longitud de %s',
'unicode of %s':
@@ -1074,6 +1074,12 @@ SnapTranslator.dict.ca = {
'Segur que vols esborrar',
'rename...':
'canvia el nom...',
+ 'Recover':
+ 'Recupera',
+ 'Today, ':
+ 'Avui, ',
+ 'Yesterday, ':
+ 'Ahir, ',
// costume editor
'Costume Editor':
@@ -1578,7 +1584,7 @@ SnapTranslator.dict.ca = {
'Logout':
'Surt',
'Change Password...':
- 'Canvia la contrassenya…',
+ 'Canvia la contrasenya…',
'Change Password':
'Canvia la contrasenya',
'Account created.':
@@ -2238,5 +2244,51 @@ SnapTranslator.dict.ca = {
'take a camera snapshot and\nimport it as a new sprite':
'pren una imatge amb la càmera\ni importa-la com un nou vestit',
'Import a new costume from your webcam':
- 'Importa un nou vestit amb la webcam'
+ 'Importa un nou vestit amb la webcam',
+ 'random':
+ 'qualsevol',
+ 'random position':
+ 'qualsevol posició',
+ 'center':
+ 'centre',
+ '%rel to %dst':
+ '%rel a %dst',
+ 'distance':
+ 'distància',
+ 'costume':
+ 'vestit',
+ 'sound':
+ 'so',
+ 'Record a new sound':
+ 'Grava un so nou',
+ 'Sound Recorder':
+ 'Gravadora de So',
+ 'recording':
+ 'gravació',
+ 'JIT compiler support':
+ 'Suport a la compilació JIT',
+ 'EXPERIMENTAL! uncheck to disable live\nsupport for compiling':
+ 'EXPERIMENTAL! Desmarqueu per deshabilitar el\nsuport a la compilació dinàmica',
+ 'EXPERIMENTAL! check to enable\nsupport for compiling':
+ 'EXPERIMENTAL! Marqueu per habilitar\nel suport a la compilació',
+ 'compile %repRing for %n args':
+ 'compila %repRing per %n arguments',
+ 'rotate':
+ 'gira',
+ 'stopped':
+ 'pari',
+ 'scrolled-up':
+ 'faci scroll amunt',
+ 'scrolled-down':
+ 'faci scroll avall',
+ 'Resend Verification Email...':
+ 'Torna a enviar l\'email de verificació...',
+ 'Resend verification email':
+ 'Reenviament del mail',
+ 'User name:':
+ 'Nom d\'usuari:',
+ 'Camera not supported':
+ 'Webcam no disponible',
+ 'Please make sure your web browser is up to date\nand your camera is properly configured. \n\nSome browsers also require you to access Snap!\nthrough HTTPS to use the camera.\n\nPlase replace the "http://" part of the address\nin your browser by "https://" and try again.':
+ 'Comproveu que el navegador està actualitzat\ni la webcam ben configurada. \n\nAlguns navegadors també requereixen\nHTTPS per a utilitzar la càmera.\n\nPodeu provar canviant a l\'adreça el "http://"\nper "https://".'
};
diff --git a/lang-ca_VA.js b/lang-ca_VA.js
new file mode 100644
index 00000000..9c525c23
--- /dev/null
+++ b/lang-ca_VA.js
@@ -0,0 +1,1394 @@
+/*
+
+ lang-ca-valencia.js
+
+ Valencian translation for SNAP!
+
+ written by Jens Mönig
+
+ Copyright (C) 2016 by Jens Mönig
+
+ This file is part of Snap!.
+
+ Snap! is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as
+ published by the Free Software Foundation, either version 3 of
+ the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see .
+
+
+
+ Note to Translators:
+ --------------------
+ At this stage of development, Snap! can be translated to any LTR language
+ maintaining the current order of inputs (formal parameters in blocks).
+
+ Translating Snap! is easy:
+
+
+ 1. Download
+
+ Download the sources and extract them into a local folder on your
+ computer:
+
+
+
+ Use the German translation file (named 'lang-de.js') as template for your
+ own translations. Start with editing the original file, because that way
+ you will be able to immediately check the results in your browsers while
+ you're working on your translation (keep the local copy of snap.html open
+ in your web browser, and refresh it as you progress with your
+ translation).
+
+
+ 2. Edit
+
+ Edit the translation file with a regular text editor, or with your
+ favorite JavaScript editor.
+
+ In the first non-commented line (the one right below this
+ note) replace "de" with the two-letter ISO 639-1 code for your language,
+ e.g.
+
+ fr - French => SnapTranslator.dict.fr = {
+ it - Italian => SnapTranslator.dict.it = {
+ pl - Polish => SnapTranslator.dict.pl = {
+ pt - Portuguese => SnapTranslator.dict.pt = {
+ es - Spanish => SnapTranslator.dict.es = {
+ el - Greek => => SnapTranslator.dict.el = {
+
+ etc. (see )
+
+
+ 3. Translate
+
+ Then work through the dictionary, replacing the German strings against
+ your translations. The dictionary is a straight-forward JavaScript ad-hoc
+ object, for review purposes it should be formatted as follows:
+
+ {
+ 'English string':
+ 'Translation string',
+ 'last key':
+ } 'last value'
+
+ and you only edit the indented value strings. Note that each key-value
+ pair needs to be delimited by a comma, but that there shouldn't be a comma
+ after the last pair (again, just overwrite the template file and you'll be
+ fine).
+
+ If something doesn't work, or if you're unsure about the formalities you
+ should check your file with
+
+
+
+ This will inform you about any missed commas etc.
+
+
+ 4. Accented characters
+
+ Depending on which text editor and which file encoding you use you can
+ directly enter special characters (e.g. Umlaut, accented characters) on
+ your keyboard. However, I've noticed that some browsers may not display
+ special characters correctly, even if other browsers do. So it's best to
+ check your results in several browsers. If you want to be on the safe
+ side, it's even better to escape these characters using Unicode.
+
+ see:
+
+
+ 5. Block specs:
+
+ At this time your translation of block specs will only work
+ correctly, if the order of formal parameters and their types
+ are unchanged. Placeholders for inputs (formal parameters) are
+ indicated by a preceding % prefix and followed by a type
+ abbreviation.
+
+ For example:
+
+ 'say %s for %n secs'
+
+ can currently not be changed into
+
+ 'say %n secs long %s'
+
+ and still work as intended.
+
+ Similarly
+
+ 'point towards %dst'
+
+ cannot be changed into
+
+ 'point towards %cst'
+
+ without breaking its functionality.
+
+
+ 6. Submit
+
+ When you're done, rename the edited file by replacing the "de" part of the
+ filename with the two-letter ISO 639-1 code for your language, e.g.
+
+ fr - French => lang-fr.js
+ it - Italian => lang-it.js
+ pl - Polish => lang-pl.js
+ pt - Portuguese => lang-pt.js
+ es - Spanish => lang-es.js
+ el - Greek => => lang-el.js
+
+ and send it to me for inclusion in the official Snap! distribution.
+ Once your translation has been included, Your name will the shown in the
+ "Translators" tab in the "About Snap!" dialog box, and you will be able to
+ directly launch a translated version of Snap! in your browser by appending
+
+ lang:xx
+
+ to the URL, xx representing your translations two-letter code.
+
+
+ 7. Known issues
+
+ In some browsers accents or ornaments located in typographic ascenders
+ above the cap height are currently (partially) cut-off.
+
+ Enjoy!
+ -Jens
+*/
+
+/*global SnapTranslator*/
+
+SnapTranslator.dict.ca_VA = {
+
+/*
+ Special characters: (see )
+
+ Ä, ä \u00c4, \u00e4
+ Ö, ö \u00d6, \u00f6
+ Ü, ü \u00dc, \u00fc
+ ß \u00df
+*/
+
+ // translations meta information
+ 'language_name':
+ 'Català - Valencià', // the name as it should appear in the language menu
+ 'language_translator':
+ 'Bernat Romagosa Carrasquer, Joan Guillén i Pelegay, Pilar Embid', // your name for the Translators tab
+ 'translator_e-mail':
+ 'bernat@snap4arduino.rocks, jguille2@xtec.cat, embid_mar@gva.es', // optional
+ 'last_changed':
+ '2018-02-08', // this, too, will appear in the Translators tab
+
+ // GUI
+ // control bar:
+ 'untitled':
+ 'Sense títol',
+ 'development mode':
+ 'mode de desenvolupament',
+
+ // categories:
+ 'Motion':
+ 'Moviment',
+ 'Looks':
+ 'Aparença',
+ 'Sound':
+ 'So',
+ 'Pen':
+ 'Llapis',
+ 'Control':
+ 'Control',
+ 'Sensing':
+ 'Sensors',
+ 'Operators':
+ 'Operadors',
+ 'Variables':
+ 'Variables',
+ 'Lists':
+ 'Llistes',
+ 'Other':
+ 'Altres',
+
+ // editor:
+ 'draggable':
+ 'arrossegable',
+
+ // tabs:
+ 'Scripts':
+ 'Programes',
+ 'Costumes':
+ 'Vestits',
+ 'Sounds':
+ 'Sons',
+
+ // names:
+ 'Sprite':
+ 'Objecte',
+ 'Stage':
+ 'Escenari',
+
+ // rotation styles:
+ 'don\'t rotate':
+ 'no gira',
+ 'can rotate':
+ 'pot girar',
+ 'only face left/right':
+ 'només mira a esquerra/dreta',
+
+ // new sprite button:
+ 'add a new sprite':
+ 'afig un nou objecte',
+
+ // tab help
+ 'costumes tab help':
+ 'podeu importar una imatge des d\'un altre lloc web o des del\n'
+ + 'vostre ordinador arrossegant-la fins ací',
+ 'import a sound from your computer\nby dragging it into here':
+ 'podeu importar un so des del vostre ordinador\narrossegant-lo fins ací',
+
+ // primitive blocks:
+
+ /*
+ Attention Translators:
+ ----------------------
+ At this time your translation of block specs will only work
+ correctly, if the order of formal parameters and their types
+ are unchanged. Placeholders for inputs (formal parameters) are
+ indicated by a preceding % prefix and followed by a type
+ abbreviation.
+
+ For example:
+
+ 'say %s for %n secs'
+
+ can currently not be changed into
+
+ 'say %n secs long %s'
+
+ and still work as intended.
+
+ Similarly
+
+ 'point towards %dst'
+
+ cannot be changed into
+
+ 'point towards %cst'
+
+ without breaking its functionality.
+ */
+
+ // motion:
+ 'Stage selected:\nno motion primitives':
+ 'Escenari seleccionat:\nno hi ha primitives de moviment\n'
+ + 'disponibles',
+
+ 'move %n steps':
+ 'mou-te %n passos',
+ 'turn %clockwise %n degrees':
+ 'gira %clockwise %n graus',
+ 'turn %counterclockwise %n degrees':
+ 'gira %counterclockwise %n graus',
+ 'point in direction %dir':
+ 'apunta en direcció %dir',
+ 'point towards %dst':
+ 'apunta cap a %dst',
+ 'go to x: %n y: %n':
+ 'vés a x: %n y: %n',
+ 'go to %dst':
+ 'vés a %dst',
+ 'glide %n secs to x: %n y: %n':
+ 'llisca en %n segons fins a x: %n y: %n',
+ 'change x by %n':
+ 'suma %n a x',
+ 'set x to %n':
+ 'assigna el valor %n a x',
+ 'change y by %n':
+ 'suma %n a y',
+ 'set y to %n':
+ 'assigna el valor %n a y',
+ 'if on edge, bounce':
+ 'rebota en tocar una vora',
+ 'x position':
+ 'posició x',
+ 'y position':
+ 'posició y',
+ 'direction':
+ 'direcció',
+
+ // looks:
+ 'switch to costume %cst':
+ 'canvia el vestit a %cst',
+ 'next costume':
+ 'següent vestit',
+ 'costume #':
+ 'número de vestit',
+ 'say %s for %n secs':
+ 'digues %s durant %n segons',
+ 'say %s':
+ 'digues %s',
+ 'think %s for %n secs':
+ 'pensa %s durant %n segons',
+ 'think %s':
+ 'pensa %s',
+ 'Hello!':
+ 'Hola!',
+ 'Hmm...':
+ 'Hmm...',
+ 'change %eff effect by %n':
+ 'augmenta l\'efecte %eff en %n',
+ 'set %eff effect to %n':
+ 'fixa l\'efecte %eff a %n',
+ 'clear graphic effects':
+ 'suprimeix els efectes gràfics',
+ 'change size by %n':
+ 'augmenta %n la mida',
+ 'set size to %n %':
+ 'fixa la mida a %n %',
+ 'size':
+ 'mida',
+ 'show':
+ 'mostra',
+ 'hide':
+ 'amaga',
+ 'go to front':
+ 'vés al front',
+ 'go back %n layers':
+ 'vés %n capes darrere',
+
+ 'development mode \ndebugging primitives:':
+ 'mode de desenvolupament \nprimitives de depuració',
+ 'console log %mult%s':
+ 'registre per consola: %mult%s',
+ 'alert %mult%s':
+ 'avís: %mult%s',
+
+ // sound:
+ 'play sound %snd':
+ 'toca el so %snd',
+ 'play sound %snd until done':
+ 'toca el so %snd fins que acabe',
+ 'stop all sounds':
+ 'para tots els sons',
+ 'rest for %n beats':
+ 'fes silenci durant %n temps',
+ 'play note %n for %n beats':
+ 'toca la nota %n durant %n temps',
+ 'change tempo by %n':
+ 'augmenta el tempo en %n',
+ 'set tempo to %n bpm':
+ 'fixa el tempo a %n',
+ 'tempo':
+ 'tempo',
+
+ // pen:
+ 'clear':
+ 'neteja',
+ 'pen down':
+ 'baixa el llapis',
+ 'pen up':
+ 'puja el llapis',
+ 'set pen color to %clr':
+ 'fixa el color del llapis a %clr',
+ 'change pen color by %n':
+ 'augmenta en %n el color del llapis',
+ 'set pen color to %n':
+ 'fixa el color del llapis a %n',
+ 'change pen shade by %n':
+ 'augmenta en %n la intensitat del llapis',
+ 'set pen shade to %n':
+ 'fixa la intensitat del llapis a %n',
+ 'change pen size by %n':
+ 'augmenta en %n la mida del llapis',
+ 'set pen size to %n':
+ 'fixa la mida del llapis en %n',
+ 'stamp':
+ 'estampa',
+ 'fill':
+ 'ompli',
+
+ // control:
+ 'when %greenflag clicked':
+ 'Quan la %greenflag es prema',
+ 'when %keyHat key pressed':
+ 'Quan la tecla %keyHat es prema',
+ 'when I am %interaction':
+ 'Quan %interaction aquest personatge',
+ 'clicked':
+ 'es clique',
+ 'pressed':
+ 'es prema',
+ 'dropped':
+ 'es deixe anar',
+ 'mouse-entered':
+ 'el ratolí toque',
+ 'mouse-departed':
+ 'el ratolí isca d\'',
+ 'when %b':
+ 'quan %b',
+ 'when I receive %msgHat':
+ 'Quan reba %msgHat',
+ 'broadcast %msg':
+ 'Envia a tots %msg',
+ 'broadcast %msg and wait':
+ 'Envia a tots %msg i espera',
+ 'Message name':
+ 'Nom del missatge',
+ 'message':
+ 'missatge',
+ 'any message':
+ 'qualsevol missatge',
+ 'wait %n secs':
+ 'espera %n segons',
+ 'wait until %b':
+ 'espera fins %b',
+ 'forever %c':
+ 'per sempre %c',
+ 'repeat %n %c':
+ 'repeteix %n vegades %c',
+ 'repeat until %b %c':
+ 'repeteix fins %b %c',
+ 'if %b %c':
+ 'si %b llavors %c',
+ 'if %b %c else %c':
+ 'si %b llavors %c si no %c',
+ 'report %s':
+ 'retorna %s',
+ 'stop %stopChoices':
+ 'para %stopChoices',
+ 'all':
+ 'tot',
+ 'this script':
+ 'aquest programa',
+ 'this block':
+ 'aquest bloc',
+ 'stop %stopOthersChoices':
+ 'para %stopOthersChoices',
+ 'all but this script':
+ 'tot excepte aquest programa',
+ 'other scripts in sprite':
+ 'els altres programes d\'aquest objecte',
+ 'pause all %pause':
+ 'posa-ho tot en pausa %pause',
+ 'run %cmdRing %inputs':
+ 'executa %cmdRing %inputs',
+ 'launch %cmdRing %inputs':
+ 'llança %cmdRing %inputs',
+ 'call %repRing %inputs':
+ 'crida %repRing %inputs',
+ 'run %cmdRing w/continuation':
+ 'executa %cmdRing amb continuació',
+ 'call %cmdRing w/continuation':
+ 'crida %cmdRing amb continuació',
+ 'warp %c':
+ 'executa tot d\'una %c',
+ 'when I start as a clone':
+ 'quan una còpia meua comence',
+ 'create a clone of %cln':
+ 'crea un clon de %cln',
+ 'myself':
+ 'mi mateix',
+ 'delete this clone':
+ 'esborra aquest clon',
+
+ // sensing:
+ 'touching %col ?':
+ 'tocant %col ?',
+ 'touching %clr ?':
+ 'tocant el color %clr ?',
+ 'color %clr is touching %clr ?':
+ 'color %clr sobre %clr ?',
+ 'ask %s and wait':
+ 'pregunta %s i espera',
+ 'what\'s your name?':
+ 'Com et dius?',
+ 'answer':
+ 'resposta',
+ 'mouse x':
+ 'ratolí x',
+ 'mouse y':
+ 'ratolí y',
+ 'mouse down?':
+ 'ratolí clicat?',
+ 'key %key pressed?':
+ 'tecla %key premuda?',
+ 'distance to %dst':
+ 'distància a %dst',
+ 'reset timer':
+ 'reinicia el cronòmetre',
+ 'timer':
+ 'cronòmetre',
+ '%att of %spr':
+ '%att de %spr',
+ 'my %get':
+ 'atribut %get',
+ 'http:// %s':
+ 'http:// %s',
+ 'turbo mode?':
+ 'mode turbo?',
+ 'set turbo mode to %b':
+ 'posa el mode turbo a %b',
+
+ 'filtered for %clr':
+ 'filtrat per a %clr',
+ 'stack size':
+ 'mida de la pila',
+ 'frames':
+ 'frames',
+
+ // operators:
+ '%n mod %n':
+ 'residu de dividir %n entre %n',
+ 'round %n':
+ 'arredoneix %n',
+ '%fun of %n':
+ '%fun de %n',
+ 'pick random %n to %n':
+ 'nombre a l\'atzar entre %n i %n',
+ '%b and %b':
+ '%b i %b',
+ '%b or %b':
+ '%b o %b',
+ 'not %b':
+ 'no %b',
+ 'true':
+ 'cert',
+ 'false':
+ 'fals',
+ 'join %words':
+ 'unir %words',
+ 'split %s by %delim':
+ 'divideix %s per %delim',
+ 'hello':
+ 'hola',
+ 'world':
+ 'món',
+ 'letter %idx of %s':
+ 'lletra %idx de %s',
+ 'length of %s':
+ 'longitud de %s',
+ 'unicode of %s':
+ 'valor Unicode de %s',
+ 'unicode %n as letter':
+ 'lletra amb valor Unicode %n',
+ 'is %s a %typ ?':
+ 'és %s un %typ ?',
+ 'is %s identical to %s ?':
+ 'és %s idèntic a %s ?',
+
+ 'type of %s':
+ 'tipus de %s',
+
+ // variables:
+ 'Make a variable':
+ 'Crea una variable',
+ 'Variable name':
+ 'Nom de variable',
+ 'Script variable name':
+ 'Nom de la variable de programa',
+ 'Delete a variable':
+ 'Esborra una variable',
+
+ 'set %var to %s':
+ 'assigna a %var el valor %s',
+ 'change %var by %n':
+ 'augmenta %var en %n',
+ 'show variable %var':
+ 'mostra la variable %var',
+ 'hide variable %var':
+ 'amaga la variable %var',
+ 'script variables %scriptVars':
+ 'variables de programa %scriptVars',
+
+ // lists:
+ 'list %exp':
+ 'llista %exp',
+ '%s in front of %l':
+ 'afig %s davant de %l',
+ 'item %idx of %l':
+ 'element %idx de %l',
+ 'all but first of %l':
+ '%l sense el primer element',
+ 'length of %l':
+ 'longitud de %l',
+ '%l contains %s':
+ '%l conté %s',
+ 'thing':
+ 'cosa',
+ 'add %s to %l':
+ 'afig %s a %l',
+ 'delete %ida of %l':
+ 'esborra %ida de %l',
+ 'insert %s at %idx of %l':
+ 'insereix %s a la posició %idx de %l',
+ 'replace item %idx of %l with %s':
+ 'substitueix l\'element %idx de %l per %s',
+
+ // other
+ 'Make a block':
+ 'Crea un bloc',
+
+ // menus
+ // snap menu
+ 'About...':
+ 'Sobre l\'Snap!',
+ 'Reference manual':
+ 'Manual de referència',
+ 'Snap! website':
+ 'Web de l\'Snap!',
+ 'Download source':
+ 'Descarrega el codi font',
+ 'Switch back to user mode':
+ 'Torna a mode d\'usuari',
+ 'disable deep-Morphic\ncontext menus\nand show user-friendly ones':
+ 'canvia els menús contextuals\nprimitius de Morphic\nper menús més amigables',
+ 'Switch to dev mode':
+ 'Canvia a mode desenvolupador',
+ 'enable Morphic\ncontext menus\nand inspectors,\nnot user-friendly!':
+ 'habilita els menús\ncontextuals de\nMorphic i inspectors,\nmode expert!',
+
+ // project menu
+ 'Project notes...':
+ 'Notes del projecte...',
+ 'New':
+ 'Nou',
+ 'Open...':
+ 'Obri...',
+ 'Save':
+ 'Guarda',
+ 'Save to disk':
+ 'Guarda al disc',
+ 'store this project\nin the downloads folder\n(in supporting browsers)':
+ 'guarda aquest projecte\na la carpeta de descàrregues\n'
+ + '(en navegadors que ho admeten)',
+ 'Save As...':
+ 'Anomena i guarda...',
+ 'Import...':
+ 'Importa...',
+ 'file menu import hint':
+ 'carrega una biblioteca de projecte\no de blocs exportada, un vestit\no un so',
+
+
+ 'Export project as plain text...':
+ 'Exporta el projecte en text pla...',
+ 'Export project...':
+ 'Exporta el projecte...',
+ 'show project data as XML\nin a new browser window':
+ 'mostra tot el projecte en format XML\nen una altra finestra del navegador',
+ 'Export blocks...':
+ 'Exporta els blocs...',
+ 'show global custom block definitions as XML\nin a new browser window':
+ 'mostra les definicions de blocs personalitzats\nen format XML en una altra finestra del\nnavegador',
+ 'Unused blocks...':
+ 'Blocs no utilitzats...',
+ 'find unused global custom blocks\nand remove their definitions':
+ 'busca blocs personalitzats globals\nno utilitzats i esborra\'ls',
+ 'Remove unused blocks':
+ 'Esborra blocs no utilitzats',
+ 'there are currently no unused\nglobal custom blocks in this project':
+ 'no hi ha cap bloc\npersonalitzat no utilitzat\nen aquest projecte',
+ 'unused block(s) removed':
+ 'bloc(s) personalitzats no utilitzats esborrats',
+ 'Export summary...':
+ 'Exporta el resum...',
+ 'open a new browser browser window\n with a summary of this project':
+ 'obri una finestra nova del navegador\namb un resum d\'aquest projecte',
+
+ 'Contents':
+ 'Continguts',
+ 'Kind of':
+ 'Espècie de',
+ 'Part of':
+ 'Part de',
+ 'Parts':
+ 'Parts',
+ 'Blocks':
+ 'Blocs',
+ 'For all Sprites':
+ 'Per a tots els objectes',
+ 'Import tools':
+ 'Importa eines',
+ 'load the official library of\npowerful blocks':
+ 'carrega la biblioteca\noficial de blocs avançats',
+ 'Libraries...':
+ 'Biblioteques...',
+ 'Import library':
+ 'Importa una biblioteca',
+
+ // cloud menu
+ 'Login...':
+ 'Inicia la sessió...',
+ 'Signup...':
+ 'Registra\'t...',
+
+ // settings menu
+ 'Language...':
+ 'Llengua...',
+ 'Zoom blocks...':
+ 'Mida dels blocs...',
+ 'Stage size...':
+ 'Mida de l\'escenari...',
+ 'Stage size':
+ 'Mida de l\'escenari',
+ 'Stage width':
+ 'Amplària de l\'escenari',
+ 'Stage height':
+ 'Alçària de l\'escenari',
+ 'Default':
+ 'Per defecte',
+ 'Blurred shadows':
+ 'Ombres suavitzades',
+ 'uncheck to use solid drop\nshadows and highlights':
+ 'desmarca\'m per a utilitzar\nombres i realçats sòlids',
+ 'check to use blurred drop\nshadows and highlights':
+ 'marca\'m per a utilitzar\nombres i realçats suavitzats',
+ 'Zebra coloring':
+ 'Coloració en zebra',
+ 'check to enable alternating\ncolors for nested blocks':
+ 'marca\'m per a habilitar la coloració\nalternada per a blocs imbricats',
+ 'uncheck to disable alternating\ncolors for nested block':
+ 'desmarca\'m per a inhabilitar la coloració\nalternada per a blocs imbricats',
+ 'Dynamic input labels':
+ 'Etiquetes dinàmiques de camps d\'entrada',
+ 'uncheck to disable dynamic\nlabels for variadic inputs':
+ 'marca\'m per a desactivar les\netiquetes dinàmiques en camps\namb aritat variable',
+ 'check to enable dynamic\nlabels for variadic inputs':
+ 'marca\'m per a habilitar les\netiquetes dinàmiques en camps\namb aritat variable',
+ 'Prefer empty slot drops':
+ 'Dóna preferència a les ranures buides',
+ 'settings menu prefer empty slots hint':
+ 'marca\'m per a fer que les ranures\nbuides tinguen preferència sobre les plenes\na l\'hora de deixar-hi caure peces',
+
+ 'uncheck to allow dropped\nreporters to kick out others':
+ 'marca\'m per a fer que les ranures\nbuides tinguen la mateixa preferència que les\nplenes a l\'hora de deixar-hi caure peces',
+
+ 'Long form input dialog':
+ 'Força el diàleg de selecció de tipus',
+ 'Plain prototype labels':
+ 'Etiquetes de prototip simples',
+ 'uncheck to always show (+) symbols\nin block prototype labels':
+ 'desmarca\'m per a mostrar sempre el\nsímbol (+) en les etiquetes de prototip\nde bloc (a l\'editor de blocs)',
+ 'check to hide (+) symbols\nin block prototype labels':
+ 'desmarca\'m per a amagar el símbol (+)\nen les etiquetes de prototip\nde bloc (a l\'editor de blocs)',
+ 'check to always show slot\ntypes in the input dialog':
+ 'marca\'m per a mostrar sempre\nel diàleg de selecció de tipus\nen afegir paràmetres als blocs\npersonalitzats',
+ 'uncheck to use the input\ndialog in short form':
+ 'desmarca\'m per a no mostrar\nautomàticament el diàleg de selecció\nde tipus en afegir paràmetres\nals blocs personalitzats',
+ 'Virtual keyboard':
+ 'Teclat virtual',
+ 'uncheck to disable\nvirtual keyboard support\nfor mobile devices':
+ 'desmarca\'m per a inhabilitar\nel suport per al teclat virtual\nen dispositius mòbils',
+
+ 'check to enable\nvirtual keyboard support\nfor mobile devices':
+ 'marca\'m per a habilitar\nel suport per al teclat virtual\nen dispositius mòbils',
+
+ 'Input sliders':
+ 'Botons lliscants d\'entrada',
+ 'uncheck to disable\ninput sliders for\nentry fields':
+ 'desmarca\'m per a inhabilitar\nels botons lliscants per als camps\nd\'entrada',
+ 'check to enable\ninput sliders for\nentry fields':
+ 'marca\'m per a habilitar\nels botons lliscants per als camps\nd\'entrada',
+ 'Clicking sound':
+ 'So de clic',
+ 'uncheck to turn\nblock clicking\nsound off':
+ 'desmarca\'m per a inhabilitar\nel so de clic en clicar sobre\nels blocs',
+ 'check to turn\nblock clicking\nsound on':
+ 'marca\'m per a habilitar\nel so de clic en clicar sobre\nels blocs',
+ 'Animations':
+ 'Animacions',
+ 'uncheck to disable\nIDE animations':
+ 'desmarca\'m per a inhabilitar\nles animacions de la interfície',
+ 'Turbo mode':
+ 'Mode turbo',
+ 'check to prioritize\nscript execution':
+ 'marca\'m per a activar el mode de\nprioritat en l\'execució de programes',
+ 'uncheck to run scripts\nat normal speed':
+ 'desmarca\'m per a executar\nels programes a la velocitat\nnormal',
+ 'check to enable\nIDE animations':
+ 'marca\'m per a habilitar\nles animacions de la interfície',
+ 'Flat design':
+ 'Disseny pla',
+ 'Keyboard Editing':
+ 'Edició per teclat',
+ 'Table support':
+ 'Edició de taules',
+ 'Table lines':
+ 'Línies de taules',
+ 'Thread safe scripts':
+ 'Fil d\'execució segur',
+ 'uncheck to allow\nscript reentrance':
+ 'desmarca\'m per a permetre\nla reentrada als programes',
+ 'check to disallow\nscript reentrance':
+ 'marca\'m per a no permetre\nla reentrada als programes',
+ 'Prefer smooth animations':
+ 'Suavitza les animacions',
+ 'uncheck for greater speed\nat variable frame rates':
+ 'desmarca\'m per a augmentar la velocitat de\nles animacions fins a la màxima capacitat d\'aquesta màquina',
+ 'check for smooth, predictable\nanimations across computers':
+ 'marca\'m per a aconseguir unes animacions\nmés suaus i a velocitat predictible en màquines diferents',
+ 'Flat line ends':
+ 'Línies del llapis rectes',
+ 'check for flat ends of lines':
+ 'marca\'m per a fer que els\nextrems de les línies del\nllapis siguen rectes',
+ 'uncheck for round ends of lines':
+ 'desmarca\'m per a fer que\nels extrems de les línies\ndel llapis siguen arredonits',
+ 'Inheritance support':
+ 'Suport per a herència',
+
+ // inputs
+ 'with inputs':
+ 'amb entrades',
+ 'input names:':
+ 'noms d\'entrades:',
+ 'Input Names:':
+ 'Noms d\'entrades:',
+ 'input list:':
+ 'llista d\'entrades:',
+
+ // context menus:
+ 'help':
+ 'ajuda',
+
+ // palette:
+ 'hide primitives':
+ 'amaga els blocs primitius',
+ 'show primitives':
+ 'mostra els blocs primitius',
+
+ // blocks:
+ 'help...':
+ 'ajuda...',
+ 'relabel...':
+ 'blocs similars...',
+ 'duplicate':
+ 'duplica\'m',
+ 'make a copy\nand pick it up':
+ 'crea una còpia\ni agafa-la',
+ 'only duplicate this block':
+ 'duplica només aquest bloc',
+ 'delete':
+ 'esborra\'m',
+ 'script pic...':
+ 'mostra la meua imatge...',
+ 'open a new window\nwith a picture of this script':
+ 'obri una nova finestra\namb una imatge d\'aquest programa',
+ 'ringify':
+ 'encapsula\'m',
+ 'unringify':
+ 'desencapsula\'m',
+ 'transient':
+ 'no persistent',
+ 'uncheck to save contents\nin the project':
+ 'desactiveu l\'opció per a guardar els continguts\nen el projecte',
+ 'check to prevent contents\nfrom being saved':
+ 'activeu l\'opció per a evitar que els continguts\nes guarden',
+
+ // custom blocks:
+ 'delete block definition...':
+ 'esborra la definició d\'aquest bloc',
+ 'edit...':
+ 'edita...',
+
+ // sprites:
+ 'edit':
+ 'edita',
+ 'move':
+ 'mou',
+ 'detach from':
+ 'desenganxa de',
+ 'detach all parts':
+ 'desenganxa totes les parts',
+ 'export...':
+ 'exporta...',
+
+ // stage:
+ 'show all':
+ 'mostra\'ls tots',
+ 'pic...':
+ 'exporta com a imatge...',
+ 'open a new window\nwith a picture of the stage':
+ 'obri una nova finestra\namb una foto de l\'escenari',
+
+ // scripting area
+ 'clean up':
+ 'neteja',
+ 'arrange scripts\nvertically':
+ 'alinea els programes\nverticalment',
+ 'add comment':
+ 'afig un comentari',
+ 'undrop':
+ 'recupera el bloc',
+ 'undo the last\nblock drop\nin this pane':
+ 'recupera l\'últim bloc\nque s\'haja llançat',
+ 'scripts pic...':
+ 'exporta com a imatge...',
+ 'open a new window\nwith a picture of all scripts':
+ 'obri una nova finestra\namb una foto d\'aquests programes',
+ 'make a block...':
+ 'crea un bloc...',
+
+ // costumes
+ 'rename':
+ 'canvia de nom',
+ 'export':
+ 'exporta',
+ 'rename costume':
+ 'canvia el nom del vestit',
+
+ // sounds
+ 'Play sound':
+ 'Toca el so',
+ 'Stop sound':
+ 'Para el so',
+ 'Stop':
+ 'Para',
+ 'Play':
+ 'Toca',
+ 'rename sound':
+ 'canvia el nom del so',
+
+ // lists and tables
+ 'list view...':
+ 'vista en format de llista...',
+ 'table view...':
+ 'vista en format de taula...',
+ 'open in dialog...':
+ 'obri en una finestra...',
+ 'reset columns':
+ 'reinicialitza les columnes',
+ 'items':
+ 'elements',
+
+ // dialogs
+ // buttons
+ 'OK':
+ 'D\'acord',
+ 'Ok':
+ 'D\'acord',
+ 'Cancel':
+ 'Cancel·la',
+ 'Yes':
+ 'Sí',
+ 'No':
+ 'No',
+
+ // help
+ 'Help':
+ 'Ajuda',
+
+ // zoom blocks
+ 'Zoom blocks':
+ 'Canvia la mida dels blocs',
+ 'build':
+ 'construeix',
+ 'your own':
+ 'els teus propis',
+ 'blocks':
+ 'blocs',
+ 'normal (1x)':
+ 'normal (1x)',
+ 'demo (1.2x)':
+ 'demostració (1.2x)',
+ 'presentation (1.4x)':
+ 'presentació (1.4x)',
+ 'big (2x)':
+ 'gran (2x)',
+ 'huge (4x)':
+ 'immens (4x)',
+ 'giant (8x)':
+ 'gegant (8x)',
+ 'monstrous (10x)':
+ 'monstruós (10x)',
+
+ // Project Manager
+ 'Untitled':
+ 'Sense títol',
+ 'Open un Project':
+ 'Obri un projecte',
+ '(empty)':
+ '(buit)',
+ 'Saved!':
+ 'Guardat!',
+ 'Delete Project':
+ 'Esborra un projecte',
+ 'Are you sure you want to delete':
+ 'Segur que vols esborrar',
+ 'rename...':
+ 'canvia el nom...',
+
+ // costume editor
+ 'Costume Editor':
+ 'Editor de vestits',
+ 'click or drag crosshairs to move the rotation center':
+ 'clica o arrossega la creueta per a moure el centre de rotació',
+
+ // project notes
+ 'Project Notes':
+ 'Notes del projecte',
+
+ // new project
+ 'New Project':
+ 'Projecte nou',
+ 'Replace the current project with a new one?':
+ 'Vols substituir el projecte actual per un de nou?',
+
+ // save project
+ 'Save Project As...':
+ 'Anomena i guarda el projecte...',
+
+ // export blocks
+ 'Export blocks':
+ 'Exporta blocs',
+ 'Import blocks':
+ 'Importa blocs',
+ 'this project doesn\'t have any\ncustom global blocks yet':
+ 'aquest projecte encara no\nté cap bloc personalitzat',
+ 'select':
+ 'selecciona',
+ 'none':
+ 'cap bloc',
+
+ // variable dialog
+ 'for all sprites':
+ 'per a tots els objectes',
+ 'for this sprite only':
+ 'només per a aquest objecte',
+
+ // block dialog
+ 'Change block':
+ 'Canvia el bloc',
+ 'Command':
+ 'Ordre',
+ 'Reporter':
+ 'Reportador',
+ 'Predicate':
+ 'Predicat',
+
+ // block editor
+ 'Block Editor':
+ 'Editor de blocs',
+ 'Apply':
+ 'Aplica',
+
+ // block deletion dialog
+ 'Delete Custom Block':
+ 'Esborra el bloc personalitzat',
+ 'block deletion dialog text':
+ 'Segur que vols esborrar la definició\nd\'aquest bloc?',
+
+
+ // input dialog
+ 'Create input name':
+ 'Crea una ranura',
+ 'Edit input name':
+ 'Edita la ranura',
+ 'Edit label fragment':
+ 'Edita el fragment d\'etiqueta',
+ 'Title text':
+ 'Text del títol',
+ 'Input name':
+ 'Nom de la ranura',
+ 'Delete':
+ 'Esborra',
+ 'Object':
+ 'Objecte',
+ 'Number':
+ 'Nombre',
+ 'Text':
+ 'Text',
+ 'List':
+ 'Llista',
+ 'Any type':
+ 'Qualsevol tipus',
+ 'Boolean (T/F)':
+ 'Booleà (C/F)',
+ 'Command\n(inline)':
+ 'Ordre\n(inserida)',
+ 'Command\n(C-shape)':
+ 'Ordre\n(en forma de C)',
+ 'Any\n(unevaluated)':
+ 'Qualsevol\n(sense avaluar)',
+ 'Boolean\n(unevaluated)':
+ 'Booleà\n(sense avaluar)',
+ 'Single input.':
+ 'Entrada única.',
+ 'Default Value:':
+ 'Valor predeterminat:',
+ 'Multiple inputs (value is list of inputs)':
+ 'Entrades múltiples (el valor és una llista d\'entrades)',
+ 'Upvar - make internal variable visible to caller':
+ 'Variable interna visible des de l\'exterior',
+
+ // About Snap
+ 'About Snap':
+ 'Sobre l\'Snap',
+ 'Back...':
+ 'Arrere...',
+ 'License...':
+ 'Llicència...',
+ 'Modules...':
+ 'Mòduls...',
+ 'Credits...':
+ 'Crèdits...',
+ 'Translators...':
+ 'Traductors',
+ 'License':
+ 'Llicència',
+ 'current module versions:':
+ 'versions actuals dels mòduls',
+ 'Contributors':
+ 'Contribuïdors',
+ 'Translations':
+ 'Traduccions',
+
+ // variable watchers
+ 'normal':
+ 'normal',
+ 'large':
+ 'gran',
+ 'slider':
+ 'botó lliscant',
+ 'slider min...':
+ 'valor mínim del botó lliscant...',
+ 'slider max...':
+ 'valor màxim del botó lliscant...',
+ 'import...':
+ 'importa...',
+ 'Slider minimum value':
+ 'Valor mínim del botó lliscant...',
+ 'Slider maximum value':
+ 'Valor màxim del botó lliscant...',
+
+ // list watchers
+ 'length: ':
+ 'longitud: ',
+
+ // coments
+ 'add comment here...':
+ 'afig un comentari ací...',
+
+ // drow downs
+ // directions
+ '(90) right':
+ '(90) dreta',
+ '(-90) left':
+ '(-90) esquerra',
+ '(0) up':
+ '(0) amunt',
+ '(180) down':
+ '(180) avall',
+
+ // collision detection
+ 'mouse-pointer':
+ 'punter del ratolí',
+ 'edge':
+ 'vora',
+ 'pen trails':
+ 'rastre del llapis',
+
+ // costumes
+ 'Turtle':
+ 'Tortuga',
+ 'Empty':
+ 'Buit',
+
+ // graphical effects
+ 'brightness':
+ 'brillantor',
+ 'ghost':
+ 'fantasma',
+ 'negative':
+ 'negatiu',
+ 'comic':
+ 'còmic',
+ 'confetti':
+ 'confeti',
+
+ // keys
+ 'space':
+ 'espai',
+ 'any key':
+ 'qualsevol tecla',
+ 'up arrow':
+ 'fletxa amunt',
+ 'down arrow':
+ 'fletxa avall',
+ 'right arrow':
+ 'fletxa dreta',
+ 'left arrow':
+ 'fletxa esquerra',
+ 'a':
+ 'a',
+ 'b':
+ 'b',
+ 'c':
+ 'c',
+ 'd':
+ 'd',
+ 'e':
+ 'e',
+ 'f':
+ 'f',
+ 'g':
+ 'g',
+ 'h':
+ 'h',
+ 'i':
+ 'i',
+ 'j':
+ 'j',
+ 'k':
+ 'k',
+ 'l':
+ 'l',
+ 'm':
+ 'm',
+ 'n':
+ 'n',
+ 'o':
+ 'o',
+ 'p':
+ 'p',
+ 'q':
+ 'q',
+ 'r':
+ 'r',
+ 's':
+ 's',
+ 't':
+ 't',
+ 'u':
+ 'u',
+ 'v':
+ 'v',
+ 'w':
+ 'w',
+ 'x':
+ 'x',
+ 'y':
+ 'y',
+ 'z':
+ 'z',
+ '0':
+ '0',
+ '1':
+ '1',
+ '2':
+ '2',
+ '3':
+ '3',
+ '4':
+ '4',
+ '5':
+ '5',
+ '6':
+ '6',
+ '7':
+ '7',
+ '8':
+ '8',
+ '9':
+ '9',
+
+ // messages
+ 'new...':
+ 'nou...',
+
+ // math functions
+ 'abs':
+ 'valor absolut',
+ 'ceiling':
+ 'sostre',
+ 'floor':
+ 'part entera',
+ 'sqrt':
+ 'arrel quadrada',
+ 'sin':
+ 'sin',
+ 'cos':
+ 'cos',
+ 'tan':
+ 'tan',
+ 'asin':
+ 'asin',
+ 'acos':
+ 'acos',
+ 'atan':
+ 'atan',
+ 'ln':
+ 'ln',
+ 'e^':
+ 'e^',
+
+ // delimiters
+ 'letter':
+ 'lletra',
+ 'whitespace':
+ 'espai en blanc',
+ 'line':
+ 'línia',
+ 'tab':
+ 'tabulador',
+ 'cr':
+ 'retorn de carro',
+
+ // data types
+ 'number':
+ 'nombre',
+ 'text':
+ 'text',
+ 'Boolean':
+ 'Booleà',
+ 'list':
+ 'llista',
+ 'command':
+ 'ordre',
+ 'reporter':
+ 'reportador',
+ 'predicate':
+ 'predicat',
+
+ // list indices
+ 'last':
+ 'últim',
+ 'any':
+ 'qualsevol',
+
+ // attributes
+ 'neighbors':
+ 'veïns',
+ 'self':
+ 'un mateix',
+ 'other sprites':
+ 'els altres objectes',
+ 'parts':
+ 'parts',
+ 'anchor':
+ 'àncora',
+ 'parent':
+ 'pare',
+ 'children':
+ 'fill',
+ 'clones':
+ 'clons',
+ 'other clones':
+ 'altres clons',
+ 'dangling?':
+ 'penjant?',
+ 'rotation x':
+ 'rotació x',
+ 'rotation y':
+ 'rotació y',
+ 'center x':
+ 'centre x',
+ 'center y':
+ 'centre y'
+
+};
diff --git a/lang-cs.js b/lang-cs.js
index b452c557..caa193ab 100755
--- a/lang-cs.js
+++ b/lang-cs.js
@@ -576,8 +576,8 @@ SnapTranslator.dict.cs = {
'světe',
'split %s by %delim':
'rozděl %s podle %delim',
- 'letter %n of %s':
- 'písmeno %n z %s',
+ 'letter %idx of %s':
+ 'písmeno %idx z %s',
'length of %s':
'délka %s',
'unicode of %s':
diff --git a/lang-de.js b/lang-de.js
index af325372..bbf87b84 100644
--- a/lang-de.js
+++ b/lang-de.js
@@ -6,7 +6,7 @@
written by Jens Mönig
- Copyright (C) 2017 by Jens Mönig
+ Copyright (C) 2018 by Jens Mönig
This file is part of Snap!.
@@ -181,11 +181,11 @@ SnapTranslator.dict.de = {
'language_name':
'Deutsch', // the name as it should appear in the language menu
'language_translator':
- 'Jens M\u00F6nig', // your name for the Translators tab
+ 'Jens M\u00F6nig, Jadga H\u00fcgle', // your name for the Translators tab
'translator_e-mail':
- 'jens@moenig.org', // optional
+ 'jens@moenig.org, jadga.huegle@sap.com', // optional
'last_changed':
- '2017-10-20', // this, too, will appear in the Translators tab
+ '2018-06-08', // this, too, will appear in the Translators tab
// GUI
// control bar:
@@ -247,13 +247,20 @@ SnapTranslator.dict.de = {
// new sprite button:
'add a new sprite':
'ein neues Objekt\nhinzuf\u00fcgen',
+ 'add a new Turtle sprite':
+ 'neues Objekt hinzufügen',
+ 'paint a new sprite':
+ 'neues Objekt zeichnen',
+ 'take a camera snapshot and\nimport it as a new sprite':
+ 'neues Objekt mit Webcam-Kostüm hinzufügen',
+
// tab help
'costumes tab help':
- 'Bilder durch hereinziehen von einer anderen\n'
- + 'Webseite or vom Computer importieren',
+ 'Bilder durch Hereinziehen von einer anderen\n'
+ + 'Webseite oder vom Computer importieren',
'import a sound from your computer\nby dragging it into here':
- 'Kl\u00e4nge durch hereinziehen importieren',
+ 'Kl\u00e4nge durch Hereinziehen importieren',
// primitive blocks:
@@ -445,6 +452,12 @@ SnapTranslator.dict.de = {
'vom Mauszeiger betreten',
'mouse-departed':
'vom Mauszeiger verlassen',
+ 'scrolled-down':
+ 'nach unten gescrollt',
+ 'scrolled-up':
+ 'nach oben gescrollt',
+ 'stopped':
+ 'gestoppt',
'when %b':
'Wenn %b',
'when I receive %msgHat':
@@ -539,8 +552,10 @@ SnapTranslator.dict.de = {
'Maustaste gedr\u00fcckt?',
'key %key pressed?':
'Taste %key gedr\u00fcckt?',
- 'distance to %dst':
- 'Entfernung von %dst',
+ '%rel to %dst':
+ '%rel zu %dst',
+ 'distance':
+ 'Entfernung',
'reset timer':
'starte Stoppuhr neu',
'timer':
@@ -608,8 +623,8 @@ SnapTranslator.dict.de = {
'Hallo',
'world':
'Welt',
- 'letter %n of %s':
- 'Zeichen %n von %s',
+ 'letter %idx of %s':
+ 'Zeichen %idx von %s',
'length of %s':
'L\u00e4nge von %s',
'unicode of %s':
@@ -622,6 +637,8 @@ SnapTranslator.dict.de = {
'ist %s identisch mit %s ?',
'JavaScript function ( %mult%s ) { %code }':
'JavaScript Funktion ( %mult%s ) { %code }',
+ 'compile %repRing':
+ 'kompiliere %repRing',
'type of %s':
'Typ von %s',
@@ -716,12 +733,14 @@ SnapTranslator.dict.de = {
'Importieren...',
'file menu import hint':
'l\u00e4dt ein exportiertes Projekt,\neine Bibliothek mit '
- + 'Bl\u00f6cken\n'
+ + 'Bl\u00f6cken,\n'
+ 'ein Kost\u00fcm oder einen Klang',
'Export project as plain text...':
'Projekt als normalen Text exportieren...',
'Export project...':
'Projekt exportieren...',
+ 'save project data as XML\nto your downloads folder':
+ 'Projekt als XML-Datei in den Download-\nOrdner des Browsers speichern',
'show project data as XML\nin a new browser window':
'zeigt das Projekt als XML\nin einem neuen Browserfenster an',
'Export blocks...':
@@ -729,19 +748,19 @@ SnapTranslator.dict.de = {
'show global custom block definitions as XML\nin a new browser window':
'zeigt globale Benutzerblockdefinitionen\nals XML im Browser an',
'Unused blocks...':
- 'Ungebrauchte Bl\u00f6cke...',
+ 'nicht verwendete Bl\u00f6cke...',
'find unused global custom blocks\nand remove their definitions':
'nicht verwendete Bl\u00f6cke finden\nund entfernen',
'Remove unused blocks':
- 'Ungebrauchte Bl\u00f6cke entfernen',
+ 'nicht verwendete Bl\u00f6cke entfernen',
'there are currently no unused\nglobal custom blocks in this project':
'momentan keine nicht verwendeten\nBl\u00f6cke in diesem Projekt',
'unused block(s) removed':
- 'ungebrauchte Bl\u00f6cke entfernt',
+ 'nicht verwendete Bl\u00f6cke entfernt',
'Export summary...':
'Zusammenfassung exportieren...',
'open a new browser browser window\n with a summary of this project':
- 'eine Zusammenfassung diese Projects\nin einem neuen Browserfenster'
+ 'eine Zusammenfassung dieses Projekts\nin einem neuen Browserfenster'
+ 'anzeigen',
'Contents':
'Inhalt',
@@ -761,14 +780,65 @@ SnapTranslator.dict.de = {
'das offizielle Modul mit\nm\u00e4chtigen Bl\u00f6cken laden',
'Libraries...':
'Module...',
+ 'Select categories of additional blocks to add to this project.':
+ 'Zusätzliche Auswahl thematisch gruppierter\nBlöcke zu diesem Projekt hinzufügen',
+ 'Select a costume from the media library':
+ 'Kostüm aus der Medienbibliothek auswählen',
+ 'Select a sound from the media library':
+ 'Klang aus der Medienbibliothek auswählen',
+
+ //Libraries
'Import library':
'Modul laden',
+ 'Loading':
+ 'Lädt',
+ 'Imported':
+ 'Importiert',
+ 'Iteration, composition':
+ 'Iteration, Komposition',
+ 'List utilities':
+ 'Listen bearbeiten',
+ 'Variadic reporters':
+ 'Variadische Funktionen',
+ 'Web services access (https)':
+ 'Zugriff auf Webservices',
+ 'Multi-branched conditional (switch)':
+ 'Mehrfach verzweigte Conditionals (Switch)',
+ 'LEAP Motion controller':
+ 'LEAP Motion Controller',
+ 'Words, sentences':
+ 'Wörter, Sätze',
+ 'Catch errors in a script':
+ 'Fehlerhandhabung im Skript',
+ 'Set RGB or HSV pen color':
+ 'Stiftfarbe auf RGB oder HSV Werte setzen',
+ 'Text to speech':
+ 'Sprachausgabe',
+ 'Provide 100 selected colors':
+ '100 ausgewählte Farben',
+ 'Infinite precision integers, exact rationals, complex':
+ 'Beliebig präzise Ganzzahlen, exakte rationale Zahlen, komplexe Zahlen',
+ 'Provide getters and setters for all GUI-controlled global settings':
+ 'GUI Elemente programmatisch bearbeiten',
+ 'Allow multi-line text input to a block':
+ 'Mehrzeiliger Text als Eingabe für Blöcke',
+ 'create variables in program':
+ 'Variablen im Skript erstellen',
// cloud menu
'Login...':
'Anmelden...',
'Signup...':
'Benutzerkonto einrichten...',
+ 'Logout':
+ 'Abmelden',
+ 'Change Password...':
+ 'Passwort ändern...',
+ 'Reset Password...':
+ 'Passwort zurücksetzen...',
+ 'Resend Verification Email...':
+ 'Bestätigungsmail nochmal senden...',
+
// settings menu
'Language...':
@@ -811,6 +881,10 @@ SnapTranslator.dict.de = {
'uncheck to allow dropped\nreporters to kick out others':
'ausschalten um das "Rauskicken"\nvon platzierten Bl\u00f6cken\n'
+ 'zu erm\u00f6glichen',
+ 'check to turn on\n visible stepping (slow)':
+ 'einschalten um Programmausführung\nzu verfolgen (schrittweise)',
+ 'uncheck to turn off\nvisible stepping':
+ 'ausschalten um Programmausführung\nnicht mehr zu verfolgen',
'Long form input dialog':
'Ausf\u00fchrlicher Input-Dialog',
'Plain prototype labels':
@@ -839,6 +913,10 @@ SnapTranslator.dict.de = {
'einschalten um Schieber\nin Eingabefeldern zu aktivieren',
'Retina display support':
'Retina Bildschirmauflösung',
+ 'uncheck for lower resolution,\nsaves computing resources':
+ 'ausschalten um eine niedrigere Auflösung zu erhalten\nund weniger Rechenleistung zu benötigen',
+ 'check for higher resolution,\nuses more computing resources':
+ 'einschalten um eine höhere Auflösung zu erhalten,\nbenötigt mehr Rechenleistung',
'Codification support':
'Kodifikation',
'Clicking sound':
@@ -861,6 +939,10 @@ SnapTranslator.dict.de = {
'einschalten um IDE-\nAnimationen zu erlauben',
'Flat design':
'Helles Design',
+ 'check for alternative\nGUI design':
+ 'einschalten für alternative Nutzeroberfläche',
+ 'uncheck for default\nGUI design':
+ 'ausschalten für Standard-Nutzeroberfläche',
'Nested auto-wrapping':
'Automatisches Umklammern',
'Keyboard Editing':
@@ -963,6 +1045,8 @@ SnapTranslator.dict.de = {
'Angelpunkt',
'edit the costume\'s\nrotation center':
'Drehpunkt des Kostüms\nanzeigen und verschieben',
+ 'rotate':
+ 'Drehen',
'detach from':
'Abtrennen von',
'detach all parts':
@@ -985,6 +1069,10 @@ SnapTranslator.dict.de = {
'Bild exportieren...',
'open a new window\nwith a picture of the stage':
'ein neues Browserfenster mit einem\nBild der B\u00fchne \u00f6ffnen',
+ 'turn all pen trails and stamps\ninto a new background for the stage':
+ 'Hintergrund aus allen Malspuren und\nStempelabdrücken auf der Bühne erstellen',
+ 'turn all pen trails and stamps\ninto a new costume for the\ncurrently selected sprite':
+ 'aus allen Malspuren und Stempelabdrücken ein\nKostüm für die momentan ausgewählte Figur erstellen',
// scripting area
'clean up':
@@ -1085,7 +1173,9 @@ SnapTranslator.dict.de = {
'Untitled':
'Unbenannt',
'Open Project':
- 'Project \u00f6ffnen',
+ 'Projekt \u00f6ffnen',
+ 'Open':
+ '\u00d6ffnen',
'(empty)':
'(leer)',
'Saved!':
@@ -1096,12 +1186,90 @@ SnapTranslator.dict.de = {
'Wirklich l\u00f6schen?',
'rename...':
'Umbenennen...',
+ 'Examples':
+ 'Beispiele',
+ 'Share':
+ 'Teilen',
+ 'Updating\nproject list...':
+ 'Projektliste laden',
+ 'Recover':
+ 'Wiederherstellen',
+ 'Today':
+ 'Heute',
+ 'Yesterday':
+ 'Gestern',
// costume editor
'Costume Editor':
'Kost\u00fcmeditor',
+ 'Paint Editor':
+ 'Kostümeditor',
'click or drag crosshairs to move the rotation center':
'Fadenkreuz anklicken oder bewegen um den Drehpunkt zu setzen',
+ 'undo':
+ 'rückgängig',
+ 'Vector':
+ 'Vektor',
+ 'Paintbrush tool\n(free draw)':
+ 'Pinsel\n(freies Zeichnen)',
+ 'Stroked Rectangle\n(shift: square)':
+ 'Rechteck\n(Shift: Quadrat)',
+ 'Stroked Ellipse\n(shift: circle)':
+ 'Ellipse\n(Shift: Kreis)',
+ 'Eraser tool':
+ 'Radiergummi',
+ 'Set the rotation center':
+ 'Drehpunkt setzen',
+ 'Line tool\n(shift: vertical/horizontal)':
+ 'Linie\n(Shift: vertikal/horizontal)',
+ 'Filled Rectangle\n(shift: square)':
+ 'gefülltes Rechteck\n(Shift: Quadrat)',
+ 'Filled Ellipse\n(shift: circle)':
+ 'gefüllte Ellipse\n(Shift: Kreis)',
+ 'Fill a region':
+ 'fülle einen Bereich mit\nder gewählten Farbe',
+ 'Pipette tool\n(pick a color anywhere)':
+ 'Pipette (klicke irgendwo auf die gewünschte\nFarbe, um sie aufzunehmen)',
+ 'Brush size':
+ 'Pinselstärke',
+ 'Constrain proportions of shapes?\n(you can also hold shift)':
+ 'Proportionen festlegen\n(auch über Shift-Taste)',
+ //'grow':
+ // 'größer',
+ //'shrink':
+ // 'kleiner',
+ //'flip ↔':
+ // 'drehen ↔',
+ //'flip ↕':
+ // 'drehen ↕',
+
+ 'Vector Paint Editor':
+ 'Vektor-Editor',
+ 'Rectangle\n(shift: square)':
+ 'Rechteck\n(Shift: Quadrat)',
+ 'Ellipse\n(shift: circle)':
+ 'Ellipse\n(Shift: Kreis)',
+ 'Selection tool':
+ 'Auswählen',
+ 'Line tool\n(shift: constrain to 45º)':
+ 'Linie\n(Shift: Vielfache von 45°)',
+ 'Closed brush\n(free draw)':
+ 'geschlossene, gefüllte Form\n(freies Zeichnen)',
+ 'Paint a shape\n(shift: secondary color)':
+ 'fülle einen Bereich mit der gewählten Farbe\n(Shift: Sekundärfarbe)',
+ 'Pipette tool\n(pick a color from anywhere\nshift: secondary color)':
+ 'Pipette\nklicke irgendwo auf die gewünschte Farbe\n um sie aufzunehmen (Shift: Sekundärfarbe)',
+ 'Primary color Secondary color':
+ 'Primärfarbe Sekundärfarbe',
+ // 'Top':
+ // 'oben',
+ // 'Bottom':
+ // 'unten',
+ // 'Up':
+ // 'nach oben',
+ // 'Down':
+ // 'nach unten',
+
// project notes
'Project Notes':
@@ -1116,6 +1284,8 @@ SnapTranslator.dict.de = {
// save project
'Save Project As...':
'Projekt Sichern Als...',
+ 'Save Project':
+ 'Projekt sichern',
// export blocks
'Export blocks':
@@ -1255,9 +1425,13 @@ SnapTranslator.dict.de = {
'length: ':
'L\u00e4nge: ',
- // coments
+ // comments
'add comment here...':
'Anmerkung hier hinzuf\u00fcgen',
+ 'comment pic...':
+ 'Kommentarbild',
+ 'open a new window\nwith a picture of this comment':
+ 'neues Fenster mit dem Bild\ndieses Kommentars öffnen',
// drow downs
// directions
@@ -1269,6 +1443,10 @@ SnapTranslator.dict.de = {
'(0) oben',
'(180) down':
'(180) unten',
+ 'random':
+ 'zufällig',
+ 'random position':
+ 'zufällige Position',
// collision detection
'mouse-pointer':
@@ -1277,12 +1455,27 @@ SnapTranslator.dict.de = {
'Kante',
'pen trails':
'Malspuren',
+ 'center':
+ 'Mitte',
// costumes
'Turtle':
'Richtungszeiger',
'Empty':
'Leer',
+ 'Paint a new costume':
+ 'neues Kostüm zeichnen',
+ 'Import a new costume from your webcam':
+ 'neues Kostüm mit der Webcam aufnehmen',
+ 'Please make sure your web browser is up to date\nand your camera is properly configured. \n\nSome browsers also require you to access Snap!\nthrough HTTPS to use the camera.\n\nPlase replace the "http://" part of the address\nin your browser by "https://" and try again.':
+ 'Überprüfe, ob der Browser auf dem aktuellsten Stand \nund die Webcam korrekt konfiguriert ist.\n\nFür einige Browser muss Snap! mit HTTPS geöffnet\nwerden, um auf die Kamera zuzugreifen.\n\nErsetze dafür den "http://"-Teil in der Adresszeile mit"https://"',
+ 'Camera':
+ 'Kamera',
+
+ // sounds
+ 'Record a new sound':
+ 'neuen Klang aufnehmen',
+
// graphical effects
'color':
@@ -1465,6 +1658,8 @@ SnapTranslator.dict.de = {
'beliebig',
// attributes
+ 'my':
+ 'Attribut',
'neighbors':
'Nachbarn',
'self':
diff --git a/lang-dk.js b/lang-dk.js
index 3d1d2c10..e9604848 100644
--- a/lang-dk.js
+++ b/lang-dk.js
@@ -547,8 +547,8 @@ SnapTranslator.dict.dk = {
'hej',
'world':
'verden',
- 'letter %n of %s':
- 'bogstav %n af %s',
+ 'letter %idx of %s':
+ 'bogstav %idx af %s',
'length of %s':
'l\u00E6ngde af %s',
'unicode of %s':
diff --git a/lang-el.js b/lang-el.js
index 8e46e7d7..8e675064 100644
--- a/lang-el.js
+++ b/lang-el.js
@@ -185,7 +185,7 @@ SnapTranslator.dict.el = {
'translator_e-mail':
'ino.samaras@berkeley.edu', // optional
'last_changed':
- '2013-09-16', // this, too, will appear in the Translators tab
+ '2018-01-19', // this, too, will appear in the Translators tab
// GUI
// control bar:
@@ -497,7 +497,7 @@ SnapTranslator.dict.el = {
'key %key pressed?':
'είναι το πλήκτρο %key πατημένο;',
'distance to %dst':
- 'αποόσταση από %dst',
+ 'απόσταση από %dst',
'reset timer':
'επανέφερε το χρονόμετρο',
'timer':
@@ -543,8 +543,8 @@ SnapTranslator.dict.el = {
'γεια',
'world':
'κόσμος',
- 'letter %n of %s':
- 'γράμμα %n του %s',
+ 'letter %idx of %s':
+ 'γράμμα %idx του %s',
'length of %s':
'μήκος του %s',
'unicode of %s':
@@ -633,7 +633,7 @@ SnapTranslator.dict.el = {
'New':
'Νέο',
'Open...':
- 'Άμοιγα...',
+ 'Άνοιγμα...',
'Save':
'Αποθήκευση',
'Save As...':
diff --git a/lang-eo.js b/lang-eo.js
index 43b1463c..7bc67b83 100644
--- a/lang-eo.js
+++ b/lang-eo.js
@@ -571,8 +571,8 @@ SnapTranslator.dict.eo = {
'saluton',
'world':
'mondo',
- 'letter %n of %s':
- 'litero %n el %s',
+ 'letter %idx of %s':
+ 'litero %idx el %s',
'length of %s':
'longeco de %s',
'unicode of %s':
diff --git a/lang-es.js b/lang-es.js
old mode 100644
new mode 100755
index 120d5193..6d207fc8
--- a/lang-es.js
+++ b/lang-es.js
@@ -181,18 +181,18 @@ SnapTranslator.dict.es = {
'language_name':
'Espa\u00F1ol', // the name as it should appear in the language menu
'language_translator':
- 'V\u00EDctor Manuel Muratalla Morales', // your name for the Translators tab
+ 'V\u00EDctor Manuel Muratalla Morales / Cristi\u00E1n Rizzi Iribarren / Alfonso Ruzafa', // your name for the Translators tab
'translator_e-mail':
- 'victor.muratalla@yahoo.com', // optional
+ 'victor.muratalla@yahoo.com / rizzi.cristian@gmail.com', // optional
'last_changed':
- '2013-03-25', // this, too, will appear in the Translators tab
+ '2018-02-19', // this, too, will appear in the Translators tab
// GUI
// control bar:
'untitled':
'Sin t\u00EDtulo',
'development mode':
- 'modo de desarrollo',
+ 'modo desarrollador',
// categories:
'Motion':
@@ -214,15 +214,23 @@ SnapTranslator.dict.es = {
'Lists':
'Listas',
'Other':
- 'Otro',
+ 'Otros',
// editor:
+ 'don\'t rotate':
+ 'no girar',
+ 'can rotate':
+ 'puede girar',
+ 'only face left/right':
+ 's\u00F3lo mirar a izquierda y derecha',
'draggable':
'arrastrable',
// tabs:
'Scripts':
- 'Objetos',
+ 'Programas',
+ 'Backgrounds':
+ 'Fondos de escenario',
'Costumes':
'Disfraces',
'Sounds':
@@ -233,25 +241,86 @@ SnapTranslator.dict.es = {
'Objeto',
'Stage':
'Escenario',
+ 'Turtle':
+ 'Tortuga',
- // rotation styles:
- 'don\'t rotate':
- 'no girar',
- 'can rotate':
- 'puede girar',
- 'only face left/right':
- 's\u00F3lo mirar izquierda/derecha',
+ // stage tab:
+ 'Empty':
+ 'Vac\u00EDo',
- // new sprite button:
- 'add a new sprite':
- 'agregar un nuevo objeto',
-
- // tab help
+ // costumes tab:
+ 'Paint a new costume':
+ 'dibujar un nuevo disfraz',
+ 'Import a new costume from your webcam':
+ 'importar un nuevo disfraz desde la c\u00E1mara',
'costumes tab help':
- 'importar una foto de otro sitio Webo desde\n'
- + 'su ordenador arrastr\u00E1ndolo hasta aqu\u00ED',
+ 'Puedes importar un disfraz de otro sitio web\no desde tu ordenador arrastr\u00E1ndolo hasta aqu\u00ED',
+
+ // paint editor dialog:
+ 'Paint Editor':
+ 'Editor gr\u00E1fico',
+ 'undo':
+ 'deshacer',
+ 'Paintbrush tool\n(free draw)':
+ 'pincel\n(dibujo libre)',
+ 'Stroked Rectangle\n(shift: square)':
+ 'rect\u00E1ngulo\n(\u21E7 = cuadrado)',
+ 'Stroked Ellipse\n(shift: circle)':
+ 'elipse\n(\u21E7 = c\u00EDrculo)',
+ 'Eraser tool':
+ 'goma de borrar',
+ 'Set the rotation center':
+ 'establecer\ncentro de rotaci\u00F3n',
+ 'Line tool\n(shift: vertical/horizontal)':
+ 'l\u00EDnea\n(\u21E7 = vertical/horizontal)',
+ 'Filled Rectangle\n(shift: square)':
+ 'rect\u00E1ngulo relleno\n(\u21E7 = cuadrado)',
+ 'Filled Ellipse\n(shift: circle)':
+ 'elipse rellena\n(\u21E7 = c\u00EDrculo)',
+ 'Fill a region':
+ 'bote de pintura',
+ 'Pipette tool\n(pick a color anywhere)':
+ 'cuentagotas\n(funciona dentro y fuera del editor)',
+ 'grow':
+ 'ampliar',
+ 'shrink':
+ 'reducir',
+ 'flip \u2194':
+ 'voltear \u2194',
+ 'flip \u2195':
+ 'voltear \u2195',
+ 'Brush size':
+ 'Tama\u00F1o de pincel',
+ 'Constrain proportions of shapes?\n(you can also hold shift)':
+ 'Figuras proporcionales\n(o mant\u00E9n pulsado \u21E7)',
+
+ // camera dialog:
+ 'Camera':
+ 'C\u00E1mara',
+ 'Camera not supported':
+ 'C\u00E1mara no soportada',
+ 'Please make sure your web browser is up to date\nand your camera is properly configured. \n\nSome browsers also require you to access Snap!\nthrough HTTPS to use the camera.\n\nPlase replace the "http://" part of the address\nin your browser by "https://" and try again.':
+ 'Por favor, comprueba que tu navegador est\u00E9 actualizado\ny tu c\u00E1mara configurada correctamente.\n\nAlgunos navegadores necesitan que accedas a Snap!\na trav\u00E9s de HTTPS para usar la c\u00E1mara.\n\nPor favor, reemplaza el "http://" en la barra de direcciones\nde tu navegador por "https://" y vuelve a intentarlo.',
+ 'camera':
+ 'c\u00E1mara',
+
+ // sound tab:
+ 'Record a new sound':
+ 'grabar un nuevo sonido',
'import a sound from your computer\nby dragging it into here':
- 'importar un sonido desde su ordenador\narrastr\u00E1ndolo hasta aqu\u00ED',
+ 'Puedes importar un sonido desde tu ordenador\narrastr\u00E1ndolo hasta aqu\u00ED',
+
+ // sound recorder dialog:
+ 'Sound Recorder':
+ 'Grabador de sonidos',
+
+ // stage & sprite corral:
+ 'add a new Turtle sprite':
+ 'a\u00F1ade una nueva tortuga',
+ 'paint a new sprite':
+ 'dibuja un nuevo objeto',
+ 'take a camera snapshot and\nimport it as a new sprite':
+ 'hace una captura de c\u00E1mara\n y la importa como nuevo objeto',
// primitive blocks:
@@ -285,10 +354,19 @@ SnapTranslator.dict.es = {
without breaking its functionality.
*/
+ // shared messages:
+ 'development mode \ndebugging primitives:':
+ 'primitivas de depuraci\u00F3n\ndel modo desarrollador:',
+ 'find blocks':
+ 'busca bloques',
+ 'show primitives':
+ 'mostrar primitivas',
+ 'hide primitives':
+ 'ocultar primitivas',
+
// motion:
'Stage selected:\nno motion primitives':
- 'Escenario seleccionado:\nno primitivos de movimiento\n'
- + 'disponibles',
+ 'Escenario seleccionado:\nno hay primitivas de movimiento\ndisponibles',
'move %n steps':
'mover %n pasos',
@@ -305,31 +383,44 @@ SnapTranslator.dict.es = {
'go to %dst':
'ir a %dst',
'glide %n secs to x: %n y: %n':
- 'deslizar %n segs a x: %n y: %n',
+ 'deslizar en %n segs a x: %n y: %n',
'change x by %n':
- 'cambiar x por %n',
+ 'cambiar x en %n',
'set x to %n':
'fijar x a %n',
'change y by %n':
- 'cambiar y por %n',
+ 'cambiar y en %n',
'set y to %n':
'fijar y a %n',
'if on edge, bounce':
- 'rebotar si est\u0061 tocando un borde',
+ 'rebotar si toca un borde',
'x position':
- 'posici\u00F3n en x',
+ 'posici\u00F3n x',
'y position':
- 'posici\u00F3n en y',
- 'direction':
- 'direcci\u00F3n',
+ 'posici\u00F3n y',
+ // already defined
+ // 'direction':
+ // 'direcci\u00F3n',
+
+ // %dir values for (point in direction %dir):
+ '(90) right':
+ '(90) derecha',
+ '(-90) left':
+ '(-90) izquierda',
+ '(0) up':
+ '(0) arriba',
+ '(180) down':
+ '(180) abajo',
// looks:
'switch to costume %cst':
- 'cambiar el disfraz a %cst',
+ 'cambiar al disfraz %cst',
'next costume':
'siguiente disfraz',
'costume #':
'# de disfraz',
+ 'costume name':
+ 'nombre del disfraz',
'say %s for %n secs':
'decir %s por %n segs',
'say %s':
@@ -338,18 +429,14 @@ SnapTranslator.dict.es = {
'pensar %s por %n segs',
'think %s':
'pensar %s',
- 'Hello!':
- '\u00A1Hola!',
- 'Hmm...':
- 'mmm...',
'change %eff effect by %n':
- 'cambiar %eff efecto por %n',
+ 'cambiar efecto %eff en %n',
'set %eff effect to %n':
- 'fijar %eff efecto a %n',
+ 'fijar efecto %eff a %n',
'clear graphic effects':
'quitar efectos gr\u00E1ficos',
'change size by %n':
- 'cambiar tama\u00F1o por %n',
+ 'cambiar tama\u00F1o en %n',
'set size to %n %':
'fijar tama\u00F1o a %n %',
'size':
@@ -361,33 +448,100 @@ SnapTranslator.dict.es = {
'go to front':
'enviar al frente',
'go back %n layers':
- 'enviar hacia atr\u00E1s %n capas',
+ 'enviar %n capas hacia atr\u00E1s',
- 'development mode \ndebugging primitives:':
- 'modo de desarrollo \nprimitivos de depuraci\u00F3n',
+ // looks' development mode primitives:
+ 'wardrobe': // objects.js:401
+ 'guardarropa',
'console log %mult%s':
- 'log de consola: %mult%s',
+ 'registrar en consola %mult%s',
'alert %mult%s':
- 'alerta: %mult%s',
+ 'mostrar mensaje %mult%s',
+ 'save %imgsource as costume named %s':
+ 'guardar %imgsource en disfraz %s',
+
+ // default values for (say %s):
+ 'Hello!':
+ '\u00A1Hola!',
+ 'Hello, World!':
+ '\u00A1Hola, Mundo!',
+
+ // default values for (think %s):
+ 'Hmm...':
+ 'Mmm...',
+
+ // %eff values for (change %eff effect by %n):
+ 'color':
+ 'color',
+ 'fisheye':
+ 'ojo de pez',
+ 'whirl':
+ 'remolino',
+ 'pixelate':
+ 'pixelado',
+ 'mosaic':
+ 'mosaico',
+ 'duplicate':
+ 'duplicar',
+ 'negative':
+ 'negativo',
+ 'comic':
+ 'historieta',
+ 'confetti':
+ 'confeti',
+ 'saturation':
+ 'saturaci\u00F3n',
+ 'brightness':
+ 'brillo',
+ 'ghost':
+ 'fantasma',
+
+ // %imgsource values for (save %imgsource as costume named %s):
+ 'pen trails':
+ 'rastro del l\u00E1piz',
+ 'stage image':
+ 'imagen del escenario',
// sound:
'play sound %snd':
- 'tocar sonido %snd',
+ 'reproducir sonido %snd',
'play sound %snd until done':
- 'tocar sonido %snd y esperar',
+ 'reproducir sonido %snd y esperar',
'stop all sounds':
'detener todos los sonidos',
'rest for %n beats':
'silencio por %n pulsos',
- 'play note %n for %n beats':
- 'tocar nota %n por %n pulsos',
+ 'play note %note for %n beats':
+ 'tocar nota %note por %n pulsos',
+ 'set instrument to %inst':
+ 'fijar instrumento a %inst',
'change tempo by %n':
- 'cambiar tempo por %n',
+ 'cambiar tempo en %n',
'set tempo to %n bpm':
'fijar tempo a %n',
'tempo':
'tempo',
+ // sound development mode blocks
+ 'jukebox': // objects.js:474
+ 'gramola',
+
+ // %note values for (play note %note for %n beats):
+ // Notes can be translated indeed but do it would break the piano layout
+ // example:
+ // 'Eb (63)':
+ // '(63) Mi♭',
+
+ // %inst values for (set instrument to %inst):
+ '(1) sine':
+ '(1) \u223F\u223F (onda sinusoidal)',
+ '(2) square':
+ '(2) \u238D\u238D (onda cuadrada)',
+ '(3) sawtooth':
+ '(3) \u2A58\u2A58 (onda dentada)',
+ '(4) triangle':
+ '(4) \u22C0\u22C0 (onda triangular)',
+
// pen:
'clear':
'borrar',
@@ -398,35 +552,44 @@ SnapTranslator.dict.es = {
'set pen color to %clr':
'fijar color de l\u00E1piz a %clr',
'change pen color by %n':
- 'cambiar color de l\u00E1piz por %n',
+ 'cambiar color de l\u00E1piz en %n',
'set pen color to %n':
'fijar color de l\u00E1piz a %n',
'change pen shade by %n':
- 'cambiar intensidad de l\u00E1piz por %n',
+ 'cambiar brillo de l\u00E1piz en %n',
'set pen shade to %n':
- 'fijar intensidad de l\u00E1piz a %n',
+ 'fijar brillo de l\u00E1piz a %n',
'change pen size by %n':
- 'cambiar tama\u00F1o de l\u00E1piz por %n',
+ 'cambiar tama\u00F1o de l\u00E1piz en %n',
'set pen size to %n':
'fijar tama\u00F1o de l\u00E1piz a %n',
'stamp':
'sellar',
+ 'fill':
+ 'llenar',
+ // already defined
+ // 'pen trails':
+ // 'rastro del l\u00E1piz',
// control:
'when %greenflag clicked':
- 'Al presionar %greenflag',
+ 'cuando se pulse %greenflag',
'when %keyHat key pressed':
- 'Al presionar tecla %keyHat',
- 'when I am clicked':
- 'Al presionar Objeto',
+ 'cuando se pulse la tecla %keyHat',
+ 'when I am %interaction':
+ 'cuando me %interaction',
+ 'when %b':
+ 'cuando %b',
'when I receive %msgHat':
- 'Al recibir %msgHat',
+ 'cuando me llegue %msgHat',
'broadcast %msg':
'enviar mensaje %msg',
'broadcast %msg and wait':
'enviar mensaje %msg y esperar',
- 'Message name':
- 'Nombre de mensaje',
+ 'message':
+ 'mensaje',
+ 'warp %c':
+ 'instrucci\u00F3n at\u00F3mica %c',
'wait %n secs':
'esperar %n segs',
'wait until %b':
@@ -440,567 +603,59 @@ SnapTranslator.dict.es = {
'if %b %c':
'si %b %c',
'if %b %c else %c':
- 'si %b %c si no %c',
+ 'si %b %c sino %c',
'report %s':
'reportar %s',
- 'stop block':
- 'detener bloque',
- 'stop script':
- 'detener programa',
- 'stop all %stop':
- 'detener todo %stop',
+ 'stop %stopChoices':
+ 'detener %stopChoices',
+
'run %cmdRing %inputs':
- 'correr %cmdRing %inputs',
+ 'ejecutar %cmdRing %inputs',
'launch %cmdRing %inputs':
'iniciar %cmdRing %inputs',
'call %repRing %inputs':
'llamar %repRing %inputs',
+ 'tell %spr to %cmdRing %inputs':
+ 'decir a %spr que %cmdRing %inputs',
+ 'ask %spr for %repRing %inputs':
+ 'preguntar a %spr por %repRing %inputs',
'run %cmdRing w/continuation':
- 'correr %cmdRing con continuaci\u00F3n',
+ 'ejecutar %cmdRing con continuaci\u00F3n',
'call %cmdRing w/continuation':
'llamar %cmdRing con continuaci\u00F3n',
- 'warp %c':
- 'ejecutar en modo turbo %c',
-
- // sensing:
- 'touching %col ?':
- '\u00BFtocando %col ?',
- 'touching %clr ?':
- '\u00BFtocando el color %clr ?',
- 'color %clr is touching %clr ?':
- '\u00BFcolor %clr tocando %clr ?',
- 'ask %s and wait':
- 'preguntar %s y esperar',
- 'what\'s your name?':
- '\u00BFCu\u00E1l es tu nombre?',
- 'answer':
- 'respuesta',
- 'mouse x':
- 'x del rat\u00F3n',
- 'mouse y':
- 'y del rat\u00F3n',
- 'mouse down?':
- '\u00BFrat\u00F3n abajo?',
- 'key %key pressed?':
- '\u00BFtecla %key presionada?',
- 'distance to %dst':
- 'distancia a %dst',
- 'reset timer':
- 'reiniciar cron\u00F3metro',
- 'timer':
- 'cron\u00F3metro',
- 'http:// %s':
- 'http:// %s',
-
- 'filtered for %clr':
- 'filtrado para %clr',
- 'stack size':
- 'tama\u00F1o de pila',
- 'frames':
- 'marcos',
-
- // operators:
- '%n mod %n':
- '%n mod %n',
- 'round %n':
- 'redondear %n',
- '%fun of %n':
- '%fun de %n',
- 'pick random %n to %n':
- 'n\u00FAmero al azar entre %n y %n',
- '%b and %b':
- '%b y %b',
- '%b or %b':
- '%b o %b',
- 'not %b':
- 'no %b',
- 'true':
- 'cierto',
- 'false':
- 'falso',
- 'join %words':
- 'unir %words',
- 'hello':
- 'hola',
- 'world':
- 'mundo',
- 'letter %n of %s':
- 'letra %n de %s',
- 'length of %s':
- 'longitud de %s',
- 'unicode of %s':
- 'UniC\u00F3digo de %s',
- 'unicode %n as letter':
- 'UniC\u00F3digo %n como letra',
- 'is %s a %typ ?':
- '\u00BFes %s un %typ ?',
- 'is %s identical to %s ?':
- '\u00BFes %s id\u00E9ntico a %s ?',
-
- 'type of %s':
- 'tipo de %s',
-
- // variables:
- 'Make a variable':
- 'Crear un variable',
- 'Variable name':
- 'Nombre de variable',
- 'Delete a variable':
- 'Borrar un variable',
-
- 'set %var to %s':
- 'fijar %var a %s',
- 'change %var by %n':
- 'cambiar %var por %n',
- 'show variable %var':
- 'mostrar variable %var',
- 'hide variable %var':
- 'esconder variable %var',
- 'script variables %scriptVars':
- 'variables de programa %scriptVars',
-
- // lists:
- 'list %exp':
- 'lista %exp',
- '%s in front of %l':
- '%s en frente de %l',
- 'item %idx of %l':
- 'elemento %idx de %l',
- 'all but first of %l':
- 'todo menos la primera de %l',
- 'length of %l':
- 'longitud de %l',
- '%l contains %s':
- '%l contiene %s',
- 'thing':
- 'cosa',
- 'add %s to %l':
- 'agregar %s a %l hinzu',
- 'delete %ida of %l':
- 'borrar %ida de %l',
- 'insert %s at %idx of %l':
- 'insertar %s en %idx de %l',
- 'replace item %idx of %l with %s':
- 'remplazar elemento %idx de %l con %s',
-
- // other
- 'Make a block':
- 'Crear un bloque',
-
- // menus
- // snap menu
- 'About...':
- 'Acerca de...',
- 'Snap! website':
- 'Sitio web de Snap!',
- 'Download source':
- 'Bajar recurso',
- 'Switch back to user mode':
- 'Regresar a modo de usuario',
- 'disable deep-Morphic\ncontext menus\nand show user-friendly ones':
- 'inhabilitar M\u00F3rfica-profunda\nmen\u0075s contextuales\ny mostrar unos f\u0061ciles de utilizar',
- 'Switch to dev mode':
- 'Cambiar a modo de elaborador',
- 'enable Morphic\ncontext menus\nand inspectors,\nnot user-friendly!':
- 'habilitar men\u0075s \nM\u00F3rficos contextuales\n e inspectores,\n\u00A1no f\u0061ciles de utilizar! ',
-
- // project menu
- 'Project notes...':
- 'Notas del proyecto...',
- 'New':
- 'Nuevo',
- 'Open...':
- 'Abrir...',
- 'Save':
- 'Guardar',
- 'Save As...':
- 'Guardar como...',
- 'Import...':
- 'Importar...',
- 'file menu import hint':
- 'men\u00FA de archivo de importaci\u00F3n indirecta',
- 'Export project as plain text...':
- 'Exportar proyecto como texto...',
- 'Export project...':
- 'Exportar proyecto...',
- 'show project data as XML\nin a new browser window':
- 'mostrar informaci\u00F3n de proyecto como XML\nen una nueva ventana',
- 'Export blocks...':
- 'Exportar bloques...',
- 'show global custom block definitions as XML\nin a new browser window':
- 'mostrar definiciones de bloques globales personalizados como XML\nen una nueva ventana',
- 'Import tools':
- 'Herramientas de importaci\u00F3n',
- 'load the official library of\npowerful blocks':
- 'cargar la biblioteca oficial de\nbloques poderosos',
-
- // settings menu
- 'Language...':
- 'Idioma...',
- 'Blurred shadows':
- 'Sombras borrosas',
- 'uncheck to use solid drop\nshadows and highlights':
- 'desmarque para usar sombras s\u00F3lidas \ne iluminaciones',
- 'check to use blurred drop\nshadows and highlights':
- 'marcar para usar sombras borrosas\ne iluminaciones',
- 'Zebra coloring':
- 'Coloraci\u00F3n de cebra',
- 'check to enable alternating\ncolors for nested blocks':
- 'marcar para habilitar alternaci\u00F3n\nde colores para bloques anidados',
- 'uncheck to disable alternating\ncolors for nested block':
- 'desmarcar para inhabilitar alternaci\u00F3n\nde colores para bloques anidados',
- 'Dynamic input labels':
- 'Etiquetas de entradas din\u00E1micas',
- 'uncheck to disable dynamic\nlabels for variadic inputs':
- 'desmarcar para inhabilitar etiquetas\ndin\u00E1micas para entradas varidic',
- 'check to enable dynamic\nlabels for variadic inputs':
- 'marcar para habilitar etiquetas\ndin\u00E1micas para entradas varidic',
- 'Prefer empty slot drops':
- 'Preferir ranuras de gotas vac\u00EDas',
- 'settings menu prefer empty slots hint':
- 'men\u00FA de ajustes prefiere pistas de ranuras vac\u00EDas',
- 'uncheck to allow dropped\nreporters to kick out others':
- 'desmarcar para permitir reporteros\nca\u00EDdos para echar a otros',
- 'Long form input dialog':
- 'di\u00E1logo de entradas de forma larga',
- 'check to always show slot\ntypes in the input dialog':
- 'marcar para siempre mostrar tipos\nde espacios en el di\u00E1logo de insumo',
- 'uncheck to use the input\ndialog in short form':
- 'desmarcar para usar el di\u00E1logo\nde insumo en forma corta',
- 'Virtual keyboard':
- 'Teclado virtual',
- 'uncheck to disable\nvirtual keyboard support\nfor mobile devices':
- 'desmarcar para inhabilitar\nsoporte al teclado virtual\npara dispositivos m\u00F3viles',
- 'check to enable\nvirtual keyboard support\nfor mobile devices':
- 'marcar para habilitar\nsoporte para el teclado virtual\npara dispositivos m\u00F3viles',
- 'Input sliders':
- 'Deslizadores de insumo',
- 'uncheck to disable\ninput sliders for\nentry fields':
- 'desmarcar para inhabilitar\ndeslizadores de insumo para\ncampos de entrada',
- 'check to enable\ninput sliders for\nentry fields':
- 'marcar para habilitar\ndeslizadores de entrada para\ncampos de entrada',
- 'Clicking sound':
- 'Sonido de clic',
- 'uncheck to turn\nblock clicking\nsound off':
- 'desmarcar para encender\nbloquear clic\napagar sonido',
- 'check to turn\nblock clicking\nsound on':
- 'marcar para encender\nbloque de clic\nencender sonido',
- 'Animations':
- 'Animaciones',
- 'uncheck to disable\nIDE animations':
- 'desmarcar para inhabilitar\nanimaciones IDE',
- 'check to enable\nIDE animations':
- 'marcar para habilitar\nanimaciones IDE',
- 'Thread safe scripts':
- 'Programas seguros para serie',
- 'uncheck to allow\nscript reentrancy':
- 'desmarcar para permitir\nreingreso de programa',
- 'check to disallow\nscript reentrancy':
- 'marcar para no permitir\nreingreso de programa',
-
- // inputs
'with inputs':
- 'con entradas',
+ 'con argumentos',
'input names:':
- 'nombres de entradas:',
+ 'par\u00E1metros:',
'Input Names:':
'Nombres de entradas:',
'input list:':
- 'lista de entradas:',
+ 'con lista de argumentos:',
- // context menus:
- 'help':
- 'ayuda',
+ 'when I start as a clone':
+ 'cuando comience como clon',
+ 'create a clone of %cln':
+ 'crear clon de %cln',
+ 'a new clone of %cln':
+ 'un nuevo clon de %cln',
+ 'delete this clone':
+ 'eliminar este clon',
+ 'pause all %pause':
+ 'pausar todos %pause',
- // blocks:
- 'help...':
- 'ayuda...',
- 'relabel...':
- 'renombrar...',
- 'duplicate':
- 'duplicar',
- 'make a copy\nand pick it up':
- 'crear una copia y recogerla',
- 'only duplicate this block':
- 's\u00F3lo duplicar este bloque',
- 'delete':
- 'borrar',
- 'script pic...':
- 'foto de programa...',
- 'open a new window\nwith a picture of this script':
- 'abrir una nueva ventana\ncon una foto de este programa',
- 'ringify':
- 'zumbar',
- 'unringify':
- 'deszumbar',
-
- // custom blocks:
- 'delete block definition...':
- 'borrar definici\u00F3n de bloque',
- 'edit...':
- 'editar...',
-
- // sprites:
- 'edit':
- 'editar',
- 'export...':
- 'exportar...',
-
- // stage:
- 'show all':
- 'mostrar todos',
-
- // scripting area
- 'clean up':
- 'limpiar',
- 'arrange scripts\nvertically':
- 'alinear programas\nverticalmente',
- 'add comment':
- 'agregar comentario',
- 'make a block...':
- 'crear un bloque...',
-
- // costumes
- 'rename':
- 'renombrar',
- 'export':
- 'exportar',
- 'rename costume':
- 'renombrar disfraz',
-
- // sounds
- 'Play sound':
- 'Tocar sonido',
- 'Stop sound':
- 'Detener sonido',
- 'Stop':
- 'Detener',
- 'Play':
- 'Tocar',
- 'rename sound':
- 'renombrar sonido',
-
- // dialogs
- // buttons
- 'OK':
- 'OK',
- 'Ok':
- 'Ok',
- 'Cancel':
- 'Cancelar',
- 'Yes':
- 'Si',
- 'No':
- 'No',
-
- // help
- 'Help':
- 'Ayuda',
-
- // Project Manager
- 'Untitled':
- 'Sin T\u00EDtulo',
- 'Open Project':
- 'Abrir Proyecto',
- '(empty)':
- '(vacio)',
- 'Saved!':
- '\u00A1Guardado!',
- 'Delete Project':
- 'Borrar Proyecto',
- 'Are you sure you want to delete':
- '\u00BFEst\u00E1s seguro que deseas borrar?',
- 'rename...':
- 'renombrar...',
-
- // costume editor
- 'Costume Editor':
- 'Editor de disfraz',
- 'click or drag crosshairs to move the rotation center':
- 'da clic o arrastra punto de mira para mover el centro de rotaci\u00F3n',
-
- // project notes
- 'Project Notes':
- 'Notas de proyecto',
-
- // new project
- 'New Project':
- 'Nuevo Proyecto',
- 'Replace the current project with a new one?':
- '\u00BFReemplazar este proyecto con uno nuevo?',
-
- // save project
- 'Save Project As...':
- 'Guardar Proyecto Como...',
-
- // export blocks
- 'Export blocks':
- 'Exportar bloques',
- 'Import blocks':
- 'Importar bloques',
- 'this project doesn\'t have any\ncustom global blocks yet':
- 'este proyecto no tiene ning\u00FAn bloque personalizado todab\u00EDa',
- 'select':
- 'seleccionar',
- 'all':
- 'todos',
- 'none':
- 'ninguno',
-
- // variable dialog
- 'for all sprites':
- 'para todos los objetos',
- 'for this sprite only':
- 'para este objeto solamente',
-
- // block dialog
- 'Change block':
- 'Cambiar bloque',
- 'Command':
- 'Comando',
- 'Reporter':
- 'Reportero',
- 'Predicate':
- 'Predicado',
-
- // block editor
- 'Block Editor':
- 'Bloquear editor',
- 'Apply':
- 'Aplicar',
-
- // block deletion dialog
- 'Delete Custom Block':
- 'Borrar Bloque Personalizado',
- 'block deletion dialog text':
- 'supreci\u00F3n de bloque de texto de di\u00E1logo',
-
- // input dialog
- 'Create input name':
- 'Crear nombre de insumo',
- 'Edit input name':
- 'Editar nombre de insumo',
- 'Edit label fragment':
- 'Editar fragmento de etiqueta',
- 'Title text':
- 'Texto de t\u00EDtulo',
- 'Input name':
- 'Nombre de insumo',
- 'Delete':
- 'Borrar',
- 'Object':
- 'Objeto',
- 'Number':
- 'N\u00FAmero',
- 'Text':
- 'Texto',
- 'List':
- 'Lista',
- 'Any type':
- 'Cualquier tipo',
- 'Boolean (T/F)':
- 'Booleano (C/F)',
- 'Command\n(inline)':
- 'Comando\n(en l\u00EDnea)',
- 'Command\n(C-shape)':
- 'Comando\n(forma C)',
- 'Any\n(unevaluated)':
- 'Cualquier\n(sin evaluar)',
- 'Boolean\n(unevaluated)':
- 'Booleano\n(sin evaluar)',
- 'Single input.':
- 'Entrada sola.',
- 'Default Value:':
- 'Valor Predeterminado:',
- 'Multiple inputs (value is list of inputs)':
- 'M\u00FAltiples entradas (valor es lista de insumos)',
- 'Upvar - make internal variable visible to caller':
- 'Crear variable interno visible al llamador',
-
- // About Snap
- 'About Snap':
- 'Acerca de Snap',
- 'Back...':
- 'Atr\u00E1s...',
- 'License...':
- 'Licencia...',
- 'Modules...':
- 'M\u00F3dulos...',
- 'Credits...':
- 'Creditos...',
- 'Translators...':
- 'Traductores',
- 'License':
- 'Licencia',
- 'current module versions:':
- 'versiones del m\u00F3dulo actual',
- 'Contributors':
- 'Contribuidores',
- 'Translations':
- 'Traducciones',
-
- // variable watchers
- 'normal':
- 'normal',
- 'large':
- 'grande',
- 'slider':
- 'deslizador',
- 'slider min...':
- 'm\u00EDnimo de deslizador...',
- 'slider max...':
- 'm\u00E1ximo de deslizador...',
- 'Slider minimum value':
- 'm\u00EDnimo valor de deslizador',
- 'Slider maximum value':
- 'm\u00E1ximo valor de deslizador',
-
- // list watchers
- 'length: ':
- 'longitud: ',
-
- // coments
- 'add comment here...':
- 'agregar comentario aqu\u00ED',
-
- // drow downs
- // directions
- '(90) right':
- '(90) derecha',
- '(-90) left':
- '(-90) izquierda',
- '(0) up':
- '(0) arriba',
- '(180) down':
- '(180) abajo',
-
- // collision detection
- 'mouse-pointer':
- 'puntero del rat\u00F3n',
- 'edge':
- 'borde',
- 'pen trails':
- 'rastro del l\u00E1piz',
-
- // costumes
- 'Turtle':
- 'Tortuga',
-
- // graphical effects
- 'ghost':
- 'fantasma',
-
- // keys
+ // %keyHat values for (when %keyHat key pressed):
'space':
'espacio',
'up arrow':
- 'flecha de arriba',
+ '↑ (flecha arriba)',
'down arrow':
- 'flecha de abajo',
+ '↓ (flecha abajo)',
'right arrow':
- 'flecha derecha',
+ '→ (flecha derecha)',
'left arrow':
- 'flecha izquierda',
+ '← (flecha izquierda)',
+ 'any key':
+ 'cualquier tecla',
'a':
'a',
'b':
@@ -1074,41 +729,288 @@ SnapTranslator.dict.es = {
'9':
'9',
- // messages
- 'new...':
- 'nuevo...',
+ // %interaction values for (when I am %interaction):
+ // In spanish read as "cuando me %interaction"
+ 'clicked':
+ 'hagan clic',
+ 'pressed':
+ 'pulsen',
+ 'dropped':
+ 'arrastren y me suelten',
+ 'mouse-entered':
+ 'toquen con el rat\u00F3n',
+ 'mouse-departed':
+ 'dejen de tocar con el rat\u00F3n',
+ 'scrolled-up':
+ 'giren la rueda del rat\u00F3n hacia abajo',
+ 'scrolled-down':
+ 'giren la rueda del rat\u00F3n hacia arriba',
- // math functions
+ // "any message" for (when I receive %msgHat):
+ 'any message':
+ 'cualquier mensaje',
+
+ // "new..." for (broadcast %msg):
+ 'new...':
+ 'nuevo mensaje...',
+
+ // %stopChoices values for (stop %stopChoices):
+ 'all':
+ 'todos',
+ 'this script':
+ 'este programa',
+ 'this block':
+ 'este bloque',
+ 'all but this script':
+ 'todos los programas excepto este',
+ 'other scripts in sprite':
+ 'el resto de programas del objeto',
+
+ // "myself" for (create a clone of %cln)
+ 'myself':
+ 'm\u00ED mismo',
+
+ // New message name dialog:
+ 'Message name':
+ 'Nombre de mensaje',
+
+ // sensing:
+ 'touching %col ?':
+ '\u00BFtocando %col ?',
+ 'touching %clr ?':
+ '\u00BFtocando el color %clr ?',
+ 'color %clr is touching %clr ?':
+ '\u00BFcolor %clr tocando %clr ?',
+ 'ask %s and wait':
+ 'preguntar %s y esperar',
+ 'answer':
+ 'respuesta',
+ 'mouse x':
+ 'rat\u00F3n x',
+ 'mouse y':
+ 'rat\u00F3n y',
+ 'mouse down?':
+ '\u00BFrat\u00F3n pulsado?',
+ 'key %key pressed?':
+ '\u00BFtecla %key pulsada?',
+ '%rel to %dst':
+ '%rel a %dst',
+ 'reset timer':
+ 'reiniciar cron\u00F3metro',
+ 'timer':
+ 'cron\u00F3metro',
+ '%att of %spr':
+ '%att de %spr',
+ 'my %get':
+ 'mi(s) %get',
+ 'url %s':
+ 'url %s',
+ 'turbo mode?':
+ '\u00BFmodo turbo?',
+ 'set turbo mode to %b':
+ 'fijar modo turbo a %b',
+ 'current %dates':
+ '%dates actual',
+
+ // sensing's development mode primitives:
+ 'processes':
+ 'procesos',
+ 'filtered for %clr':
+ 'filtrado por %clr',
+ 'stack size':
+ 'tama\u00F1o de pila',
+ 'frames':
+ 'cuadros',
+
+ // %col values for (touching %col ?):
+ 'mouse-pointer':
+ 'puntero del rat\u00F3n',
+ 'edge':
+ 'borde del escenario',
+ // already defined
+ // 'pen trails':
+ // 'rastro del l\u00E1piz',
+
+ // default value for (ask %s and wait):
+ 'what\'s your name?':
+ '\u00BFCu\u00E1l es tu nombre?',
+
+ // %rel values for (%rel to %dst):
+ 'distance':
+ 'distancia',
+ // already defined
+ 'direction':
+ 'direcci\u00F3n',
+
+ // %get values for (my %get):
+ 'neighbors':
+ 'vecinos',
+ 'self':
+ 'mismo',
+ 'other sprites':
+ 'otros objetos',
+ 'clones':
+ 'clones',
+ 'other clones':
+ 'otros clones',
+ 'parts':
+ 'partes',
+ 'anchor':
+ 'anclaje',
+ 'stage':
+ 'escenario',
+ 'children':
+ 'hijos',
+ 'parent':
+ 'padre',
+ 'temporary?':
+ '\u00BFsoy temporal?',
+ 'name':
+ 'nombre',
+ 'costumes':
+ 'disfraces',
+ 'sounds':
+ 'sonidos',
+ 'dangling?':
+ '\u00BFcuelgo de otro objeto?',
+ 'rotation x':
+ 'rotaci\u00F3n x',
+ 'rotation y':
+ 'rotaci\u00F3n y',
+ 'center x':
+ 'centro x',
+ 'center y':
+ 'centro y',
+
+ // %dates values for (current %dates):
+ 'year':
+ 'a\u00F1o',
+ 'month':
+ 'mes',
+ 'date':
+ 'd\u00EDa',
+ 'day of week':
+ 'd\u00EDa de la semana',
+ 'hour':
+ 'hora',
+ 'minute':
+ 'minuto',
+ 'second':
+ 'segundo',
+ 'time in milliseconds':
+ 'tiempo en milisegundos',
+
+ // operators:
+ '%n mod %n':
+ '%n m\u00F3dulo %n',
+ 'round %n':
+ 'redondear %n',
+ '%fun of %n':
+ '%fun de %n',
+ 'pick random %n to %n':
+ 'n\u00FAmero al azar entre %n y %n',
+ '%b and %b':
+ '%b y %b',
+ '%b or %b':
+ '%b o %b',
+ 'not %b':
+ 'no %b',
+ 'true':
+ 'verdadero',
+ 'false':
+ 'falso',
+ 'join %words':
+ 'unir %words',
+ 'split %s by %delim':
+ 'separar %s por %delim',
+ 'letter %idx of %s':
+ 'letra %idx de %s',
+ 'length of %s':
+ 'longitud de %s',
+ 'unicode of %s':
+ 'unic\u00F3digo de %s',
+ 'unicode %n as letter':
+ 'unic\u00F3digo %n como letra',
+ 'is %s a %typ ?':
+ '\u00BFes %s un %typ ?',
+ 'is %s identical to %s ?':
+ '\u00BFes %s id\u00E9ntico a %s ?',
+ 'JavaScript function ( %mult%s ) { %code }':
+ 'funci\u00F3n JavaScript ( %mult%s ) { %code }',
+
+ // operators' developer mode primitives:
+ 'type of %s':
+ 'tipo de %s',
+ '%txtfun of %s':
+ '%txtfun de %s',
+ 'compile %repRing':
+ 'compilar %repRing',
+
+ // %fun values for (%fun of %n):
'abs':
- 'abs',
+ 'valor absoluto',
+ 'ceiling':
+ 'techo', // https://es.wikipedia.org/wiki/Funciones_de_parte_entera
+ 'floor':
+ 'suelo',
'sqrt':
'ra\u00EDz cuadrada',
'sin':
- 'sin',
+ 'seno',
'cos':
- 'cos',
+ 'coseno',
'tan':
- 'tan',
+ 'tangente',
'asin':
- 'asin',
+ 'arcoseno',
'acos':
- 'acos',
+ 'arcocoseno',
'atan':
- 'atan',
+ 'arcotangente',
'ln':
'ln',
+ 'log':
+ 'log',
'e^':
'e^',
+ '10^':
+ '10^',
- // data types
+ // default values for (join %words):
+ 'hello':
+ 'hola',
+ 'world':
+ 'mundo',
+
+ // %delim values (split by %delim)
+ 'letter':
+ 'letra',
+ 'whitespace':
+ 'espacio',
+ 'line':
+ 'l\u00EDnea',
+ 'tab':
+ 'tabulador',
+ 'cr':
+ 'retorno de carro',
+ 'csv':
+ 'coma',
+
+ // %typ values for (is %s a %typ ?)
'number':
'n\u00FAmero',
'text':
'texto',
'Boolean':
- 'Booleano',
+ 'booleano',
'list':
'lista',
+ 'sprite':
+ 'objeto',
+ 'costume':
+ 'disfraz',
+ 'sound':
+ 'sonido',
'command':
'comando',
'reporter':
@@ -1116,9 +1018,1680 @@ SnapTranslator.dict.es = {
'predicate':
'predicado',
- // list indices
+ // %txtfun values for (%txtfun of %s):
+ 'encode URI':
+ 'codificar URI',
+ 'decode URI':
+ 'decodificar URI',
+ 'encode URI component':
+ 'codificar componente URI',
+ 'decode URI component':
+ 'decodificar componente URI',
+ 'XML escape':
+ 'escapar XML',
+ 'XML unescape':
+ 'desescapar XML',
+ 'hex sha512 hash':
+ 'hash sha512 (hexadecimal)',
+
+ // variables:
+ 'Make a variable':
+ 'Declarar variable',
+ 'Script variable name':
+ 'Nombre de variable de programa',
+ 'Variable name':
+ 'Nombre de variable',
+ 'Delete a variable':
+ 'Borrar variable',
+
+ 'set %var to %s':
+ 'asignar a %var el valor %s',
+ 'change %var by %n':
+ 'incrementar %var en %n',
+ 'show variable %var':
+ 'mostrar variable %var',
+ 'hide variable %var':
+ 'esconder variable %var',
+ 'script variables %scriptVars':
+ 'variables de programa %scriptVars',
+ 'inherit %shd':
+ 'heredar %shd',
+ 'a variable of name \'':
+ 'no existe ninguna variable llamada\n\'',
+ '\'\ndoes not exist in this context':
+ '\'\nen este contexto',
+
+ // lists:
+ 'list %exp':
+ 'lista %exp',
+ '%s in front of %l':
+ '%s delante de %l',
+ 'item %idx of %l':
+ 'elemento %idx de %l',
+ 'all but first of %l':
+ '%l sin el primer elemento',
+ 'length of %l':
+ 'longitud de %l',
+ '%l contains %s':
+ '\u00BF %l contiene %s ?',
+ 'add %s to %l':
+ 'a\u00F1adir %s a %l',
+ 'delete %ida of %l':
+ 'borrar %ida de %l',
+ 'insert %s at %idx of %l':
+ 'insertar %s en %idx de %l',
+ 'replace item %idx of %l with %s':
+ 'reemplazar elemento %idx de %l con %s',
+
+ // lists' development mode blocks:
+ 'map %repRing over %l':
+ 'mapear %repRing sobre %l',
+ 'for %upvar in %l %cl':
+ 'para %upvar en %l %cl',
+ 'show table %l':
+ 'mostrar tabla %l',
+
+ // %idx values for (item %idx of %l):
'last':
'\u00FAltimo',
'any':
- 'cualquier'
+ 'aleatorio',
+
+ // default value for (%l contains %s):
+ 'thing':
+ 'cosa',
+
+ // table view dialog:
+ 'Table view':
+ 'Visor de tablas',
+
+ // variable watchers
+ 'normal':
+ 'normal',
+ 'large':
+ 'grande',
+ 'slider':
+ 'deslizador',
+ 'slider min...':
+ 'm\u00EDnimo valor del deslizador...',
+ 'slider max...':
+ 'm\u00E1ximo valor del deslizador...',
+ 'import...':
+ 'importar...',
+
+ // slider dialog:
+ 'Slider minimum value':
+ 'M\u00EDnimo valor del deslizador',
+ 'Slider maximum value':
+ 'M\u00E1ximo valor del deslizador',
+
+ // list watchers
+ 'length: ':
+ 'longitud: ',
+
+ // other
+ 'Make a block':
+ 'Crear bloque',
+
+ // other development mode blocks:
+ 'code of %cmdRing':
+ 'c\u00F3digo de %cmdRing',
+ 'map %cmdRing to %codeKind %code':
+ 'mapear %cmdRing a %codeKind %code',
+ 'map %mapValue to code %code':
+ 'mapear %mapValue a c\u00F3digo %code',
+ 'map %codeListPart of %codeListKind to code %code':
+ 'mapear %codeListPart de %codeListKind a c\u00F3digo %code',
+
+ // %cmdRing values for (map %cmdRing to %codeKind %code):
+ 'String':
+ 'String',
+ 'Number':
+ 'n\u00FAmero',
+ // already defined
+ // 'true':
+ // 'verdadero',
+ // 'false':
+ // 'falso',
+
+ // %codeKind values for (map %cmdRing to %codeKind %code)
+ 'code':
+ 'c\u00F3digo',
+ 'header':
+ 'cabecera',
+
+ // %mapValue values for (map %mapValue to code %code):
+ // already defined
+ // 'list':
+ // 'lista',
+ 'item':
+ 'elemento',
+ 'delimiter':
+ 'delimitador',
+
+ // $codeListKind values for (map %codeListPart of %codeListKind to code %code)
+ 'collection':
+ 'colecci\u00F3n',
+ 'variables':
+ 'variables',
+ 'parameters':
+ 'par\u00E1metros',
+
+ // menus
+ // snap menu
+ 'About...':
+ 'Acerca de...',
+ 'Reference manual':
+ 'Manual de referencia',
+ 'Snap! website':
+ 'Sitio web de Snap!',
+ 'Download source':
+ 'Descargar c\u00F3digo fuente',
+ 'Switch back to user mode':
+ 'Regresar a modo usuario',
+ 'disable deep-Morphic\ncontext menus\nand show user-friendly ones':
+ 'desactiva los men\u0075s contextuales de Morphic\ny muestra unos m\u00E1s f\u00E1ciles de utilizar',
+ 'Switch to dev mode':
+ 'Cambiar a modo desarrollador',
+ 'enable Morphic\ncontext menus\nand inspectors,\nnot user-friendly!':
+ 'activa los men\u0075s contextuales\n e inspectores de Morphic\n(\u00A1no son f\u00E1ciles de utilizar)',
+ 'entering development mode.\n\nerror catching is turned off,\nuse the browser\'s web console\nto see error messages.':
+ 'Se ha activado el modo desarrollador.\n\nEl cacheo de errores est\u00E1 desactivado,\nusa la consola del navegador\npara ver mensajes de error.',
+ 'entering user mode':
+ 'Se ha activado el modo usuario.',
+
+ // project menu
+ 'Project notes...':
+ 'Notas del proyecto...',
+ 'New':
+ 'Nuevo',
+ 'Open...':
+ 'Abrir...',
+ 'Save':
+ 'Guardar',
+ 'Save As...':
+ 'Guardar como...',
+
+ 'Import...':
+ 'Importar...',
+ 'file menu import hint':
+ 'importa proyectos, bloques,\ndisfraces o sonidos',
+
+ 'Export project...':
+ 'Exportar proyecto...',
+ '(in a new window)':
+ '(en una nueva ventana)',
+ 'save project data as XML\nto your downloads folder':
+ 'guarda el proyecto en XML\nen tu carpeta de descargas',
+ 'show project data as XML\nin a new browser window':
+ 'muestra el proyecto en XML\nen una nueva ventana del navegador',
+
+ 'Export project as plain text...':
+ 'Exportar proyecto como texto...',
+
+ 'Export blocks...':
+ 'Exportar bloques...',
+ 'show global custom block definitions as XML\nin a new browser window':
+ 'muestra definiciones de\nbloques personalizados en XML\nen una nueva ventana',
+
+ 'Unused blocks...':
+ 'Bloques no utilizados...',
+ 'find unused global custom blocks\nand remove their definitions':
+ 'busca bloques personalizados\nque no se est\u00E9n siendo utilizados\ny borra sus definiciones',
+
+ 'Export summary...':
+ 'Exportar resumen...',
+ 'open a new browser browser window\n with a summary of this project':
+ 'muestra un resumen de este proyecto\nen una nueva ventana del navegador',
+
+ 'Export summary with drop-shadows...':
+ 'Exportar resumen (im\u00E1genes con sombra)...',
+ 'open a new browser browser window\nwith a summary of this project\nwith drop-shadows on all pictures.\nnot supported by all browsers':
+ 'muestra un resumen de este proyecto\ndonde las im\u00E1genes tienen sombra\nen una nueva ventana del navegador\n(no funciona en todos los navegadores)',
+
+ 'Export all scripts as pic...':
+ 'Exportar todos los programas como imagen',
+ 'show a picture of all scripts\nand block definitions':
+ 'muestra una imagen con todos\nlos programas y definiciones de bloques',
+
+ 'Import tools':
+ 'Importar utilidades',
+ 'load the official library of\npowerful blocks':
+ 'carga la biblioteca oficial de\nbloques avanzados',
+ 'Opening blocks...':
+ 'Abriendo bloques...',
+
+ 'Libraries...':
+ 'Bibliotecas...',
+ 'Select categories of additional blocks to add to this project.':
+ 'a\u00F1ade bloques adicionales\npor categor\u00EDas a este proyecto',
+
+ // already defined
+ // 'Costumes':
+ // 'Disfraces',
+ 'Select a costume from the media library':
+ 'a\u00F1ade un disfraz desde la biblioteca',
+
+ // already defined
+ // 'Sounds':
+ // 'Sonidos',
+ 'Select a sound from the media library':
+ 'a\u00F1ade un sonido desde la biblioteca',
+
+ // export project as... dialog
+ 'Export Project As...':
+ 'Exportar proyecto como...',
+
+ // remove unused blocks dialog:
+ 'Remove unused blocks':
+ 'Borrar bloques no utilizados',
+ 'there are currently no unused\nglobal custom blocks in this project':
+ 'No hay bloques personalizados\nno utilizados en este proyecto',
+ 'unused block(s) removed':
+ 'bloque(s) no utilizado(s) eliminados',
+
+ // cloud menu
+ 'Login...':
+ 'Iniciar sesi\u00F3n...',
+ 'Signup...':
+ 'Registrarse...',
+ 'Reset Password...':
+ 'Reiniciar contrase\u00F1a...',
+ 'Resend Verification Email...':
+ 'Reenviar correo de verificaci\u00F3n...',
+
+ 'Logout':
+ 'Cerrar sesi\u00F3n',
+ 'Change Password...':
+ 'Cambiar contrase\u00F1a',
+
+ 'url...':
+ 'Url...',
+ 'export project media only...':
+ 'Exportar s\u00F3lo medios del proyecto...',
+ 'export project without media...':
+ 'Exportar proyecto sin medios...',
+ 'export project as cloud data...':
+ 'Exportar proyecto como datos en la nube...',
+ 'open shared project from cloud...':
+ 'Abrir proyecto compartido en la nube...',
+
+ // cloud url dialog
+ 'Cloud URL':
+ 'URL de la nube',
+ 'Snap!Cloud':
+ 'Snap!Cloud',
+ 'localhost':
+ 'localhost',
+ 'localhost (secure)':
+ 'localhost (seguro)',
+
+ // signup dialog
+ 'Sign up':
+ 'Registro',
+ 'User name:':
+ 'Nombre de usuario:',
+ 'Birth date:':
+ 'Fecha de nacimiento:',
+ 'year:':
+ 'a\u00F1o:',
+ 'E-mail address:':
+ 'Correo electr\u00F3nico:',
+ 'E-mail address of parent or guardian:':
+ 'Correo electr\u00F3nico del padre/madre o tutor legal',
+ 'Password:':
+ 'Contrase\u00F1a:',
+ 'Repeat Password:':
+ 'Repetir contrase\u00F1a',
+ 'Terms of Service...':
+ 'T\u00E9rminos y condiciones de uso...',
+ 'Privacy...':
+ 'Privacidad...',
+ 'I have read and agree\nto the Terms of Service':
+ 'He le\u00EDdo y acepto los t\u00E9rminos\n y condiciones de uso',
+
+ 'January':
+ 'Enero',
+ 'February':
+ 'Febrero',
+ 'March':
+ 'Marzo',
+ 'April':
+ 'Abril',
+ 'May':
+ 'Mayo',
+ 'June':
+ 'Junio',
+ 'July':
+ 'Julio',
+ 'August':
+ 'Agosto',
+ 'September':
+ 'Septiembre',
+ 'October':
+ 'Octubre',
+ 'November':
+ 'Noviembre',
+ 'December':
+ 'Diciembre',
+ 'or before':
+ 'o antes',
+
+ 'please fill out\nthis field':
+ 'por favor,\ncompleta este campo',
+ 'User name must be four\ncharacters or longer':
+ 'el nombre de usuario ha de tener\ncomo m\u00EDnimo 4 caracteres',
+ 'please provide a valid\nemail address':
+ 'por favor, escribe\nuna direcci\u00F3n de correo v\u00E1lida',
+ 'password must be six\ncharacters or longer':
+ 'la contrase\u00F1a ha de tener\ncomo m\u00EDnimo 6 caracteres',
+ 'passwords do\nnot match':
+ 'las contrase\u00F1a no coindicen',
+ 'please agree to\nthe TOS':
+ 'por favor, acepta los\nt\u00E9rminos y condiciones de uso',
+
+ // signin dialog
+ 'Sign in':
+ 'Iniciar sesi\u00F3n',
+ 'stay signed in on this computer\nuntil logging out':
+ 'Mantener la sesi\u00F3n iniciada en este ordenador',
+
+ // reset password dialog
+ 'Reset password':
+ 'Reiniciar contrase\u00F1a',
+
+ // resend verification email dialog
+ 'Resend verification email':
+ 'Reenviar correo de verificaci\u00F3n',
+
+ // change password dialog
+ 'Change Password':
+ 'Cambiar contrase\u00F1a',
+ 'Old password:':
+ 'Contrase\u00F1a actual:',
+ 'New password:':
+ 'Nueva contrase\u00F1a:',
+ 'Repeat new password:':
+ 'Repetir nueva contrase\u00F1a:',
+ 'password has been changed.':
+ 'Contrase\u00F1a cambiada.',
+
+ // open shared project in cloud dialog
+ 'Author name\u2026':
+ 'Nombre del autor...',
+ 'Project name...':
+ 'Nombre del proyecto...',
+ 'Fetching project\nfrom the cloud...':
+ 'Descargando proyecto\ndesde la nube...',
+ 'Opening project...':
+ 'Abriendo proyecto...',
+
+ 'Cloud Connection':
+ 'Conexi\u00F3n con la nube',
+ 'Successfully connected to:': // would this be translated? (gui.js:5439)
+ 'Conectado correctamente a:',
+ 'disconnected.':
+ 'Desconectado.',
+ 'You are not logged in':
+ 'No has iniciado sesi\u00F3n',
+
+ // settings menu
+ 'Language...':
+ 'Idioma...',
+ 'Zoom blocks...':
+ 'Tama\u00F1o de bloque...',
+ 'Stage size...':
+ 'Tama\u00F1o del escenario...',
+
+ 'Dragging threshold...':
+ 'Umbral de arrastre...',
+ 'specify the distance the hand has to move\nbefore it picks up an object':
+ 'especifica cu\u00E1nto hay que arrastrar\nun objeto para comenzar a moverlo',
+
+ 'Retina display support':
+ 'Soporte para pantallas Retina',
+ 'uncheck for lower resolution,\nsaves computing resources':
+ 'desmarcar para una menor resoluci\u00F3n\n(ahorra recursos de computaci\u00F3n)',
+ 'check for higher resolution,\nuses more computing resources':
+ 'marcar para una mayor resoluci\u00F3n\n(usa m\u00E1s recursos de computaci\u00F3n)',
+
+ 'Input sliders':
+ 'Deslizadores en campos de entrada',
+ 'uncheck to disable\ninput sliders for\nentry fields':
+ 'desmarcar para desactivar\nlos deslizadores\nen los campos de entrada',
+ 'check to enable\ninput sliders for\nentry fields':
+ 'marcar para activar\nlos deslizadores\nen los campos de entrada',
+
+ 'Execute on slider change':
+ 'Ejecutar cuando un deslizador cambie',
+ 'uncheck to suppress\nrunning scripts\nwhen moving the slider':
+ 'desmarcar para no ejecutar el programa\ncuando se mueva el deslizador',
+ 'check to run\nthe edited script\nwhen moving the slider':
+ 'marcar para ejecutar el programa\ncuando se mueva el deslizador',
+
+ 'Turbo mode':
+ 'Modo turbo',
+ 'uncheck to run scripts\nat normal speed':
+ 'desmarcar para ejecutar\nlos programas a velocidad normal',
+ 'check to prioritize\nscript execution':
+ 'marcar para priorizar\nla ejecuci\u00F3n del programa',
+
+ 'Visible stepping':
+ 'Depuraci\u00F3n paso a paso',
+ 'uncheck to turn off\nvisible stepping':
+ 'desmarcar para desactivar\nla depuraci\u00F3n paso a paso',
+ 'check to turn on\n visible stepping (slow)':
+ 'marcar para activar\nla depuraci\u00F3n paso a paso (lento)',
+
+ 'Ternary Boolean slots':
+ 'Huecos booleanos triestado',
+ 'uncheck to limit\nBoolean slots to true / false':
+ 'desmarcar para restringir\nlos huecos booleanos a verdadero / falso',
+ 'check to allow\nempty Boolean slots':
+ 'marcar para permitir\nhuecos booleanos vac\u00EDos',
+
+ 'Camera support':
+ 'Soporte para c\u00E1mara',
+ 'uncheck to disable\ncamera support':
+ 'desmarcar para desactivar\nel soporte de c\u00E1mara',
+ 'check to enable\ncamera support':
+ 'marcar para activar\nel soporte de c\u00E1mara',
+
+ 'Blurred shadows':
+ 'Sombras difuminadas',
+ 'uncheck to use solid drop\nshadows and highlights':
+ 'desmarcar para usar sombras\ny brillos s\u00F3lidos',
+ 'check to use blurred drop\nshadows and highlights':
+ 'marcar para usar sombras\ny brillos difuminados',
+
+ 'Zebra coloring':
+ 'Coloreado de cebra',
+ 'check to enable alternating\ncolors for nested blocks':
+ 'marcar para activar\nla alternaci\u00F3n\nde colores\npara bloques anidados',
+ 'uncheck to disable alternating\ncolors for nested block':
+ 'desmarcar para desactivar\nla alternaci\u00F3n de colores\npara bloques anidados',
+
+ 'Dynamic input labels':
+ 'Etiquetas de entrada din\u00E1micas',
+ 'uncheck to disable dynamic\nlabels for variadic inputs':
+ 'desmarcar para desactivar\nlas etiquetas din\u00E1micas\npara entradas variables',
+ 'check to enable dynamic\nlabels for variadic inputs':
+ 'marcar para activar\nlas etiquetas din\u00E1micas\npara entradas variables',
+
+ 'Prefer empty slot drops':
+ 'Dar preferencia a huecos vac\u00EDos',
+ 'settings menu prefer empty slots hint':
+ 'marcar para impedir que los bloques puedan\nocupar el lugar de otros al ser soltados',
+ 'uncheck to allow dropped\nreporters to kick out others':
+ 'desmarcar para permitir que los bloques puedan\nocupar el lugar de otros al ser soltados',
+
+ 'Long form input dialog':
+ 'Editor de par\u00E1metros extendido',
+ 'uncheck to use the input\ndialog in short form':
+ 'desmarcar para ocultar autom\u00E1ticamente\nlos tipos en el editor de par\u00E1metros',
+ 'check to always show slot\ntypes in the input dialog':
+ 'marcar para mostrar autom\u00E1ticamente\nlos tipos en el editor de par\u00E1metros',
+
+ 'Plain prototype labels':
+ 'Etiquetas planas',
+ 'uncheck to always show (+) symbols\nin block prototype labels':
+ 'desmarcar para mostrar los (+)\ndurante la definici\u00F3n de bloques',
+ 'check to hide (+) symbols\nin block prototype labels':
+ 'marcar para ocultar los (+)\ndurante la definici\u00F3n de bloques',
+
+ 'Virtual keyboard':
+ 'Teclado virtual',
+ 'uncheck to disable\nvirtual keyboard support\nfor mobile devices':
+ 'desmarcar para desactivar\nel soporte de teclado virtual\npara dispositivos m\u00F3viles',
+ 'check to enable\nvirtual keyboard support\nfor mobile devices':
+ 'marcar para activar\nel soporte de teclado virtual\npara dispositivos m\u00F3viles',
+
+ 'Clicking sound':
+ 'Sonido de clic',
+ 'uncheck to turn\nblock clicking\nsound off':
+ 'desmarcar para que no suene un "clic"\ncuando se coloca un bloque',
+ 'check to turn\nblock clicking\nsound on':
+ 'marcar para que suene un "clic"\ncuando se coloca un bloque',
+
+ 'Animations':
+ 'Animaciones',
+ 'uncheck to disable\nIDE animations':
+ 'desmarcar para desactivar\nlas animaciones del IDE',
+ 'check to enable\nIDE animations':
+ 'marcar para activar\nlas animaciones del IDE',
+
+ 'Cache Inputs':
+ 'Cachear entradas',
+ 'uncheck to stop caching\ninputs (for debugging the evaluator)':
+ 'desmarcar para no cachear entradas\n(para depurar el evaluador)',
+ 'check to cache inputs\nboosts recursion':
+ 'marcar para cachear entradas\n(dispara la recusi\u00F3n)',
+
+ 'Rasterize SVGs':
+ 'Rasterizar SVGs',
+ 'uncheck for smooth\nscaling of vector costumes':
+ 'desmarcar para usar escalado suave\nen im\u00E1genes vectoriales',
+ 'check to rasterize\nSVGs on import':
+ 'marcar para rasterizar los SVGs\ntras haberlos importado',
+
+ 'Flat design':
+ 'Dise\u00F1o plano',
+ 'uncheck for default\nGUI design':
+ 'desmarcar para utilizar\nla interfaz predeterminada',
+ 'check for alternative\nGUI design':
+ 'marcar para utilizar\nla interfaz alternativa',
+
+ 'Nested auto-wrapping':
+ 'Encapsular bloques internos',
+ 'uncheck to confine auto-wrapping\nto top-level block stacks':
+ 'desmarcar para que bloques tipo C\ns\u00F3lo puedan encapsular a otros m\u00E1s externos',
+ 'check to enable auto-wrapping\ninside nested block stacks':
+ 'marcar para permitir que bloques tipo C\npuedan encapsular a otros m\u00E1s internos',
+
+ 'Project URLs':
+ 'URLs con datos de proyecto',
+ 'uncheck to disable\nproject data in URLs':
+ 'desmarcar para no incluir\ndatos del proyecto en las URLs',
+ 'check to enable\nproject data in URLs':
+ 'marcar para incluir\ndatos del proyecto en las URLs',
+
+ 'Sprite Nesting':
+ 'Anidamiento de objetos',
+ 'uncheck to disable\nsprite composition':
+ 'desmarcar para desactivar\nla composici\u00F3n de objetos',
+ 'check to enable\nsprite composition':
+ 'marcar para activar\nla composici\u00F3n de objetos',
+
+ 'First-Class Sprites':
+ 'Objetos de primera clase',
+ 'uncheck to disable support\nfor first-class sprites':
+ 'desmarcar para desactivar\nel soporte de objetos de primera clase',
+ 'check to enable support\n for first-class sprite':
+ 'marcar para activar\nel soporte de objetos de primera clase',
+
+ 'Keyboard Editing':
+ 'Edici\u00F3n de teclado',
+ 'uncheck to disable\nkeyboard editing support':
+ 'desmarcar para desactivar\nel soporte de edici\u00F3n de teclado',
+ 'check to enable\nkeyboard editing support':
+ 'marcar para activar\nel soporte de edici\u00F3n de teclado',
+
+ 'Table support':
+ 'Soporte para tablas',
+ 'uncheck to disable\nmulti-column list views':
+ 'desmarcar para no visualizar\nlistas multicolumna como tablas',
+ 'check for multi-column\nlist view support':
+ 'marcar para visualizar\nlistas multicolumna como tablas',
+
+ 'Table lines':
+ 'L\u00EDneas de tablas',
+ 'uncheck for less contrast\nmulti-column list views':
+ 'desmarcar para ver tablas\n con un menor contraste',
+ 'check for higher contrast\ntable views':
+ 'marcar para ver tablas\ncon un mayor contraste',
+
+ 'Live coding support':
+ 'Soporte para programaci\u00F3n en vivo',
+ 'EXPERIMENTAL! uncheck to disable live\ncustom control structures':
+ '\u00A1EXPERIMENTAL! desmarcar para desactivar las\nestructuras de control personalizadas en vivo',
+ 'EXPERIMENTAL! check to enable\n live custom control structures':
+ '\u00A1EXPERIMENTAL! marcar para activar las\nestructuras de control personalizadas en vivo',
+
+ 'Thread safe scripts':
+ 'Hilos de ejecuci\u00F3n seguros',
+ 'uncheck to allow\nscript reentrance':
+ 'desmarcar para permitir\nla reentrada de programas',
+ 'check to disallow\nscript reentrance':
+ 'marcar para no permitir\nla reentrada de programas',
+
+ 'Prefer smooth animations':
+ 'Preferir animaciones suaves',
+ 'uncheck for greater speed\nat variable frame rates':
+ 'desmarcar para una mayor velocidad\na cuadros por segundo variables',
+ 'check for smooth, predictable\nanimations across computers':
+ 'marcar para unas animaciones suaves\ny predecibles entre ordenadores',
+
+ 'Flat line ends':
+ 'Extremos de l\u00EDnea rectos',
+ 'uncheck for round ends of lines':
+ 'desmarcar para dibujar\nl\u00EDneas con extremos redondeados',
+ 'check for flat ends of lines':
+ 'marcar para dibujar\nl\u00EDneas con extremos rectos',
+
+ 'Codification support':
+ 'Soporte de codificaci\u00F3n',
+ 'uncheck to disable\nblock to text mapping features':
+ 'desmarcar para desactivar el soporte\nde conversi\u00F3n de bloques a c\u00F3digo',
+ 'check for block\nto text mapping features':
+ 'marcar para activar el soporte\nde conversi\u00F3n de bloques a c\u00F3digo',
+
+ 'Inheritance support':
+ 'Soporte de herencia',
+ 'uncheck to disable\nsprite inheritance features':
+ 'desmarcar para desactivar\nel soporte de herencia de objetos',
+ 'check for sprite\ninheritance features':
+ 'marcar para activar\nel soporte de herencia de objetos',
+
+ 'Persist linked sublist IDs':
+ 'IDs de sublistas enlazadas persistentes',
+ 'uncheck to disable\nsaving linked sublist identities':
+ 'desmarcar para impedir guardar\nlas identidades de sublistas enlazadas',
+ 'check to enable\nsaving linked sublist identities':
+ 'marcar para permitir guardar\nlas identidades de sublistas enlazadas',
+
+ // zoom blocks dialog
+ 'Zoom blocks':
+ 'Tama\u00F1o de bloque',
+ 'build':
+ 'construye',
+ 'your own':
+ 'tus propios',
+ 'blocks':
+ 'bloques',
+ 'normal (1x)':
+ 'normal (1x)',
+ 'demo (1.2x)':
+ 'demo (1.2x)',
+ 'presentation (1.4x)':
+ 'presentaci\u00F3n (1.4x)',
+ 'big (2x)':
+ 'grande (2x)',
+ 'huge (4x)':
+ 'enorme (4x)',
+ 'giant (8x)':
+ 'gigantesco (8x)',
+ 'monstrous (10x)':
+ 'monstruoso (10x)',
+
+ // stage size dialog:
+ 'Stage size':
+ 'Tama\u00F1o del escenario',
+ 'Stage width':
+ 'Anchura del escenario',
+ 'Stage height':
+ 'Altura del escenario',
+
+ // dragging threshold dialog:
+ 'Dragging threshold':
+ 'Umbral de arrastre',
+
+ // context menu:
+ 'help':
+ 'ayuda',
+
+ // blocks context menu:
+ 'help...':
+ 'ayuda...',
+ 'relabel...':
+ 'renombrar...',
+ // already defined
+ // 'duplicate':
+ // 'duplicar',
+ 'make a copy\nand pick it up':
+ 'crea una copia y\npermite moverla a otra parte',
+ 'only duplicate this block':
+ 'duplica s\u00F3lo este bloque',
+ 'delete':
+ 'eliminar',
+
+ 'script pic...':
+ 'imagen de programa...',
+ 'open a new window\nwith a picture of this script':
+ 'abre una nueva ventana\ncon una imagen de este programa',
+
+ 'download script':
+ 'descargar programa',
+ 'download this script\nas an XML file':
+ 'descarga este programa en XML',
+
+ 'script pic with result...':
+ 'imagen de programa con resultado...',
+ 'open a new window\nwith a picture of both\nthis script and its result':
+ 'abre una nueva ventana\ncon una imagen de este programa\ny su resultado',
+
+ 'ringify':
+ 'encapsular',
+ 'unringify':
+ 'desencapsular',
+
+ 'header mapping...':
+ 'mapeo a cabecera...',
+ 'code mapping...':
+ 'mapeo a c\u00F3digo...',
+
+ // Map to header/code dialog:
+ 'Header mapping':
+ 'Mapeo a cabecera',
+ 'Enter code that corresponds to the block\'s definition. Use the formal parameter\nnames as shown and to reference the definition body\'s generated text code.':
+ 'Escribe el c\u00F3digo correspondiente a la definici\u00F3n del bloque.\nUsa los nombres de los par\u00E1metros formales aqu\u00ED mostrados\ny para referenciar el c\u00F3digo generado del cuerpo de la definici\u00F3n.',
+ 'Enter code that corresponds to the block\'s definition. Choose your own\nformal parameter names (ignoring the ones shown).':
+ 'Escribe el c\u00F3digo correspondiente a la definici\u00F3n del bloque.\nPuedes usar tus propios par\u00E1metros formales (ignora los aqu\u00ED mostrados).',
+ 'Code mapping':
+ 'Mapeo a c\u00F3digo',
+ 'Enter code that corresponds to the block\'s operation (usually a single\nfunction invocation). Use <#n> to reference actual arguments as shown.':
+ 'Escribe el c\u00F3digo correspondiente a la implementaci\u00F3n del bloque\n(normalmente una \u00FAnica llamada a funci\u00F3n).\nUsa <#n> para referenciar los argumentos aqu\u00ED mostrados.',
+
+ // custom blocks context menu:
+ 'delete block definition...':
+ 'eliminar definici\u00F3n de bloque...',
+ 'edit...':
+ 'editar...',
+ 'rename...':
+ 'renombrar...',
+ 'rename all...':
+ 'renombrar todos...',
+ 'translations...':
+ 'traducciones...',
+ 'block variables...':
+ 'variables de bloque...',
+ 'remove block variables...':
+ 'eliminar variables de bloque...',
+ 'experimental -\nunder construction':
+ 'experimental\n(en construcci\u00F3n)',
+
+ // sprites context menu:
+ 'edit':
+ 'editar',
+ 'rotate':
+ 'rotar',
+ 'move':
+ 'mover',
+ 'clone':
+ 'clonar',
+ 'parent...':
+ 'padre...',
+ 'current parent':
+ 'padre actual',
+ 'none':
+ 'ninguno',
+ 'release':
+ 'liberar',
+ 'make temporary and\nhide in the sprite corral':
+ 'lo hace temporal y oculta\nen el corral de objetos',
+ 'make permanent and\nshow in the sprite corral':
+ 'lo hace permanente y\nlo muestra en el corral de objetos',
+ 'detach from':
+ 'desvincular de',
+ 'detach all parts':
+ 'desvincular todo',
+ 'export...':
+ 'exportar...',
+
+ // stage context menu:
+ 'show all':
+ 'mostrar todos',
+
+ 'pic...':
+ 'imagen...',
+ 'open a new window\nwith a picture of the stage':
+ 'abre una nueva ventana\ncon una imagen del escenario',
+
+ // already defined
+ // 'pen trails':
+ // 'rastro del l\u00E1piz',
+ 'turn all pen trails and stamps\ninto a new costume for the\ncurrently selected sprite':
+ 'convierte todo rastro del l\u00E1piz\nen un nuevo disfraz para el objeto\nactualmente seleccionado',
+ 'turn all pen trails and stamps\ninto a new background for the stage':
+ 'convierte todo rastro del l\u00E1piz\nen un nuevo fondo para el escenario',
+
+ // scripting area context menu:
+ 'undrop':
+ 'deshacer',
+ 'undo the last\nblock drop\nin this pane':
+ 'deshace el \u00FAltimo cambio\nhecho con bloques',
+
+ 'redrop':
+ 'rehacer',
+ 'redo the last undone\nblock drop\nin this pane':
+ 'rehace el \u00FAltimo cambio\ndeshecho con bloques',
+
+ 'clear undrop queue':
+ 'vaciar historial de cambios',
+ 'forget recorded block drops\non this pane':
+ 'olvida el historial\nde cambios hechos con bloques',
+
+ 'clean up':
+ 'ordenar',
+ 'arrange scripts\nvertically':
+ 'alinea los programas\nverticalmente',
+
+ 'add comment':
+ 'a\u00F1adir comentario',
+ // comment default text:
+ 'add comment here...':
+ 'a\u00F1adir comentario aqu\u00ED...',
+
+ 'scripts pic...':
+ 'imagen de programas...',
+ 'open a new window\nwith a picture of all scripts':
+ 'abre una nueva ventana\ncon una imagen de todos los programas',
+
+ 'make a block...':
+ 'crear bloque...',
+
+ 'use the keyboard\nto enter blocks':
+ 'permite utilizar el teclado\npara escribir bloques',
+
+ // costumes
+ 'rename':
+ 'renombrar',
+ 'export':
+ 'exportar',
+ 'rename costume':
+ 'renombrar disfraz',
+
+ // input context menu
+ 'options...':
+ 'opciones...',
+ 'read-only':
+ 'solo lectura',
+
+ // sounds
+ 'Play':
+ 'Reproducir',
+ 'Play sound':
+ 'reproduce este sonido',
+ 'Stop':
+ 'Detener',
+ 'Stop sound':
+ 'detiene este sonido',
+
+ // variables context menu
+ 'transient':
+ 'excluir al guardar',
+ 'uncheck to save contents\nin the project':
+ 'desmarcar para guardar\nel contenido junto con el proyecto',
+ 'check to prevent contents\nfrom being saved':
+ 'marcar para no guardar\nel contenido junto con el proyecto',
+
+ // already defined
+ // 'rename...':
+ // 'renombrar...',
+ 'rename only\nthis reporter':
+ 'renombra s\u00F3lo\neste reportero',
+ // already defined
+ // 'rename all...':
+ // 'renombrar todos...',
+ 'rename all blocks that\naccess this variable':
+ 'renombra todos los bloques\nque acceden a esta variable',
+ 'inherited':
+ 'herencia',
+ 'uncheck to\ndisinherit':
+ 'desmarcar para no heredar',
+ 'check to inherit\nfrom':
+ 'marcar para heredar de',
+
+ // lists context menu
+ 'items':
+ 'elementos',
+ 'reset columns':
+ 'reiniciar columnas',
+ 'open in another dialog...':
+ 'abrir en otro di\u00E1logo',
+ 'table view...':
+ 'ver como tabla...',
+ 'list view...':
+ 'ver como lista...',
+ 'open in dialog...':
+ 'abrir en di\u00E1logo...',
+
+ // rename costume dialog:
+ 'rename background':
+ 'Renombrar disfraz',
+
+ // rename sound dialog:
+ 'rename sound':
+ 'Renombrar sonido',
+
+ // comments context menu:
+ 'comment pic...':
+ 'imagen de comentario...',
+ 'open a new window\nwith a picture of this comment':
+ 'abre una nueva ventana\ncon una imagen de este comentario',
+
+ // morph context menu:
+ 'user features':
+ 'opciones de usuario',
+
+ 'color...':
+ 'color...',
+ '\ncolor:':
+ '\ncolor:',
+ 'choose another color \nfor this morph':
+ 'cambia el color\nde este morph',
+
+ 'transparency...':
+ 'transparencia...',
+ '\nalpha\nvalue:':
+ '\nvalor alfa',
+ 'set this morph\'s\nalpha value':
+ 'establece el valor alfa\nde este morph',
+
+ 'resize...':
+ 'redimensionar...',
+ 'show a handle\nwhich can be dragged\nto change this morph\'s extent':
+ 'muestra una muesca que puede ser arrastrada\npara cambiar el tama\u00F1o de este morph',
+
+ // already defined
+ // 'duplicate':
+ // 'duplicar',
+ // 'make a copy\nand pick it up':
+ // 'crea una copia y\npermite moverla a otro lugar',
+
+ 'pick up':
+ 'coger',
+ 'detach and put \ninto the hand':
+ 'permite moverlo a otro lugar',
+
+ 'attach...':
+ 'vincular',
+ 'stick this morph\nto another one':
+ 'pega este morph a otro',
+
+ 'move...':
+ 'mover...',
+ 'show a handle\nwhich can be dragged\nto move this morph':
+ 'muestra una muesca que puede ser\narrastrada para mover este morph',
+
+ 'inspect...':
+ 'inspeccionar',
+ 'open a window\non all properties':
+ 'abre una ventana\ncon todas las propiedades',
+ 'open a window on\nall properties':
+ 'abre una ventana\ncon todas las propiedades',
+
+ // already defined
+ // 'pic...':
+ // 'imagen...',
+ 'open a new window\nwith a picture of this morph':
+ 'abre una nueva ventana\ncon una image de este morph',
+
+ 'lock':
+ 'bloquear',
+ 'make this morph\nunmovable':
+ 'impide que este morph\nse pueda mover',
+
+ 'unlock':
+ 'desbloquear',
+ 'make this morph\nmovable':
+ 'permite que este morph\nse pueda mover',
+
+ // already defined
+ // 'hide':
+ // 'ocultar',
+ // 'delete':
+ // 'eliminar',
+
+ 'World...':
+ 'Mundo...',
+ 'show the\nWorld\'s menu':
+ 'muestra el men\u00FA del Mundo',
+
+ 'set rotation':
+ 'rotar',
+ 'interactively turn this morph\nusing a dial widget':
+ 'gira este morph\nutilizando un control de disco',
+
+ 'edit rotation point only...':
+ 'editar s\u00F3lo punto de rotaci\u00F3n...',
+
+ 'pivot':
+ 'pivote',
+ 'edit the costume\'s\nrotation center':
+ 'edita el centro\nde rotaci\u00F3n del disfraz',
+
+ // already defined
+ // 'edit':
+ // 'editar',
+
+ 'move all inside...':
+ 'mover todos dentro...',
+ 'keep all submorphs\nwithin and visible':
+ 'retiene dentro y hace visibles\ntodos los sub-morphs',
+
+ 'stop':
+ 'detener',
+ 'terminate all running threads':
+ 'termina todos los hilos en ejecuci\u00F3n',
+
+ 'auto line wrap off...':
+ 'desactivar ajuste de l\u00EDnea...',
+ 'turn automatic\nline wrapping\noff':
+ 'desactiva el ajuste\nde l\u00EDnea autom\u00E1tico',
+ 'auto line wrap on...':
+ 'activar ajuste de l\u00EDnea...',
+ 'enable automatic\nline wrapping':
+ 'activa el ajuste\nde l\u00EDnea autom\u00E1tico',
+
+ 'delete block':
+ 'eliminar bloque',
+ 'spec...':
+ 'definici\u00F3n...',
+
+ 'font size...':
+ 'tama\u00F1o de fuente...',
+ 'set this String\'s\nfont point size':
+ 'establece el tama\u00F1o de fuente\nde este texto',
+
+ 'serif':
+ 'serif',
+ 'sans-serif':
+ 'sans-serif',
+ 'normal weight':
+ 'grosor normal',
+ 'bold':
+ 'negrita',
+ 'normal style':
+ 'estilo normal',
+ 'italic':
+ 'cursiva',
+ 'hide blanks':
+ 'ocultar espacios',
+ 'show blanks':
+ 'mostrar espacios',
+ 'show characters':
+ 'mostrar caracteres',
+ 'hide characters':
+ 'ocultar caracteres',
+
+ 'middle':
+ 'mitad',
+ 'tip':
+ 'punta',
+
+ 'screenshot':
+ 'imagen',
+
+ 'make a morph':
+ 'crear un morph',
+
+ // WorldMorph context menu
+ 'demo...':
+ 'demo...',
+ 'sample morphs':
+ 'morphs de muestra',
+ 'hide all...':
+ 'ocultar todos...',
+ 'show all...':
+ 'mostrar todos...',
+ 'screenshot...':
+ 'captura de pantalla...',
+ 'restore display':
+ 'restaurar pantalla',
+ 'redraw the\nscreen once':
+ 'redibuja la pantalla',
+ 'fill page...':
+ 'llenar p\u00E1gina',
+ 'let the World automatically\nadjust to browser resizing':
+ 'hace que el Mundo se ajuste\nautom\u00E1ticamente cuando\nse redimensiona el navegador', // @todo
+ 'sharp shadows...':
+ 'sombras n\u00EDtidas...',
+ 'sharp drop shadows\nuse for old browsers':
+ 'sombras n\u00EDtidas\n(para navegadores antiguos)',
+ 'blurred shadows...':
+ 'sombras difuminadas...',
+ 'blurry shades,\n use for new browsers':
+ 'sombras difuminadas\n(para navegadores modernos)',
+ // already defined
+ // 'color...':
+ // 'color...',
+ 'choose the World\'s\nbackground color':
+ 'selecciona el color\nde fondo del Mundo',
+ 'touch screen settings':
+ 'perfil de pantallas t\u00E1ctiles',
+ 'bigger menu fonts\nand sliders':
+ 'fuentes y deslizadores\nm\u00E1s grandes',
+ 'standard settings':
+ 'perfil de ordenador',
+ 'smaller menu fonts\nand sliders':
+ 'fuentes y deslizadores m\u00E1s peque\u00F1os',
+ 'user mode...':
+ 'modo usuario...',
+ 'disable developers\'\ncontext menus':
+ 'desactiva los men\u00FAs\ncontextuales de desarrollador',
+ 'development mode...':
+ 'modo desarrollador...',
+ 'about morphic.js...':
+ 'acerca de morphic.js',
+
+ // morph samples
+ 'rectangle':
+ 'rect\u00E1ngulo',
+ 'box':
+ 'caja',
+ 'circle box':
+ 'caja circular',
+ // already defined
+ // 'slider':
+ // 'deslizador',
+ 'dial':
+ 'disco',
+ 'frame':
+ 'panel',
+ 'scroll frame':
+ 'panel con deslizadores',
+ 'handle':
+ 'muesca',
+ 'string':
+ 'string',
+ // already defined
+ // 'text':
+ // 'texto',
+ 'speech bubble':
+ 'mensaje popup',
+ 'gray scale palette':
+ 'paleta de grises',
+ 'color palette':
+ 'paleta de color',
+ 'color picker':
+ 'medidor de color',
+ 'sensor demo':
+ 'demo: sensor',
+ 'animation demo':
+ 'demo: animaci\u00F3n',
+ 'pen':
+ 'tortuga',
+
+ // custom block's text fragment symbols:
+ 'square':
+ 'cuadrado',
+ 'pointRight':
+ 'apuntar a la derecha',
+ 'stepForward':
+ 'siguiente paso',
+ 'gears':
+ 'engranaje',
+ 'file':
+ 'fichero',
+ 'fullScreen':
+ 'pantalla completa',
+ 'normalScreen':
+ 'pantalla normal',
+ 'smallStage':
+ 'escenario peque\u00F1o',
+ 'normalStage':
+ 'escenario normal',
+ 'turtle':
+ 'tortuga',
+ // already defined
+ // 'stage':
+ // 'escenario',
+ 'turtleOutline':
+ 'tortuga (contorno)',
+ 'pause':
+ 'pausa',
+ 'flag':
+ 'bander\u00EDn',
+ 'octagon':
+ 'oct\u00F3gono',
+ 'cloud':
+ 'nube',
+ 'cloudOutline':
+ 'nube (contorno)',
+ 'cloudGradient':
+ 'nube (degradado)',
+ 'turnRight':
+ 'giro a la derecha',
+ 'turnLeft':
+ 'giro a la izquierda',
+ 'storage':
+ 'almacenamiento',
+ 'poster':
+ 'p\u00F3ster',
+ 'flash':
+ 'rel\u00E1mpago',
+ 'brush':
+ 'pincel',
+ // already defined
+ // 'rectangle':
+ // 'rect\u00E1ngulo',
+ 'rectangleSolid':
+ 'rect\u00E1ngulo (s\u00F3lido)',
+ 'circle':
+ 'c\u00EDrculo',
+ 'circleSolid':
+ 'c\u00EDrculo (s\u00F3lido)',
+ // already defined
+ // 'line':
+ // 'l\u00EDnea',
+ 'cross':
+ 'cruz',
+ 'crosshairs':
+ 'punto de mira',
+ 'paintbucket':
+ 'bote de pintura',
+ 'eraser':
+ 'goma de borrar',
+ 'pipette':
+ 'cuentagotas',
+ 'speechBubble':
+ 'bocadillo',
+ 'speechBubbleOutline':
+ 'bocadillo (contorno)',
+ 'turnBack':
+ 'ir atr\u00E1s',
+ 'turnForward':
+ 'ir adelante',
+ 'arrowUp':
+ 'flecha arriba',
+ 'arrowUpOutline':
+ 'flecha arriba (contorno)',
+ 'arrowLeft':
+ 'flecha izquierda',
+ 'arrowLeftOutline':
+ 'flecha izquierda (contorno)',
+ 'arrowDown':
+ 'flecha abajo',
+ 'arrowDownOutline':
+ 'flecha abajo (contorno)',
+ 'arrowRight':
+ 'flecha derecha',
+ 'arrowRightOutline':
+ 'flecha derecha (contorno)',
+ 'robot':
+ 'robot',
+ 'magnifyingGlass':
+ 'lupa',
+ 'magnifierOutline':
+ 'lupa (contorno)',
+ 'notes':
+ 'notas musicales',
+ // already defined
+ // 'camera':
+ // 'c\u00E1mara',
+ 'location':
+ 'ubicaci\u00F3n',
+ 'footprints':
+ 'huellas de pasos',
+ 'keyboard':
+ 'teclado',
+ 'keyboardFilled':
+ 'teclado (s\u00F3lido)',
+ 'new line':
+ 'nueva l\u00EDnea',
+
+ // dialogs
+ // buttons
+ 'OK':
+ 'Aceptar',
+ 'Ok':
+ 'Aceptar',
+ 'Cancel':
+ 'Cancelar',
+ 'Apply':
+ 'Aplicar',
+ 'Default':
+ 'Predeterminado',
+ 'Yes':
+ 'S\u00ED',
+ 'No':
+ 'No',
+
+ // help
+ 'Help':
+ 'Ayuda',
+
+ // Project Manager
+ 'Untitled':
+ 'Sin T\u00EDtulo',
+ 'Open Project':
+ 'Abrir proyecto',
+ 'Save Project':
+ 'Guardar proyecto',
+ 'Save Project As...':
+ 'Guardar proyecto como...',
+ '(empty)':
+ '(vac\u00EDo)',
+ '(no matches)':
+ '(ninguna coincidencia)',
+ 'Open':
+ 'Abrir',
+ 'Delete':
+ 'Eliminar',
+ 'Share':
+ 'Compartir',
+ 'Share Project':
+ 'Compartir',
+ 'Unshare':
+ 'No compartir',
+ 'Unshare Project':
+ 'Dejar de compartir',
+ 'Saved!':
+ '\u00A1Guardado!',
+ 'Delete Project':
+ 'Eliminar proyecto',
+ 'Are you sure you want to delete': // + proyect name?
+ '\u00BFSeguro que quieres eliminar', // + proyect name?
+ 'Cloud':
+ 'Nube',
+ 'Browser':
+ 'Navegador',
+ 'Examples':
+ 'Ejemplos',
+ 'Updating\nproject list...':
+ 'Actualizando\nlista de proyectos...',
+ 'Saving project\nto the cloud...':
+ 'Guardando proyecto\nen la nube...',
+ 'saved.':
+ 'Guardado.',
+ 'last changed':
+ '\u00FAltima modificaci\u00F3n',
+ 'Are you sure you want to share': // + project name?
+ '\u00BFSeguro que quieres\ncompartir', // + project name?
+ 'Are you sure you want to unshare': // + project name?
+ '\u00BFSeguro que quieres\ndejar de compartir', // + project name?
+ 'sharing\nproject...':
+ 'Compartiendo proyecto...',
+ 'shared.':
+ 'Compartido.',
+ 'unsharing\nproject...':
+ 'Dejando de compartir...',
+ 'unshared.':
+ 'No compartido.',
+ 'Are you sure you want to publish':
+ '\u00BFSeguro que quieres\npublicar', // + project name?
+ 'Are you sure you want to unpublish':
+ '\u00BFSeguro que quieres\ndejar de publicar', // + project name?
+ 'Publish Project':
+ 'Publicar proyecto',
+ 'Unpublish Project':
+ 'Dejar de publicar',
+ 'publishing\nproject...':
+ 'Publicando proyecto...',
+ 'unpublishing\nproject...':
+ 'Cancelando publicaci\u00F3n...',
+ 'published.':
+ 'Publicado.',
+ 'unpublished.':
+ 'No publicado',
+ 'Recover':
+ 'Recuperar',
+ 'Today, ':
+ 'Hoy, ',
+ 'Yesterday, ':
+ 'Ayer, ',
+
+ // costume editor @todo (wasn't this superseed by paint editor?)
+ 'Costume Editor':
+ 'Editor de disfraces',
+ 'click or drag crosshairs to move the rotation center':
+ 'haz clic o arrastra el punto de mira\npara mover el centro de rotaci\u00F3n',
+
+ // project notes dialog:
+ 'Project Notes':
+ 'Notas del proyecto',
+
+ // new project dialog:
+ 'New Project':
+ 'Nuevo proyecto',
+ 'Replace the current project with a new one?':
+ '\u00BFSeguro que quieres\ndescartar el actual proyecto\ny empezar un proyecto nuevo?',
+
+ // export blocks
+ 'Export blocks':
+ 'Exportar bloques',
+ 'Import blocks':
+ 'Importar bloques',
+ 'this project doesn\'t have any\ncustom global blocks yet':
+ 'este proyecto no tiene ning\u00FAn bloque personalizado todav\u00EDa',
+ 'no blocks were selected':
+ 'No se ha seleccionado ning\u00FAn bloque',
+ 'select':
+ 'seleccionar',
+ // already defined
+ // 'all':
+ // 'todos',
+ // 'none':
+ // 'ninguno',
+
+ // variable dialog:
+ 'for all sprites':
+ 'para todos los objetos',
+ 'for this sprite only':
+ 's\u00F3lo para este objeto',
+
+ // block editor dialog:
+ 'Block Editor':
+ 'Editor de bloques',
+ 'Method Editor':
+ 'Editor de m\u00E9todos',
+ 'Change block':
+ 'Cambiar bloque',
+ 'Command':
+ 'Comando',
+ 'Reporter':
+ 'Reportero',
+ 'Predicate':
+ 'Predicado',
+ 'block variables':
+ 'variables de bloque',
+
+ // custom block translations dialog:
+ 'Custom Block Translations':
+ 'Traducciones del bloque personalizado',
+ 'Enter one translation per line. use colon (":") as lang/spec delimiter\nand underscore ("_") as placeholder for an input, e.g.:\n\nen:say _ for _ secs':
+ 'Escribe cada traducci\u00F3n en una l\u00EDnea.\nUtiliza (:) para separar el idioma y el mensaje\ny (_) para argumentos de entrada, por ejemplo:\n\n es:decir _ durante _ segs',
+
+ // block deletion dialog:
+ 'Delete Custom Block':
+ 'Eliminar bloque personalizado',
+ 'block deletion dialog text':
+ '\u00BFSeguro que quieres eliminar\neste bloque personalizado\ny todas sus instancias?',
+
+ // input dialog:
+ 'Create input name':
+ 'Crear par\u00E1metro',
+ 'Edit input name':
+ 'Editar par\u00E1metro',
+ 'Edit label fragment':
+ 'Editar fragmento de texto',
+ 'Title text':
+ 'Texto',
+ 'Input name':
+ 'Par\u00E1metro',
+ // already defined
+ // 'Delete':
+ // 'Eliminar',
+
+ 'Object':
+ 'Objeto',
+ 'Text':
+ 'Texto',
+ 'List':
+ 'Lista',
+ // already defined
+ // 'Number':
+ // 'N\u00FAmero',
+ 'Any type':
+ 'Cualquier tipo',
+ 'Boolean (T/F)':
+ 'Booleano (V/F)',
+ 'Command\n(inline)':
+ 'Comando\n(en l\u00EDnea)',
+ // already defined
+ // 'Reporter':
+ // 'Reportero',
+ // 'Predicate':
+ // 'Predicado',
+ 'Command\n(C-shape)':
+ 'Comando\n(tipo C)',
+ 'Any\n(unevaluated)':
+ 'Cualquier tipo\n(no evaluado)',
+ 'Boolean\n(unevaluated)':
+ 'Booleano\n(no evaluado)',
+
+ 'Single input.':
+ 'Entrada simple.',
+ 'Default Value:':
+ 'Valor predeterminado:',
+ 'Multiple inputs (value is list of inputs)':
+ 'Entrada m\u00FAltiple (valores accesibles como lista)',
+ 'Upvar - make internal variable visible to caller':
+ 'Salida (hace visible una variable interna)',
+
+ 'Input Slot Options':
+ 'Opciones de par\u00E1metro de entrada',
+ 'Enter one option per line.\nOptionally use "=" as key/value delimiter and {} for submenus. e.g.\n the answer=42':
+ 'Escribe cada opci\u00F3n en una l\u00EDnea.\nUsa (=) para asignar un valor y {} para submen\u00FAs, por ejemplo:\n respuesta=42',
+
+ // About Snap
+ 'About Snap':
+ 'Acerca de Snap',
+ 'Back...':
+ 'Atr\u00E1s...',
+ 'License...':
+ 'Licencia...',
+ 'Modules...':
+ 'M\u00F3dulos...',
+ 'Credits...':
+ 'Cr\u00E9ditos...',
+ 'Translators...':
+ 'Traductores...',
+ 'License':
+ 'Licencia',
+ 'current module versions:':
+ 'Versiones actuales de los m\u00F3dulos:',
+ 'Contributors':
+ 'Colaboradores',
+ 'Translations':
+ 'Traducciones',
+
+ // exported summary in HTML:
+ 'Contents':
+ 'Contenido',
+ 'Kind of':
+ 'Clase de',
+ 'Part of':
+ 'Parte de',
+ 'Parts':
+ 'Partes',
+ // already defined
+ // 'Costumes':
+ // 'Disfraces',
+ // 'Sounds':
+ // 'Sonidos',
+ // 'Scripts':
+ // 'Programas',
+ 'For all Sprites':
+ 'Para todos los objetos',
+ 'Blocks':
+ 'Bloques',
+ // already defined
+ // 'Variables':
+ // 'Variables',
+
+ // exported summary dialog
+ 'Could not export':
+ 'No se ha podido exportar',
+ 'unable to export text':
+ 'No se ha podido exportar el texto',
+
+ //libraries
+ 'Tools':
+ 'Utilidades',
+ 'Standard library of powerful blocks (for, map, etc.)':
+ 'Biblioteca est\u00E1ndar de bloques avanzados (for, map, etc...)',
+
+ 'Iteration, composition':
+ 'Iteraci\u00F3n, composici\u00F3n',
+ '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.':
+ 'Bucles tradicionales (while, until, etc...) + el "named let" de Lisp (una generalizaci\u00F3n del for) + iteraci\u00F3n funcional (invocaci\u00F3n repetida de una funci\u00F3n) y composici\u00F3n de funciones.',
+
+ 'List utilities':
+ 'Utilidades de lista',
+ 'Some standard functions on lists (append, reverse, etc.)':
+ 'Algunas funciones est\u00E1ndar de listas (append, reverse, etc...)',
+
+ 'Streams (lazy lists)':
+ 'Streams (listas perezosas)',
+ '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.)':
+ 'Una variaci\u00F3n del tipo de dato lista en el que cada elemento se calcula s\u00F3lo cuando es necesario, as\u00ED que puedes construir listas de un mill\u00F3n de elementos sin gastar tiempo o memoria, o incluso listas infinitas. (Se incluye un bloque de ejemplo que reporta todos los n\u00FAmeros primos)',
+
+ 'Variadic reporters':
+ 'Reporteros de aridad variable',
+ 'Versions of +, x, AND, and OR that take more than two inputs.':
+ 'Versiones de +, x, AND, y OR que toman m\u00E1s de dos argumentos.',
+
+ 'Web services access (https)':
+ 'Acceso a servicios web (https)',
+ 'An extended version of the HTTP:// block that allows POST, PUT, and DELETE as well as GET requests, allows using the secure HTTPS protocol, and gives control over headers, etc.':
+ 'Una versi\u00F3n extendida del bloque HTTP:// que permite hacer peticiones POST, PUT y DELETE adem\u00E1s de GET, utilizar el protocolo HTTPS, controlar cabeceras, etc...',
+
+ 'Words, sentences':
+ 'Palabras, frases',
+ 'One of the big ideas in Logo that they left out of Scratch is thinking of text as structured into words and sentences, rather than just a string of characters. This library (along with the JOIN WORDS block in the Tools library) brings back that idea.':
+ 'Una de las mejores ideas de Logo no inclu\u00EDda en Scratch es la de considerar un texto como una secuencia de palabras y frases, en lugar de simplemente una cadena de caracteres. Esta biblioteca (junto al bloque UNIR de la biblioteca Utilidades) recupera esa idea.',
+
+ 'Multi-branched conditional (switch)':
+ 'Condicionales multirama (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!':
+ 'Como el "switch" de C o el "cond" de Lisp. \u00A1Gracias a Nathan Dinsmore por inventar la idea de un bloque separado para cada rama!',
+
+ 'LEAP Motion controller':
+ 'Control gestual (LEAP)',
+ 'Report hand positions from LEAP Motion controller (leapmotion.com).':
+ 'Reporta las posiciones de las manos desde el controlador de LEAP Motion (leapmotion.com).',
+
+ 'Set RGB or HSV pen color':
+ 'Colores RGB o HSV',
+ 'Set or report pen color as RGB (red, green, blue) or HSV (hue, saturation, value).':
+ 'Fija o devuelve el color del l\u00E1piz como RGB (rojo, verde, azul) o HSV (matiz, saturaci\u00F3n, valor).',
+
+ 'Catch errors in a script':
+ 'Captura de errores en programas',
+ '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.':
+ 'Ejecuta un programa. Si ocurre alg\u00FAn error, en lugar de detener la ejecuci\u00F3n el programa con un mensaje en rojo puedes ejecutar otro programa para tratar el error. Tambi\u00E9n incluye un bloque para lanzar un error con un mensaje, un bloque para crear una variable de programa y darle un valor.',
+
+ 'Allow multi-line text input to a block':
+ 'Texto multilinea',
+ '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.':
+ 'En general, las entradas de texto s\u00F3lo aceptan una \u00FAnica l\u00EDnea. El bloque MULTILINEA acepta texto en varias l\u00EDneas y puede ser usado como texto de entrada en otros bloques.',
+
+ 'Provide getters and setters for all GUI-controlled global settings':
+ 'Manejo de opciones globales',
+ 'Eisenberg\'s Law: Anything that can be done from the GUI should be doable from the programming language, and vice versa.':
+ 'Ley de Eisenberg: Cualquier cosa que pueda hacerse desde la interfaz gr\u00E1fica tambi\u00E9n deber\u00EDa de poder hacerse desde el lenguaje de programaci\u00F3n y viceversa.',
+
+ 'Infinite precision integers, exact rationals, complex':
+ 'Precisi\u00F3n arbitraria, racionales exactos, n\u00FAmeros complejos.',
+ 'The full Scheme numeric tower. "USE BIGNUMS " to enable.':
+ 'La torre num\u00E9rica completa de Scheme. "UTILIZAR BIGNUMS " para activarla.',
+
+ 'Provide 100 selected colors':
+ 'Paleta de 100 colores preseleccionados',
+ 'to use instead of hue for better selection':
+ 'Para seleccionar un color por nombre en lugar de por su matiz.',
+
+ 'Animation':
+ 'Animaci\u00F3n',
+ 'glide, grow and rotate using easing functions.':
+ 'Deslizamientos, zooms y rotaciones utilizando funciones curva.',
+
+ 'Pixels':
+ 'P\u00EDxeles',
+ 'manipulate costumes pixel-wise.':
+ 'Manipula disfraces a nivel de pixel.',
+
+ 'Audio Comp':
+ 'Audio',
+ 'analyze, manipulate and generate sound samples.':
+ 'Analiza, manipula y genera muestras de sonido.',
+
+ // library dialog:
+ 'Import library':
+ 'Importar biblioteca',
+ 'Import':
+ 'Importar',
+ 'Imported':
+ 'Se ha importado',
+ 'Loading':
+ 'Cargando',
+
+ // need to be relocated:
+ 'expecting':
+ 'se esperaban las entradas',
+ 'input(s), but getting':
+ 'pero se ha encontrado',
+ '(temporary)':
+ '(temporal)',
};
diff --git a/lang-et.js b/lang-et.js
index ad576911..f36510c9 100644
--- a/lang-et.js
+++ b/lang-et.js
@@ -390,8 +390,8 @@ SnapTranslator.dict.et = {
'Tere',
'world':
'maailm',
- 'letter %n of %s':
- 'sümbol nr %n tekstis %s',
+ 'letter %idx of %s':
+ 'sümbol nr %idx tekstis %s',
'length of %s':
'teksti %s pikkus',
'unicode of %s':
diff --git a/lang-eu.js b/lang-eu.js
new file mode 100644
index 00000000..17ce34f4
--- /dev/null
+++ b/lang-eu.js
@@ -0,0 +1,2157 @@
+/*
+
+ lang-eu.js
+
+ Basque translation for SNAP!
+
+ written by Jens Mönig
+
+ Copyright (C) 2018 by Jens Mönig
+
+ This file is part of Snap!.
+
+ Snap! is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as
+ published by the Free Software Foundation, either version 3 of
+ the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see .
+
+
+
+ Note to Translators:
+ --------------------
+ At this stage of development, Snap! can be translated to any LTR language
+ maintaining the current order of inputs (formal parameters in blocks).
+
+ Translating Snap! is easy:
+
+
+ 1. Download
+
+ Download the sources and extract them into a local folder on your
+ computer:
+
+
+
+ Use the German translation file (named 'lang-de.js') as template for your
+ own translations. Start with editing the original file, because that way
+ you will be able to immediately check the results in your browsers while
+ you're working on your translation (keep the local copy of snap.html open
+ in your web browser, and refresh it as you progress with your
+ translation).
+
+
+ 2. Edit
+
+ Edit the translation file with a regular text editor, or with your
+ favorite JavaScript editor.
+
+ In the first non-commented line (the one right below this
+ note) replace "de" with the two-letter ISO 639-1 code for your language,
+ e.g.
+
+ fr - French => SnapTranslator.dict.fr = {
+ it - Italian => SnapTranslator.dict.it = {
+ pl - Polish => SnapTranslator.dict.pl = {
+ pt - Portuguese => SnapTranslator.dict.pt = {
+ es - Spanish => SnapTranslator.dict.es = {
+ el - Greek => => SnapTranslator.dict.el = {
+
+ etc. (see )
+
+
+ 3. Translate
+
+ Then work through the dictionary, replacing the German strings against
+ your translations. The dictionary is a straight-forward JavaScript ad-hoc
+ object, for review purposes it should be formatted as follows:
+
+ {
+ 'English string':
+ 'Translation string',
+ 'last key':
+ } 'last value'
+
+ and you only edit the indented value strings. Note that each key-value
+ pair needs to be delimited by a comma, but that there shouldn't be a comma
+ after the last pair (again, just overwrite the template file and you'll be
+ fine).
+
+ If something doesn't work, or if you're unsure about the formalities you
+ should check your file with
+
+
+
+ This will inform you about any missed commas etc.
+
+
+ 4. Accented characters
+
+ Depending on which text editor and which file encoding you use you can
+ directly enter special characters (e.g. Umlaut, accented characters) on
+ your keyboard. However, I've noticed that some browsers may not display
+ special characters correctly, even if other browsers do. So it's best to
+ check your results in several browsers. If you want to be on the safe
+ side, it's even better to escape these characters using Unicode.
+
+ see:
+
+
+ 5. Block specs:
+
+ At this time your translation of block specs will only work
+ correctly, if the order of formal parameters and their types
+ are unchanged. Placeholders for inputs (formal parameters) are
+ indicated by a preceding % prefix and followed by a type
+ abbreviation.
+
+ For example:
+
+ 'say %s for %n secs'
+
+ can currently not be changed into
+
+ 'say %n secs long %s'
+
+ and still work as intended.
+
+ Similarly
+
+ 'point towards %dst'
+
+ cannot be changed into
+
+ 'point towards %cst'
+
+ without breaking its functionality.
+
+
+ 6. Submit
+
+ When you're done, rename the edited file by replacing the "de" part of the
+ filename with the two-letter ISO 639-1 code for your language, e.g.
+
+ fr - French => lang-fr.js
+ it - Italian => lang-it.js
+ pl - Polish => lang-pl.js
+ pt - Portuguese => lang-pt.js
+ es - Spanish => lang-es.js
+ el - Greek => => lang-el.js
+
+ and send it to me for inclusion in the official Snap! distribution.
+ Once your translation has been included, Your name will the shown in the
+ "Translators" tab in the "About Snap!" dialog box, and you will be able to
+ directly launch a translated version of Snap! in your browser by appending
+
+ lang:xx
+
+ to the URL, xx representing your translations two-letter code.
+
+
+ 7. Known issues
+
+ In some browsers accents or ornaments located in typographic ascenders
+ above the cap height are currently (partially) cut-off.
+
+ Enjoy!
+ -Jens
+*/
+
+/*global SnapTranslator*/
+
+SnapTranslator.dict.eu = {
+
+/*
+ Special characters: (see )
+
+ Ä, ä \u00c4, \u00e4
+ Ö, ö \u00d6, \u00f6
+ Ü, ü \u00dc, \u00fc
+ ß \u00df
+*/
+
+ // translations meta information
+ 'language_name':
+ 'Euskara', // the name as it should appear in the language menu
+ 'language_translator':
+ 'Asier Iturralde Sarasola', // your name for the Translators tab
+ 'translator_e-mail':
+ 'aiturralde@iametza.eus', // optional
+ 'last_changed':
+ '2018-06-26', // this, too, will appear in the Translators tab
+
+ // GUI
+ // control bar:
+ 'untitled':
+ 'izengabea',
+ 'development mode':
+ 'garapeneko modua',
+
+ // categories:
+ 'Motion':
+ 'Mugimendua',
+ 'Looks':
+ 'Itxura',
+ 'Sound':
+ 'Soinua',
+ 'Pen':
+ 'Arkatza',
+ 'Control':
+ 'Kontrola',
+ 'Sensing':
+ 'Sentsoreak',
+ 'Operators':
+ 'Eragiketak',
+ 'Variables':
+ 'Aldagaiak',
+ 'Lists':
+ 'Zerrendak',
+ 'Other':
+ 'Besteak',
+
+ // editor:
+ 'draggable':
+ 'Arrastagarria',
+
+ // tabs:
+ 'Scripts':
+ 'Programak',
+ 'Costumes':
+ 'Mozorroak',
+ 'Backgrounds':
+ 'Atzeko planoak',
+ 'Sounds':
+ 'Soinuak',
+
+ // names:
+ 'Sprite':
+ 'Objektua',
+ 'Stage':
+ 'Agertokia',
+
+ // costumes tab:
+ 'Paint a new costume':
+ 'Marraztu mozorro berria',
+ 'Import a new costume from your webcam':
+ 'inportatu mozorro berria web-kameratik',
+ 'costumes tab help':
+ 'Mozorroa zure ordenagailutik edo beste webgune batetik\ninporta dezakezu hona arrastatuz',
+
+ // paint editor dialog:
+ 'Paint Editor':
+ 'Editore grafikoa',
+ 'undo':
+ 'desegin',
+ 'Vector':
+ 'Bektorea',
+ 'Bitmap':
+ 'Bit-mapa',
+ 'Paintbrush tool\n(free draw)':
+ 'Pintzela\n(marrazki librea)',
+ 'Stroked Rectangle\n(shift: square)':
+ 'Laukizuzena\n(maius \u21E7 = karratua)',
+ 'Stroked Ellipse\n(shift: circle)':
+ 'Elipsea\n(maius \u21E7 = zirkulua)',
+ 'Eraser tool':
+ 'Borragoma',
+ 'Set the rotation center':
+ 'Ezarri biraketa zentroa',
+ 'Line tool\n(shift: vertical/horizontal)':
+ 'Lerroa\n(maius \u21E7 = bertikala/horizontala)',
+ 'Filled Rectangle\n(shift: square)':
+ 'Laukizuzen betea\n(maius \u21E7 = karratua)',
+ 'Filled Ellipse\n(shift: circle)':
+ 'elipse betea\n(maius \u21E7 = zirkulua)',
+ 'Fill a region':
+ 'Betetzeko tresna',
+ 'Pipette tool\n(pick a color anywhere)':
+ 'Tanta-kontagailua\n(hautatu kolore bat edonon)',
+ 'grow':
+ 'handitu',
+ 'shrink':
+ 'txikiagotu',
+ 'flip \u2194':
+ 'irauli \u2194',
+ 'flip \u2195':
+ 'irauli \u2195',
+ 'Brush size':
+ 'Pintzelaren tamaina',
+ 'Constrain proportions of shapes?\n(you can also hold shift)':
+ 'Mantendu formen proportzioak\n(edo mantendu maius \u21E7 sakatuta)',
+
+ 'Vector Paint Editor':
+ 'Bektore editore grafikoa',
+ 'Rectangle\n(shift: square)':
+ 'Laukizuzena\n(maius \u21E7 = karratua)',
+ 'Ellipse\n(shift: circle)':
+ 'Elipsea\n(maius \u21E7 = zirkulua)',
+ 'Selection tool':
+ 'Hautapen tresna',
+ 'Line tool\n(shift: constrain to 45º)':
+ 'Lerroa\n(maius \u21E7 = mugatu 45°ra)',
+ 'Closed brush\n(free draw)':
+ 'Pintzel itxia\n(marrazki librea)',
+ 'Paint a shape\n(shift: secondary color)':
+ 'Marraztu forma\n(maius \u21E7 = kolore sekundarioa)',
+ 'Pipette tool\n(pick a color from anywhere\nshift: secondary color)':
+ 'Tanta-kontagailua\n(hautatu kolorea edonondik\nmaius \u21E7 = kolore sekundarioa)',
+ 'Primary color Secondary color':
+ 'Kolore primarioa Kolore sekundarioa',
+ 'Top':
+ 'Goia',
+ 'Bottom':
+ 'Behea',
+ 'Up':
+ 'Gora',
+ 'Down':
+ 'Behera',
+ 'Polygon':
+ 'Poligonoa',
+
+ // camera dialog:
+ 'Camera':
+ 'Kamera',
+ 'Camera not supported':
+ 'Kamera ezin da erabili',
+ 'Please make sure your web browser is up to date\nand your camera is properly configured. \n\nSome browsers also require you to access Snap!\nthrough HTTPS to use the camera.\n\nPlase replace the "http://" part of the address\nin your browser by "https://" and try again.':
+ 'Mesedez, egiaztatu nabigatzailea eguneratuta dagoela\neta kamera behar bezala konfiguratuta dagoela.\n\nZenbait nabigatzailek Snap!-era HTTPS bidez\nsartzea eskatzen dute kamera erabiltzeko.\n\nMesedez, ordezkatu helbideko "http://"\n"https://"-rekin eta saiatu berriro.',
+ 'camera':
+ 'kamera',
+
+ // sound tab:
+ 'Record a new sound':
+ 'Grabatu soinu berria',
+ 'import a sound from your computer\nby dragging it into here':
+ 'inportatu soinua zure ordenagailutik\nhona arrastatuz',
+
+ // sound recorder dialog:
+ 'Sound Recorder':
+ 'Soinu grabagailua',
+
+ // stage & sprite corral:
+ 'add a new Turtle sprite':
+ 'gehitu dortoka objektu berria',
+ 'paint a new sprite':
+ 'marraztu objektu berria',
+ 'take a camera snapshot and\nimport it as a new sprite':
+ 'egin argazki berria eta\ninportatu objektu berri bezala',
+
+ // rotation styles:
+ 'don\'t rotate':
+ 'ez biratu',
+ 'can rotate':
+ 'biragarria',
+ 'only face left/right':
+ 'begiratu ezkerrera/eskuinera soilik',
+
+ // new sprite button:
+ 'add a new sprite':
+ 'gehitu objektu berri bat',
+
+ // tab help
+ 'costumes tab help':
+ 'inportatu mozorro bat ordenagailutik\nhona arrastatuz',
+ 'import a sound from your computer\nby dragging it into here':
+ 'inportatu soinu bat ordenagailutik\nhona arrastatuz',
+
+ // primitive blocks:
+
+ /*
+ Attention Translators:
+ ----------------------
+ At this time your translation of block specs will only work
+ correctly, if the order of formal parameters and their types
+ are unchanged. Placeholders for inputs (formal parameters) are
+ indicated by a preceding % prefix and followed by a type
+ abbreviation.
+
+ For example:
+
+ 'say %s for %n secs'
+
+ can currently not be changed into
+
+ 'say %n secs long %s'
+
+ and still work as intended.
+
+ Similarly
+
+ 'point towards %dst'
+
+ cannot be changed into
+
+ 'point towards %cst'
+
+ without breaking its functionality.
+ */
+
+ // motion:
+ 'Stage selected:\nno motion primitives':
+ 'Agertokia hautatuta:\nmugimendu primitiborik ez',
+
+ 'move %n steps':
+ 'mugitu %n pauso',
+ 'turn %clockwise %n degrees':
+ 'biratu %clockwise %n gradu',
+ 'turn %counterclockwise %n degrees':
+ 'biratu %counterclockwise %n gradu',
+ 'point in direction %dir':
+ 'apuntatu norabidea %dir',
+ 'point towards %dst':
+ 'apuntatu hona %dst',
+ 'go to x: %n y: %n':
+ 'joan x: %n y: %n',
+ 'go to %dst':
+ 'joan %dst',
+ 'glide %n secs to x: %n y: %n':
+ 'irristatu %n segundotan x: %n y: %n',
+ 'change x by %n':
+ 'aldatu x %n',
+ 'set x to %n':
+ 'ezarri x %n',
+ 'change y by %n':
+ 'aldatu y %n',
+ 'set y to %n':
+ 'ezarri y %n',
+ 'if on edge, bounce':
+ 'ertzean egin punpa',
+ 'x position':
+ 'x posizioa',
+ 'y position':
+ 'y posizioa',
+ 'direction':
+ 'norabidea',
+
+ // looks:
+ 'switch to costume %cst':
+ 'aldatu mozorroa %cst',
+ 'next costume':
+ 'hurrengo mozorroa',
+ 'costume #':
+ 'mozorroa',
+ 'costume name':
+ 'mozorroaren izena',
+ 'say %s for %n secs':
+ 'esan %s %n segundoz',
+ 'say %s':
+ 'esan %s',
+ 'think %s for %n secs':
+ 'pentsatu %s %n segundoz',
+ 'think %s':
+ 'pentsatu %s',
+ 'Hello!':
+ 'Kaixo!',
+ 'Hmm...':
+ 'Umm...',
+ 'change %eff effect by %n':
+ 'aldatu %eff efektua %n',
+ 'set %eff effect to %n':
+ 'ezarri %eff efektua %n',
+ 'clear graphic effects':
+ 'garbitu efektu grafikoak',
+ 'change size by %n':
+ 'aldatu tamaina %n',
+ 'set size to %n %':
+ 'ezarri tamaina %n %',
+ 'size':
+ 'tamaina',
+ 'show':
+ 'erakutsi',
+ 'hide':
+ 'ezkutatu',
+ 'go to front':
+ 'joan aurreko planora',
+ 'go back %n layers':
+ 'joan atzera %n geruza',
+
+ 'development mode \ndebugging primitives:':
+ 'garapen modua \nprimitiboak arazten:',
+ 'console log %mult%s':
+ 'idatzi kontsolan %mult%s',
+ 'alert %mult%s':
+ 'alerta %mult%s',
+
+ // sound:
+ 'play sound %snd':
+ 'jo %snd soinua',
+ 'play sound %snd until done':
+ 'jo %snd soinua amaitu arte',
+ 'stop all sounds':
+ 'gelditu soinu guztiak',
+ 'rest for %n beats':
+ 'itxaron %n aldiz',
+ 'play note %note for %n beats':
+ 'jo %note nota %n aldiz',
+ 'set instrument to %inst':
+ 'ezarri instrumentua %inst',
+ 'change tempo by %n':
+ 'aldatu tempoa %n',
+ 'set tempo to %n bpm':
+ 'ezarri tempoa %n',
+ 'tempo':
+ 'tempoa',
+
+ // "instruments", i.e. wave forms
+ '(1) sine':
+ '(1) \u223F\u223F sinua',
+ '(2) square':
+ '(2) \u238D\u238D karratua',
+ '(3) sawtooth':
+ '(3) \u2A58\u2A58 zerra',
+ '(4) triangle':
+ '(4) \u22C0\u22C0 triangulua',
+
+ // pen:
+ 'clear':
+ 'garbitu',
+ 'pen down':
+ 'arkatza behera',
+ 'pen up':
+ 'arkatza gora',
+ 'set pen color to %clr':
+ 'ezarri arkatzaren kolorea %clr',
+ 'change pen color by %n':
+ 'aldatu arkatzaren kolorea %n',
+ 'set pen color to %n':
+ 'ezarri arkatzaren kolorea %n',
+ 'change pen shade by %n':
+ 'aldatu arkatzaren \u00F1abardura %n',
+ 'set pen shade to %n':
+ 'ezarri arkatzaren \u00F1abardura %n',
+ 'change pen size by %n':
+ 'aldatu arkatzaren tamaina %n',
+ 'set pen size to %n':
+ 'ezarri arkatzaren tamaina %n',
+ 'stamp':
+ 'zigilua',
+ 'fill':
+ 'bete',
+
+ // control:
+ 'when %greenflag clicked':
+ '%greenflag klik egitean',
+ 'when %keyHat key pressed':
+ '%keyHat tekla sakatzean',
+ 'when I am %interaction':
+ 'niri %interaction',
+ 'clicked':
+ 'klik egitean',
+ 'pressed':
+ 'sakatzean',
+ 'dropped':
+ 'jaregitean',
+ 'mouse-entered':
+ 'sagua gainean jartzean',
+ 'mouse-departed':
+ 'sagua gainetik kentzean',
+ 'scrolled-down':
+ 'beherantz korritzean',
+ 'scrolled-up':
+ 'gorantz korritzean',
+ 'when %b':
+ '%b denean',
+ 'when I receive %msgHat':
+ '%msgHat jasotzen dudanean',
+ 'broadcast %msg':
+ 'igorri %msg',
+ 'broadcast %msg and wait':
+ 'igorri %msg eta itxaron',
+ 'Message name':
+ 'Mezuaren izena',
+ 'message':
+ 'mezua',
+ 'any message':
+ 'edozein mezu',
+ 'wait %n secs':
+ 'itxaron %n segundo',
+ 'wait until %b':
+ 'itxaron %b arte',
+ 'forever %c':
+ 'beti %c',
+ 'repeat %n %c':
+ 'errepikatu %n aldiz %c',
+ 'repeat until %b %c':
+ 'errepikatu %b den arte %c',
+ 'if %b %c':
+ 'baldin %b %c',
+ 'if %b %c else %c':
+ 'baldin %b %c bestela %c',
+ 'report %s':
+ 'aurkeztu %s',
+ 'stop %stopChoices':
+ 'gelditu %stopChoices',
+ 'all':
+ 'guztiak',
+ 'this script':
+ 'programa hau',
+ 'this block':
+ 'bloke hau',
+ 'stop %stopOthersChoices':
+ 'gelditu %stopOthersChoices',
+ 'all but this script':
+ 'dena programa hau ezik',
+ 'other scripts in sprite':
+ 'objektuaren beste programak',
+ 'pause all %pause':
+ 'pausatu guztiak %pause',
+ 'run %cmdRing %inputs':
+ 'exekutatu %cmdRing %inputs',
+ 'launch %cmdRing %inputs':
+ 'abiarazi %cmdRing %inputs',
+ 'call %repRing %inputs':
+ 'deitu %repRing %inputs',
+ 'run %cmdRing w/continuation':
+ 'exekutatu %cmdRing jarraipenarekin',
+ 'call %cmdRing w/continuation':
+ 'deitu %cmdRing jarraipenarekin',
+ 'warp %c':
+ 'exekutatu jarraian %c',
+ 'when I start as a clone':
+ 'klon bezala hasten naizenean',
+ 'create a clone of %cln':
+ 'sortu klon bat %cln',
+ 'a new clone of %cln':
+ 'honen klon berri bat %cln',
+ 'myself':
+ 'ni neu',
+ 'delete this clone':
+ 'ezabatu klon hau',
+ 'tell %spr to %cmdRing %inputs':
+ 'esan honi %spr %cmdRing %inputs',
+ 'ask %spr for %repRing %inputs':
+ 'eskatu honi %spr %repRing %inputs',
+
+ // sensing:
+ 'touching %col ?':
+ '%col ukitzen?',
+ 'touching %clr ?':
+ '%clr ukitzen?',
+ 'color %clr is touching %clr ?':
+ '%clr kolorea %clr ukitzen?',
+ 'ask %s and wait':
+ 'galdetu %s eta itxaron',
+ 'what\'s your name?':
+ 'nola izena duzu?',
+ 'answer':
+ 'erantzuna',
+ 'mouse x':
+ 'saguaren x',
+ 'mouse y':
+ 'saguaren y',
+ 'mouse down?':
+ 'sagua sakatuta?',
+ 'key %key pressed?':
+ '%key tekla sakatuta?',
+ '%rel to %dst':
+ '%rel hona %dst',
+ 'distance':
+ 'distantzia',
+ 'reset timer':
+ 'berrezarri kronometroa',
+ 'timer':
+ 'kronometroa',
+ '%att of %spr':
+ '%att honena %spr',
+ 'my %get':
+ 'nire %get',
+ 'http:// %s':
+ 'http:// %s',
+ 'turbo mode?':
+ 'turbo modua?',
+ 'set turbo mode to %b':
+ 'ezarri turbo modua %b',
+ 'current %dates':
+ 'uneko %dates',
+ 'year':
+ 'urtea',
+ 'month':
+ 'hilabetea',
+ 'date':
+ 'data',
+ 'day of week':
+ 'asteko eguna',
+ 'hour':
+ 'ordua',
+ 'minute':
+ 'minutua',
+ 'second':
+ 'segundoa',
+ 'time in milliseconds':
+ 'denbora milisegundotan',
+
+ 'filtered for %clr':
+ 'iragazi %clr',
+ 'stack size':
+ 'pilaren tamaina',
+ 'frames':
+ 'fotogramak',
+
+ // operators:
+ '%n mod %n':
+ '%n modulu %n',
+ 'round %n':
+ 'borobildu %n',
+ '%fun of %n':
+ '%fun %n',
+ 'pick random %n to %n':
+ 'hartu ausaz %n eta %n artean',
+ '%b and %b':
+ '%b eta %b',
+ '%b or %b':
+ '%b edo %b',
+ 'not %b':
+ 'ez %b',
+ 'true':
+ 'egia',
+ 'false':
+ 'gezurra',
+ 'join %words':
+ 'batu %words',
+ 'split %s by %delim':
+ 'banatu %s honekin %delim',
+ 'hello':
+ 'kaixo',
+ 'world':
+ 'mundua',
+ 'letter %idx of %s':
+ '%idx . letra hemendik %s',
+ 'length of %s':
+ 'honen luzera %s',
+ 'unicode of %s':
+ 'honen unicode %s',
+ 'unicode %n as letter':
+ 'unicode %n letra bezala',
+ 'is %s a %typ ?':
+ '%s %typ da?',
+ 'is %s identical to %s ?':
+ '%s eta %s berdinak dira?',
+ 'JavaScript function ( %mult%s ) { %code }':
+ 'JavaScript funtzioa ( %mult%s ) { %code }',
+ 'compile %repRing':
+ 'konpilatu %repRing',
+
+ 'type of %s':
+ 'honen mota %s',
+
+ // variables:
+ 'Make a variable':
+ 'Sortu aldagaia',
+ 'Variable name':
+ 'Aldagaiaren izena',
+ 'Script variable name':
+ 'Programaren aldagaiaren izena',
+ 'inherit %shd':
+ 'heredatu %shd',
+ 'Delete a variable':
+ 'Ezabatu aldagaia',
+
+ 'set %var to %s':
+ 'ezarri %var %s',
+ 'change %var by %n':
+ 'aldatu %var honela %n',
+ 'show variable %var':
+ 'erakutsi %var aldagaia',
+ 'hide variable %var':
+ 'ezkutatu %var aldagaia',
+ 'script variables %scriptVars':
+ 'programaren aldagaiak %scriptVars',
+
+ // lists:
+ 'list %exp':
+ 'zerrenda %exp',
+ '%s in front of %l':
+ '%s %l ren aurrean',
+ 'item %idx of %l':
+ '%idx elementua %l tik',
+ 'all but first of %l':
+ 'guztiak lehena ezik %l',
+ 'length of %l':
+ '%l ren luzera',
+ '%l contains %s':
+ '%l k barne dauka %s',
+ 'thing':
+ 'gauza',
+ 'add %s to %l':
+ 'gehitu %s %l ri',
+ 'delete %ida of %l':
+ 'ezabatu %ida %l tik',
+ 'insert %s at %idx of %l':
+ 'txertatu %s %idx posizioan %l n',
+ 'replace item %idx of %l with %s':
+ 'ordezkatu %idx elementua %l n honekin %s',
+
+ // other
+ 'Make a block':
+ 'Sortu blokea',
+
+ // menus
+ // snap menu
+ 'About...':
+ 'Honi buruz...',
+ 'Reference manual':
+ 'Erreferentzia eskuliburua',
+ 'Snap! website':
+ 'Snap! webgunea',
+ 'Download source':
+ 'Deskargatu iturburu-kodea',
+ 'Switch back to user mode':
+ 'Itzuli erabiltzaile modura',
+ 'disable deep-Morphic\ncontext menus\nand show user-friendly ones':
+ 'desgaitu itxura sakoneko\nlaster-menuak\neta erakutsi erabilerrazak',
+ 'Switch to dev mode':
+ 'Aldatu garatzaile modura',
+ 'enable Morphic\ncontext menus\nand inspectors,\nnot user-friendly!':
+ 'gaitu itxura sakoneko\laster-menuak\neta ikuskatzaileak,\nez erabilerrazak',
+ 'entering development mode.\n\nerror catching is turned off,\nuse the browser\'s web console\nto see error messages.':
+ 'Garatzaile moduan sartzen.\n\nErroreak atzematea desgaituta dago,\nerabili nabigatzailearen web kontsola\nerrore-mezuak ikusteko.',
+ 'entering user mode':
+ 'Erabiltzaile moduan sartzen',
+
+ // project menu
+ 'Project notes...':
+ 'Proiektuaren oharrak...',
+ 'New':
+ 'Berria',
+ 'Open...':
+ 'Ireki...',
+ 'Save':
+ 'Gorde',
+ 'Save to disk':
+ 'Gorde diskoan',
+ 'store this project\nin the downloads folder\n(in supporting browsers)':
+ 'gorde proiektu hau\nDeskargak karpetan\n(euskarria duten nabigatzaileetan)',
+ 'Save As...':
+ 'Gorde honela...',
+ 'Import...':
+ 'Inportatu...',
+ 'file menu import hint':
+ 'inportatu proiektuak, blokeak, irudiak edo soinuak',
+ 'Export project as plain text...':
+ 'Esportatu proiektua testu arrunt bezala...',
+ 'Export project...':
+ 'Esportatu proiektua...',
+ '(in a new window)':
+ '(leiho berri batean)',
+ 'save project data as XML\nto your downloads folder':
+ 'gorde proiektuaren datuak\nXML bezala Deskargak karpetan',
+ 'show project data as XML\nin a new browser window':
+ 'erakutsi proiektuaren datuak XML bezala\nnabigatzailearen leiho berri batean',
+ 'Export blocks...':
+ 'Esportatu blokeak...',
+ 'show global custom block definitions as XML\nin a new browser window':
+ 'erakutsi bloke pertsonalizatuen definizio globalak\nXML bezala nabigatzailearen leiho berri batean',
+ 'Unused blocks...':
+ 'Erabili gabeko blokeak...',
+ 'find unused global custom blocks\nand remove their definitions':
+ 'bilatu erabili gabeko bloke pertsonalizatu globalak\neta kendu beren definizioak',
+ 'Remove unused blocks':
+ 'Kendu erabili gabeko blokeak',
+ 'there are currently no unused\nglobal custom blocks in this project':
+ 'une honetan ez dago erabili gabeko\nbloke pertsonalizatu globalik proiektu honetan',
+ 'unused block(s) removed':
+ 'erabili gabeko bloke kendu d(ir)a',
+ 'Export summary...':
+ 'Esportatu laburpena...',
+ 'open a new browser browser window\n with a summary of this project':
+ 'ireki nabigatzailearen leiho berri bat\nproiektu honen laburpenarekin',
+ 'Export summary with drop-shadows...':
+ 'Esportatu laburpena itzaldun irudiekin...',
+ 'open a new browser browser window\nwith a summary of this project\nwith drop-shadows on all pictures.\nnot supported by all browsers':
+ 'ireki nabigatzailearen leiho berri bat irudi guztietan\nitzalak dituen proiektu honen laburpenarekin.\nEz du nabigatzaile guztietan funtzionatzen',
+
+ 'Export all scripts as pic...':
+ 'Esportatu programa guztiak irudi bezala...',
+ 'show a picture of all scripts\nand block definitions':
+ 'erakutsi programa eta bloke definizio\nguztien irudi bat',
+ 'Contents':
+ 'Edukiak',
+ 'Kind of':
+ 'Mota',
+ 'Part of':
+ 'Honen zatia',
+ 'Parts':
+ 'Zatiak',
+ 'Blocks':
+ 'Blokeak',
+ 'For all Sprites':
+ 'Objektu guztientzat',
+ 'Import tools':
+ 'Inportatu tresnak',
+ 'load the official library of\npowerful blocks':
+ 'kargatu bloke ahaltsuak dituen\nliburutegi ofiziala',
+ 'Libraries...':
+ 'Liburutegiak...',
+ 'Import library':
+ 'Inportatu liburutegia',
+ 'Select categories of additional blocks to add to this project.':
+ 'hautatu bloke gehigarrien kategoriak proiektu honi gehitzeko.',
+ 'Select a costume from the media library':
+ 'hautatu mozorro bat multimedia liburutegitik',
+ 'Select a sound from the media library':
+ 'hautatu soinu bat multimedia liburutegitik',
+ 'Opening blocks...':
+ 'Blokeak irekitzen...',
+
+ // cloud menu
+ 'Login...':
+ 'Hasi saioa...',
+ 'Signup...':
+ 'Erregistratu...',
+ 'Reset Password...':
+ 'Berrezarri pasahitza...',
+ 'Resend Verification Email...':
+ 'Bidali berriz egiaztapen posta elektronikoa...',
+ 'Cloud':
+ 'Hodeia',
+ 'Browser':
+ 'Nabigatzailea',
+ 'Examples':
+ 'Adibideak',
+ 'Updating\nproject list...':
+ 'Proiektuen zerrenda\neguneratzen...',
+
+ 'url...':
+ 'URLa...',
+ 'export project media only...':
+ 'esportatu proiektuaren soinu eta irudiak soilik',
+ 'export project without media...':
+ 'esportatu proiektua soinu eta irudirik gabe...',
+ 'export project as cloud data...':
+ 'esportatu proiektua hodeiko datu bezala...',
+ 'open shared project from cloud...':
+ 'ireki partekatutako proiektua hodeitik...',
+ 'Export Project As...':
+ 'esportatu proiektua honela...',
+
+ // cloud url dialog
+ 'Cloud URL':
+ 'Hodeiko URLa',
+ 'Snap!Cloud':
+ 'Snap!Cloud',
+ 'localhost':
+ 'localhost',
+ 'localhost (secure)':
+ 'localhost (segurua)',
+ 'Exported!':
+ 'Esportatuta!',
+
+ // signup dialog
+ 'Sign up':
+ 'Erregistratu',
+ 'User name:':
+ 'Erabiltzaile-izena:',
+ 'Birth date:':
+ 'Jaiotza-data:',
+ 'year:':
+ 'urtea:',
+ 'E-mail address:':
+ 'Helbide elektronikoa:',
+ 'E-mail address of parent or guardian:':
+ 'Guraso edo tutorearen helbide elektronikoa',
+ 'Password:':
+ 'Pasahitza:',
+ 'Repeat Password:':
+ 'Errepikatu pasahitza',
+ 'Terms of Service...':
+ 'Zerbitzu-baldintzak...',
+ 'Privacy...':
+ 'Pribatutasuna...',
+ 'I have read and agree\nto the Terms of Service':
+ 'Irakurri ditut eta onartzen ditut zerbitzu-baldintzak',
+
+ 'January':
+ 'Urtarrila',
+ 'February':
+ 'Otsaila',
+ 'March':
+ 'Martxoa',
+ 'April':
+ 'Apirila',
+ 'May':
+ 'Maiatza',
+ 'June':
+ 'Ekaina',
+ 'July':
+ 'Uztaila',
+ 'August':
+ 'Abuztua',
+ 'September':
+ 'Iraila',
+ 'October':
+ 'Urria',
+ 'November':
+ 'Azaroa',
+ 'December':
+ 'Abendua',
+ 'or before':
+ 'edo lehenago',
+
+ 'please fill out\nthis field':
+ 'mesedez bete eremu hau',
+ 'User name must be four\ncharacters or longer':
+ 'erabiltzaile-izenak lau karaktere\nedo gehiago izan behar ditu',
+ 'please provide a valid\nemail address':
+ 'mesedez idatzi baliozko\nhelbide elektroniko bat',
+ 'password must be six\ncharacters or longer':
+ 'pasahitzak sei karaktere\nedo gehiago izan behar ditu',
+ 'passwords do\nnot match':
+ 'pasahitzak ez datoz bat',
+ 'please agree to\nthe TOS':
+ 'mesedez onartu zerbitzu-baldintzak',
+
+ // signin dialog
+ 'Sign in':
+ 'Hasi saioa',
+ 'stay signed in on this computer\nuntil logging out':
+ 'mantendu saioa hasita ordenagailu honetan',
+
+ // reset password dialog
+ 'Reset password':
+ 'Berrezarri pasahitza',
+
+ // resend verification email dialog
+ 'Resend verification email':
+ 'Bidali berriz egiaztapen-mezua',
+
+ // change password dialog
+ 'Change Password':
+ 'Aldatu pasahitza',
+ 'Old password:':
+ 'Pasahitz zaharra:',
+ 'New password:':
+ 'Pasahitz berria:',
+ 'Repeat new password:':
+ 'Errepikatu pasahitz berria:',
+ 'password has been changed.':
+ 'Pasahitza aldatu da.',
+
+ // open shared project in cloud dialog
+ 'Author name\u2026':
+ 'Egilearen izena...',
+ 'Project name...':
+ 'Proiektuaren izena...',
+ 'Fetching project\nfrom the cloud...':
+ 'Proiektua hodeitik eskuratzen...',
+ 'Opening project...':
+ 'Proiektua irekitzen...',
+
+ 'Cloud Connection':
+ 'Hodeiko konexioa',
+ 'Successfully connected to:': // would this be translated? (gui.js:5439)
+ 'Behar bezala konektatuta:',
+ 'disconnected.':
+ 'Deskonektatuta.',
+ 'You are not logged in':
+ 'Ez duzu saioa hasi',
+
+ // settings menu
+ 'Language...':
+ 'Hizkuntza...',
+ 'Zoom blocks...':
+ 'Handitu blokeak',
+ 'Stage size...':
+ 'Agertokiaren tamaina...',
+ 'Stage size':
+ 'Agertokiaren tamaina',
+ 'Stage width':
+ 'Agertokiaren zabalera',
+ 'Stage height':
+ 'Agertokiaren altuera',
+ 'Default':
+ 'Lehenetsia',
+ 'Dragging threshold...':
+ 'Arrastatzeko atalasea...',
+ 'Dragging threshold':
+ 'Arrastatzeko atalasea',
+ 'specify the distance the hand has to move\nbefore it picks up an object':
+ 'zehaztu objektu bat arrastatzen hasteko\neskuarekin egin beharreko distantzia',
+ 'Blurred shadows':
+ 'Lausotutako itzalak',
+ 'uncheck to use solid drop\nshadows and highlights':
+ 'kendu marka itzal eta nabarmentze\nsolidoak erabiltzeko ',
+ 'check to use blurred drop\nshadows and highlights':
+ 'markatu itzal eta nabarmentze\nlausotuak erabiltzeko',
+ 'Zebra coloring':
+ 'Zebra koloreak',
+ 'check to enable alternating\ncolors for nested blocks':
+ 'markatu bloke habiaratuetan\ntxandakako koloreak gaitzeko',
+ 'uncheck to disable alternating\ncolors for nested block':
+ 'kendu marka bloke habiaratuetan\ntxandakako koloreak desgaitzeko',
+ 'Dynamic input labels':
+ 'Sarrera etiketa dinamikoak',
+ 'uncheck to disable dynamic\nlabels for variadic inputs':
+ 'kendu marka argumentu kopuru aldakorreko\nsarreretan etiketa dinamikoak desgaitzeko',
+ 'check to enable dynamic\nlabels for variadic inputs':
+ 'markatu argumentu kopuru aldakorreko\nsarreretan etiketa dinamikoak gaitzeko',
+ 'Prefer empty slot drops':
+ 'Lehenetsi erreten hutsak jaregitean',
+ 'settings menu prefer empty slots hint':
+ 'markatu jaregindako blokeek\nbesteak ordezkatzea galarazteko',
+ 'uncheck to allow dropped\nreporters to kick out others':
+ 'kendu marka jaregindako blokeek\nbesteak ordezkatzea baimentzeko',
+ 'Long form input dialog':
+ 'Sarreren elkarrizketa-koadro hedatua',
+ 'uncheck to use the input\ndialog in short form':
+ 'kendu marka sarreren\nelkarrizketa-koadro sinplea erabiltzeko',
+ 'check to always show slot\ntypes in the input dialog':
+ 'markatu sarreren elkarrizketa-koadroan\nerreten motak beti bistaratzeko',
+ 'Plain prototype labels':
+ 'Prototipoen etiketa lauak',
+ 'uncheck to always show (+) symbols\nin block prototype labels':
+ 'kendu marka blokeen prototipoen etiketetan\n(+) sinboloa beti bistaratzeko',
+ 'check to hide (+) symbols\nin block prototype labels':
+ 'markatu blokeen prototipoen etiketetan\n(+) sinboloa ezkutatzeko',
+ 'Virtual keyboard':
+ 'Teklatu birtuala',
+ 'uncheck to disable\nvirtual keyboard support\nfor mobile devices':
+ 'kendu marka gailu mugikorretan\nteklatu birtualaren\neuskarria desgaitzeko',
+ 'check to enable\nvirtual keyboard support\nfor mobile devices':
+ 'markatu gailu mugikorretan\nteklatu birtualaren\neuskarria gaitzeko',
+ 'Input sliders':
+ 'Graduatzaile sarrerak',
+ 'uncheck to disable\ninput sliders for\nentry fields':
+ 'kendu marka\nsarrerako eremuetako\ngraduatzaileak desgaitzeko',
+ 'check to enable\ninput sliders for\nentry fields':
+ 'markatu sarrerako eremuetako\ngraduatzaileak desgaitzeko',
+ 'Retina display support':
+ 'Retina pantailen euskarria',
+ 'uncheck for lower resolution,\nsaves computing resources':
+ 'kendu marka bereizmen baxuagoa erabiltzeko\n(baliabideak aurrezten ditu)',
+ 'check for higher resolution,\nuses more computing resources':
+ 'markatu bereizmen altuagoa erabiltzeko\n(baliabide gehiago erabiltzen ditu)',
+ 'Codification support':
+ 'Soporte de codificaci\u00F3n',
+ 'uncheck to disable\nblock to text mapping features':
+ 'kendu marka blokeetatik testura\nbihurtzea desgaitzeko',
+ 'check for block\nto text mapping features':
+ 'markatu blokeetatik testura\nbihurtzea gaitzeko',
+ 'Clicking sound':
+ 'Klik egitean soinua',
+ 'uncheck to turn\nblock clicking\nsound off':
+ 'kendu marka blokeak kokatzean\nklik soinurik ez egiteko',
+ 'check to turn\nblock clicking\nsound on':
+ 'markatu blokeak kokatzean\nklik soinua egiteko',
+ 'Animations':
+ 'Animazioak',
+ 'uncheck to disable\nIDE animations':
+ 'kendu marka IDEaren animazioak desgaitzeko',
+ 'check to enable\nIDE animations':
+ 'markatu IDEaren animazioak gaitzeko',
+ 'Turbo mode':
+ 'Turbo modua',
+ 'check to prioritize\nscript execution':
+ 'markatu programen exekuzioa lehenesteko',
+ 'uncheck to run scripts\nat normal speed':
+ 'kendu marka programak\nabiadura normalean exekutatzeko',
+ 'Flat design':
+ 'Diseinu laua',
+ 'uncheck for default\nGUI design':
+ 'kendu marka erabiltzaile interfaze\nlehenetsia erabiltzeko',
+ 'check for alternative\nGUI design':
+ 'markatu erabiltzaile interfaze\nalternatiboa erabiltzeko',
+ 'Nested auto-wrapping':
+ 'Habiaratutakoak automatikoki biltzea',
+ 'uncheck to confine auto-wrapping\nto top-level block stacks':
+ 'kendu marka goi mailako bloke pilak\nsoilik biltzeko automatikoki',
+ 'check to enable auto-wrapping\ninside nested block stacks':
+ 'markatu habiratutako bloke pilak\nautomatikoki biltzea gaitzeko',
+ 'Keyboard Editing':
+ 'Teklatu edizioa',
+ 'uncheck to disable\nkeyboard editing support':
+ 'kendu marka teklatu edizioaren\neuskarria desgaitzeko',
+ 'check to enable\nkeyboard editing support':
+ 'markatu teklatu edizioaren\neuskarria gaitzeko',
+ 'Table support':
+ 'Taulen euskarria',
+ 'check for multi-column\nlist view support':
+ 'markatu zerrenda ikuspegian\nhainbat zutaberen euskarria gaitzeko',
+ 'uncheck to disable\nmulti-column list views':
+ 'kendu marka zerrenda ikuspegian\nhainbat zutaberen euskarria desgaitzeko',
+ 'Table lines':
+ 'Taula lerroak',
+ 'check for higher contrast\ntable views':
+ 'markatu kontraste handiagoko\ntaula ikuspegia gaitzeko',
+ 'uncheck for less contrast\nmulti-column list views':
+ 'kendu marka kontraste handiagoko\ntaula ikuspegia desgaitzeko',
+ 'Visible stepping':
+ 'Pausoz pauso ikusgai',
+ 'check to turn on\n visible stepping (slow)':
+ 'markatu programan pausoz pauso joateko (mantsoa)',
+ 'uncheck to turn off\nvisible stepping':
+ 'kendu marka programan pausoz pauso joatea desgaitzeko',
+ 'Thread safe scripts':
+ 'Programa hari seguruak',
+ 'uncheck to allow\nscript reentrance':
+ 'kendu marka programetara\nberriz sartzea desgaitzeko',
+ 'check to disallow\nscript reentrance':
+ 'markatu programetara\nberriz sartzea gaitzeko',
+ 'Prefer smooth animations':
+ 'Hobetsi animazio leunak',
+ 'uncheck for greater speed\nat variable frame rates':
+ 'kendu marka fotograma-abiadura\naldakorrekin abiadura handiagoa izateko',
+ 'check for smooth, predictable\nanimations across computers':
+ 'markatu ordenagailu desberdinetan animazio\nleun eta aurreikusteko modukoak izateko',
+ 'Flat line ends':
+ 'Arkatzaren arrastoen amaiera zuzenak',
+ 'check for flat ends of lines':
+ 'markatu arrastoek\namaiera zuzenak izateko',
+ 'uncheck for round ends of lines':
+ 'kendu marka arrastoek\namaiera borobilduak izateko',
+ 'Ternary Boolean slots':
+ 'Erreten boolear hirutarrak',
+ 'check to allow\nempty Boolean slots':
+ 'markatu erreten boolear\nhutsak onartzeko',
+ 'uncheck to limit\nBoolean slots to true / false':
+ 'kendu marka erreten boolearrak\negia / gezurra balioetara mugatzeko',
+ 'Camera support':
+ 'Kameraren euskarria',
+ 'uncheck to disable\ncamera support':
+ 'kendu marka kameraren\neuskarria desgaitzeko',
+ 'check to enable\ncamera support':
+ 'markatu kameraren\neuskarria gaitzeko',
+ 'Cache Inputs':
+ 'Sarreren cache-a',
+ 'uncheck to stop caching\ninputs (for debugging the evaluator)':
+ 'kendu marka sarrerak cache-an\nez gordetzeko (ebaluatzailea arazteko)',
+ 'check to cache inputs\nboosts recursion':
+ 'markatu sarrerak cache-an gordetzeko\n(errekurtsioa azkartzen du)',
+ 'Rasterize SVGs':
+ 'Sortu SVGen bilbea',
+ 'uncheck for smooth\nscaling of vector costumes':
+ 'kendu marka mozorro\nbektorialen eskalatze leunerako',
+ 'check to rasterize\nSVGs on import':
+ 'markatu SVGak inportatzean\nbilbea sortzeko',
+ 'Project URLs':
+ 'Proiektuaren URLak',
+ 'uncheck to disable\nproject data in URLs':
+ 'kendu marka proiektuen datuak\nURLean ez gehitzeko',
+ 'check to enable\nproject data in URLs':
+ 'markatu proiektuen datuak\nURLean gehitzeko',
+ 'Sprite Nesting':
+ 'Objektuak habiaratzea',
+ 'uncheck to disable\nsprite composition':
+ 'kendu marka objektuen\nkonposizioa desgaitzeko',
+ 'check to enable\nsprite composition':
+ 'markatu objektuen\nkonposizioa gaitzeko',
+ 'First-Class Sprites':
+ 'Lehen mailako objektuak',
+ 'uncheck to disable support\nfor first-class sprites':
+ 'kendu marka lehen mailako\nobjektuen euskarria desgaitzeko',
+ 'check to enable support\n for first-class sprite':
+ 'markatu lehen mailako\nobjektuen euskarria gaitzeko',
+ 'Live coding support':
+ 'Zuzenean programatzeko euskarria',
+ 'EXPERIMENTAL! uncheck to disable live\ncustom control structures':
+ 'ESPERIMENTALA! kendu marka zuzeneko\nkontrol egitura pertsonalizatuak desgaitzeko',
+ 'EXPERIMENTAL! check to enable\n live custom control structures':
+ 'ESPERIMENTALA! markatu zuzeneko\nkontrol egitura pertsonalizatuak gaitzeko',
+ 'JIT compiler support':
+ 'JIT konpiladorearen euskarria',
+ 'EXPERIMENTAL! uncheck to disable live\nsupport for compiling':
+ 'ESPERIMENTALA! kendu marka zuzenean konpilatzeko\neuskarria desgaitzeko',
+ 'EXPERIMENTAL! check to enable\nsupport for compiling':
+ 'ESPERIMENTALA! markatu konpilatzeko\neuskarria gaitzeko',
+ 'Inheritance support':
+ 'Herentziaren euskarria',
+ 'uncheck to disable\nsprite inheritance features':
+ 'kendu marka objektuen\nherentzia ezaugarriak desgaitzeko',
+ 'check for sprite\ninheritance features':
+ 'markatu objektuen\nherentzia ezaugarriak gaitzeko',
+ 'Persist linked sublist IDs':
+ 'Estekatutako azpi-zerrenda ID iraunkorrak',
+ 'uncheck to disable\nsaving linked sublist identities':
+ 'kendu marka estekatutako azpi-zerrenden\nidentitateak gordetzea desgaitzeko',
+ 'check to enable\nsaving linked sublist identities':
+ 'markatu estekatutako azpi-zerrenden\nidentitateak gordetzea gaitzeko',
+
+ // inputs
+ 'with inputs':
+ 'sarrerekin',
+ 'input names:':
+ 'sarreren izenak:',
+ 'Input Names:':
+ 'Sarreren izenak:',
+ 'input list:':
+ 'Sarrera zerrenda:',
+
+ // context menus:
+ 'help':
+ 'laguntza',
+
+ // palette:
+ 'find blocks':
+ 'bilatu blokeak',
+ 'hide primitives':
+ 'ezkutatu primitiboak',
+ 'show primitives':
+ 'erakutsi primitiboak',
+
+ // blocks:
+ 'help...':
+ 'laguntza...',
+ 'relabel...':
+ 'aldatu izena...',
+ 'duplicate':
+ 'bikoiztu',
+ 'make a copy\nand pick it up':
+ 'egin kopia\neta hartu',
+ 'only duplicate this block':
+ 'bikoiztu soilik bloke hau',
+ 'delete':
+ 'ezabatu',
+ 'script pic...':
+ 'programaren argazkia...',
+ 'open a new window\nwith a picture of this script':
+ 'ireki programa honen argazkia\nleiho berri batean',
+ 'ringify':
+ 'eraztundu',
+ 'unringify':
+ 'deseraztundu',
+ 'transient':
+ 'behin-behinekoa',
+ 'uncheck to save contents\nin the project':
+ 'kendu marka proiektuaren\nedukiak gordetzeko',
+ 'check to prevent contents\nfrom being saved':
+ 'markatu edukiak\ngordetzea eragozteko',
+ 'new line':
+ 'lerro berria',
+
+ // custom blocks:
+ 'delete block definition...':
+ 'ezabatu blokearen definizioa...',
+ 'duplicate block definition...':
+ 'bikoiztu blokearen definizioa...',
+ 'Same Named Blocks':
+ 'Izen bereko blokeak',
+ 'Another custom block with this name exists.\nWould you like to replace it?':
+ 'Izen bereko bloke pertsonalizatu bat dago.\nOrdezkatu nahi duzu?',
+ 'edit...':
+ 'editatu...',
+ 'rename...':
+ 'aldatu izena...',
+ 'rename all...':
+ 'aldatu izena guztiei...',
+ 'translations...':
+ 'itzulpenak...',
+ 'block variables...':
+ 'blokearen aldagaiak...',
+ 'remove block variables...':
+ 'kendu blokearen aldagaiak...',
+ 'experimental -\nunder construction':
+ 'esperimentala -\eraikitzen',
+
+ // sprites:
+ 'edit':
+ 'editatu',
+ 'clone':
+ 'klonatu',
+ 'move':
+ 'mugitu',
+ 'pivot':
+ 'ardatza',
+ 'edit the costume\'s\nrotation center':
+ 'editatu mozorroaren\nbiraketa-zentroa',
+ 'rotate':
+ 'biratu',
+ 'detach from':
+ 'bereizi hemendik',
+ 'detach all parts':
+ 'bereizi zati guztiak',
+ 'export...':
+ 'esportatu...',
+ 'parent...':
+ 'gurasoa...',
+ 'current parent':
+ 'uneko gurasoa',
+ 'release':
+ 'askatu',
+ 'make temporary and\nhide in the sprite corral':
+ 'bihurtu behin-behineko eta\nezkutatu objektuen multzotik',
+ 'make permanent and\nshow in the sprite corral':
+ 'bihurtu behin-betiko eta\nerakutsi objektuen multzoan',
+
+ // stage:
+ 'show all':
+ 'erakutsi guztiak',
+ 'pic...':
+ 'argazkia...',
+ 'open a new window\nwith a picture of the stage':
+ 'ireki agertokiaren argazki bat\nleiho berrian',
+
+ // scripting area
+ 'clean up':
+ 'garbitu',
+ 'arrange scripts\nvertically':
+ 'antolatu programak\nbertikalki',
+ 'add comment':
+ 'gehitu iruzkina',
+ 'undrop':
+ 'desegin jaregitea',
+ 'undo the last\nblock drop\nin this pane':
+ 'desegin panel honetan\nazken blokea jaregitea',
+ 'redrop':
+ 'berriz jaregin',
+ 'use the keyboard\nto enter blocks':
+ 'erabili teklatua\nblokeak sartzeko',
+ 'scripts pic...':
+ 'programen argazkiak...',
+ 'open a new window\nwith a picture of all scripts':
+ 'ireki programa guztien\nargazkiak leiho berrian',
+ 'make a block...':
+ 'sortu blokea...',
+
+ // costumes
+ 'rename':
+ 'aldatu izena',
+ 'export':
+ 'esportatu',
+ 'rename costume':
+ 'aldatu izena mozorroari',
+
+ // sounds
+ 'Play sound':
+ 'Erreproduzitu soinua',
+ 'Stop sound':
+ 'Gelditu soinua',
+ 'Stop':
+ 'Gelditu',
+ 'Play':
+ 'Erreproduzitu',
+ 'rename sound':
+ 'Aldatu izena soinuari',
+
+ // lists and tables
+ 'list view...':
+ 'zerrenda ikuspegia...',
+ 'table view...':
+ 'taula ikuspegia...',
+ 'open in dialog...':
+ 'ireki elkarrizketa-koadroan...',
+ 'reset columns':
+ 'berrezarri zutabeak',
+ 'items':
+ 'elementuak',
+
+ // custom block's text fragment symbols:
+ 'square':
+ 'karratua',
+ 'pointRight':
+ 'eskuinera',
+ 'stepForward':
+ 'hurrengo pausoa',
+ 'gears':
+ 'engranajeak',
+ 'file':
+ 'fitxategia',
+ 'fullScreen':
+ 'pantaila osoa',
+ 'normalScreen':
+ 'pantaila normala',
+ 'smallStage':
+ 'agertoki txikia',
+ 'normalStage':
+ 'agertoki handia',
+ 'turtle':
+ 'dortoka',
+ // already defined
+ // 'stage':
+ // 'agertokia',
+ 'turtleOutline':
+ 'dortoka (silueta)',
+ 'pause':
+ 'pausatu',
+ 'flag':
+ 'bandera',
+ 'octagon':
+ 'oktogonoa',
+ 'cloud':
+ 'hodeia',
+ 'cloudOutline':
+ 'hodeia (silueta)',
+ 'cloudGradient':
+ 'hodeia (gradientea)',
+ 'turnRight':
+ 'biratu eskuinera',
+ 'turnLeft':
+ 'biratu ezkerrera',
+ 'storage':
+ 'biltegiratzea',
+ 'poster':
+ 'posterra',
+ 'flash':
+ 'tximista',
+ 'brush':
+ 'pintzela',
+ 'rectangle':
+ 'laukizuzena',
+ 'rectangleSolid':
+ 'laukizuzena (betea)',
+ 'circle':
+ 'zirkulua',
+ 'circleSolid':
+ 'zirkulua (betea)',
+ 'ellipse':
+ 'elipsea',
+ // already defined
+ // 'line':
+ // 'lerroa',
+ 'cross':
+ 'gurutzea',
+ 'crosshairs':
+ 'mira',
+ 'paintbucket':
+ 'pintura',
+ 'eraser':
+ 'borragoma',
+ 'pipette':
+ 'tanta-kontagailua',
+ 'speechBubble':
+ 'bunbuiloa',
+ 'speechBubbleOutline':
+ 'bunbuiloa (silueta)',
+ 'turnBack':
+ 'atzera',
+ 'turnForward':
+ 'aurrera',
+ 'arrowUp':
+ 'gezia gora',
+ 'arrowUpOutline':
+ 'gezia gora (silueta)',
+ 'arrowLeft':
+ 'gezia ezkerrera',
+ 'arrowLeftOutline':
+ 'gezia ezkerrera (silueta)',
+ 'arrowDown':
+ 'gezia behera',
+ 'arrowDownOutline':
+ 'gezia behera (silueta)',
+ 'arrowRight':
+ 'gezia eskuinera',
+ 'arrowRightOutline':
+ 'gezia eskuinera (silueta)',
+ 'robot':
+ 'robota',
+ 'magnifyingGlass':
+ 'lupa',
+ 'magnifierOutline':
+ 'lupa (silueta)',
+ 'selection':
+ 'hautapena',
+ 'polygon':
+ 'poligonoa',
+ 'closedBrush':
+ 'pintzel itxia',
+ 'notes':
+ 'musika nota',
+ // already defined
+ // 'camera':
+ // 'kamera',
+ 'location':
+ 'kokapena',
+ 'footprints':
+ 'oinatzak',
+ 'keyboard':
+ 'teklatua',
+ 'keyboardFilled':
+ 'teklatua (betea)',
+ 'new line':
+ 'lerro berria',
+
+ // dialogs
+ // buttons
+ 'OK':
+ 'Ados',
+ 'Ok':
+ 'Ados',
+ 'Cancel':
+ 'Utzi',
+ 'Yes':
+ 'Bai',
+ 'No':
+ 'Ez',
+ 'Import':
+ 'Inportatu',
+
+ // help
+ 'Help':
+ 'Laguntza',
+
+ // zoom blocks
+ 'Zoom blocks':
+ 'Handitu blokeak',
+ 'build':
+ 'eraiki',
+ 'your own':
+ 'zure',
+ 'blocks':
+ 'blokeak',
+ 'normal (1x)':
+ 'normala (1x)',
+ 'demo (1.2x)':
+ 'demoa (1.2x)',
+ 'presentation (1.4x)':
+ 'aurkezpena (1.4x)',
+ 'big (2x)':
+ 'handia (2x)',
+ 'huge (4x)':
+ 'oso handia (4x)',
+ 'giant (8x)':
+ 'erraldoia (8x)',
+ 'monstrous (10x)':
+ 'ikaragarria (10x)',
+
+ // Project Manager
+ 'Untitled':
+ 'Izenik gabea',
+ 'Open Project':
+ 'Ireki proiektua',
+ 'Open':
+ 'Ireki',
+ '(empty)':
+ '(hutsa)',
+ 'Saved!':
+ 'Gordeta!',
+ 'Delete Project':
+ 'Ezabatu proiektua',
+ 'Are you sure you want to delete':
+ 'Ziur zaude ezabatu nahi duzula?',
+ 'rename...':
+ 'aldatu izena...',
+
+ // costume editor
+ 'Costume Editor':
+ 'Mozorro editorea',
+ 'click or drag crosshairs to move the rotation center':
+ 'egin klik edo arrastatu mira biraketa zentroa mugitzeko',
+
+ // project notes
+ 'Project Notes':
+ 'Proiektuaren oharrak',
+
+ // new project
+ 'New Project':
+ 'Proiektu berria',
+ 'Replace the current project with a new one?':
+ 'Uneko proiektua berriarekin ordezkatu nahi duzu?',
+
+ // save project
+ 'Save Project':
+ 'Gorde proiektua',
+ 'Save Project As...':
+ 'Gorde proiektua honela...',
+
+ // export blocks
+ 'Export blocks':
+ 'Esportatu blokeak',
+ 'Import blocks':
+ 'Inportatu blokeak',
+ 'this project doesn\'t have any\ncustom global blocks yet':
+ 'proiektu honek oraindik\nez dauka bloke pertsonalizaturik',
+ 'select':
+ 'hautatu',
+ 'none':
+ 'bat ere ez',
+
+ // variable dialog
+ 'for all sprites':
+ 'objektu guztientzat',
+ 'for this sprite only':
+ 'objektu honentzat bakarrik',
+
+ // variables refactoring
+ 'rename only\nthis reporter':
+ 'aldatu izena\nberriemaile honi',
+ 'rename all...':
+ 'aldatu izena guztiei...',
+ 'rename all blocks that\naccess this variable':
+ 'aldatu izena aldagai hau\natzitzen duten bloke guztiei',
+
+
+ // block dialog
+ 'Change block':
+ 'Aldatu blokea',
+ 'Command':
+ 'Komandoa:',
+ 'Reporter':
+ 'Berriemailea',
+ 'Predicate':
+ 'Predikatua',
+
+ // block editor
+ 'Block Editor':
+ 'Bloke editorea',
+ 'Method Editor':
+ 'Metodo editorea',
+ 'Apply':
+ 'Aplikatu',
+
+ // block deletion dialog
+ 'Delete Custom Block':
+ 'Ezabatu bloke pertsonalizatua',
+ 'block deletion dialog text':
+ 'Ziur zaude bloke pertsonalizatu hau\neta bere instantzia guztiak\nezabatu nahi dituzula?',
+
+ // input dialog
+ 'Create input name':
+ 'Sortu sarrera',
+ 'Edit input name':
+ 'Editatu sarrera',
+ 'Edit label fragment':
+ 'Editatu etiketaren zatia',
+ 'Title text':
+ 'Izenburua',
+ 'Input name':
+ 'Sarrera',
+ 'Delete':
+ 'Ezabatu',
+ 'Object':
+ 'Objektua',
+ 'Number':
+ 'Zenbakia',
+ 'Text':
+ 'Testua',
+ 'List':
+ 'Zerrenda',
+ 'Any type':
+ 'Edozein mota',
+ 'Boolean (T/F)':
+ 'Boolearra (E/G)',
+ 'Command\n(inline)':
+ 'Komandoa\n(linean)',
+ 'Command\n(C-shape)':
+ 'Komandoa\n(C itxura)',
+ 'Any\n(unevaluated)':
+ 'Edozein\n(ez ebaluatua)',
+ 'Boolean\n(unevaluated)':
+ 'Boolearra\n(ez ebaluatua)',
+ 'Single input.':
+ 'Sarrera bakarra.',
+ 'Default Value:':
+ 'Balio lehenetsia:',
+ 'Multiple inputs (value is list of inputs)':
+ 'Hainbat sarrera (balioa sarreren zerrenda da)',
+ 'Upvar - make internal variable visible to caller':
+ 'Upvar - egin barne aldagaia ikusgai deitzaileari',
+
+ // About Snap
+ 'About Snap':
+ 'Snap-i buruz',
+ 'Back...':
+ 'Atzera...',
+ 'License...':
+ 'Lizentzia...',
+ 'Modules...':
+ 'Modulua...',
+ 'Credits...':
+ 'Kredituak...',
+ 'Translators...':
+ 'Itzultzaileak',
+ 'License':
+ 'Lizentzia',
+ 'current module versions:':
+ 'Uneko moduluen bertsioak',
+ 'Contributors':
+ 'Laguntzaileak',
+ 'Translations':
+ 'Itzulpenak',
+
+ // variable watchers
+ 'normal':
+ 'normala',
+ 'large':
+ 'handia',
+ 'slider':
+ 'graduatzailea',
+ 'slider min...':
+ 'graduatzailea min...',
+ 'slider max...':
+ 'graduatzailea max...',
+ 'import...':
+ 'inportatu...',
+ 'Slider minimum value':
+ 'Graduatzailearen balio minimoa',
+ 'Slider maximum value':
+ 'Graduatzailearen balio maximoa',
+
+ // list watchers
+ 'length: ':
+ 'luzera: ',
+
+ // coments
+ 'add comment here...':
+ 'gehitu iruzkina hemen...',
+
+ // drow downs
+ // directions
+ '(90) right':
+ '(90) eskuinera',
+ '(-90) left':
+ '(-90) ezkerrera',
+ '(0) up':
+ '(0) gora',
+ '(180) down':
+ '(180) behera',
+ 'random':
+ 'ausazkoa',
+ 'random position':
+ 'ausazko posizioa',
+
+ // collision detection
+ 'mouse-pointer':
+ 'saguaren erakuslea',
+ 'edge':
+ 'ertza',
+ 'pen trails':
+ 'arkatzaren arrastoak',
+
+ // costumes
+ 'Turtle':
+ 'Dortoka',
+ 'Empty':
+ 'Hutsa',
+
+ // graphical effects
+ 'color':
+ 'kolorea',
+ 'fisheye':
+ 'arrain begia',
+ 'whirl':
+ 'zurrunbiloa',
+ 'pixelate':
+ 'pixelatu',
+ 'mosaic':
+ 'mosaikoa',
+ 'saturation':
+ 'saturazioa',
+ 'brightness':
+ 'distira',
+ 'ghost':
+ 'mamua',
+ 'negative':
+ 'negatiboa',
+ 'comic':
+ 'komikia',
+ 'confetti':
+ 'konfetia',
+
+ // keys
+ 'space':
+ 'zuriunea',
+ 'up arrow':
+ 'gora gezia',
+ 'down arrow':
+ 'behera gezia',
+ 'right arrow':
+ 'eskuinera gezia',
+ 'left arrow':
+ 'ezkerrera gezia',
+ 'any key':
+ 'edozein tekla',
+ 'a':
+ 'a',
+ 'b':
+ 'b',
+ 'c':
+ 'c',
+ 'd':
+ 'd',
+ 'e':
+ 'e',
+ 'f':
+ 'f',
+ 'g':
+ 'g',
+ 'h':
+ 'h',
+ 'i':
+ 'i',
+ 'j':
+ 'j',
+ 'k':
+ 'k',
+ 'l':
+ 'l',
+ 'm':
+ 'm',
+ 'n':
+ 'n',
+ 'o':
+ 'o',
+ 'p':
+ 'p',
+ 'q':
+ 'q',
+ 'r':
+ 'r',
+ 's':
+ 's',
+ 't':
+ 't',
+ 'u':
+ 'u',
+ 'v':
+ 'v',
+ 'w':
+ 'w',
+ 'x':
+ 'x',
+ 'y':
+ 'y',
+ 'z':
+ 'z',
+ '0':
+ '0',
+ '1':
+ '1',
+ '2':
+ '2',
+ '3':
+ '3',
+ '4':
+ '4',
+ '5':
+ '5',
+ '6':
+ '6',
+ '7':
+ '7',
+ '8':
+ '8',
+ '9':
+ '9',
+
+ // messages
+ 'new...':
+ 'berria...',
+
+ // math functions
+ 'abs':
+ 'Betrag',
+ 'ceiling':
+ 'sabaia',
+ 'floor':
+ 'lurra',
+ 'sqrt':
+ 'erroa',
+ 'sin':
+ 'sin',
+ 'cos':
+ 'cos',
+ 'tan':
+ 'tan',
+ 'asin':
+ 'asin',
+ 'acos':
+ 'acos',
+ 'atan':
+ 'atan',
+ 'ln':
+ 'ln',
+ 'e^':
+ 'e^',
+
+ // Boolean expressions keyboard entry
+ 'not':
+ 'ez',
+
+ // delimiters
+ 'letter':
+ 'letra',
+ 'whitespace':
+ 'zuriunea',
+ 'line':
+ 'lerroa',
+ 'tab':
+ 'tabuladorea',
+ 'cr':
+ 'orga-itzulera',
+
+ // data types
+ 'number':
+ 'zenbakia',
+ 'text':
+ 'testua',
+ 'Boolean':
+ 'boolearra',
+ 'list':
+ 'zerrenda',
+ 'command':
+ 'komandoa',
+ 'reporter':
+ 'berriemailea',
+ 'predicate':
+ 'predikatua',
+ 'sprite':
+ 'objektua',
+
+ // list indices
+ 'last':
+ 'azkena',
+ 'any':
+ 'edozein',
+
+ // attributes
+ 'neighbors':
+ 'auzokoak',
+ 'self':
+ 'norbera',
+ 'other sprites':
+ 'beste objektuak',
+ 'parts':
+ 'zatiak',
+ 'anchor':
+ 'aingura',
+ 'parent':
+ 'gurasoa',
+ 'children':
+ 'umea',
+ 'clones':
+ 'klonak',
+ 'other clones':
+ 'beste klonak',
+ 'dangling?':
+ 'zintzilik?',
+ 'rotation x':
+ 'x biraketa',
+ 'rotation y':
+ 'y biraketa',
+ 'center x':
+ 'x erdia',
+ 'center y':
+ 'y erdia',
+ 'name':
+ 'izena',
+ 'stage':
+ 'agertokia',
+ 'costumes':
+ 'mozorroak',
+ 'sounds':
+ 'soinuak',
+ 'scripts':
+ 'programak',
+
+ 'pen':
+ 'arkatza',
+ 'middle':
+ 'erdia',
+ 'tip':
+ 'punta',
+
+ 'center':
+ 'erdia',
+
+ // inheritance
+ 'inherited':
+ 'heredatua',
+ 'check to inherit\nfrom':
+ 'markatu hemendik\nheredatzeko',
+ 'uncheck to\ndisinherit':
+ 'kendu marka\nheredentzia kentzeko',
+
+ //costumes and backgrounds
+ 'rename background':
+ 'aldatu izena atzeko planoari',
+ 'turn pen trails into new background...':
+ 'sortu atzeko plano berria arkatzaren arrastoetatik',
+ 'turn all pen trails and stamps\ninto a new background for the stage':
+ 'sortu atzeko plano berria agertoki honentzat\narkatzaren arrastoetatik eta zigiluetatik',
+
+ //libraries
+ 'Tools':
+ 'Tresnak',
+ 'Standard library of powerful blocks (for, map, etc.)':
+ 'Bloke aurreratuen liburutegi estandarra (for, map...)',
+
+ 'Iteration, composition':
+ 'Iterazioa, konposizioa',
+ '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.':
+ 'Begizta egitura tradizionalak (while, until...) + Lisp-eko "named let" (for-aren orokortze bat) + iterazio funtzionala (funtzio bat modu errepikatuan deitzea) eta funtzioen konposizioa.',
+
+ 'List utilities':
+ 'Zerrenden utilitateak',
+ 'Some standard functions on lists (append, reverse, etc.)':
+ 'Zerrenden funtzio estandar batzuk (append, reverse...)',
+
+ 'Streams (lazy lists)':
+ 'Jarioak (zerrenda alferrak)',
+ '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.)':
+ 'Zerrenda datu motaren aldaera bat, non zerrendako elementu bakoitza ez den kalkulatzen behar den arte. Horri esker, milioi bat elementuko, edo tamaina infinitoko, zerrendak sor ditzakezu, beharrezkoa dena baino denbora edo memoria gehiago erabili gabe. (Zenbaki lehen guztiak itzultzen dituen bloke bat gehitu da adibide bezala).',
+
+ 'Variadic reporters':
+ 'Argumentu kopuru aldakorreko berriemaileak',
+ 'Versions of +, x, AND, and OR that take more than two inputs.':
+ 'Bi sarrera baino gehiago hartzen dituzten +, x, AND, eta OR-en aldaerak.',
+
+ 'Web services access (https)':
+ 'Web zerbitzuak atzitzea (https)',
+ 'An extended version of the HTTP:// block that allows POST, PUT, and DELETE as well as GET requests, allows using the secure HTTPS protocol, and gives control over headers, etc.':
+ 'GET, POST, PUT eta DELETE eskaerak egiteko aukera ematen duen HTTP:// blokearen hedapena. HTTPS protokolo segurua erabiltzeko aukera eta goiburuen gaineko kontrola ere ematen du...',
+
+ 'Words, sentences':
+ 'Hitzak, esaldiak',
+ 'One of the big ideas in Logo that they left out of Scratch is thinking of text as structured into words and sentences, rather than just a string of characters. This library (along with the JOIN WORDS block in the Tools library) brings back that idea.':
+ 'Testuak karaktere-kate soil gisa hartu ordez hitz eta esaldietan egituratutzea da, Scratch-en ez dagoen, Logo-ren ideia handietako bat. Liburutegi honek, Tresnak liburutegiko JOIN WORDS blokearekin batera, ideia hori berreskuratzen du.',
+
+ 'Multi-branched conditional (switch)':
+ 'Adar anitzeko baldintzak (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!':
+ 'C bezalako lengoaietako "switch" edo Lisp-eko "cond"-en modukoa. Mila esker Nathan Dinsmore adar bakoitzerako bloke bereizia erabiltzeko ideia izateagatik!',
+
+ 'LEAP Motion controller':
+ 'LEAP mugimendu kontrolatzailea',
+ 'Report hand positions from LEAP Motion controller (leapmotion.com).':
+ 'Eskuen kokapena lortu LEAP mugimendu kontrolatzailea erabiliz (leapmotion.com).',
+
+ 'Set RGB or HSV pen color':
+ 'Ezarri RGB edo HSV arkatz koloreak',
+ 'Set or report pen color as RGB (red, green, blue) or HSV (hue, saturation, value).':
+ 'Arkatzaren kolorea ezarri edo itzultzen du RGB (gorria, berdea, urdina) edo HSV (ñabardura, saturazioa, balioa) bezala',
+
+ 'Catch errors in a script':
+ 'Atzeman programa bateko erroreak',
+ '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.':
+ 'Exekutatu programa bat; Errore bat gertatzen bada, programa gelditu eta gorriz nabarmendu ordez, beste programa bat exekuta dezakezu errorea maneiatzeko. Sarrera bezala mezu bat jasotzen duen errore-bloke bat ere badakar eta programa aldagai bat sortu eta balioa emateko beste bloke bat ere bai.',
+
+ 'Allow multi-line text input to a block':
+ 'Lerro anitzeko testu sarrera blokeetan',
+ '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.':
+ 'Orokorrean, testu sarrerek lerro bakarra izaten dute. Bloke honek hainbat lerrotako testu sarrerak edukitzeko aukera ematen dizu. Testu sarrera erretenetan edo bestelako blokeetan erabil daiteke.',
+
+ 'Provide getters and setters for all GUI-controlled global settings':
+ 'GUI bidez kontrolatutako aukera global guztientzako getter eta setter-ak',
+ 'Eisenberg\'s Law: Anything that can be done from the GUI should be doable from the programming language, and vice versa.':
+ 'Eisenberg-en legea: Erabiltzaile interfaze grafiko bidez egin daitekeen edozein gauza programazio lengoaia erabiliz ere egin daiteke, eta alderantziz.',
+
+ 'Infinite precision integers, exact rationals, complex':
+ 'Zehaztasun infinituko zenbaki osoak, arrazional zehatzak, konplexuak',
+ 'The full Scheme numeric tower. "USE BIGNUMS " to enable.':
+ 'Scheme-ren dorre numeriko osoa. "USE BIGNUMS " aktibatzeko.',
+
+ 'Provide 100 selected colors':
+ 'Hautatutako 100 koloreko paleta',
+ 'to use instead of hue for better selection':
+ 'Hautatzea errazte aldera ñabarduraren ordez erabiltzeko',
+
+ 'Animation':
+ 'Animazioa',
+ 'glide, grow and rotate using easing functions.':
+ 'Irristatu, egin zoom eta biratu easing funtzioak erabiliz.',
+
+ 'Pixels':
+ 'Pixelak',
+ 'manipulate costumes pixel-wise.':
+ 'Manipulatu mozorroak pixel mailan.',
+
+ 'Audio Comp':
+ 'Audioa',
+ 'analyze, manipulate and generate sound samples.':
+ 'Analizatu, manipulatu eta sortu audio laginak.',
+};
diff --git a/lang-fi.js b/lang-fi.js
index bb70a9fc..b8d4e8bd 100644
--- a/lang-fi.js
+++ b/lang-fi.js
@@ -556,8 +556,8 @@ SnapTranslator.dict.fi = {
'Hei',
'world':
'maailma',
- 'letter %n of %s':
- 'kirjain nro %n tekstist\u00e4 %s',
+ 'letter %idx of %s':
+ 'kirjain nro %idx tekstist\u00e4 %s',
'length of %s':
'tekstin %s pituus',
'unicode of %s':
diff --git a/lang-fr.js b/lang-fr.js
index 5128d83c..940ca3e3 100644
--- a/lang-fr.js
+++ b/lang-fr.js
@@ -391,8 +391,8 @@ SnapTranslator.dict.fr = {
'arr\u00EAter tous les sons',
'rest for %n beats':
'faire une pause pour %n temps',
- 'play note %n for %n beats':
- 'jouer la note %n pour %n temps',
+ 'play note %note for %n beats':
+ 'jouer la note %note pour %n temps',
'change tempo by %n':
'ajouter %n au tempo',
'set tempo to %n bpm':
@@ -475,6 +475,10 @@ SnapTranslator.dict.fr = {
'lance %cmdRing %inputs',
'call %repRing %inputs':
'appelle %repRing %inputs',
+ 'tell %spr to %cmdRing %inputs':
+ 'dis à %spr de %cmdRing %inputs',
+ 'ask %spr for %repRing %inputs':
+ 'demande à %spr sa %repRing %inputs',
'run %cmdRing w/continuation':
'ex\u00E9cute %cmdRing avec continuation',
'call %cmdRing w/continuation':
@@ -485,6 +489,8 @@ SnapTranslator.dict.fr = {
'Quand je commence comme clone',
'create a clone of %cln':
'Clone %cln',
+ 'a new clone of %cln':
+ 'un nouveau clone de %cln',
'myself':
'moi-m\u00EAme',
'delete this clone':
@@ -572,8 +578,8 @@ SnapTranslator.dict.fr = {
'Bonjour',
'world':
'Monde',
- 'letter %n of %s':
- 'lettre %n de %s',
+ 'letter %idx of %s':
+ 'lettre %idx de %s',
'length of %s':
'longueur de %s',
'unicode of %s':
diff --git a/lang-gl.js b/lang-gl.js
index 5349b551..0c221aee 100644
--- a/lang-gl.js
+++ b/lang-gl.js
@@ -571,8 +571,8 @@ SnapTranslator.dict.gl = {
'Ola',
'world':
'mundo!',
- 'letter %n of %s':
- 'letra %n de %s',
+ 'letter %idx of %s':
+ 'letra %idx de %s',
'length of %s':
'lonxitude de %s',
'unicode of %s':
diff --git a/lang-hr.js b/lang-hr.js
index 94063310..1ea7af57 100755
--- a/lang-hr.js
+++ b/lang-hr.js
@@ -566,8 +566,8 @@ SnapTranslator.dict.hr = {
'pozdrav',
'world':
'svijet',
- 'letter %n of %s':
- 'znak %n od %s',
+ 'letter %idx of %s':
+ 'znak %idx od %s',
'length of %s':
'duljina od %s',
'unicode of %s':
diff --git a/lang-hu.js b/lang-hu.js
index 245f58ea..e8c0efca 100644
--- a/lang-hu.js
+++ b/lang-hu.js
@@ -562,8 +562,8 @@ SnapTranslator.dict.hu = {
'szia',
'world':
'világ',
- 'letter %n of %s':
- '%n karaktere ennek: %s',
+ 'letter %idx of %s':
+ '%idx karaktere ennek: %s',
'length of %s':
'%s hossza',
'unicode of %s':
diff --git a/lang-ia.js b/lang-ia.js
index 3c8fec91..c0788c43 100644
--- a/lang-ia.js
+++ b/lang-ia.js
@@ -574,8 +574,8 @@ SnapTranslator.dict.ia = {
'Hallo',
'world':
'Mundo',
- 'letter %n of %s':
- 'character %n de %s',
+ 'letter %idx of %s':
+ 'character %idx de %s',
'length of %s':
'longor de %s',
'unicode of %s':
diff --git a/lang-id.js b/lang-id.js
index 6b0dd76b..d2afd858 100644
--- a/lang-id.js
+++ b/lang-id.js
@@ -568,8 +568,8 @@ SnapTranslator.dict.id = {
'halo',
'world':
'dunia',
- 'letter %n of %s':
- 'huruf %n dari %s',
+ 'letter %idx of %s':
+ 'huruf %idx dari %s',
'length of %s':
'panjang dari %s',
'unicode of %s':
diff --git a/lang-it.js b/lang-it.js
index 84d85435..9698ef8a 100644
--- a/lang-it.js
+++ b/lang-it.js
@@ -598,8 +598,8 @@ SnapTranslator.dict.it = {
'ciao',
'world':
'mondo',
- 'letter %n of %s':
- 'lettera in posizione %n di %s',
+ 'letter %idx of %s':
+ 'lettera in posizione %idx di %s',
'length of %s':
'lunghezza di %s',
'unicode of %s':
diff --git a/lang-ja.js b/lang-ja.js
index ad80e910..549c9cb5 100755
--- a/lang-ja.js
+++ b/lang-ja.js
@@ -535,8 +535,8 @@ SnapTranslator.dict.ja = {
'ハロー',
'world':
'ワールド',
- 'letter %n of %s':
- '%n 文字目の文字 %s',
+ 'letter %idx of %s':
+ '%idx 文字目の文字 %s',
'length of %s':
'%s の長さ',
'unicode of %s':
diff --git a/lang-ja_HIRA.js b/lang-ja_HIRA.js
index f017f93b..905a1043 100755
--- a/lang-ja_HIRA.js
+++ b/lang-ja_HIRA.js
@@ -535,8 +535,8 @@ SnapTranslator.dict.ja_HIRA = {
'ハロー',
'world':
'ワールド',
- 'letter %n of %s':
- '%n もじめのもじ %s',
+ 'letter %idx of %s':
+ '%idx もじめのもじ %s',
'length of %s':
'%s のながさ',
'unicode of %s':
diff --git a/lang-kn.js b/lang-kn.js
index 24da2675..97287dbc 100644
--- a/lang-kn.js
+++ b/lang-kn.js
@@ -546,8 +546,8 @@ SnapTranslator.dict.kn = {
'\u0CA8\u0CAE\u0CB8\u0CCD\u0C95\u0CBE\u0CB0',
'world':
'\u0CAA\u0CCD\u0CB0\u0CAA\u0C82\u0C9A',
- 'letter %n of %s':
- '\u0C85\u0C95\u0CCD\u0CB7\u0CB0 %n \u0CB0\u0CB2\u0CCD\u0CB2\u0CBF %s',
+ 'letter %idx of %s':
+ '\u0C85\u0C95\u0CCD\u0CB7\u0CB0 %idx \u0CB0\u0CB2\u0CCD\u0CB2\u0CBF %s',
'length of %s':
'\u0CA8\u0020\u0C89\u0CA6\u0CCD\u0CA6 %s',
'unicode of %s':
diff --git a/lang-ko.js b/lang-ko.js
index 8cedf44c..f8e9abbc 100755
--- a/lang-ko.js
+++ b/lang-ko.js
@@ -580,8 +580,8 @@ SnapTranslator.dict.ko = {
'안녕',
'world':
'세상',
- 'letter %n of %s':
- '%n 번째 글자 ( %s 에 대한)',
+ 'letter %idx of %s':
+ '%idx 번째 글자 ( %s 에 대한)',
'length of %s':
'%s 의 길이',
'unicode of %s':
diff --git a/lang-ml.js b/lang-ml.js
index 415a280e..4178a6e5 100644
--- a/lang-ml.js
+++ b/lang-ml.js
@@ -553,8 +553,8 @@ SnapTranslator.dict.ml = {
'ഹലോ',
'world':
'ലോകം',
- 'letter %n of %s':
- '%s ന്റെ %n മത്തെ അക്ഷരം',
+ 'letter %idx of %s':
+ '%idx ന്റെ %n മത്തെ അക്ഷരം',
'length of %s':
'%s ന്റെ നീള',
'unicode of %s':
diff --git a/lang-nl.js b/lang-nl.js
index a08eb471..d8e367ae 100644
--- a/lang-nl.js
+++ b/lang-nl.js
@@ -564,8 +564,8 @@ SnapTranslator.dict.nl = {
'hallo',
'world':
'wereld',
- 'letter %n of %s':
- 'letter %n van %s',
+ 'letter %idx of %s':
+ 'letter %idx van %s',
'length of %s':
'lengte van %s',
'unicode of %s':
diff --git a/lang-no.js b/lang-no.js
index 96142b3d..70b7e866 100644
--- a/lang-no.js
+++ b/lang-no.js
@@ -1 +1 @@
-/*
lang-no.js
Norwegian translation for SNAP!
written by Olav A Marschall
Copyright (C) 2013 by Jens Mnig
This file is part of Snap!.
Snap! is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see .
Note to Translators:
--------------------
At this stage of development, Snap! can be translated to any LTR language
maintaining the current order of inputs (formal parameters in blocks).
Translating Snap! is easy:
1. Download
Download the sources and extract them into a local folder on your
computer:
Use the German translation file (named 'lang-de.js') as template for your
own translations. Start with editing the original file, because that way
you will be able to immediately check the results in your browsers while
you're working on your translation (keep the local copy of snap.html open
in your web browser, and refresh it as you progress with your
translation).
2. Edit
Edit the translation file with a regular text editor, or with your
favorite JavaScript editor.
In the first non-commented line (the one right below this
note) replace "de" with the two-letter ISO 639-1 code for your language,
e.g.
fr - French => SnapTranslator.dict.fr = {
it - Italian => SnapTranslator.dict.it = {
pl - Polish => SnapTranslator.dict.pl = {
pt - Portuguese => SnapTranslator.dict.pt = {
es - Spanish => SnapTranslator.dict.es = {
el - Greek => => SnapTranslator.dict.el = {
etc. (see )
3. Translate
Then work through the dictionary, replacing the German strings against
your translations. The dictionary is a straight-forward JavaScript ad-hoc
object, for review purposes it should be formatted as follows:
{
'English string':
'Translation string',
'last key':
} 'last value'
and you only edit the indented value strings. Note that each key-value
pair needs to be delimited by a comma, but that there shouldn't be a comma
after the last pair (again, just overwrite the template file and you'll be
fine).
If something doesn't work, or if you're unsure about the formalities you
should check your file with
This will inform you about any missed commas etc.
4. Accented characters
Depending on which text editor and which file encoding you use you can
directly enter special characters (e.g. Umlaut, accented characters) on
your keyboard. However, I've noticed that some browsers may not display
special characters correctly, even if other browsers do. So it's best to
check your results in several browsers. If you want to be on the safe
side, it's even better to escape these characters using Unicode.
see:
5. Block specs:
At this time your translation of block specs will only work
correctly, if the order of formal parameters and their types
are unchanged. Placeholders for inputs (formal parameters) are
indicated by a preceding % prefix and followed by a type
abbreviation.
For example:
'say %s for %n secs'
can currently not be changed into
'say %n secs long %s'
and still work as intended.
Similarly
'point towards %dst'
cannot be changed into
'point towards %cst'
without breaking its functionality.
6. Submit
When you're done, rename the edited file by replacing the "de" part of the
filename with the two-letter ISO 639-1 code for your language, e.g.
fr - French => lang-fr.js
it - Italian => lang-it.js
pl - Polish => lang-pl.js
pt - Portuguese => lang-pt.js
es - Spanish => lang-es.js
el - Greek => => lang-el.js
and send it to me for inclusion in the official Snap! distribution.
Once your translation has been included, Your name will the shown in the
"Translators" tab in the "About Snap!" dialog box, and you will be able to
directly launch a translated version of Snap! in your browser by appending
lang:xx
to the URL, xx representing your translations two-letter code.
7. Known issues
In some browsers accents or ornaments located in typographic ascenders
above the cap height are currently (partially) cut-off.
Enjoy!
-Jens
*/
/*global SnapTranslator*/
SnapTranslator.dict.no = {
/*
Special characters: (see )
, \u00F8
, \u00E6
, \u00E5
, \u00D8
; \u00C6
, \u00C5
*/
// translations meta information
'language_name':
'Norsk', // the name as it should appear in the language menu
'language_translator':
'Olav A Marschall', // your name for the Translators tab
'translator_e-mail':
'mattebananer@gmail.com', // optional
'last_changed':
'2013-09-16', // this, too, will appear in the Translators tab
// GUI
// control bar:
'untitled':
'uten navn',
'development mode':
'utviklermodus',
// categories:
'Motion':
'Bevegelse',
'Looks':
'Utseende',
'Sound':
'Lyd',
'Pen':
'Penn',
'Control':
'Styring',
'Sensing':
'Sansning',
'Operators':
'Operatorer',
'Variables':
'Variabler',
'Lists':
'Lister',
'Other':
'Andre',
// editor:
'draggable':
'flyttbar',
// tabs:
'Scripts':
'Skripter',
'Costumes':
'Drakter',
'Sounds':
'Lyder',
// names:
'Sprite':
'Objekt',
'Stage':
'Scene',
// rotation styles:
'don\'t rotate':
'roterer ikke',
'can rotate':
'roterer',
'only face left/right':
'kun mot venstre/h\u00F8yre',
// new sprite button:
'add a new Sprite':
'legg til nytt objekt',
// tab help
'costumes tab help':
'drakter tab hjelp',
'import a sound from your computer\nby dragging it into here':
'importer lyder fra din datamaskin',
// primitive blocks:
/*
Attention Translators:
----------------------
At this time your translation of block specs will only work
correctly, if the order of formal parameters and their types
are unchanged. Placeholders for inputs (formal parameters) are
indicated by a preceding % prefix and followed by a type
abbreviation.
For example:
'say %s for %n secs'
can currently not be changed into
'say %n secs long %s'
and still work as intended.
Similarly
'point towards %dst'
cannot be changed into
'point towards %cst'
without breaking its functionality.
*/
// motion:
'Stage selected:\nno motion primitives':
'Scene valgte:\ningen standard bevegelse'
+ 'finnes',
'move %n steps':
'g\u00E5 %n steg',
'turn %clockwise %n degrees':
'vend %clockwise %n grader',
'turn %counterclockwise %n degrees':
'vend %counterclockwise %n grader',
'point in direction %dir':
'pek i retning %dir',
'point towards %dst':
'pek mot %dst',
'go to x: %n y: %n':
'g\u00E5 til x: %n y: %n',
'go to %dst':
'g\u00E5 til %dst',
'glide %n secs to x: %n y: %n':
'gli %n sek til x: %n y: %n',
'change x by %n':
'endre x med %n',
'set x to %n':
'sett x til %n',
'change y by %n':
'endre y med %n',
'set y to %n':
'sett y til %n',
'if on edge, bounce':
'sprett tilbake ved kanten',
'x position':
'x-posisjon',
'y position':
'y-posisjon',
'direction':
'retning',
// looks:
'switch to costume %cst':
'switch til drakt %cst',
'next costume':
'neste drakt',
'costume #':
'drakt nr.',
'say %s for %n secs':
'si %s i %n sek',
'say %s':
'si %s',
'think %s for %n secs':
'tenk %s i %n sek',
'think %s':
'tenk %s',
'Hello!':
'Heisann!',
'Hmm...':
'Vel...',
'change %eff effect by %n':
'endre %eff -effekt med %n',
'set %eff effect to %n':
'sett %eff -effekt til %n',
'clear graphic effects':
'nullstill grafiske effekter',
'change size by %n':
'endre st\u00F8rrelse med %n',
'set size to %n %':
'sett st\u00F8rrelse til %n %',
'size':
'st\u00F8rrelse',
'show':
'vis',
'hide':
'skjul',
'go to front':
'g\u00E5 fremst',
'go back %n layers':
'g\u00E5 %n lag tilbake',
'development mode \ndebugging primitives:':
'utviklermodus \nDebugging av blokker',
'console log %mult%s':
'skrive i konsoll: %mult%s',
'alert %mult%s':
'Pop-up: %mult%s',
// sound:
'play sound %snd':
'spill lyd %snd',
'play sound %snd until done':
'spill lyd %snd ferdig',
'stop all sounds':
'stopp all lyd',
'rest for %n beats':
'pause i %n slag',
'play note %n for %n beats':
'spill note %n i %n slag',
'change tempo by %n':
'endre tempo med %n',
'set tempo to %n bpm':
'sett tempo til %n bpm',
'tempo':
'tempo',
// pen:
'clear':
'slett',
'pen down':
'penn ned',
'pen up':
'penn opp',
'set pen color to %clr':
'sett pennfargen til %clr',
'change pen color by %n':
'endre pennfargen med %n',
'set pen color to %n':
'sett penfargen til %n',
'change pen shade by %n':
'endre pennintensitet med %n',
'set pen shade to %n':
'sett pennintensitet til %n',
'change pen size by %n':
'endre pennbredde med %n',
'set pen size to %n':
'sett pennbredde til %n',
'stamp':
'stemple',
// control:
'when %greenflag clicked':
'N\u00E5r %greenflag klikkes',
'when %key key pressed':
'N\u00E5r tast %key trykkes',
'when I am clicked':
'N\u00E5r jeg klikkes',
'when I receive %msg':
'N\u00E5r jeg %msg mottar',
'broadcast %msg':
'send melding %msg',
'broadcast %msg and wait':
'send melding %msg og vent',
'Message name':
'Meldingens navn',
'wait %n secs':
'vent i %n sek',
'wait until %b':
'vent til %b',
'forever %c':
'for alltid %c',
'repeat %n %c':
'gjenta %n ganger %c',
'repeat until %b %c':
'gjenta til %b %c',
'if %b %c':
'hvis %b %c',
'if %b %c else %c':
'hvis %b %c ellers %c',
'report %s':
'returner %s',
'stop block':
'stopp denne blokk',
'stop script':
'stopp dette skript',
'stop all %stop':
'stopp alt %stop',
'pause all %pause':
'pause (alle) %pause',
'run %cmdRing %inputs':
'kj\u00F8r %cmdRing fra %inputs',
'launch %cmdRing %inputs':
'start %cmdRing %inputs',
'call %repRing %inputs':
'kall %repRing fra %inputs',
'run %cmdRing w/continuation':
'kj\u00F8r %cmdRing med kontinuering',
'call %cmdRing w/continuation':
'kall %cmdRing med kontinuering',
'when I start as a clone':
'n\u00E5r klon startes',
'create a clone of %cln':
'opprett %cln',
'myself':
'meg',
'delete this clone':
'slett klon',
'warp %c':
'warp %c',
// sensing:
'touching %col ?':
'ber\u00F8rer %col ?',
'touching %clr ?':
'ber\u00F8rer %clr ?',
'color %clr is touching %clr ?':
'farge %clr ber\u00F8rer %clr ?',
'ask %s and wait':
'sp\u00F8r %s og vent',
'what\'s your name?':
'hva heter du?',
'answer':
'svar',
'mouse x':
'mus x-posisjon',
'mouse y':
'mus y-posisjon',
'mouse down?':
'mustast trykket?',
'key %key pressed?':
'tast %key trykket?',
'distance to %dst':
'avstand til %dst',
'reset timer':
'start stoppeklokke',
'timer':
'stoppeklokke',
'http:// %s':
'http:// %s',
'turbo mode?':
'turbo modus?',
'set turbo mode to %b':
'sett turbo modus til %b',
'filtered for %clr':
'filter %clr',
'stack size':
'stack-st\u00F8rrelse',
'frames':
'rammer',
// operators:
'%n mod %n':
'%n mod %n',
'round %n':
'rund av %n',
'%fun av %n':
'%fun von %n',
'pick random %n to %n':
'tilfeldig fra %n til %n',
'%b and %b':
'%b OG %b',
'%b or %b':
'%b ELLER %b',
'not %b':
'IKKE %b',
'true':
'SANN',
'false':
'USANN',
'join %words':
'skj\u00F8t %words',
'hello':
'hei',
'world':
'verden',
'letter %n of %s':
'bokstav %n av %s',
'length of %s':
'lengde av %s',
'unicode of %s':
'unicode av %s',
'unicode %n as letter':
'unicode %n som bokstav',
'is %s a %typ ?':
'%s er %typ ?',
'is %s identical to %s ?':
'%s identisk med %s ?',
'type of %s':
'type %s',
// variables:
'Make a variable':
'Ny variabel',
'Variable name':
'Variabelnavn',
'Delete a variable':
'Slett variabel',
'set %var to %s':
'sett %var til %s',
'change %var by %n':
'endre %var med %n',
'show variable %var':
'vis variabel %var',
'hide variable %var':
'skjul variabel %var',
'script variables %scriptVars':
'skriptvariable %scriptVars',
// lists:
'list %exp':
'liste %exp',
'%s in front of %l':
'%s framfor %l',
'item %idx of %l':
'element %idx av %l',
'all but first of %l':
'alt utenom f\u00F8rste av %l',
'length of %l':
'lengde av %l',
'%l contains %s':
'%l inneholder %s',
'thing':
'noe',
'add %s to %l':
'legg %s til %l',
'delete %ida of %l':
'fjern %ida fra %l',
'insert %s at %idx of %l':
'sett inn %s ved %idx i %l ein',
'replace item %idx of %l with %s':
'erstatt element %idx i %l med %s',
// other
'Make a block':
'Ny blokk',
// menus
// snap menu
'About...':
'Om Snap!...',
'Snap! website':
'Snap! websiden',
'Download source':
'Last ned kildekoden',
'Switch back to user mode':
'Tilbake til brukermodus',
'disable deep-Morphic\ncontext menus\nand show user-friendly ones':
'ut av Morphic\nkontekst menyer\nog vis kun brukervennlige',
'Switch to dev mode':
'inn i utviklermodus',
'enable Morphic\ncontext menus\nand inspectors,\nnot user-friendly!':
'inn i Morphic funksjoner\nog inspektorer,\nikke brukervennlig',
// project menu
'Project notes...':
'Prosjektnotater...',
'New':
'Nytt',
'Open...':
'\u00C5pne...',
'Save':
'Lagre',
'Save As...':
'Lagre som...',
'Import...':
'Importer...',
'file menu import hint':
'laster inn eksportertes prosjekt,\net bibliotek med '
+ 'blokker\n'
+ 'et kostym eller en lyd',
'Export project as plain text ...':
'Eksporter prosjekt som ren tekst...',
'Export project...':
'Eksporter prosjekt...',
'show project data as XML\nin a new browser window':
'vis prosjektdata som XML\ni et nytt nettleser vindu',
'Export blocks...':
'Eksporter blokker...',
'show global custom block definitions as XML\nin a new browser window':
'viser globale blokkdefinisjoner fra bruker\nsom XML i et nytt nettleser vindu',
'Import tools...':
'Importer verkt\u00F8y...',
'load the official library of\npowerful blocks':
'last ned snap-bibliotek med ekstra blokker',
'Libraries...':
'Biblioteker...',
'Import library':
'Importer biblioteker',
// cloud menu
'Login...':
'Logg inn...',
'Registrer deg...':
'Registrering...',
// settings menu
'Language...':
'Spr\u00E5k...',
'Zoom blocks...':
'Zoom blokkene...',
'Blurred shadows':
'Mjuke skygger (blurred)',
'uncheck to use solid drop\nshadows and highlights':
'fjern kryss for hard skygge\nog lyssetting',
'check to use blurred drop\nshadows and highlights':
'kryss av for hard skygge\nog lyssetting',
'Zebra coloring':
'Zebra farget',
'check to enable alternating\ncolors for nested blocks':
'kryss av for vekslende fargenyanser\ni nestede blokker',
'uncheck to disable alternating\ncolors for nested block':
'fjern kryss for \u00E5 hindre vekslende\nfargenyanser i nestede blokker',
'Dynamic input labels':
'Dynamisk inndata navn',
'uncheck to disable dynamic\nlabels for variadic inputs':
'fjern kryss for \u00E5 hindre dynamisk benevning\nav inndata med flere variabelfelt',
'check to enable dynamic\nlabels for variadic inputs':
'kryss av for\ndynamisk benevning av inndata med flere variabelfelt',
'Prefer empty slot drops':
'Preferanse for tomme variabelfelt',
'settings menu prefer empty slots hint':
'Valg meny\ntomme variabelfelt'
+ 'preferanse',
'uncheck to allow dropped\nreporters to kick out others':
'kryss vekk for at flyttede reportere vil ta plassen til andre\n',
'Long form input dialog':
'Lange dialoger for inndata',
'check to always show slot\ntypes in the input dialog':
'kryss av for \u00E5 vise variabelfelttype\ni inndata dialoger',
'uncheck to use the input\ndialog in short form':
'kryss vekk for \u00E5 bruke korte inndata\ndialoger',
'Virtual keyboard':
'Virtuelt tastatur',
'uncheck to disable\nvirtual keyboard support\nfor mobile devices':
'kryss vekk for \u00E5 sl\u00E5 av virtuelt\ntastatur p\u00E5 mobile enheter',
'check to enable\nvirtual keyboard support\nfor mobile devices':
'kryss av for \u00E5 sl\u00E5 p\u00E5 virtuelt\ntastatur p\u00E5 mobile enheter',
'Input sliders':
'Skyveknapp inndata ',
'uncheck to disable\ninput sliders for\nentry fields':
'kryss vekk for \u00E5 sl\u00E5 av\nskyveknapper i inndatafelt',
'check to enable\ninput sliders for\nentry fields':
'kryss av for \u00E5 sl\u00E5 p\u00E5 skyveknapper\ni inndatafelt',
'Clicking sound':
'Klikkelyd',
'uncheck to turn\nblock clicking\nsound off':
'kryss vekk for sl\u00E5 av klikkelyd',
'check to turn\nblock clicking\nsound on':
'kryss av for \u00E5 sl\u00E5 p\u00E5 klikkelyd',
'Animations':
'Animasjoner',
'uncheck to disable\nIDE animations':
'kryss vekk for \u00E5 sl\u00E5 av IDE-animasjoner',
'Turbo mode':
'Turbo modus',
'check to enable\nIDE animations':
'kryss av for \u00E5 sl\u00E5 p\u00E5 IDE-animasjoner',
'Thread safe scripts':
'Tr\u00E5dsikker skripting',
'uncheck to allow\nscript reentrancy':
'kryss vekk for \u00E5 sl\u00E5 p\u00E5 gjenbruk av p\u00E5begynte skripter',
'check to disallow\nscript reentrancy':
'kryss av for \u00E5 sl\u00E5 av gjenbruk av p\u00E5begynte skripter',
'Prefer smooth animations':
'Jevnere animasjoner',
'uncheck for greater speed\nat variable frame rates':
'kryss bort for strre fart ved variabel frame rate',
'check for smooth, predictable\nanimations across computers':
'kryss av for jevne animasjoner p alle maskinplattformer',
// inputs
'with inputs':
'med inndata',
'input names:':
'inndata navn:',
'Input Names:':
'Inndata navn:',
'input list:':
'inndata liste:',
// context menus:
'help':
'hjelp',
// blocks:
'hjelp...':
'hjelp...',
'relabel...':
'gi nytt navn...',
'duplicate':
'dupliser',
'make a copy\nand pick it up':
'lag kopi\n og plukk opp',
'only duplicate this block':
'dupliser kun denne blokk',
'delete':
'slette',
'script pic...':
'skript bilde...',
'open a new window\nwith a picture of this script':
'\u00E5pne nytt vindu med bildet av dette skriptet',
'ringify':
'ring rundt',
'unringify':
'fjerne ringen rundt',
// custom blocks:
'delete block definition...':
'slett blokk definisjoner',
'edit...':
'rediger...',
// sprites:
'edit':
'redigere',
'export...':
'eksporter...',
// stage:
'show all':
'vis alt',
// scripting area
'clean up':
'rydd',
'arrange scripts\nvertically':
'sett opp skriptene\nvertikalt',
'add comment':
'legg til kommentar',
'make a block...':
'lag ny blokk...',
// costumes
'rename':
'nytt navn',
'export':
'eksportere',
'rename costume':
'nytt navn for drakt',
// sounds
'Play sound':
'Spill lyd',
'Stop sound':
'Stop lyd',
'Stop':
'Stop',
'Play':
'Start',
'rename sound':
'nytt navn lyd',
// dialogs
// buttons
'OK':
'OK',
'Ok':
'OK',
'Cancel':
'Avbryt',
'Yes':
'Ja',
'No':
'Nei',
// help
'Help':
'Hjelp',
// Project Manager
'Untitled':
'Uten navn',
'Open Project':
'pne prosjekt',
'(empty)':
'(tomt)',
'Saved!':
'Lagret!',
'Delete Project':
'Slett prosjekt',
'Are you sure you want to delete':
'Vil du virkelig slette?',
'rename...':
'nytt navn...',
// costume editor
'Costume Editor':
'Drakt editor',
'click or drag crosshairs to move the rotation center':
'Klikk p\u00E5 kors for \u00E5 flytte rotasjonssenteret',
// project notes
'Project Notes':
'Prosjekt notater',
// new project
'New Project':
'Nytt prosjekt',
'Replace the current project with a new one?':
'Erstatt n\u00E5v\u00E6rende prosjekt med nytt prosjekt?',
// save project
'Save Project As...':
'Lagre prosjekt som...',
// export blocks
'Export blocks':
'Eksporter blokker',
'Import blocks':
'Importer blokker',
'this project doesn\'t have any\ncustom global blocks yet':
'dette prosjektet har s\u00E5langt ingen\nglobale blokker fra bruker',
'select':
'velg',
'all':
'alt',
'none':
'ingenting',
// variable dialog
'for all sprites':
'for alle objekter',
'for this sprite only':
'kun for dette objektet',
// block dialog
'Change block':
'Endre blokker',
'Command':
'Styring',
'Reporter':
'Funksjon',
'Predicate':
'Predikat',
// block editor
'Block Editor':
'Blokk editor',
'Apply':
'Gj\u00F8r gjeldende',
// block deletion dialog
'Delete Custom Block':
'Slett custom blokk',
'block deletion dialog text':
'Skal denne blokken med alle dens instanser\n' +
'bli slettet?',
// input dialog
'Create input name':
'Lag inndata navn',
'Edit input name':
'Rediger inndata navn',
'Edit label fragment':
'Rediger label fragment',
'Title text':
'Tittel',
'Input name':
'Inndata navn',
'Delete':
'Slett',
'Object':
'Objekt',
'Number':
'Tall',
'Text':
'Tekst',
'List':
'Liste',
'Any type':
'Type valgfritt',
'Boolean (T/F)':
'Boolsk (S/U)',
'Command\n(inline)':
'Kommando\n(inline)',
'Command\n(C-shape)':
'Kommando\n(C-Form)',
'Any\n(unevaluated)':
'Hvilken som helst\n(uevaluert)',
'Boolean\n(unevaluated)':
'Boolsk\n(uevaluert)',
'Single input.':
'Singel inndata.',
'Default Value:':
'Standardverdi:',
'Multiple inputs (value is list of inputs)':
'Fler-inndata (verdi er liste over inndata)',
'Upvar - make internal variable visible to caller':
'Upvar - gj\u00F8r interne variable synlig for den som kaller',
// About Snap
'About Snap':
'Om Snap',
'Back...':
'Tilbake...',
'License...':
'Lisens...',
'Modules...':
'Moduler...',
'Credits...':
'Takk til...',
'Translators...':
'Oversettere',
'License':
'Lisens',
'current module versions:':
'Komponent-versjoner',
'Contributors':
'Bidragsytere',
'Translations':
'Oversettelser',
// variable watchers
'normal':
'normal',
'large':
'stor',
'slider':
'skyveknapp',
'slider min...':
'skyveknapp min...',
'slider max...':
'skyveknapp max...',
'import...':
'importer...',
'Slider minimum value':
'Skyveknapp - minimumsverdi',
'Slider maximum value':
'Skyveknapp - maksimumsverdi',
// list watchers
'length: ':
'lengde: ',
// coments
'add comment here...':
'legg til kommentar her...',
// drow downs
// directions
'(90) h\u00F8yre':
'(90) h\u00F8yre',
'(-90) venstre':
'(-90) venstre',
'(0) opp':
'(0) oppe',
'(180) ned':
'(180) nede',
// collision detection
'mouse-pointer':
'musepeker',
'edge':
'kant',
'pen trails':
'pennspor',
// costumes
'Turtle':
'Objekt',
// graphical effects
'ghost':
'gjennomsiktig',
// keys
'space':
'mellomrom',
'up arrow':
'pil opp',
'down arrow':
'pil ned',
'right arrow':
'pil h\u00F8yre',
'left arrow':
'pil',
'a':
'a',
'b':
'b',
'c':
'c',
'd':
'd',
'e':
'e',
'f':
'f',
'g':
'g',
'h':
'h',
'i':
'i',
'j':
'j',
'k':
'k',
'l':
'l',
'm':
'm',
'n':
'n',
'o':
'o',
'p':
'p',
'q':
'q',
'r':
'r',
's':
's',
't':
't',
'u':
'u',
'v':
'v',
'w':
'w',
'x':
'x',
'y':
'y',
'z':
'z',
'0':
'0',
'1':
'1',
'2':
'2',
'3':
'3',
'4':
'4',
'5':
'5',
'6':
'6',
'7':
'7',
'8':
'8',
'9':
'9',
// messages
'new...':
'ny...',
// math functions
'abs':
'abs',
'sqrt':
'kvardrat',
'sin':
'sin',
'cos':
'cos',
'tan':
'tan',
'asin':
'arc-1',
'acos':
'cos-1',
'atan':
'tan-1',
'ln':
'ln',
'e^':
'e^',
// data types
'number':
'tall',
'text':
'tekst',
'Boolean':
'boolsk',
'list':
'liste',
'command':
'kommando',
'reporter':
'funksjonsblokk',
'predicate':
'predikat',
// list indices
'last':
'siste',
'any':
'hvilken som helst'
};
\ No newline at end of file
+/*
lang-no.js
Norwegian translation for SNAP!
written by Olav A Marschall
Copyright (C) 2013 by Jens Mnig
This file is part of Snap!.
Snap! is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see .
Note to Translators:
--------------------
At this stage of development, Snap! can be translated to any LTR language
maintaining the current order of inputs (formal parameters in blocks).
Translating Snap! is easy:
1. Download
Download the sources and extract them into a local folder on your
computer:
Use the German translation file (named 'lang-de.js') as template for your
own translations. Start with editing the original file, because that way
you will be able to immediately check the results in your browsers while
you're working on your translation (keep the local copy of snap.html open
in your web browser, and refresh it as you progress with your
translation).
2. Edit
Edit the translation file with a regular text editor, or with your
favorite JavaScript editor.
In the first non-commented line (the one right below this
note) replace "de" with the two-letter ISO 639-1 code for your language,
e.g.
fr - French => SnapTranslator.dict.fr = {
it - Italian => SnapTranslator.dict.it = {
pl - Polish => SnapTranslator.dict.pl = {
pt - Portuguese => SnapTranslator.dict.pt = {
es - Spanish => SnapTranslator.dict.es = {
el - Greek => => SnapTranslator.dict.el = {
etc. (see )
3. Translate
Then work through the dictionary, replacing the German strings against
your translations. The dictionary is a straight-forward JavaScript ad-hoc
object, for review purposes it should be formatted as follows:
{
'English string':
'Translation string',
'last key':
} 'last value'
and you only edit the indented value strings. Note that each key-value
pair needs to be delimited by a comma, but that there shouldn't be a comma
after the last pair (again, just overwrite the template file and you'll be
fine).
If something doesn't work, or if you're unsure about the formalities you
should check your file with
This will inform you about any missed commas etc.
4. Accented characters
Depending on which text editor and which file encoding you use you can
directly enter special characters (e.g. Umlaut, accented characters) on
your keyboard. However, I've noticed that some browsers may not display
special characters correctly, even if other browsers do. So it's best to
check your results in several browsers. If you want to be on the safe
side, it's even better to escape these characters using Unicode.
see:
5. Block specs:
At this time your translation of block specs will only work
correctly, if the order of formal parameters and their types
are unchanged. Placeholders for inputs (formal parameters) are
indicated by a preceding % prefix and followed by a type
abbreviation.
For example:
'say %s for %n secs'
can currently not be changed into
'say %n secs long %s'
and still work as intended.
Similarly
'point towards %dst'
cannot be changed into
'point towards %cst'
without breaking its functionality.
6. Submit
When you're done, rename the edited file by replacing the "de" part of the
filename with the two-letter ISO 639-1 code for your language, e.g.
fr - French => lang-fr.js
it - Italian => lang-it.js
pl - Polish => lang-pl.js
pt - Portuguese => lang-pt.js
es - Spanish => lang-es.js
el - Greek => => lang-el.js
and send it to me for inclusion in the official Snap! distribution.
Once your translation has been included, Your name will the shown in the
"Translators" tab in the "About Snap!" dialog box, and you will be able to
directly launch a translated version of Snap! in your browser by appending
lang:xx
to the URL, xx representing your translations two-letter code.
7. Known issues
In some browsers accents or ornaments located in typographic ascenders
above the cap height are currently (partially) cut-off.
Enjoy!
-Jens
*/
/*global SnapTranslator*/
SnapTranslator.dict.no = {
/*
Special characters: (see )
, \u00F8
, \u00E6
, \u00E5
, \u00D8
; \u00C6
, \u00C5
*/
// translations meta information
'language_name':
'Norsk', // the name as it should appear in the language menu
'language_translator':
'Olav A Marschall', // your name for the Translators tab
'translator_e-mail':
'mattebananer@gmail.com', // optional
'last_changed':
'2013-09-16', // this, too, will appear in the Translators tab
// GUI
// control bar:
'untitled':
'uten navn',
'development mode':
'utviklermodus',
// categories:
'Motion':
'Bevegelse',
'Looks':
'Utseende',
'Sound':
'Lyd',
'Pen':
'Penn',
'Control':
'Styring',
'Sensing':
'Sansning',
'Operators':
'Operatorer',
'Variables':
'Variabler',
'Lists':
'Lister',
'Other':
'Andre',
// editor:
'draggable':
'flyttbar',
// tabs:
'Scripts':
'Skripter',
'Costumes':
'Drakter',
'Sounds':
'Lyder',
// names:
'Sprite':
'Objekt',
'Stage':
'Scene',
// rotation styles:
'don\'t rotate':
'roterer ikke',
'can rotate':
'roterer',
'only face left/right':
'kun mot venstre/h\u00F8yre',
// new sprite button:
'add a new Sprite':
'legg til nytt objekt',
// tab help
'costumes tab help':
'drakter tab hjelp',
'import a sound from your computer\nby dragging it into here':
'importer lyder fra din datamaskin',
// primitive blocks:
/*
Attention Translators:
----------------------
At this time your translation of block specs will only work
correctly, if the order of formal parameters and their types
are unchanged. Placeholders for inputs (formal parameters) are
indicated by a preceding % prefix and followed by a type
abbreviation.
For example:
'say %s for %n secs'
can currently not be changed into
'say %n secs long %s'
and still work as intended.
Similarly
'point towards %dst'
cannot be changed into
'point towards %cst'
without breaking its functionality.
*/
// motion:
'Stage selected:\nno motion primitives':
'Scene valgte:\ningen standard bevegelse'
+ 'finnes',
'move %n steps':
'g\u00E5 %n steg',
'turn %clockwise %n degrees':
'vend %clockwise %n grader',
'turn %counterclockwise %n degrees':
'vend %counterclockwise %n grader',
'point in direction %dir':
'pek i retning %dir',
'point towards %dst':
'pek mot %dst',
'go to x: %n y: %n':
'g\u00E5 til x: %n y: %n',
'go to %dst':
'g\u00E5 til %dst',
'glide %n secs to x: %n y: %n':
'gli %n sek til x: %n y: %n',
'change x by %n':
'endre x med %n',
'set x to %n':
'sett x til %n',
'change y by %n':
'endre y med %n',
'set y to %n':
'sett y til %n',
'if on edge, bounce':
'sprett tilbake ved kanten',
'x position':
'x-posisjon',
'y position':
'y-posisjon',
'direction':
'retning',
// looks:
'switch to costume %cst':
'switch til drakt %cst',
'next costume':
'neste drakt',
'costume #':
'drakt nr.',
'say %s for %n secs':
'si %s i %n sek',
'say %s':
'si %s',
'think %s for %n secs':
'tenk %s i %n sek',
'think %s':
'tenk %s',
'Hello!':
'Heisann!',
'Hmm...':
'Vel...',
'change %eff effect by %n':
'endre %eff -effekt med %n',
'set %eff effect to %n':
'sett %eff -effekt til %n',
'clear graphic effects':
'nullstill grafiske effekter',
'change size by %n':
'endre st\u00F8rrelse med %n',
'set size to %n %':
'sett st\u00F8rrelse til %n %',
'size':
'st\u00F8rrelse',
'show':
'vis',
'hide':
'skjul',
'go to front':
'g\u00E5 fremst',
'go back %n layers':
'g\u00E5 %n lag tilbake',
'development mode \ndebugging primitives:':
'utviklermodus \nDebugging av blokker',
'console log %mult%s':
'skrive i konsoll: %mult%s',
'alert %mult%s':
'Pop-up: %mult%s',
// sound:
'play sound %snd':
'spill lyd %snd',
'play sound %snd until done':
'spill lyd %snd ferdig',
'stop all sounds':
'stopp all lyd',
'rest for %n beats':
'pause i %n slag',
'play note %n for %n beats':
'spill note %n i %n slag',
'change tempo by %n':
'endre tempo med %n',
'set tempo to %n bpm':
'sett tempo til %n bpm',
'tempo':
'tempo',
// pen:
'clear':
'slett',
'pen down':
'penn ned',
'pen up':
'penn opp',
'set pen color to %clr':
'sett pennfargen til %clr',
'change pen color by %n':
'endre pennfargen med %n',
'set pen color to %n':
'sett penfargen til %n',
'change pen shade by %n':
'endre pennintensitet med %n',
'set pen shade to %n':
'sett pennintensitet til %n',
'change pen size by %n':
'endre pennbredde med %n',
'set pen size to %n':
'sett pennbredde til %n',
'stamp':
'stemple',
// control:
'when %greenflag clicked':
'N\u00E5r %greenflag klikkes',
'when %key key pressed':
'N\u00E5r tast %key trykkes',
'when I am clicked':
'N\u00E5r jeg klikkes',
'when I receive %msg':
'N\u00E5r jeg %msg mottar',
'broadcast %msg':
'send melding %msg',
'broadcast %msg and wait':
'send melding %msg og vent',
'Message name':
'Meldingens navn',
'wait %n secs':
'vent i %n sek',
'wait until %b':
'vent til %b',
'forever %c':
'for alltid %c',
'repeat %n %c':
'gjenta %n ganger %c',
'repeat until %b %c':
'gjenta til %b %c',
'if %b %c':
'hvis %b %c',
'if %b %c else %c':
'hvis %b %c ellers %c',
'report %s':
'returner %s',
'stop block':
'stopp denne blokk',
'stop script':
'stopp dette skript',
'stop all %stop':
'stopp alt %stop',
'pause all %pause':
'pause (alle) %pause',
'run %cmdRing %inputs':
'kj\u00F8r %cmdRing fra %inputs',
'launch %cmdRing %inputs':
'start %cmdRing %inputs',
'call %repRing %inputs':
'kall %repRing fra %inputs',
'run %cmdRing w/continuation':
'kj\u00F8r %cmdRing med kontinuering',
'call %cmdRing w/continuation':
'kall %cmdRing med kontinuering',
'when I start as a clone':
'n\u00E5r klon startes',
'create a clone of %cln':
'opprett %cln',
'myself':
'meg',
'delete this clone':
'slett klon',
'warp %c':
'warp %c',
// sensing:
'touching %col ?':
'ber\u00F8rer %col ?',
'touching %clr ?':
'ber\u00F8rer %clr ?',
'color %clr is touching %clr ?':
'farge %clr ber\u00F8rer %clr ?',
'ask %s and wait':
'sp\u00F8r %s og vent',
'what\'s your name?':
'hva heter du?',
'answer':
'svar',
'mouse x':
'mus x-posisjon',
'mouse y':
'mus y-posisjon',
'mouse down?':
'mustast trykket?',
'key %key pressed?':
'tast %key trykket?',
'distance to %dst':
'avstand til %dst',
'reset timer':
'start stoppeklokke',
'timer':
'stoppeklokke',
'http:// %s':
'http:// %s',
'turbo mode?':
'turbo modus?',
'set turbo mode to %b':
'sett turbo modus til %b',
'filtered for %clr':
'filter %clr',
'stack size':
'stack-st\u00F8rrelse',
'frames':
'rammer',
// operators:
'%n mod %n':
'%n mod %n',
'round %n':
'rund av %n',
'%fun av %n':
'%fun von %n',
'pick random %n to %n':
'tilfeldig fra %n til %n',
'%b and %b':
'%b OG %b',
'%b or %b':
'%b ELLER %b',
'not %b':
'IKKE %b',
'true':
'SANN',
'false':
'USANN',
'join %words':
'skj\u00F8t %words',
'hello':
'hei',
'world':
'verden',
'letter %idx of %s':
'bokstav %idx av %s',
'length of %s':
'lengde av %s',
'unicode of %s':
'unicode av %s',
'unicode %n as letter':
'unicode %n som bokstav',
'is %s a %typ ?':
'%s er %typ ?',
'is %s identical to %s ?':
'%s identisk med %s ?',
'type of %s':
'type %s',
// variables:
'Make a variable':
'Ny variabel',
'Variable name':
'Variabelnavn',
'Delete a variable':
'Slett variabel',
'set %var to %s':
'sett %var til %s',
'change %var by %n':
'endre %var med %n',
'show variable %var':
'vis variabel %var',
'hide variable %var':
'skjul variabel %var',
'script variables %scriptVars':
'skriptvariable %scriptVars',
// lists:
'list %exp':
'liste %exp',
'%s in front of %l':
'%s framfor %l',
'item %idx of %l':
'element %idx av %l',
'all but first of %l':
'alt utenom f\u00F8rste av %l',
'length of %l':
'lengde av %l',
'%l contains %s':
'%l inneholder %s',
'thing':
'noe',
'add %s to %l':
'legg %s til %l',
'delete %ida of %l':
'fjern %ida fra %l',
'insert %s at %idx of %l':
'sett inn %s ved %idx i %l ein',
'replace item %idx of %l with %s':
'erstatt element %idx i %l med %s',
// other
'Make a block':
'Ny blokk',
// menus
// snap menu
'About...':
'Om Snap!...',
'Snap! website':
'Snap! websiden',
'Download source':
'Last ned kildekoden',
'Switch back to user mode':
'Tilbake til brukermodus',
'disable deep-Morphic\ncontext menus\nand show user-friendly ones':
'ut av Morphic\nkontekst menyer\nog vis kun brukervennlige',
'Switch to dev mode':
'inn i utviklermodus',
'enable Morphic\ncontext menus\nand inspectors,\nnot user-friendly!':
'inn i Morphic funksjoner\nog inspektorer,\nikke brukervennlig',
// project menu
'Project notes...':
'Prosjektnotater...',
'New':
'Nytt',
'Open...':
'\u00C5pne...',
'Save':
'Lagre',
'Save As...':
'Lagre som...',
'Import...':
'Importer...',
'file menu import hint':
'laster inn eksportertes prosjekt,\net bibliotek med '
+ 'blokker\n'
+ 'et kostym eller en lyd',
'Export project as plain text ...':
'Eksporter prosjekt som ren tekst...',
'Export project...':
'Eksporter prosjekt...',
'show project data as XML\nin a new browser window':
'vis prosjektdata som XML\ni et nytt nettleser vindu',
'Export blocks...':
'Eksporter blokker...',
'show global custom block definitions as XML\nin a new browser window':
'viser globale blokkdefinisjoner fra bruker\nsom XML i et nytt nettleser vindu',
'Import tools...':
'Importer verkt\u00F8y...',
'load the official library of\npowerful blocks':
'last ned snap-bibliotek med ekstra blokker',
'Libraries...':
'Biblioteker...',
'Import library':
'Importer biblioteker',
// cloud menu
'Login...':
'Logg inn...',
'Registrer deg...':
'Registrering...',
// settings menu
'Language...':
'Spr\u00E5k...',
'Zoom blocks...':
'Zoom blokkene...',
'Blurred shadows':
'Mjuke skygger (blurred)',
'uncheck to use solid drop\nshadows and highlights':
'fjern kryss for hard skygge\nog lyssetting',
'check to use blurred drop\nshadows and highlights':
'kryss av for hard skygge\nog lyssetting',
'Zebra coloring':
'Zebra farget',
'check to enable alternating\ncolors for nested blocks':
'kryss av for vekslende fargenyanser\ni nestede blokker',
'uncheck to disable alternating\ncolors for nested block':
'fjern kryss for \u00E5 hindre vekslende\nfargenyanser i nestede blokker',
'Dynamic input labels':
'Dynamisk inndata navn',
'uncheck to disable dynamic\nlabels for variadic inputs':
'fjern kryss for \u00E5 hindre dynamisk benevning\nav inndata med flere variabelfelt',
'check to enable dynamic\nlabels for variadic inputs':
'kryss av for\ndynamisk benevning av inndata med flere variabelfelt',
'Prefer empty slot drops':
'Preferanse for tomme variabelfelt',
'settings menu prefer empty slots hint':
'Valg meny\ntomme variabelfelt'
+ 'preferanse',
'uncheck to allow dropped\nreporters to kick out others':
'kryss vekk for at flyttede reportere vil ta plassen til andre\n',
'Long form input dialog':
'Lange dialoger for inndata',
'check to always show slot\ntypes in the input dialog':
'kryss av for \u00E5 vise variabelfelttype\ni inndata dialoger',
'uncheck to use the input\ndialog in short form':
'kryss vekk for \u00E5 bruke korte inndata\ndialoger',
'Virtual keyboard':
'Virtuelt tastatur',
'uncheck to disable\nvirtual keyboard support\nfor mobile devices':
'kryss vekk for \u00E5 sl\u00E5 av virtuelt\ntastatur p\u00E5 mobile enheter',
'check to enable\nvirtual keyboard support\nfor mobile devices':
'kryss av for \u00E5 sl\u00E5 p\u00E5 virtuelt\ntastatur p\u00E5 mobile enheter',
'Input sliders':
'Skyveknapp inndata ',
'uncheck to disable\ninput sliders for\nentry fields':
'kryss vekk for \u00E5 sl\u00E5 av\nskyveknapper i inndatafelt',
'check to enable\ninput sliders for\nentry fields':
'kryss av for \u00E5 sl\u00E5 p\u00E5 skyveknapper\ni inndatafelt',
'Clicking sound':
'Klikkelyd',
'uncheck to turn\nblock clicking\nsound off':
'kryss vekk for sl\u00E5 av klikkelyd',
'check to turn\nblock clicking\nsound on':
'kryss av for \u00E5 sl\u00E5 p\u00E5 klikkelyd',
'Animations':
'Animasjoner',
'uncheck to disable\nIDE animations':
'kryss vekk for \u00E5 sl\u00E5 av IDE-animasjoner',
'Turbo mode':
'Turbo modus',
'check to enable\nIDE animations':
'kryss av for \u00E5 sl\u00E5 p\u00E5 IDE-animasjoner',
'Thread safe scripts':
'Tr\u00E5dsikker skripting',
'uncheck to allow\nscript reentrancy':
'kryss vekk for \u00E5 sl\u00E5 p\u00E5 gjenbruk av p\u00E5begynte skripter',
'check to disallow\nscript reentrancy':
'kryss av for \u00E5 sl\u00E5 av gjenbruk av p\u00E5begynte skripter',
'Prefer smooth animations':
'Jevnere animasjoner',
'uncheck for greater speed\nat variable frame rates':
'kryss bort for strre fart ved variabel frame rate',
'check for smooth, predictable\nanimations across computers':
'kryss av for jevne animasjoner p alle maskinplattformer',
// inputs
'with inputs':
'med inndata',
'input names:':
'inndata navn:',
'Input Names:':
'Inndata navn:',
'input list:':
'inndata liste:',
// context menus:
'help':
'hjelp',
// blocks:
'hjelp...':
'hjelp...',
'relabel...':
'gi nytt navn...',
'duplicate':
'dupliser',
'make a copy\nand pick it up':
'lag kopi\n og plukk opp',
'only duplicate this block':
'dupliser kun denne blokk',
'delete':
'slette',
'script pic...':
'skript bilde...',
'open a new window\nwith a picture of this script':
'\u00E5pne nytt vindu med bildet av dette skriptet',
'ringify':
'ring rundt',
'unringify':
'fjerne ringen rundt',
// custom blocks:
'delete block definition...':
'slett blokk definisjoner',
'edit...':
'rediger...',
// sprites:
'edit':
'redigere',
'export...':
'eksporter...',
// stage:
'show all':
'vis alt',
// scripting area
'clean up':
'rydd',
'arrange scripts\nvertically':
'sett opp skriptene\nvertikalt',
'add comment':
'legg til kommentar',
'make a block...':
'lag ny blokk...',
// costumes
'rename':
'nytt navn',
'export':
'eksportere',
'rename costume':
'nytt navn for drakt',
// sounds
'Play sound':
'Spill lyd',
'Stop sound':
'Stop lyd',
'Stop':
'Stop',
'Play':
'Start',
'rename sound':
'nytt navn lyd',
// dialogs
// buttons
'OK':
'OK',
'Ok':
'OK',
'Cancel':
'Avbryt',
'Yes':
'Ja',
'No':
'Nei',
// help
'Help':
'Hjelp',
// Project Manager
'Untitled':
'Uten navn',
'Open Project':
'pne prosjekt',
'(empty)':
'(tomt)',
'Saved!':
'Lagret!',
'Delete Project':
'Slett prosjekt',
'Are you sure you want to delete':
'Vil du virkelig slette?',
'rename...':
'nytt navn...',
// costume editor
'Costume Editor':
'Drakt editor',
'click or drag crosshairs to move the rotation center':
'Klikk p\u00E5 kors for \u00E5 flytte rotasjonssenteret',
// project notes
'Project Notes':
'Prosjekt notater',
// new project
'New Project':
'Nytt prosjekt',
'Replace the current project with a new one?':
'Erstatt n\u00E5v\u00E6rende prosjekt med nytt prosjekt?',
// save project
'Save Project As...':
'Lagre prosjekt som...',
// export blocks
'Export blocks':
'Eksporter blokker',
'Import blocks':
'Importer blokker',
'this project doesn\'t have any\ncustom global blocks yet':
'dette prosjektet har s\u00E5langt ingen\nglobale blokker fra bruker',
'select':
'velg',
'all':
'alt',
'none':
'ingenting',
// variable dialog
'for all sprites':
'for alle objekter',
'for this sprite only':
'kun for dette objektet',
// block dialog
'Change block':
'Endre blokker',
'Command':
'Styring',
'Reporter':
'Funksjon',
'Predicate':
'Predikat',
// block editor
'Block Editor':
'Blokk editor',
'Apply':
'Gj\u00F8r gjeldende',
// block deletion dialog
'Delete Custom Block':
'Slett custom blokk',
'block deletion dialog text':
'Skal denne blokken med alle dens instanser\n' +
'bli slettet?',
// input dialog
'Create input name':
'Lag inndata navn',
'Edit input name':
'Rediger inndata navn',
'Edit label fragment':
'Rediger label fragment',
'Title text':
'Tittel',
'Input name':
'Inndata navn',
'Delete':
'Slett',
'Object':
'Objekt',
'Number':
'Tall',
'Text':
'Tekst',
'List':
'Liste',
'Any type':
'Type valgfritt',
'Boolean (T/F)':
'Boolsk (S/U)',
'Command\n(inline)':
'Kommando\n(inline)',
'Command\n(C-shape)':
'Kommando\n(C-Form)',
'Any\n(unevaluated)':
'Hvilken som helst\n(uevaluert)',
'Boolean\n(unevaluated)':
'Boolsk\n(uevaluert)',
'Single input.':
'Singel inndata.',
'Default Value:':
'Standardverdi:',
'Multiple inputs (value is list of inputs)':
'Fler-inndata (verdi er liste over inndata)',
'Upvar - make internal variable visible to caller':
'Upvar - gj\u00F8r interne variable synlig for den som kaller',
// About Snap
'About Snap':
'Om Snap',
'Back...':
'Tilbake...',
'License...':
'Lisens...',
'Modules...':
'Moduler...',
'Credits...':
'Takk til...',
'Translators...':
'Oversettere',
'License':
'Lisens',
'current module versions:':
'Komponent-versjoner',
'Contributors':
'Bidragsytere',
'Translations':
'Oversettelser',
// variable watchers
'normal':
'normal',
'large':
'stor',
'slider':
'skyveknapp',
'slider min...':
'skyveknapp min...',
'slider max...':
'skyveknapp max...',
'import...':
'importer...',
'Slider minimum value':
'Skyveknapp - minimumsverdi',
'Slider maximum value':
'Skyveknapp - maksimumsverdi',
// list watchers
'length: ':
'lengde: ',
// coments
'add comment here...':
'legg til kommentar her...',
// drow downs
// directions
'(90) h\u00F8yre':
'(90) h\u00F8yre',
'(-90) venstre':
'(-90) venstre',
'(0) opp':
'(0) oppe',
'(180) ned':
'(180) nede',
// collision detection
'mouse-pointer':
'musepeker',
'edge':
'kant',
'pen trails':
'pennspor',
// costumes
'Turtle':
'Objekt',
// graphical effects
'ghost':
'gjennomsiktig',
// keys
'space':
'mellomrom',
'up arrow':
'pil opp',
'down arrow':
'pil ned',
'right arrow':
'pil h\u00F8yre',
'left arrow':
'pil',
'a':
'a',
'b':
'b',
'c':
'c',
'd':
'd',
'e':
'e',
'f':
'f',
'g':
'g',
'h':
'h',
'i':
'i',
'j':
'j',
'k':
'k',
'l':
'l',
'm':
'm',
'n':
'n',
'o':
'o',
'p':
'p',
'q':
'q',
'r':
'r',
's':
's',
't':
't',
'u':
'u',
'v':
'v',
'w':
'w',
'x':
'x',
'y':
'y',
'z':
'z',
'0':
'0',
'1':
'1',
'2':
'2',
'3':
'3',
'4':
'4',
'5':
'5',
'6':
'6',
'7':
'7',
'8':
'8',
'9':
'9',
// messages
'new...':
'ny...',
// math functions
'abs':
'abs',
'sqrt':
'kvardrat',
'sin':
'sin',
'cos':
'cos',
'tan':
'tan',
'asin':
'arc-1',
'acos':
'cos-1',
'atan':
'tan-1',
'ln':
'ln',
'e^':
'e^',
// data types
'number':
'tall',
'text':
'tekst',
'Boolean':
'boolsk',
'list':
'liste',
'command':
'kommando',
'reporter':
'funksjonsblokk',
'predicate':
'predikat',
// list indices
'last':
'siste',
'any':
'hvilken som helst'
};
\ No newline at end of file
diff --git a/lang-pl.js b/lang-pl.js
index 3ad6fe23..734078dd 100644
--- a/lang-pl.js
+++ b/lang-pl.js
@@ -170,11 +170,11 @@ SnapTranslator.dict.pl = {
/*
Special characters: (see )
- Ą, ą \u0104, \u0105
+ Ą, ą \u0104,
Ć, ć \u0106, \u0107
Ę, ę \u0118, \u0119
Ł, ł \u0141, \u0142
- Ń, ń \u0143, \u0144
+ Ń, ń \u0143, \u0144\u0105
Ś, ś \u015A, \u015B
Ó, ó \u00D3, \u00F3
Ź, ź \u0179, \u017A
@@ -186,11 +186,11 @@ SnapTranslator.dict.pl = {
'language_name':
'Polski', // the name as it should appear in the language menu
'language_translator':
- 'Witek Kranas & deKrain', // your name for the Translators tab
+ 'Witek Kranas & deKrain & AB', // your name for the Translators tab
'translator_e-mail':
'witek@oeiizk.waw.pl', // optional
'last_changed':
- '2016-11-14', // this, too, will appear in the Translators tab
+ '2017-11-09', // this, too, will appear in the Translators tab
// GUI
// control bar:
@@ -230,7 +230,9 @@ SnapTranslator.dict.pl = {
'Skrypty',
'Costumes':
'Kostiumy',
- 'Sounds':
+ 'Backgrounds':
+ 'T\u0142a',
+ 'Sounds':
'D\u017Awi\u0119ki',
// names:
@@ -243,7 +245,7 @@ SnapTranslator.dict.pl = {
'don\'t rotate':
'nie obracaj',
'can rotate':
- 'dowolny obrót',
+ 'dowolny obr\u00F3t',
'only face left/right':
'tylko lewo/prawo',
@@ -383,16 +385,28 @@ SnapTranslator.dict.pl = {
'zatrzymaj wszystkie d\u017Awi\u0119ki',
'rest for %n beats':
'pauzuj przez %n takt\u00F3w',
- 'play note %n for %n beats':
- 'zagraj nut\u0119 %n przez %n takt\u00F3w',
- 'change tempo by %n':
+ 'play note %note for %n beats':
+ 'zagraj nut\u0119 %note przez %n takt\u00F3w',
+ 'set instrument to %inst':
+ 'ustaw instrument (fal\u0119) na %inst',
+ 'change tempo by %n':
'zmie\u0144 tempo o %n',
'set tempo to %n bpm':
'ustaw tempo na %n takt\u00F3w na min.',
'tempo':
'tempo',
- // pisak:
+ // "instrumenty", i.e. wave forms
+ '(1) sine':
+ '(1) sinusoidalna',
+ '(2) square':
+ '(2) prostok\u0105tna',
+ '(3) sawtooth':
+ '(3) pi\u0142okszta\u0142tna',
+ '(4) triangle':
+ '(4) tr\u00F3jk\u0105tna',
+
+ // pisak:
'clear':
'wyczy\u015B\u0107',
'pen down':
@@ -400,7 +414,7 @@ SnapTranslator.dict.pl = {
'pen up':
'podnie\u015B pisak',
'set pen color to %clr':
- 'ustaw kolor piaka na %clr',
+ 'ustaw kolor pisaka %clr',
'change pen color by %n':
'zmie\u0144 kolor pisaka o %n',
'set pen color to %n':
@@ -444,7 +458,7 @@ SnapTranslator.dict.pl = {
'broadcast %msg and wait':
'nadaj %msg do wszystkich i czekaj',
'Message name':
- 'nazwa wiadomo\u015Bci',
+ 'Nazwa wiadomo\u015Bci',
'message':
'wiadomo\u015B\u0107',
'any message':
@@ -466,7 +480,7 @@ SnapTranslator.dict.pl = {
'if %b %c else %c':
'je\u017Celi %b to %c w przeciwnym razie %c',
'report %s':
- 'zwr\u00F3\u0107 %s',
+ 'wynik %s',
'stop %stopChoices':
'zatrzymaj %stopChoices',
'all':
@@ -497,10 +511,16 @@ SnapTranslator.dict.pl = {
'kiedy zaczynam jako klon',
'create a clone of %cln':
'sklonuj %cln',
- 'myself':
+ 'a new clone of %cln':
+ 'nowy klon %cln',
+ 'myself':
'ja',
'delete this clone':
'usu\u0144 tego klona',
+ 'tell %spr to %cmdRing %inputs':
+ 'powiedz %spr do %cmdRing %inputs',
+ 'ask %spr for %repRing %inputs':
+ 'zapytaj %spr o %repRing %inputs',
// sensing:
'touching %col ?':
@@ -532,13 +552,31 @@ SnapTranslator.dict.pl = {
'%att of %spr':
'%att z %spr',
'my %get':
- 'atrybut %get',
+ 'ja %get',
'http:// %s':
'http:// %s',
'turbo mode?':
'tryb turbo?',
'set turbo mode to %b':
'ustaw tryb turbo na %b',
+ 'current %dates':
+ 'obecnie %dates',
+ 'year':
+ 'rok',
+ 'month':
+ 'miesi\u0105c',
+ 'date':
+ 'dzie\u0144',
+ 'day of week':
+ 'dzie\u0144 tygodnia',
+ 'hour':
+ 'godzina',
+ 'minute':
+ 'minuta',
+ 'second':
+ 'sekunda',
+ 'time in milliseconds':
+ 'czas w milisekundach',
'filtered for %clr':
'przefiltrowane dla %clr',
@@ -568,12 +606,14 @@ SnapTranslator.dict.pl = {
'fa\u0142sz',
'join %words':
'po\u0142\u0105cz %words',
- 'hello':
+ 'split %s by %delim':
+ 'podziel %s na %delim',
+ 'hello':
'witaj',
'world':
'świecie',
- 'letter %n of %s':
- 'litera %n z %s',
+ 'letter %idx of %s':
+ 'litera %idx z %s',
'length of %s':
'd\u0142ugo\u015B\u0107 %s',
'unicode of %s':
@@ -584,19 +624,23 @@ SnapTranslator.dict.pl = {
'jest %s typu %typ ?',
'is %s identical to %s ?':
'jest %s identyczne z %s ?',
+ 'JavaScript function ( %mult%s ) { %code }':
+ 'funkcja JavaScript ( %mult%s ) { %code }',
'type of %s':
'typ %s',
// variables:
'Make a variable':
- 'Stw\u00F3rz zmienn\u0105',
+ 'Utw\u00F3rz zmienn\u0105',
'Variable name':
'nazwa zmiennej',
'Script variable name':
'nazwa zmiennej skryptu',
- 'Delete a variable':
- 'usu\u0144 zmienn\u0105',
+ 'inherit %shd':
+ 'dziedzicz %shd',
+ 'Delete a variable':
+ 'Usu\u0144 zmienn\u0105',
'set %var to %s':
'ustaw %var na %s',
@@ -635,7 +679,7 @@ SnapTranslator.dict.pl = {
// other
'Make a block':
- 'nowy blok',
+ 'Nowy blok',
// menus
// snap menu
@@ -650,11 +694,11 @@ SnapTranslator.dict.pl = {
'Switch back to user mode':
'Prze\u0142\u0105cz do trybu u\u017Cytkownika',
'disable deep-Morphic\ncontext menus\nand show user-friendly ones':
- 'disable Morphic',
+ 'wy\u0142\u0105cz menu kontekstowe\ndeep-Morphic\ni poka\u017C przyjazne dla u\u017Cytkownika',
'Switch to dev mode':
'do trybu budowania',
'enable Morphic\ncontext menus\nand inspectors,\nnot user-friendly!':
- 'enable Morphic',
+ 'w\u0142\u0105z Morphic\nmenu kontekstowe\ni inspectors,\nniezbyt przyjazne dla u\u017Cytkownika!',
// project menu
'Project notes...':
@@ -665,7 +709,12 @@ SnapTranslator.dict.pl = {
'Otw\u00F3rz...',
'Save':
'Zapisz',
- 'Save As...':
+ 'Save to disk':
+ 'Zapisz na dysku',
+ 'store this project\nin the downloads folder\n(in supporting browsers)':
+ 'pobierz ten projekt\ni zapisz go lokalnie\n'
+ + '(nieobs\u0142ugiwane przez wszystkie przegl\u0105darki)',
+ 'Save As...':
'Zapisz jako...',
'Import...':
'Importuj...',
@@ -678,17 +727,45 @@ SnapTranslator.dict.pl = {
'Export project...':
'Eksportuj projekt...',
'show project data as XML\nin a new browser window':
- 'poka\u017C projekt jako XML\nin w nowej karcie',
- 'Export blocks...':
+ 'poka\u017C projekt jako XML\nw nowej karcie',
+ 'save project data as XML\nto your downloads folder':
+ 'zapisz dane projektu jako XML\nw folderze \u0142adowania',
+ 'Export blocks...':
'Eksportuj bloki...',
'show global custom block definitions as XML\nin a new browser window':
- 'poka\u017C definicje blok\u00F3w jako XML/min w nowej karcie',
- 'Import tools':
+ 'poka\u017C definicje blok\u00F3w jako XML/mw nowej karcie',
+ 'Unused blocks...':
+ 'Niewykorzystane bloki...',
+ 'find unused global custom blocks\nand remove their definitions':
+ 'znajdź i usuń\nniewykorzystane bloki',
+ 'Remove unused blocks':
+ 'Usu\u0144 niewykorzystane bloki',
+ 'there are currently no unused\nglobal custom blocks in this project':
+ 'obecnie nie ma niewykorzystanych\nbloków w tym projekcie',
+ 'unused block(s) removed':
+ 'usunięto niewykorzystane bloki',
+ 'Export summary...':
+ 'Eksport podsumowania...',
+ 'open a new browser browser window\n with a summary of this project':
+ 'otwórz podsumowanie tego projektu\nw nowym oknie przeglądarki',
+ 'Contents':
+ 'Zawartość',
+ 'Kind of':
+ 'Rodzaj',
+ 'Part of':
+ 'Część',
+ 'Parts':
+ 'Części',
+ 'Blocks':
+ 'Bloki',
+ 'For all Sprites':
+ 'Dla wszystkich duszków',
+ 'Import tools':
'Importuj narz\u0119dzia',
'load the official library of\npowerful blocks':
'za\u0142aduj oficjaln\u0105 bibliotek\u0119 blok\u00F3w',
'Libraries...':
- 'Biblioteka...',
+ 'Biblioteki...',
'Import library':
'Importuj bibliotek\u0119',
@@ -705,7 +782,17 @@ SnapTranslator.dict.pl = {
'J\u0119zyk...',
'Zoom blocks...':
'Powi\u0119ksz bloki...',
- 'Blurred shadows':
+ 'Stage size...':
+ 'Rozmiar sceny...',
+ 'Stage size':
+ 'Rozmiar sceny',
+ 'Stage width':
+ 'Szeroko\u015B\u0107 sceny',
+ 'Stage height':
+ 'Wysoko\u015B\u0107 sceny',
+ 'Default':
+ 'Domy\u015Blny',
+ 'Blurred shadows':
'Rozmyte cienie',
'uncheck to use solid drop\nshadows and highlights':
'Odznacz, aby uzyska\u0107\nmocne cienie i granice',
@@ -720,9 +807,9 @@ SnapTranslator.dict.pl = {
'Dynamic input labels':
'Dynamiczne opisy parametr\u00F3w',
'uncheck to disable dynamic\nlabels for variadic inputs':
- 'odznacz to disable dynamic\nlabels for variadic inputs',
+ 'odznacz, aby wy\u0142\u0105czy\u0107 dynamiczne\nopisy dla wej\u015B\u0107 variadic',
'check to enable dynamic\nlabels for variadic inputs':
- 'zaznacz to enable dynamic\nlabels for variadic inputs',
+ 'zaznacz, aby w\u0142\u0105czy\u0107 dynamiczne\nopisy dla wej\u015B\u0107 variadic',
'Prefer empty slot drops':
'Preferuj empty slot drops',
'settings menu prefer empty slots hint':
@@ -731,21 +818,47 @@ SnapTranslator.dict.pl = {
'odznacz to allow dropped\nreporters to kick out others',
'Long form input dialog':
'D\u0142uga forma dialogu wej\u015Bcia',
- 'check to always show slot\ntypes in the input dialog':
- 'zaznacz to always show slot\ntypes in the input dialog',
+ 'Plain prototype labels':
+ 'Prosta etykieta prototypu',
+ 'uncheck to always show (+) symbols\nin block prototype labels':
+ 'odznacz, aby pokazywać symbol (+)\nna etykietach prototypowych bloków',
+ 'check to hide (+) symbols\nin block prototype labels':
+ 'zaznacz, aby ukryć symbol (+)\nna etykietach prototypowych bloków',
+ 'check to always show slot\ntypes in the input dialog':
+ 'zaznacz, aby włączyć długą\nformę dialogu wejścia',
'uncheck to use the input\ndialog in short form':
- 'odznacz to use the input\ndialog in short form',
+ 'odznacz, aby używać dialogu\nwejścia w krótkiej formie',
'Virtual keyboard':
'Witualna klawiatura',
'uncheck to disable\nvirtual keyboard support\nfor mobile devices':
'odznacz, aby nie u\u017Cywa\u0107 klawiatury\nwirtualnej dla urzdze\u0144 mobilnych',
'check to enable\nvirtual keyboard support\nfor mobile devices':
'zaznacz, aby u\u017Cywa\u0107 klawiatury\nwirtualnej dla urzdze\u0144 mobilnych',
- 'uncheck to disable\ninput sliders for\nentry fields':
+ 'Input sliders':
+ 'Suwaki wej\u015Bciowe',
+ 'uncheck to disable\ninput sliders for\nentry fields':
'odznacz, aby nie pozwoli\u0107 na suwaki w polach wej\u015Bciowych',
'check to enable\ninput sliders for\nentry fields':
'zaznacz, aby pozwoli\u0107 na suwaki w polach wej\u015Bciowych',
- 'Clicking sound':
+ 'Retina display support':
+ 'Wsparcie wy\u015Bwietlacza Retina',
+ 'Codification support':
+ 'Kodowanie bloków',
+ 'uncheck to disable\nblock to text mapping features':
+ 'odznacz, aby wyłączy\u0107\nfunkcje mapowania tekstu',
+ 'check for block\nto text mapping features':
+ 'zaznacz, aby włączy\u0107\nfunkcje mapowania tekstu',
+ 'header mapping...':
+ 'mapowanie nagłówka...',
+ 'Header mapping':
+ 'Mapowanie nagłówka',
+ 'Enter code that corresponds to the block\'s definition. Choose your own\nformal parameter names (ignoring the ones shown).':
+ 'Wprowadź kod odpowiadający definicji bloków. ' +
+ 'Wybierz własne\nformalne nazwy parametrów (ignorując te, ' +
+ 'które są wyświetlane).',
+ 'Enter code that corresponds to the block\'s definition. Use the formal parameter\nnames as shown and to reference the definition body\'s generated text code.':
+ 'Wprowadź kod odpowiadający definicji bloków. Use the formal parameter\nnames as shown and to reference the definition body\'s generated text code.',
+ 'Clicking sound':
'D\u017Awi\u0119k klikni\u0119cia',
'uncheck to turn\nblock clicking\nsound off':
'odznacz, aby wy\u0142\u0105czy\u0107 \nd\u017Awi\u0119k klikni\u0119cia',
@@ -779,7 +892,9 @@ SnapTranslator.dict.pl = {
'Edytowanie Klawiatur\u0105',
'Table support':
'Tablice 2D',
- 'Visible stepping':
+ 'Table lines':
+ 'Tabele z liniami',
+ 'Visible stepping':
'Debugowanie krokowe',
'check to turn on\n visible stepping (slow)':
'zaznacz, aby widzie\u0107 poszczeg\u00F3lne\nkroki skrypt\u00F3w (wolne)',
@@ -797,6 +912,20 @@ SnapTranslator.dict.pl = {
'odznacz, aby pozwoli\u0107na\nwi\u0119ksz pr\u0119dko\u015B\u0107 ramek animacji',
'check for smooth, predictable\nanimations across computers':
'zaznacz, aby zapewni\u0107na\njednakowe, g\u0142adkie animacje',
+ 'Flat line ends':
+ 'P\u0142askie ko\u0144ce linii',
+ 'check for flat ends of lines':
+ 'zaznacz, aby końce linii\nbyły płaskie',
+ 'uncheck for round ends of lines':
+ 'odznacz, aby końce linii\nbyły zaokrąglone',
+ 'Ternary Boolean slots':
+ 'Ternäre Bool\'sche Inputs',
+ 'Inheritance support':
+ 'Podtrzymywanie dziedziczenia',
+ 'check for sprite\ninheritance features':
+ 'zaznacz, aby włączyć\nfunkcje dziedziczenia duszka',
+ 'uncheck to disable\nsprite inheritance features':
+ 'odznacz, aby wyłączyć\nfunkcje dziedziczenia duszka',
// inputs
'with inputs':
@@ -813,7 +942,9 @@ SnapTranslator.dict.pl = {
'pomoc',
// palette:
- 'hide primitives':
+ 'find blocks':
+ 'znajd\u017A bloki',
+ 'hide primitives':
'ukryj pierwotne',
'show primitives':
'poka\u017C pierwotne',
@@ -839,6 +970,14 @@ SnapTranslator.dict.pl = {
'obwiednia',
'unringify':
'bez obwiedni',
+ 'transient':
+ 'chwilowo',
+ 'uncheck to save contents\nin the project':
+ 'odznacz, aby zapisać\nzawartość w projekcie',
+ 'check to prevent contents\nfrom being saved':
+ 'zaznacz, aby zapobiec\nzapisaniu zawartości',
+ 'new line':
+ 'nowa linia',
// custom blocks:
'delete block definition...':
@@ -849,8 +988,28 @@ SnapTranslator.dict.pl = {
// sprites:
'edit':
'edytuj',
- 'export...':
+ 'clone':
+ 'klonuj',
+ 'move':
+ 'porusz',
+ 'pivot':
+ 'oś',
+ 'edit the costume\'s\nrotation center':
+ 'edytuj środek\nobrotu kostiumu',
+ 'detach from':
+ 'odłącz od',
+ 'detach all parts':
+ 'odłącz wszystkie części',
+ 'export...':
'eksportuj...',
+ 'parent...':
+ 'pierwowzór...',
+ 'current parent':
+ 'aktualny pierwowzór',
+ 'release':
+ 'wydanie',
+ 'make temporary and\nhide in the sprite corral':
+ 'zr\u00F3b tymczasowy\ni ukryj ikonę',
// stage:
'show all':
@@ -864,13 +1023,17 @@ SnapTranslator.dict.pl = {
'clean up':
'wyczy\u015B\u0107',
'arrange scripts\nvertically':
- 'ustaw skrypty pionowo',
+ 'ustaw skrypty w pionie',
'add comment':
'dodaj komentarz',
'undrop':
'odklej',
'undo the last\nblock drop\nin this pane':
'cofnij ostatnie upuszczenie\nbloku na tej planszy',
+ 'redrop':
+ 'ponownie upuść',
+ 'use the keyboard\nto enter blocks':
+ 'użyj klawiatury,\naby wprowadzić bloki',
'scripts pic...':
'obrazek skryptu...',
'open a new window\nwith a picture of all scripts':
@@ -898,6 +1061,18 @@ SnapTranslator.dict.pl = {
'rename sound':
'zmie\u0144 nazw\u0119 d\u017Cwi\u0119ku',
+ // lists and tables
+ 'list view...':
+ 'widok listy...',
+ 'table view...':
+ 'widok tabeli...',
+ 'open in dialog...':
+ 'otwórz w nowym oknie dialogowym',
+ 'reset columns':
+ 'zresetuj szerokość kolumn',
+ 'items':
+ 'pozycje',
+
// dialogs
// buttons
'OK':
@@ -1003,7 +1178,7 @@ SnapTranslator.dict.pl = {
'Costume Editor':
'Edytor kostium\u00F3w',
'click or drag crosshairs to move the rotation center':
- 'Kliknij lub przeci\u0105gnij krzy\u017Cyk, aby ustawi\u0107 centrum obrotu',
+ 'Kliknij lub przeci\u0105gnij krzy\u017Cyk, aby ustawi\u0107 środek obrotu',
// project notes
'Project Notes':
@@ -1039,6 +1214,14 @@ SnapTranslator.dict.pl = {
'for this sprite only':
'tylko dla tego duszka',
+ // variables refactoring
+ 'rename only\nthis reporter':
+ 'zmie\u0144 nazw\u0119\ntylko tej zmiennej',
+ 'rename all...':
+ 'zmie\u0144 nazwy wszystkich...',
+ 'rename all blocks that\naccess this variable':
+ 'zmie\u0144 nazwy wszystkich blok\u00F3w,z nazw\u0105 tej zmiennej',
+
// block dialog
'Change block':
'Zmie\u0144 blok',
@@ -1052,7 +1235,9 @@ SnapTranslator.dict.pl = {
// block editor
'Block Editor':
'Edytor blok\u00F3w',
- 'Apply':
+ 'Method Editor':
+ 'Methodeneditor',
+ 'Apply':
'Zastosuj',
// block deletion dialog
@@ -1070,7 +1255,7 @@ SnapTranslator.dict.pl = {
'Edit label fragment':
'Edytuj opis parametru',
'Title text':
- 'Tekst tutu\u0142owy',
+ 'Tekst tytu\u0142owy',
'Input name':
'Nazwa',
'Delete':
@@ -1146,7 +1331,7 @@ SnapTranslator.dict.pl = {
// list watchers
'length: ':
- 'du\u0142go\u015B\u0107: ',
+ 'd\u0142ugo\u015B\u0107: ',
// coments
'add comment here...':
@@ -1178,21 +1363,95 @@ SnapTranslator.dict.pl = {
'Pusty',
// graphical effects
- 'ghost':
+ 'color':
+ 'kolor',
+ 'fisheye':
+ 'rybie oko',
+ 'whirl':
+ 'wir',
+ 'pixelate':
+ 'pikselizacja',
+ 'mosaic':
+ 'mozaika',
+ 'saturation':
+ 'nasycenie',
+ 'brightness':
+ 'jasność',
+ 'ghost':
'duch',
+ 'negative':
+ 'negatyw',
+ 'comic':
+ 'mora ',
+ 'confetti':
+ 'konfetti',
- // keys
+ // paint editor
+ 'paint a new sprite':
+ 'namaluj nowego duszka',
+ 'Paint Editor':
+ 'Edytor obrazów',
+ 'Paintbrush tool\n(free draw)':
+ 'Narzędzie pędzel\n(swobodne malowanie)',
+ 'Stroked Rectangle\n(shift: square)':
+ 'Prostokąt\n(+shift: kwadrat)',
+ 'Stroked Ellipse\n(shift: circle)':
+ 'Elipsa\n(+shift: okrąg)',
+ 'Eraser tool':
+ 'Narzędzie gumka',
+ 'Set the rotation center':
+ 'Ustaw środek obrotu',
+ 'Line tool\n(shift: vertical/horizontal)':
+ 'Narzędzie linia\n(+shift: pionowa/pozioma)',
+ 'Filled Rectangle\n(shift: square)':
+ 'Wypełniony prostokąt\n(+shift: kwadrat)',
+ 'Filled Ellipse\n(shift: circle)':
+ 'Wypełniona elipsa\n(+shift: koło)',
+ 'Fill a region':
+ 'Wypełnij obszar',
+ 'Pipette tool\n(pick a color anywhere)':
+ 'Narzędzie pipeta\n(wybierz kolor z obrazu)',
+ 'undo':
+ 'cofnij',
+ 'clear':
+ 'wyczyść',
+ 'grow':
+ 'powiększ',
+ 'shrink':
+ 'zmniejsz',
+ 'flip ↔':
+ 'przerzuć ↔',
+ 'flip ↕':
+ 'przerzuć ↕',
+ 'Constrain proportions of shapes?\n(you can also hold shift)':
+ 'Ograniczyć proporcje kształtów?\n(można również przytrzymać shift)',
+ 'Brush size':
+ 'Rozmiar pędzla',
+ 'Paint a new costume':
+ 'namaluj nowy kostium',
+
+ // camera
+ 'take a camera snapshot and\nimport it as a new sprite':
+ 'nowy duszek z kamery',
+ 'Camera':
+ 'Kamera',
+ 'Import a new costume from your webcam':
+ 'nowy kostium z kamery',
+
+ // keys
'space':
'spacja',
'up arrow':
- 'strz\u0142aka w g\u00F3r\u0119',
+ 'strza\u0142ka w g\u00F3r\u0119',
'down arrow':
- 'strz\u0142aka w d\u00F3\u0142',
+ 'strza\u0142ka w d\u00F3\u0142',
'right arrow':
- 'strz\u0142aka w prawo',
+ 'strza\u0142ka w prawo',
'left arrow':
- 'strz\u0142aka w lewo',
- 'a':
+ 'strza\u0142ka w lewo',
+ 'any key':
+ 'dowolny klawisz',
+ 'a':
'a',
'b':
'b',
@@ -1272,8 +1531,10 @@ SnapTranslator.dict.pl = {
// math functions
'abs':
'modu\u0142',
- 'floor':
- 'pod\u0142oga',
+ 'ceiling':
+ 'zaokr\u0105glenie w g\u00F3r\u0119',
+ 'floor':
+ 'zaokr\u0105glenie w d\u00F3\u0142',
'sqrt':
'pierwiastek kwadratowy',
'sin':
@@ -1293,6 +1554,22 @@ SnapTranslator.dict.pl = {
'e^':
'e^',
+ // Boolean expressions keyboard entry
+ 'not':
+ 'nie',
+
+ // delimiters
+ 'letter':
+ 'litera',
+ 'whitespace':
+ 'spacja',
+ 'line':
+ 'linia',
+ 'tab':
+ 'tabulator',
+ 'cr':
+ 'koniec linii',
+
// data types
'number':
'liczba',
@@ -1308,6 +1585,12 @@ SnapTranslator.dict.pl = {
'funkcja',
'predicate':
'predykat',
+ 'sprite':
+ 'duszek',
+ 'costume':
+ 'kostium',
+ 'sound':
+ 'dźwięk',
// list indices
'last':
@@ -1315,6 +1598,54 @@ SnapTranslator.dict.pl = {
'any':
'dowolny',
+ // attributes
+ 'neighbors':
+ 'sąsiedzi',
+ 'self':
+ 'sam',
+ 'other sprites':
+ 'inne duszki',
+ 'parts':
+ 'części',
+ 'anchor':
+ 'kotwica',
+ 'parent':
+ 'rodzic',
+ 'children':
+ 'potomstwo',
+ 'clones':
+ 'klony',
+ 'other clones':
+ 'inne klony',
+ 'dangling?':
+ 'wiszący?',
+ 'rotation x':
+ 'obrót x',
+ 'rotation y':
+ 'obrót y',
+ 'center x':
+ 'x środka',
+ 'center y':
+ 'y środka',
+ 'name':
+ 'nazwa',
+ 'stage':
+ 'scena',
+ 'costumes':
+ 'kostiumy',
+ 'sounds':
+ 'dźwięki',
+ 'scripts':
+ 'skrypty',
+
+ // inheritance
+ 'inherited':
+ 'odziedziczone',
+ 'check to inherit\nfrom':
+ 'zaznacz, żeby dziedziczyć\nod',
+ 'uncheck to\ndisinherit':
+ 'odznacz, żeby\nnie dziedziczyć',
+
// Sign up dialog
'Sign up':
'Rejestracja',
diff --git a/lang-pt.js b/lang-pt.js
index 15c6abf1..7a74d359 100755
--- a/lang-pt.js
+++ b/lang-pt.js
@@ -185,7 +185,7 @@ SnapTranslator.dict.pt = {
'translator_e-mail':
'mmsequeira@gmail.com',
'last_changed':
- '2016-10-30',
+ '2017-10-30',
// GUI
// control bar:
@@ -381,8 +381,10 @@ SnapTranslator.dict.pt = {
'pára todos os sons',
'rest for %n beats':
'faz uma pausa de %n tempos',
- 'play note %n for %n beats':
- 'toca a nota %n durante %n tempos',
+ 'play note %note for %n beats':
+ 'toca a nota %note durante %n tempos',
+ 'set instrument to %inst':
+ 'altera o teu instrumento para %inst',
'change tempo by %n':
'adiciona %n bpm ao teu andamento',
'set tempo to %n bpm':
@@ -390,6 +392,16 @@ SnapTranslator.dict.pt = {
'tempo':
'o andamento',
+ // "instrumento", i.e. formas de onda:
+ '(1) sine':
+ '(1) sinusoisal',
+ '(2) square':
+ '(2) quadrada',
+ '(3) sawtooth':
+ '(3) dente de serra',
+ '(4) triangle':
+ '(4) triangular',
+
// caneta:
'clear':
'apaga tudo do palco',
@@ -433,6 +445,10 @@ SnapTranslator.dict.pt = {
'entrar em ti',
'mouse-departed':
'sair de ti',
+ 'scrolled-down':
+ 'rolar para baixo',
+ 'scrolled-up':
+ 'rolar para cima',
'when %b':
'Quando %b',
'when I receive %msgHat':
@@ -494,11 +510,17 @@ SnapTranslator.dict.pt = {
'when I start as a clone':
'Quando fores criado como clone',
'create a clone of %cln':
- 'clona %cln',
+ 'cria um novo clone de %cln',
+ 'a new clone of %cln':
+ 'um novo clone de %cln',
'myself':
- '-te',
+ 'ti',
'delete this clone':
'remove-te',
+ 'tell %spr to %cmdRing %inputs':
+ 'diz a %spr para executar %cmdRing %inputs',
+ 'ask %spr for %repRing %inputs':
+ 'o resultado de %spr invocar %repRing %inputs',
// sensores:
'touching %col ?':
@@ -521,8 +543,10 @@ SnapTranslator.dict.pt = {
'o botão do rato está pressionado',
'key %key pressed?':
'a tecla %key está a ser pressionada',
- 'distance to %dst':
- 'a distância até %dst',
+ '%rel to %dst':
+ '%rel até %dst',
+ 'distance':
+ 'a distância',
'reset timer':
'reinicia o cronómetro',
'timer':
@@ -537,6 +561,24 @@ SnapTranslator.dict.pt = {
'o modo turbo está activo',
'set turbo mode to %b':
'alterar o modo turbo para %b',
+ 'current %dates':
+ '%dates corrente',
+ 'year':
+ 'o ano',
+ 'month':
+ 'o mês',
+ 'date':
+ 'o dia',
+ 'day of week':
+ 'o dia da semana',
+ 'hour':
+ 'a hora',
+ 'minute':
+ 'o minuto',
+ 'second':
+ 'o segundo',
+ 'time in milliseconds':
+ 'o tempo (em milisegundos)',
'filtered for %clr':
'filtrado para %clr',
@@ -572,8 +614,8 @@ SnapTranslator.dict.pt = {
'Olá',
'world':
'mundo!',
- 'letter %n of %s':
- 'o caractere %n de %s',
+ 'letter %idx of %s':
+ 'o caractere %idx de %s',
'length of %s':
'o comprimento de %s',
'unicode of %s':
@@ -584,6 +626,10 @@ SnapTranslator.dict.pt = {
'%s é um/uma %typ',
'is %s identical to %s ?':
'%s é idêntico a %s',
+ 'JavaScript function ( %mult%s ) { %code }':
+ 'função JavaScript ( %mult%s ) { %code }',
+ 'compile %repRing':
+ 'a compilação de %repRing',
'type of %s':
'o tipo de %s',
@@ -595,6 +641,8 @@ SnapTranslator.dict.pt = {
'Qual o nome da variável?',
'Script variable name':
'Qual o nome da variável de guião?',
+ 'inherit %shd':
+ 'herda %shd do teu progenitor',
'Delete a variable':
'Remover uma variável',
@@ -797,12 +845,16 @@ SnapTranslator.dict.pt = {
'Desassinalar para desactivar\ndeslizadores nas ranhuras dos blocos.',
'check to enable\ninput sliders for\nentry fields':
'Assinalar para activar deslizadores\nnas ranhuras dos blocos.',
+ 'Retina display support':
+ 'Suporte para o ecrã retina',
+ 'Codification support':
+ 'Suportar produção de código',
'Clicking sound':
'Som de cliques',
'uncheck to turn\nblock clicking\nsound off':
'Desassinalar para desactivar o som\nproduzido ao clicar nos blocos.',
'check to turn\nblock clicking\nsound on':
- 'Assinar para activar o som\nproduzido ao clicar nos blocos.',
+ 'Assinalar para activar o som\nproduzido ao clicar nos blocos.',
'Animations':
'Animações',
'uncheck to disable\nIDE animations':
@@ -845,6 +897,8 @@ SnapTranslator.dict.pt = {
'Assinalar para que os extremos das linhas\ndesenhadas pela caneta sejam planos.',
'uncheck for round ends of lines':
'Desassinalar para que os extremos das linhas\ndesenhadas pela caneta sejam redondos.',
+ 'Ternary Boolean slots':
+ 'Ranhuras booleanas ternárias',
'Inheritance support':
'Suporte para herança',
@@ -863,6 +917,8 @@ SnapTranslator.dict.pt = {
'ajuda',
// palette:
+ 'find blocks':
+ 'procurar blocos',
'hide primitives':
'esconder blocos primitivos',
'show primitives':
@@ -895,6 +951,8 @@ SnapTranslator.dict.pt = {
'Desassinalar para guardar\no conteúdo no projecto',
'check to prevent contents\nfrom being saved':
'Assinalar para não guardar\no conteúdo no projecto',
+ 'new line':
+ 'nova linha',
// blocos personalizados:
'delete block definition...':
@@ -905,14 +963,30 @@ SnapTranslator.dict.pt = {
// actores:
'edit':
'editar',
+ 'clone':
+ 'clonar',
'move':
'mover',
+ 'pivot':
+ 'editar centro de rotação',
+ 'edit the costume\'s\nrotation center':
+ 'Drehpunkt des Kostüms\nanzeigen und verschieben',
+ 'rotate':
+ 'rodar',
'detach from':
'soltar de',
'detach all parts':
'soltar todas as partes',
'export...':
'exportar…',
+ 'parent...':
+ 'progenitor…',
+ 'current parent':
+ 'progenitor actual',
+ 'release':
+ 'tornar clone temporário',
+ 'make temporary and\nhide in the sprite corral':
+ 'tornar temporário e\nesconder da lista de actores',
// palco:
'show all':
@@ -935,6 +1009,8 @@ SnapTranslator.dict.pt = {
'Desfazer a última largada de um bloco\nneste separador.',
'redrop':
'refazer última largada',
+ 'use the keyboard\nto enter blocks':
+ 'usar o teclado\npara introduzir blocos',
'scripts pic...':
'fotografia dos guiões…',
'open a new window\nwith a picture of all scripts':
@@ -1020,6 +1096,8 @@ SnapTranslator.dict.pt = {
'Sem título',
'Open Project':
'Abrir Projecto',
+ 'Open':
+ 'Abrir',
'(empty)':
'(nada)',
'Saved!':
@@ -1069,6 +1147,15 @@ SnapTranslator.dict.pt = {
'for this sprite only':
'apenas para este objecto',
+ // refactorização de variáveis
+ 'rename only\nthis reporter':
+ 'alterar o nome\napenas neste repórter',
+ 'rename all...':
+ 'alterar o nome em todo o lado…',
+ 'rename all blocks that\naccess this variable':
+ 'alterar todos os blocos que\nusam esta variável',
+
+
// caixa de diálogo de blocos
'Change block':
'Alterar tipo de bloco',
@@ -1082,6 +1169,8 @@ SnapTranslator.dict.pt = {
// editor de blocos
'Block Editor':
'Editor de Blocos',
+ 'Method Editor':
+ 'Editor de Métodos',
'Apply':
'Aplicar',
@@ -1192,6 +1281,10 @@ SnapTranslator.dict.pt = {
'0° (cima)',
'(180) down':
'180° (baixo)',
+ 'random':
+ 'um valor ao acaso',
+ 'random position':
+ 'um ponto ao acaso',
// detecção de colisões
'mouse-pointer':
@@ -1199,7 +1292,9 @@ SnapTranslator.dict.pt = {
'edge':
'a borda',
'pen trails':
- 'traços da caneta',
+ 'os traços da caneta',
+ 'center':
+ 'o centro',
// trajes
'Turtle':
@@ -1347,6 +1442,10 @@ SnapTranslator.dict.pt = {
'e^':
'a exponencial',
+ // Introdução pelo teclado de expressões booleanas
+ 'not':
+ 'é falso que',
+
// delimitadores
'letter':
'letra',
@@ -1384,6 +1483,8 @@ SnapTranslator.dict.pt = {
'um item ao acaso',
// attributes
+ 'my':
+ 'próprios',
'neighbors':
'os vizinhos',
'self':
@@ -1416,12 +1517,28 @@ SnapTranslator.dict.pt = {
'o nome',
'stage':
'o palco',
+ 'costumes':
+ 'os trajes',
+ 'sounds':
+ 'os sons',
+ 'scripts':
+ 'os guiões',
+
+ // herança
+ 'inherited':
+ 'herdado',
+ 'check to inherit\nfrom':
+ 'assinalar para\nherdar de',
+ 'uncheck to\ndisinherit':
+ 'desassinalar para\nnão herdar',
// em falta no ficheiro lang-de.js
+ 'log':
+ 'o logaritmo',
+ 'url %s':
+ 'o recurso http:// %s',
'delete %shd':
'remove %shd',
- 'Retina display support':
- 'Suporte para o ecrã retina',
'uncheck for lower resolution,\nsaves computing resources':
'Desassinalar para menor resolução;\npoupa recursos computacionais.',
'check for higher resolution,\nuses more computing resources':
@@ -1456,30 +1573,10 @@ SnapTranslator.dict.pt = {
'Exportar todos os guiões como fotografia…',
'show a picture of all scripts\nand block definitions':
'Mostra uma imagem com todos\nos guiões e definições de blocos',
- 'current %dates':
- '%dates corrente',
- 'year':
- 'o ano',
- 'month':
- 'o mês',
- 'date':
- 'o dia',
- 'day of week':
- 'o dia da semana',
- 'hour':
- 'a hora',
- 'minute':
- 'o minuto',
- 'second':
- 'o segundo',
- 'time in milliseconds':
- 'o tempo (em milisegundos)',
'find blocks...':
'procurar blocos…',
'costume name':
'o nome do traje',
- 'Open':
- 'Abrir',
'Share':
'Partilhar',
'Snap!Cloud':
@@ -1600,8 +1697,6 @@ SnapTranslator.dict.pt = {
'Actores compostos',
'uncheck to disable\nsprite composition':
'Desassinalar para desactivar\na composição de actores.',
- 'Codification support':
- 'Suportar produção de código',
'check for block\nto text mapping features':
'Assinalar para funcionalidades\nde mapeamento entre blocos e texto.',
'saved.':
@@ -1781,8 +1876,6 @@ SnapTranslator.dict.pt = {
'fotografia do guião incluindo resultado…',
'open a new window\nwith a picture of both\nthis script and its result':
'Abrir uma nova janela com\numa fotografia tanto deste guião\ncomo do seu resultado.',
- 'JavaScript function ( %mult%s ) { %code }':
- 'função JavaScript ( %mult%s ) { %code }',
'Select categories of additional blocks to add to this project.':
'Seleccionar categorias de blocos adicionais a acrescentar a este projecto.',
'Import sound':
@@ -1807,10 +1900,6 @@ SnapTranslator.dict.pt = {
'esperavam-se',
'input(s), but getting':
'argumento(s), mas foram passados',
- 'parent...':
- 'progenitor…',
- 'current parent':
- 'progenitor actual',
'Dragging threshold...':
'Limiar de arrastamento…',
'Cache Inputs':
@@ -1870,12 +1959,14 @@ SnapTranslator.dict.pt = {
// produção de código
'map %cmdRing to %codeKind %code':
'mapear %cmdRing no %codeKind %code',
- 'map String to code %code':
- 'mapear texto no código %code',
+ 'map %mapValue to code %code':
+ 'mapear %mapValue no código %code',
'map %codeListPart of %codeListKind to code %code':
'mapear %codeListPart de %codeListKind no código %code',
'code of %cmdRing':
'o código de %cmdRing',
+ 'String':
+ 'texto',
'delimiter':
'delimitador',
'collection':
@@ -1909,6 +2000,24 @@ SnapTranslator.dict.pt = {
'Desassinalar para desactivar\na edição usando o teclado.',
'check to enable\nkeyboard editing support':
'Assinalar para activar\na edição usando o teclado.',
+ 'check to turn on\nvisible stepping (slow)':
+ 'assinalar para activar\nexecução passo a passo visível (lento)',
+ 'uncheck to turn off\nvisible stepping':
+ 'desassinalar para desactivar\nexecução passo a passo visível',
+ 'check to allow\nempty Boolean slots':
+ 'assinalar para permitir\nranhuras booleanas vazias',
+ 'uncheck to limit\nBoolean slots to true / false':
+ 'desassinalar para limitar\nas ranhuras booleanas aos valores verdadeiro / falso',
+ 'Camera support':
+ 'Suporte para câmara',
+ 'check to enable\ncamera support':
+ 'assinalar para activar\no suporte para a câmara',
+ 'uncheck to disable\ncamera support':
+ 'desassinalar para desactivar\no suporte para a câmara',
+ 'check to enable auto-wrapping\ninside nested block stacks':
+ 'assinalar para activar as quebras de linha\nem pilhas de blocos aninhadas',
+ 'uncheck to confine auto-wrapping\nto top-level block stacks':
+ 'desassinalar para limitar as quebras de linha\nàs pilhas de blocos do nível de topo',
'uncheck to disable\nsprite inheritance features':
'Desassinalar para desactivar\nfuncionalidades de herança de actores.',
'check for sprite\ninheritance features':
diff --git a/lang-pt_BR.js b/lang-pt_BR.js
old mode 100755
new mode 100644
index f277c452..2086c15e
--- a/lang-pt_BR.js
+++ b/lang-pt_BR.js
@@ -554,8 +554,8 @@ SnapTranslator.dict.pt_BR = {
'Olá',
'world':
'mundo',
- 'letter %n of %s':
- 'o caractere %n de %s',
+ 'letter %idx of %s':
+ 'o caractere %idx de %s',
'length of %s':
'o comprimento de %s',
'unicode of %s':
@@ -752,7 +752,7 @@ SnapTranslator.dict.pt_BR = {
'check to prioritize\nscript execution':
'Marque para priorizar\na execução de roteiros.',
'uncheck to run scripts\nat normal speed':
- 'Desmarque para executar os roteiros\nna velocidade normal.',
+ 'Desmarque para executar os roteiros\nna velocidade normal.',
'check to enable\nIDE animations':
'Marque para ativar\nas animações da interface de usuário.',
'Thread safe scripts':
@@ -935,7 +935,7 @@ SnapTranslator.dict.pt_BR = {
'click or drag crosshairs to move the rotation center':
'Clique ou arraste a mira para mudar o centro de rotação.',
- // Anotaes de projeto
+ // Anotaes de projeto
'Project Notes':
'Notas do Projeto',
diff --git a/lang-ro.js b/lang-ro.js
index 47bf82a9..3702948c 100644
--- a/lang-ro.js
+++ b/lang-ro.js
@@ -562,8 +562,8 @@ SnapTranslator.dict.ro = {
'salut',
'world':
'lume',
- 'letter %n of %s':
- 'litera %n din %s',
+ 'letter %idx of %s':
+ 'litera %idx din %s',
'length of %s':
'lungimea lui %s',
'unicode of %s':
diff --git a/lang-ru.js b/lang-ru.js
index 50a03de9..61cdf412 100644
--- a/lang-ru.js
+++ b/lang-ru.js
@@ -182,7 +182,7 @@ SnapTranslator.dict.ru = {
'translator_e-mail':
'svetlanap@berkeley.edu, tema@school830.ru', // optional
'last_changed':
- '2017-09-01', // this, too, will appear in the Translators tab
+ '2017-12-29', // this, too, will appear in the Translators tab
// GUI
// control bar:
@@ -247,9 +247,9 @@ SnapTranslator.dict.ru = {
// tab help
'costumes tab help':
- 'импортируйте изображение с другого веб-сайта\nили со своего компьютера, скопировав его сюда',
+ 'Вы можете перенести и бросить сюда изображение со своего компьютера',
'import a sound from your computer\nby dragging it into here':
- 'импортируйте звук со своего компьютера\nскопировав его сюда',
+ 'Вы можете перенести и бросить сюда звуковой файл со своего компьютера',
// primitive blocks:
@@ -375,19 +375,37 @@ SnapTranslator.dict.ru = {
'stop all sounds':
'остановить все звуки',
'rest for %n beats':
- 'пауза %n тактов',
- 'play note %n for %n beats':
- 'сыграть ноту %n %n тактов',
+ 'пауза в тактах %n',
+ 'play note %note for %n beats':
+ 'играть ноту %note длит. %n',
'change tempo by %n':
'изменить темп на %n',
'set tempo to %n bpm':
- 'устан. темп %n ударов в мин.',
+ 'уст. темп %n такт/мин',
+ 'set instrument to %inst':
+ 'инструмент %inst',
'tempo':
'темп',
+ 'sine':
+ 'синус (sine)',
+ 'square':
+ 'квадрат (square)',
+ 'sawtooth':
+ 'пила (sawtooth)',
+ 'triangle':
+ 'треугольник (triangle)',
+ '(1) sine':
+ '(1) синус (sine)',
+ '(2) square':
+ '(2) квадрат (square)',
+ '(3) sawtooth':
+ '(3) пила (sawtooth)',
+ '(4) triangle':
+ '(4) треугольник (triangle)',
// pen:
'clear':
- 'убрать все',
+ 'очистить всё',
'pen down':
'опустить перо',
'pen up':
@@ -473,21 +491,27 @@ SnapTranslator.dict.ru = {
'other scripts in sprite':
'все другие мои скрипты',
'run %cmdRing %inputs':
- 'выполнять %cmdRing %inputs',
+ 'выполнить %cmdRing %inputs',
'launch %cmdRing %inputs':
'запустить %cmdRing %inputs',
'call %repRing %inputs':
'вызвать %repRing %inputs',
'run %cmdRing w/continuation':
- 'выполнять %cmdRing с продолжением',
+ 'выполнить %cmdRing с продолжением',
'call %cmdRing w/continuation':
'вызвать %cmdRing с продолжением',
+ 'tell %spr to %cmdRing %inputs':
+ 'передать %spr команды %cmdRing %inputs',
+ 'ask %spr for %repRing %inputs':
+ 'запросить у %spr информацию %cmdRing %inputs',
'warp %c':
'сразу %c',
'when I start as a clone':
'когда я создан как клон',
'create a clone of %cln':
'клонировать %cln',
+ 'a new clone of %cln':
+ 'новый клон %cln',
'myself':
'меня',
'delete this clone':
@@ -565,8 +589,8 @@ SnapTranslator.dict.ru = {
'Привет',
'world':
'мир',
- 'letter %n of %s':
- '%n буква слова %s',
+ 'letter %idx of %s':
+ '%idx буква слова %s',
'length of %s':
'длина %s',
'unicode of %s':
@@ -601,6 +625,8 @@ SnapTranslator.dict.ru = {
'спрятать переменную %var',
'script variables %scriptVars':
'переменные скрипта %scriptVars',
+ 'inherit %shd':
+ 'наследовать %shd',
// lists:
'list %exp':
@@ -631,15 +657,17 @@ SnapTranslator.dict.ru = {
// other
'Make a block':
'Новый блок',
+ 'find blocks...':
+ 'Найти блоки...',
// menus
// snap menu
'About...':
- 'Snap! Реквизиты',
+ 'О программе...',
'Snap! website':
- 'Snap! веб-сайт',
+ 'Веб-сайт программы Snap!',
'Download source':
- 'Загрузить материалы источника',
+ 'Загрузить исходники программы',
'Switch back to user mode':
'Вернуться в режим пользователя',
'disable deep-Morphic\ncontext menus\nand show user-friendly ones':
@@ -704,6 +732,12 @@ SnapTranslator.dict.ru = {
'Увеличение блоков кода...',
'Stage size...':
'Размер сцены...',
+ 'Retina display support':
+ 'Поддержка технологии Retina display',
+ 'uncheck for lower resolution,\nsaves computing resources':
+ 'снимите флажок для использования низкого разрешения\nэто уменьшит нагрузку на ресурсы компьютера',
+ 'check for higher resolution,\nuses more computing resources':
+ 'отметьте, чтобы использовать высокое разрешение\nэто увеличит нагрузку на ресурсы компьютера',
'Stage size':
'Размер сцены',
'Stage width':
@@ -713,57 +747,57 @@ SnapTranslator.dict.ru = {
'Blurred shadows':
'Контрастность тени',
'uncheck to use solid drop\nshadows and highlights':
- 'убрать метку - использовать сплошные\nтени и подсветки',
+ 'снимите флажок, чтобы использовать сплошные\nтени и подсветки',
'check to use blurred drop\nshadows and highlights':
- 'поставить метку - использовать размытые\nтени и подсветки',
+ 'отметьте, чтобы использовать размытые\nтени и подсветки',
'Zebra coloring':
'Использование альтернативных цветов',
'check to enable alternating\ncolors for nested blocks':
- 'поставить метку - разрешить использование\nперемежающихся цветов для вложенных блоков',
+ 'отметьте, чтобы разрешить использование\nперемежающихся цветов для вложенных блоков',
'uncheck to disable alternating\ncolors for nested block':
- 'убрать метку - отключить использование\nперемежающихся цветов для вложенных блоков',
+ 'снимите флажок, чтобы отключить использование\nперемежающихся цветов для вложенных блоков',
'Dynamic input labels':
'Использование динамических обозначений',
'uncheck to disable dynamic\nlabels for variadic inputs':
- 'убрать метку - отключить использование динамических обозначений\nпри вводе с переменным числом аргументов',
+ 'снимите флажок, чтобы отключить использование динамических обозначений\nпри вводе с переменным числом аргументов',
'check to enable dynamic\nlabels for variadic inputs':
- 'поставить метку - разрешить использование динамических обозначений\nпри вводе с переменным числом аргументов',
+ 'отметьте, чтобы разрешить использование динамических обозначений\nпри вводе с переменным числом аргументов',
'Prefer empty slot drops':
'Использование незанятых ячеек ввода',
'settings menu prefer empty slots hint':
- 'поставить метку - помещать генераторы значений\nтолько в незанятые ячейки ввода',
+ 'отметьте, чтобы помещать генераторы значений\nтолько в незанятые ячейки ввода',
'uncheck to allow dropped\nreporters to kick out others':
- 'убрать метку - разрешить помещать генераторы значений\nв занятые ячейки ввода',
+ 'снимите флажок, чтобы разрешить помещать генераторы значений\nв занятые ячейки ввода',
'Long form input dialog':
'Расширенная форма диалога ввода',
'check to always show slot\ntypes in the input dialog':
- 'поставить метку - указывать типы ячеек ввода\nв диалоге ввода',
+ 'отметьте, чтобы указывать типы ячеек ввода\nв диалоге ввода',
'uncheck to use the input\ndialog in short form':
- 'убрать метку - использовать краткую форму\nдиалога ввода',
+ 'снимите флажок, чтобы использовать краткую форму\nдиалога ввода',
'Virtual keyboard':
'Виртуальная клавиатура',
'uncheck to disable\nvirtual keyboard support\nfor mobile devices':
- 'убрать метку - отключить использование виртуальной клавиатуры\nдля мобильных устройств',
+ 'снимите флажок, чтобы отключить использование виртуальной клавиатуры\nдля мобильных устройств',
'check to enable\nvirtual keyboard support\nfor mobile devices':
- 'поставить метку - разрешить использование виртуальной клавиатуры\nдля мобильных устройств',
+ 'отметьте, чтобы разрешить использование виртуальной клавиатуры\nдля мобильных устройств',
'Input sliders':
'Использование бегунков ввода',
'uncheck to disable\ninput sliders for\nentry fields':
- 'убрать метку - отключить использование бегунков\nпри заполнении полей ввода',
+ 'снимите флажок, чтобы отключить использование бегунков\nпри заполнении полей ввода',
'check to enable\ninput sliders for\nentry fields':
- 'поставить метку - разрешить использование бегунков\nпри заполнении полей ввода',
+ 'отметьте, чтобы разрешить использование бегунков\nпри заполнении полей ввода',
'Clicking sound':
'Звук щелчка',
'uncheck to turn\nblock clicking\nsound off':
- 'убрать метку - выключить звук\nпри щелчке на блок',
+ 'снимите флажок, чтобы выключить звук\nпри щелчке на блок',
'check to turn\nblock clicking\nsound on':
- 'поставить метку - включить звук\nпри щелчке на блок',
+ 'отметьте, чтобы включить звук\nпри щелчке на блок',
'Animations':
'Aнимация',
'uncheck to disable\nIDE animations':
- 'убрать метку - отключить\nIDE aнимацию',
+ 'снимите флажок, чтобы отключить\nIDE aнимацию',
'check to enable\nIDE animations':
- 'поставить метку - разрешить\nIDE aнимацию',
+ 'отметьте, чтобы разрешить\nIDE aнимацию',
'Turbo mode':
'Режим Турбо',
'check to prioritize\nscript execution':
@@ -797,17 +831,41 @@ SnapTranslator.dict.ru = {
'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':
- 'убрать метку - разрешить\nповторное вхождение в скрипт',
- 'check to disallow\nscript reentrancy':
- 'поставить метку - отключить\nповторное вхождение в скрипт',
+ 'uncheck to allow\nscript reentrance':
+ 'снимите флажок, чтобы разрешить\nповторный вход в скрипт',
+ 'check to disallow\nscript reentrance':
+ 'отметьте, чтобы отключить\nповторный вход в скрипт',
+ 'Plain prototype labels':
+ 'Простые заголовки блоков',
+ 'uncheck to always show (+) symbols\nin block prototype labels':
+ 'снимите флажок, чтобы показывать (+)\nпри редактировании заголовка в редакторе блоков',
+ 'check to hide (+) symbols\nin block prototype labels':
+ 'отметьте, чтобы отключить (+)\nпри редактировании заголовка в редакторе блоков',
+ 'Flat line ends':
+ 'Прямоугольные завершения линий',
+ 'uncheck for round ends of lines':
+ 'снимите флажок, чтобы\nконцы нарисованных линий закруглялись',
+ 'check for flat ends of lines':
+ 'отметьте, чтобы отключить\nзакругления на концах нарисованных линий',
+ 'Codification support':
+ 'Поддержка кодификации блоков',
+ 'uncheck to disable\nblock to text mapping features':
+ 'снимите флажок, чтобы убрать блоки\nтрансляции в текст на другой язык программирования',
+ 'check for block\nto text mapping features':
+ 'отметьте, чтобы добавить блоки\nтрансляции в текст на другой язык программирования',
+ 'Inheritance support':
+ 'Поддержка наследования',
+ 'uncheck to disable\nsprite inheritance features':
+ 'снимите флажок, чтобы отключить\nнаследование свойств спрайтов',
+ 'check for sprite\ninheritance features':
+ 'отметьте, чтобы включить\nнаследование свойств спрайтов',
// inputs
'with inputs':
@@ -844,6 +902,12 @@ SnapTranslator.dict.ru = {
'обвести',
'unringify':
'убрать обводку',
+ 'find blocks':
+ 'найти блоки',
+ 'hide primitives':
+ 'скрыть стандартные блоки',
+ 'show primitives':
+ 'отобразить стандартные блоки',
// custom blocks:
'delete block definition...':
@@ -854,12 +918,38 @@ SnapTranslator.dict.ru = {
// sprites:
'edit':
'редактировать',
+ 'move':
+ 'переместить',
+ 'clone':
+ 'клонировать',
'export...':
'экспорт...',
+ 'parent...':
+ 'родитель...',
+ 'release':
+ 'освободить',
+ 'make temporary and\nhide in the sprite corral':
+ 'сделать временным и\nубрать отдельный спрайт',
+ 'current parent':
+ 'родитель спрайта',
+ 'add a new Turtle sprite':
+ 'создать новый стандартный спрайт',
+ 'paint a new sprite':
+ 'нарисовать новый спрайт',
+ 'take a camera snapshot and\nimport it as a new sprite':
+ 'сделать фотографию камерой и\nиспользовать изображение как новый спрайт',
+ 'pivot':
+ 'центр вращения',
+ 'edit the costume\'s\nrotation center':
+ 'указать центр вращения для костюма',
// stage:
'show all':
'показать все',
+ 'pic...':
+ 'картинка...',
+ 'open a new window\nwith a picture of the stage':
+ 'преобразовать вид текущей сцены\nв картинку',
// scripting area
'clean up':
@@ -868,8 +958,22 @@ SnapTranslator.dict.ru = {
'размещать скрипты\nвертикально',
'add comment':
'добавить комментарий',
+ 'scripts pic...':
+ 'скрипты в картинку...',
+ 'open a new window\nwith a picture of all scripts':
+ 'преобразовать скрипты на листе\nв картинку',
'make a block...':
'новый блок...',
+ 'use the keyboard\nto enter blocks':
+ 'использовать клавиатуру\nдля работы с блоками',
+ 'undrop':
+ 'отменить',
+ 'undo the last\nblock drop\nin this pane':
+ 'отменить последнее\nдействие с блоком',
+ 'redrop':
+ 'вернуть',
+ 'redo the last undone\nblock drop\nin this pane':
+ 'повторить отменённое\nдействие с блоком',
// costumes
'rename':
@@ -877,7 +981,7 @@ SnapTranslator.dict.ru = {
'export':
'экспорт',
'rename costume':
- 'переименовать маску',
+ 'переименовать костюм',
// sounds
'Play sound':
@@ -923,6 +1027,8 @@ SnapTranslator.dict.ru = {
'Открыть',
'Empty':
'Пусто',
+ 'Import':
+ 'Импортировать',
// help
'Help':
@@ -973,16 +1079,14 @@ SnapTranslator.dict.ru = {
'У этого проекта пока нет глобальных\nпользовательских блоков',
'select':
'выделить',
- 'all':
- 'все',
'none':
'ничего',
// variable dialog
'for all sprites':
- 'применительно ко всем Образам',
+ 'для всех спрайтов',
'for this sprite only':
- 'применительно только к данному Образу',
+ 'только для текущего спрайта',
// block dialog
'Change block':
@@ -999,6 +1103,18 @@ SnapTranslator.dict.ru = {
'Редактор Блоков',
'Apply':
'Применить',
+ 'translations...':
+ 'переводы',
+ 'block variables...':
+ 'переменные блока...',
+ 'rename all...':
+ 'переименовать все...',
+ 'block variables':
+ 'переменные блока',
+ 'Block variable name':
+ 'Имя переменной блока',
+ 'remove block variables...':
+ 'убрать переменные блока',
// block deletion dialog
'Delete Custom Block':
@@ -1050,7 +1166,7 @@ SnapTranslator.dict.ru = {
// About Snap
'About Snap':
- 'О Snap!',
+ 'О программе',
'Back...':
'Bозврат...',
'License...':
@@ -1069,6 +1185,8 @@ SnapTranslator.dict.ru = {
'Участники',
'Translations':
'Переводы',
+ 'Reference manual':
+ 'Инструкция пользователя',
// variable watchers
'normal':
@@ -1091,6 +1209,14 @@ SnapTranslator.dict.ru = {
// list watchers
'length: ':
'длина: ',
+ 'list view...':
+ 'в виде списка...',
+ 'table view...':
+ 'в виде таблицы...',
+ 'open in dialog...':
+ 'открыть в отдельном окне...',
+ 'open in another dialog...':
+ 'открыть в ещё одном окне...',
// coments
'add comment here...':
@@ -1117,11 +1243,43 @@ SnapTranslator.dict.ru = {
// costumes
'Turtle':
- 'Черепашка',
+ 'Стрела',
+ 'Opening Costumes...':
+ 'Загрузка костюмов...',
+ 'pen':
+ 'перо',
+ 'tip':
+ 'на острие',
+ 'middle':
+ 'посередине',
+ 'Paint a new costume':
+ 'Нарисовать новый костюм',
+ 'Import a new costume from your webcam':
+ 'Сделать костюм из фотографии вебкамерой',
// graphical effects
'ghost':
'прозрачность',
+ 'color':
+ 'цвет',
+ 'fisheye':
+ 'рыбий глаз',
+ 'whirl':
+ 'вихрь',
+ 'pixelate':
+ 'пикселизация',
+ 'mosaic':
+ 'мозаика',
+ 'negative':
+ 'негатив',
+ 'comic':
+ 'комикс',
+ 'confetti':
+ 'конфетти',
+ 'saturation':
+ 'насыщенность',
+ 'brightness':
+ 'яркость',
// keys
'space':
@@ -1268,6 +1426,10 @@ SnapTranslator.dict.ru = {
'предикат',
'sprite':
'спрайт',
+ 'costume':
+ 'костюм',
+ 'sound':
+ 'звук',
// list indices
'last':
@@ -1275,7 +1437,7 @@ SnapTranslator.dict.ru = {
'any':
'любой',
'now connected':
- 'вы вошли в систему',
+ 'соединение установлено',
'undo':
'отменить',
@@ -1312,7 +1474,56 @@ SnapTranslator.dict.ru = {
'имя',
'stage':
'сцена',
+ 'costumes':
+ 'костюмы',
+ 'sounds':
+ 'звуки',
+
+ //Paint editor
+ 'Paint Editor':
+ 'Графический редактор',
+ 'flip \u2194':
+ 'отраж. \u2194',
+ 'flip \u2195':
+ 'отраж. \u2195',
+ 'grow':
+ 'увел.',
+ 'shrink':
+ 'умен.',
+ 'Brush size':
+ 'Размер кисти',
+ 'Constrain proportions of shapes?\n(you can also hold shift)':
+ 'Сохранять пропорции фигур (круг, квадрат)?\nТак же можно использовать Shift',
+ 'Paintbrush tool\n(free draw)':
+ 'Кисть (свободное рисование)',
+ 'Stroked Rectangle\n(shift: square)':
+ 'Прямоугольник\n(shift: квадрат)',
+ 'Stroked Ellipse\n(shift: circle)':
+ 'Эллипс\n(shift: окружность)',
+ 'Eraser tool':
+ 'Ластик',
+ 'Set the rotation center':
+ 'Установка центра вращения',
+ 'Line tool\n(shift: vertical/horizontal)':
+ 'Линия\n(shift: вертикальная/горизонтальная)',
+ 'Filled Rectangle\n(shift: square)':
+ 'Закрашенный прямоугольник\n(shift: квадрат)',
+ 'Filled Ellipse\n(shift: circle)':
+ 'Закрашенный эллипс\n(shift: окружность)',
+ 'Fill a region':
+ 'Заливка',
+ 'Pipette tool\n(pick a color anywhere)':
+ 'Пипетка\n(взять цвет кликом на любую точку)',
+
//Переводы найденых в программе, но не в файле перевода
+ 'experimental -\nunder construction':
+ 'экспериментальная возможность -\nв разработке',
+ 'Camera':
+ 'Камера',
+ 'Camera not supported':
+ 'Камера не поддерживается',
+ 'Please make sure your web browser is up to date\nand your camera is properly configured. \n\nSome browsers also require you to access Snap!\nthrough HTTPS to use the camera.\n\nPlase replace the "http://" part of the address\nin your browser by "https://" and try again.':
+ 'Пожалуйста, проверьте, что Ваш браузер обновлён до последней версии\nи Ваша камера правильно сконфигурирована. \n\nНекоторые браузеры требуют протокола HTTPS\nдля доступа к СНАП к камере.\n\nПопробуйте заменить "http://" в адресной строке\nВашего браузера на "https://" и попробуйте ещё раз.',
'current %dates':
'сейчас %dates',
'year':
diff --git a/lang-si.js b/lang-si.js
index 31c63712..326cfc8c 100644
--- a/lang-si.js
+++ b/lang-si.js
@@ -583,8 +583,8 @@ SnapTranslator.dict.si = {
'Halo',
'world':
'Svet',
- 'letter %n of %s':
- '\u010Drka %n od %s',
+ 'letter %idx of %s':
+ '\u010Drka %idx od %s',
'length of %s':
'dol\u017Eina %s',
'unicode of %s':
diff --git a/lang-sv.js b/lang-sv.js
index acba22f2..bc689849 100644
--- a/lang-sv.js
+++ b/lang-sv.js
@@ -595,8 +595,8 @@ SnapTranslator.dict.sv = {
'hej',
'world':
'v\u00E4rlden',
- 'letter %n of %s':
- 'bokstav %n av %s',
+ 'letter %idx of %s':
+ 'bokstav %idx av %s',
'length of %s':
'l\u00E4ngden av %s',
'unicode of %s':
diff --git a/lang-ta.js b/lang-ta.js
index c672e918..f63859aa 100644
--- a/lang-ta.js
+++ b/lang-ta.js
@@ -554,8 +554,8 @@ SnapTranslator.dict.ta = {
'வணக்கம்',
'world':
'உலகம்',
- 'letter %n of %s':
- '%s ன் %n வது எழுத்து',
+ 'letter %idx of %s':
+ '%idx வது எழுத்து , %s ன்',
'length of %s':
'%s ன் நீளம்',
'unicode of %s':
diff --git a/lang-te.js b/lang-te.js
index bd1c5dd1..945ea2d5 100644
--- a/lang-te.js
+++ b/lang-te.js
@@ -554,8 +554,8 @@ SnapTranslator.dict.te = {
'హలో',
'world':
'ప్రపంచం',
- 'letter %n of %s':
- 'Zeichen %n von %s',
+ 'letter %idx of %s':
+ 'Zeichen %idx von %s',
'length of %s':
'L\u00e4nge von %s',
'unicode of %s':
diff --git a/lang-tr.js b/lang-tr.js
index 0b852bbd..c3c49029 100644
--- a/lang-tr.js
+++ b/lang-tr.js
@@ -179,13 +179,13 @@ SnapTranslator.dict.tr = {
// translations meta information
'language_name':
- 'Türkçe', // the name as it should appear in the language menu
+ 'Türkçe', // the name as it should appear in the language menu (Dil menüsünde görünmesi gereken isim)
'language_translator':
- 'Hakan Atas', // your name for the Translators tab
+ 'Hakan Atas, www.3drobolab.com', // your name for the Translators tab (Çevirenlerin isimleri)
'translator_e-mail':
- 'hakanatas@gmail.com', // optional
+ 'hakanatas@gmail.com, mustafaipekbayrak@gmail.com', // optional (Mail adresleri)
'last_changed':
- '2015-7-26', // this, too, will appear in the Translators tab
+ '2018-01-22', // this, too, will appear in the Translators tab (Son güncelleme tarihi)
// GUI
// control bar:
@@ -303,7 +303,7 @@ SnapTranslator.dict.tr = {
'go to %dst':
'%dst git',
'glide %n secs to x: %n y: %n':
- 'x: %n y: %n noktasına %n saniyede süzül',
+ '%n saniyede x: %n y: %n noktasına git',
'change x by %n':
'x\'i %n değiştir',
'set x to %n':
@@ -417,6 +417,8 @@ SnapTranslator.dict.tr = {
'%keyHat tuşu basılınca',
'when I am clicked':
'bu kukla tıklanınca',
+ 'when I am %interaction':
+ 'kukla %interaction zaman',
'when I receive %msgHat':
'%msgHat haberi gelince',
'broadcast %msg':
@@ -552,14 +554,14 @@ SnapTranslator.dict.tr = {
'merhaba',
'world':
'dünya',
- 'letter %n of %s':
- '%n in harfleri %s',
+ 'letter %idx of %s':
+ '%idx in harfleri %s',
'length of %s':
'%s in uzunluğu',
'unicode of %s':
'%s in unicode hali',
'unicode %n as letter':
- 'harf olarak %n in unicode hali',
+ 'unicode %n in harf hali',
'is %s a %typ ?':
'%s bir %typ mi?',
'is %s identical to %s ?':
@@ -1271,5 +1273,147 @@ SnapTranslator.dict.tr = {
'last':
'son',
'any':
- 'herhangi'
+ 'herhangi',
+
+ // miscellaneous
+ 'find blocks...':
+ 'blokları bul...',
+ 'hide primitives':
+ 'temelleri sakla',
+ 'show primitives':
+ 'temelleri göster',
+ 'Login...':
+ 'Bağlan...',
+ 'Signup...':
+ 'Giriş Yap...',
+ 'Reset Password...':
+ 'Şifre Değiştir...',
+ 'show all':
+ 'tümünü göster',
+ 'pic...':
+ 'görüntü...',
+ 'open a new window\nwith a picture of the stage':
+ 'yeni pencere aç\nyeni bölüm ile',
+ 'scripts pic...':
+ 'görüntü betikleri...',
+ 'open a new window\nwith a picture of all scripts':
+ 'yeni pencere aç\ntüm görüntü betikleri ile',
+ 'Stage size...':
+ 'Bölüm Boyutu...',
+ 'Zoom blocks...':
+ 'Blokları yakınlaştır...',
+
+ 'Plain prototype labels':
+ 'Düz prototip isimleri',
+ 'uncheck to always show (+) symbols\nin block prototype labels':
+ 'daima göster (+) sembollerini kapat\nblok prototip isimleri içinde',
+ 'check to hide (+) symbols\nin block prototype labels':
+ 'gizle (+) sembollerini işaretle\nblok prototip isimleri içinde',
+
+ 'check for flat ends of lines':
+ 'çizgilerin düz sonları için işaretle',
+ 'uncheck for round ends of lines':
+ 'çizgilerin yuvarlatılmış sonları için işareti kaldır',
+ 'Flat line ends':
+ 'Fiz çizgi sonları',
+
+ 'Codification support':
+ 'Kodlaştırma desteği',
+ 'uncheck to disable\nblock to text mapping features':
+ 'etkisizleştirmeyi kaldır\nbloktan metine haritalandırma özellikleri',
+ 'check for block\nto text mapping features':
+ 'blok için kontrol et\nmetine dönüştürme özellikleri ',
+
+ 'Inheritance support':
+ 'Miras Desteği',
+
+ 'current %dates':
+ 'mevcut %dates',
+ 'year':
+ 'yıl',
+ 'month':
+ 'ay',
+ 'date':
+ 'tarih',
+ 'hour':
+ 'saat',
+ 'minute':
+ 'dakika',
+ 'second':
+ 'saniye',
+ 'time in milliseconds':
+ 'milisaniye zaman',
+ 'day of week':
+ 'haftanın günü',
+
+ 'brightness':
+ 'parlaklık',
+ 'transparence':
+ 'transparant',
+ 'negative':
+ 'negatif',
+ 'comic':
+ 'dergi',
+
+ 'clicked':
+ 'tıklandığı',
+ 'pressed':
+ 'basıldığı',
+ 'dropped':
+ 'hareket ettiği',
+ 'mouse-entered':
+ 'fare geldiği',
+ 'mouse-departed':
+ 'fare gittiği',
+ 'when %b':
+ '%b olduğunda',
+
+ 'JavaScript function ( %mult%s ) { %code }':
+ 'fonction JavaScript ( %mult%s ) { %code }',
+
+
+ // Copy / Paste
+ 'Press CTRL+C one more time to effectively copy to clipboard.':
+ 'CTRL+C tuşuna bir kez daha basarak panoya kopyala',
+ 'Press CTRL+V one more time to effectively paste from clipboard.':
+ 'CTRL+V tuşuna bir kez daha basarak tabloya yapıştır',
+ 'Press CTRL+X one more time to effectively cut to clipboard.':
+ 'CTRL+X tuşuna bir kezdaha basarak panodan kes',
+
+ // Paint.js
+ 'undo':
+ 'geri',
+ 'Paintbrush tool\n(free draw)':
+ 'fırça\n(serbest çizim)',
+ 'Stroked Rectangle\n(shift: square)':
+ 'dikdörtgen\n(shift: kare)',
+ 'Stroked Ellipse\n(shift: circle)':
+ 'ekips\n(shift : cercle)',
+ 'Eraser tool':
+ 'silgi',
+ 'Set the rotation center':
+ 'döndürme merkezini ayarla',
+ 'Line tool\n(shift: vertical/horizontal)':
+ 'çizgi çizme\n(shift: yatay/dikey)',
+ 'Filled Rectangle\n(shift: square)':
+ 'içi dolu dikdörtgen\n(shift: kare)',
+ 'Filled Ellipse\n(shift: circle)':
+ 'içi dolu elips\n(shift: daire)',
+ 'Fill a region':
+ 'bölgenin içini doldur',
+ 'Pipette tool\n(pick a color anywhere)':
+ 'pipet\n(herhangibir yerden renk seç)',
+ 'grow':
+ 'büyüt',
+ 'shrink':
+ 'küçült',
+ 'flip \u2194':
+ 'ayna görüntüsü \u2194',
+ 'flip \u2195':
+ 'ayna görüntüsü \u2195',
+ 'Brush size':
+ 'fırça boyutu',
+ 'Constrain proportions of shapes?\n(you can also hold shift)':
+ 'şekil bölümleri içerir mi?\n(shift tuşuna basabilirsiniz)'
+
};
diff --git a/lang-zh_CN.js b/lang-zh_CN.js
index af2c4346..3359da64 100644
--- a/lang-zh_CN.js
+++ b/lang-zh_CN.js
@@ -185,7 +185,7 @@ SnapTranslator.dict.zh_CN = {
'translator_e-mail':
'ubertao@qq.com/djh@rhjxx.cn',
'last_changed':
- '2016-5-6',
+ '2018-01-22',
// GUI
// control bar:
@@ -846,8 +846,8 @@ SnapTranslator.dict.zh_CN = {
'你好',
'world':
'世界',
- 'letter %n of %s':
- '第 %n 个字符在文字 %s 中',
+ 'letter %idx of %s':
+ '第 %idx 个字符在文字 %s 中',
'length of %s':
'%s 的长度',
'unicode of %s':
diff --git a/lang-zh_TW.js b/lang-zh_TW.js
index 5e7420e2..dd7c1e10 100644
--- a/lang-zh_TW.js
+++ b/lang-zh_TW.js
@@ -536,8 +536,8 @@ SnapTranslator.dict.zh_TW = {
'歡迎',
'world':
'光臨',
- 'letter %n of %s':
- '第 %n 位元在文字 %s 中',
+ 'letter %idx of %s':
+ '第 %idx 位元在文字 %s 中',
'length of %s':
'%s 的長度',
'unicode of %s':
diff --git a/libraries/Eisenbergification.xml b/libraries/Eisenbergification.xml
index 6e49d020..cd0032ad 100644
--- a/libraries/Eisenbergification.xml
+++ b/libraries/Eisenbergification.xml
@@ -1,4 +1,4 @@
-
Project notes
+
Project notes
Project name
User
Presentation mode
@@ -20,7 +20,8 @@ Thread safe scripts
Prefer smooth animations
Flat line ends
Codification support
-Inheritance support
Presentation mode
+Inheritance support
+Visible palette
Presentation mode
Retina display support
Long form input dialog
Plain prototype labels
@@ -35,9 +36,10 @@ Thread safe scripts
Prefer smooth animations
Flat line ends
Codification support
-Inheritance support
Project notes
+Inheritance support
Project notes
Project name
Language
Zoom blocks
Stage size
-Stage scale
\ No newline at end of file
+Stage scale
+Visible palette
\ No newline at end of file
diff --git a/libraries/LIBRARIES b/libraries/LIBRARIES
index f0422bfe..08afdc6b 100644
--- a/libraries/LIBRARIES
+++ b/libraries/LIBRARIES
@@ -7,10 +7,17 @@ httpBlocks.xml Web services access (https) An extended version of the HTTP:// bl
word-sentence.xml Words, sentences One of the big ideas in Logo that they left out of Scratch is thinking of text as structured into words and sentences, rather than just a string of characters. This library (along with the JOIN WORDS block in the Tools library) brings back that idea.
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).
+setrgb.xml Set RGB or HSV pen color Set or report pen color as RGB (red, green, blue) or HSV (hue, saturation, value).
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.
bignumbers.xml Infinite precision integers, exact rationals, complex The full Scheme numeric tower. "USE BIGNUMS " to enable.
+crayons.xml Provide 100 selected colors to use instead of hue for better selection
+speech_module.xml Text to speech output text using speech synthesis.
animation_module.xml Animation glide, grow and rotate using easing functions.
pixel_module.xml Pixels manipulate costumes pixel-wise.
+audioComp_module.xml Audio Comp analyze, manipulate and generate sound samples.
+atomic_HOFs_module.xml "Bigger" Data [EXPERIMENTAL] crunch large lists very fast
+make-variables.xml create variables in program declare global or sprite-local variables in a script
+json.xml Deal with JSON data Turn JSON strings into lists with the listify block, then retrieve data out of them by using the value at key block.
+parallel_module.xml Parallelization Run several scripts in parallel and wait until all are done.
diff --git a/libraries/atomic_HOFs_module.xml b/libraries/atomic_HOFs_module.xml
new file mode 100644
index 00000000..db703e14
--- /dev/null
+++ b/libraries/atomic_HOFs_module.xml
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/libraries/audioComp_module.xml b/libraries/audioComp_module.xml
new file mode 100644
index 00000000..83dd9471
--- /dev/null
+++ b/libraries/audioComp_module.xml
@@ -0,0 +1,18 @@
+
durationname
+duration
+length
+number of channels
+sample rate
+samplessamplessound
var base64, binaryString, len, bytes, i,
arrayBuffer, audioCtx;
if (sound.audioBuffer) {
return;
}
base64 = sound.audio.src.split(',')[1];
binaryString = window.atob(base64);
len = binaryString.length;
bytes = new Uint8Array(len);
for (i = 0; i < len; i += 1) {
bytes[i] = binaryString.charCodeAt(i);
}
arrayBuffer = bytes.buffer;
Note.prototype.setupContext();
audioCtx = Note.prototype.audioContext;
audioCtx.decodeAudioData(arrayBuffer, function(buffer) {
sound.audioBuffer = buffer;
});
sound
return !isNil(sound.audioBuffer);
soundchoice
switch (choice) {
case 'sample rate':
return sound.audioBuffer.sampleRate;
case 'duration':
return sound.audioBuffer.duration;
case 'length':
return sound.audioBuffer.length;
case 'number of channels':
return sound.audioBuffer.numberOfChannels;
default:
return sound.audioBuffer;
}
play back a sound, a list of samples (single channel), or a list of lists (multiple channels) at the given sample rate.
for demo purposes this block also fast-plots the samples on the stage
4410022.05 kHz=22050
+44.1 kHz=44100
+88.2 kHz=88200
+96 kHz=96000samplesrate
var audioCtx, channels, frameCount, arrayBuffer, i, source;
Note.prototype.setupContext();
audioCtx = Note.prototype.audioContext;
channels = (samples.at(1) instanceof List) ? samples.length() : 1;
frameCount = (channels === 1) ? samples.length() : samples.at(1).length();
arrayBuffer = audioCtx.createBuffer(channels, frameCount, rate);
if (!arrayBuffer.copyToChannel) {
arrayBuffer.copyToChannel = function (src, channel) {
var buffer = this.getChannelData(channel);
for (i = 0; i < src.length; i += 1) {
buffer[i] = src[i];
}
}
}
if (channels === 1) {
arrayBuffer.copyToChannel(Float32Array.from(samples.asArray()), 0, 0);
} else {
for (i = 0; i < channels; i += 1) {
arrayBuffer.copyToChannel(Float32Array.from(samples.at(i + 1).asArray()), i, 0);
}
}
source = audioCtx.createBufferSource();
source.buffer = arrayBuffer;
source.connect(audioCtx.destination);
source.start();
source.pause = source.stop;
this.parentThatIsA(StageMorph).activeSounds.push(source);
generate a list of samples representing a sine wave at the given frequency, duration and sample rate
440a 55=55
+a 110=110
+a 220=220
+a 440=440
+a 880=880
+a 1760=1760
+a 3520=352014410022.05 kHz=22050
+44.1 kHz=44100
+88.2 kHz=88200
+96 kHz=96000
0.5
1
quickly plot the samples of a sound, a list of samples (single channel), or a list of lists (multiple channels) to the stage at a lower resolution.
1offset-2
records an audio snippet and reports it as a new sound, or zero if the user cancels
ca:volum del so
es:volumen del sonido
return typeof meter != 'undefined'
return Math.floor(meter.volume * 100);
\ No newline at end of file
diff --git a/libraries/crayons.xml b/libraries/crayons.xml
new file mode 100644
index 00000000..9ec3bb8c
--- /dev/null
+++ b/libraries/crayons.xml
@@ -0,0 +1,120 @@
+
grays={
+0 black #000000=0
+1 gray7 #121212=1
+2 gray14 #242424=2
+3 gray21 #363636=3
+4 gray28 #484848=4
+5 gray36 #5b5b5b=5
+6 gray43 #6d6d6d=6
+7 gray50 #7f7f7f=7
+8 gray57 #919191=8
+9 gray64 #a3a3a3=9
+10 gray71 #b6b6b6=10
+11 gray78 #c8c8c8=11
+12 gray85 #dadada=12
+13 gray92 #ececec=13
+14 white #ffffff=14
+}
+pinks={
+15 deep pink #ff1493=15
+16 amaranth #e52b50=16
+17 bright pink #ff007f=17
+18 hot pink #ff69b4=18
+19 raspberry #e30b5d=19
+}
+reds={
+20 red #ff0000=20
+21 candy apple red #ff0800=21
+22 dark candy apple red #a40000=22
+23 sanguine #c00000=23
+24 currant #f31112=24
+25 firebrick #b22222=25
+26 cherry #990000=26
+27 crimson #c90016=27
+28 coquelicot #ff3800=28
+29 burgundy #900020=29
+}
+browns={
+30 saddle brown #8b4513=30
+31 copper #b87333=31
+32 golden brown #996515=32
+33 brown #964b00=33
+34 sepia #704214=34
+35 maroon #800000=35
+36 chocolate #d2691e=36
+37 cinnamon #7b3f00=37
+38 chestnut #954535=38
+39 kobicha #6b4423=39
+}
+oranges={
+40 orange #ff7f00=40
+41 pumpkin #ff7518=41
+42 ochre #cc7722=42
+43 dark orange ff8c00=43
+44 tangerine #f28500=44
+45 burnt orange #cc5500=45
+46 web orange #ffa500=46
+47 Pantone orange #ff5800=47
+48 Spanish orange #e86100=48
+49 carrot #ed9121=49
+}
+yellows={
+50 yellow #ffff00=50
+51 saffron #f4c430=51
+52 sandstorm #ecd540=52
+53 canary #ffef00=53
+54 egg yolk #fee33e=54
+55 rubber duck #fbe108=55
+56 dark goldenrod #b8860b=56
+57 goldenrod #daa520=57
+58 gold #ffd700=58
+59 mustard #ffdb58=59
+}
+greens={
+60 lime #00ff00=60
+61 emerald #50c878=61
+62 dark pastel green #03c03c=62
+63 forest green #228b22=63
+64 green 008000=64
+65 dark green 006400=65
+66 mint #3eb489=66
+67 sea green #2e8b57=67
+68 apple green #8db600=68
+69 bright green #66ff00=69
+}
+cyans={
+70 aqua (cyan) #00ffff=70
+71 iceberg #71a6d2=71
+72 cadet blue #5f9ea0=72
+73 cerulean #007ba7=73
+74 dark cyan #008b8b=74
+75 teal #008080=75
+76 light sky blue #87cefa=76
+77 deep sky blue #00bfff=77
+78 dodger blue #1e90ff=78
+79 azure #007fff=79
+}
+blues={
+80 blue #0000ff=80
+81 steel blue #4682b4=81
+82 royal blue #4169e1=82
+83 cobalt #0047ab=83
+84 dark powder blue #003399=84
+85 navy blue #000080=85
+86 midnight blue #191970=86
+87 slate blue #6a5acd=87
+88 denim #1560bd=88
+89 cornflower #6495ed=89
+}
+violets={
+90 violet #8f00ff=90
+91 blue violet #8a2be2=91
+92 x11 purple #a020f0=92
+93 dark orchid #9932cc=93
+94 indigo #4b0082=94
+95 magenta (fuchia) #ff00ff=95
+96 web violet #ee82ee=96
+97 dark magenta #8b008b=97
+98 purple #7f007f=98
+99 grape #6f2da8=99
+}
distance112233index11
25500
1
1
data
mapmany1
data lists
\ No newline at end of file
diff --git a/libraries/json.xml b/libraries/json.xml
new file mode 100644
index 00000000..1b37cc27
--- /dev/null
+++ b/libraries/json.xml
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/libraries/leap-library.xml b/libraries/leap-library.xml
index 951f978d..fab8de66 100644
--- a/libraries/leap-library.xml
+++ b/libraries/leap-library.xml
@@ -1,7 +1,7 @@
-
xx
+
xx
y
-z1
yawyaw
+z1
yawyaw
pitch
-roll1
xx
+roll1
xx
y
-z1
1
1
test
test
var leapScript,
done = false;
leapScript = document.createElement('script'),
leapScript.onload = function () {
done = true;
console.log('leap is ready');
};
document.head.appendChild(leapScript);
leapScript.src = 'http://js.leapmotion.com/leap-0.6.4.min.js';
return function () {return done; }
window.controller = new Leap.Controller({enableGestures: true, background: true}); window.controller.connect();
\ No newline at end of file
+z1
1
1
test
test
var leapScript,
done = false;
leapScript = document.createElement('script'),
leapScript.onload = function () {
done = true;
console.log('leap is ready');
};
document.head.appendChild(leapScript);
leapScript.src = 'https://js.leapmotion.com/leap-0.6.4.min.js';
return function () {return done; }
window.controller = new Leap.Controller({enableGestures: true, background: true}); window.controller.connect();
\ No newline at end of file
diff --git a/libraries/make-variables.xml b/libraries/make-variables.xml
new file mode 100644
index 00000000..571db30a
--- /dev/null
+++ b/libraries/make-variables.xml
@@ -0,0 +1 @@
+
var ide = this.parentThatIsA(IDE_Morph);
ide.flushBlocksCache('variables'); // b/c of inheritance
ide.refreshPalette();
var ide = this.parentThatIsA(IDE_Morph);
ide.flushBlocksCache('variables'); // b/c of inheritance
ide.refreshPalette();
\ No newline at end of file
diff --git a/libraries/parallel_module.xml b/libraries/parallel_module.xml
new file mode 100644
index 00000000..7f2e6c39
--- /dev/null
+++ b/libraries/parallel_module.xml
@@ -0,0 +1 @@
+
each script
test
de:ist _ leer?
de:behalte Elemente, die _ aus _
1
de:wende _ an auf _
1
\ No newline at end of file
diff --git a/libraries/pixel_module.xml b/libraries/pixel_module.xml
index 11565c34..76fd3701 100644
--- a/libraries/pixel_module.xml
+++ b/libraries/pixel_module.xml
@@ -1 +1,4 @@
-reports a list of all pixels in the given costume. Each pixel is represented by a 4-item sublist of RGBA values
overwrite the pixels in the given costume with another list of pixels.
Caution! This operation directly changes the costume. Make sure to use a copy in case you would like to revert changes later.
reports the sprite's actual current costume, which may or may not be part of its wardrobe e.g. if it is a copy
reports a copy of the given costume. This is especially useful when modifying a costume, so you can keep the original around
display the given bitmap in a copy of the current costume (so you can switch back to the original costume again)
\ No newline at end of file
+reports a list of all pixels in the given costume. Each pixel is represented by a 4-item sublist of RGBA values
pixelsname
+pixels
+width
+heightwidthheightcostume
var i,
pixels = [],
src = costume.contents.getContext('2d').getImageData(
0,
0,
costume.width(),
costume.height()
);
for (i = 0; i < src.data.length; i += 4) {
pixels.push(new List([
src.data[i],
src.data[i + 1],
src.data[i + 2],
src.data[i + 3]
]));
}
return new List(pixels);
overwrite the pixels in the given costume with another list of pixels.
Caution! This operation directly changes the costume. Make sure to use a copy in case you would like to revert changes later.
reports the sprite's actual current costume, which may or may not be part of its wardrobe e.g. if it is a copy
reports a copy of the given costume. This is especially useful when modifying a costume, so you can keep the original around
display the given bitmap in a copy of the current costume (so you can switch back to the original costume again)
takes a snapshot with the webcam and reports it as a new costume, or zero if the user cancels
\ No newline at end of file
diff --git a/libraries/speech_module.xml b/libraries/speech_module.xml
new file mode 100644
index 00000000..6553006d
--- /dev/null
+++ b/libraries/speech_module.xml
@@ -0,0 +1,43 @@
+
de:sprich _ mit _ Aussprache _ und Höhe _ Geschwindigkeit _
Hello, World!en-USAmerican=en-US
+Arabic=ar
+British=en-GB
+Chinese=zh
+Czech=cs
+Dutch=nl
+Estonian=et
+Finish=fi
+French=fr
+German=de
+Greek=el
+Hungarian=hu
+Indonesian=id
+Italian=it
+Japanese=ja
+Korean=ko
+Portuguese=pt
+Romanian=ro
+Russian=ru
+Spanish=es
+Swedish=sv
+Turkish=tr11
de:sprich _ mit _ Aussprache _ und Höhe _ Geschwindigkeit _ und warte
Hello, World!en-USAmerican=en-US
+Arabic=ar
+British=en-GB
+Chinese=zh
+Czech=cs
+Dutch=nl
+Estonian=et
+Finish=fi
+French=fr
+German=de
+Greek=el
+Hungarian=hu
+Indonesian=id
+Italian=it
+Japanese=ja
+Korean=ko
+Portuguese=pt
+Romanian=ro
+Russian=ru
+Spanish=es
+Swedish=sv
+Turkish=tr11
\ No newline at end of file
diff --git a/libraries/tools.xml b/libraries/tools.xml
index e409fb7b..e5d3a58c 100644
--- a/libraries/tools.xml
+++ b/libraries/tools.xml
@@ -1 +1 @@
-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.
Hello!12
1
1
110i
121
cont
3
catchtag
cont
catchtag
1101
1
\ No newline at end of file
+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.
de:drucke _ in Größe _
ca:etiqueta _ de mida _
es:etiqueta _ de tamaño _
fr:étiquette _ d'une taille de _
Hello!12
de:ist _ leer?
ca:_ buida?
es:_ vacía?
fr:_ vide?
de:behalte Elemente, die _ aus _
ca:manté els elements on _ de _
es:mantener los elementos donde _ de _
fr:garder les items tels que _ de _
1
de:kombiniere mit _ die Elemente von _
ca:combina amb _ els elements de _
es:combinar con _ los elementos de _
fr:combine avec _ les items de _
1
de:wenn _ dann _ sonst _
ca:si _ llavors _ si no _
es:si _ entonces _ sino _
fr:si _ alors _ sinon _
de:für _ = _ bis _ _
ca:per _ = _ fins _ _
es:para _ = _ hasta _ _
fr:pour _ allant de _ à _ _
110i
de:füge Wörter zusammen _
ca:uneix les paraules _
es:unir las palabras _
fr:fusionne les mots _
121
de:Liste $arrowRight Satz _
ca:llista $arrowRight frase _
es:lista $arrowRight frase _
fr:liste $arrowRight phrase _
de:Satz $arrowRight Liste _
ca:frase $arrowRight llista _
es:frase $arrowRight lista _
fr:phrase $arrowRight liste _
de:fange _ _
ca:agafa _ _
es:atrapar _ _
fr:attrape _ _
cont
3
de:wirf _
ca:llança _
es:lanzar _
fr:lance _
catchtag
de:fange _ _
ca:agafa _ _
es:atrapar _ _
fr:attrape _ _
cont
de:wirf _ _
ca:llança _ _
es:lanzar _ _
fr:lance _ _
catchtag
de:für jedes _ von _ _
ca:per cada _ de _ _
es:para cada _ de _ _
fr:pour chaque _ de _ _
de:falls _ dann _ und pause $pause-1-255-220-0
ca:si _ fes _ i atura-ho tot $pause-1-255-220-0
es:si _ haz _ y páralo todo $pause-1-255-220-0
fr:si _ faire _ et mettre tout en pause $pause-1-255-220-0
de:Wort $arrowRight Liste _
ca:paraula $arrowRight llista _
es:palabra $arrowRight lista _
fr:mot $arrowRight liste _
de:ignoriere _
ca:ignora _
es:ignorar _
fr:ignore _
de:Liste $arrowRight Wort _
ca:llista $arrowRight paraula _
es:lista $arrowRight palabra _
fr:liste $arrowRight mot _
de:Zahlen von _ bis _
ca:nombres des de _ a _
es:números de _ a _
fr:nombres de _ à _
1101
de:wende _ an auf _
ca:mapeja _ sobre _
es:mapear _ sobre _
fr:appliquer _ à _
1
diff --git a/lists.js b/lists.js
index afce2626..9b9fa467 100644
--- a/lists.js
+++ b/lists.js
@@ -7,7 +7,7 @@
written by Jens Mönig and Brian Harvey
jens@moenig.org, bh@cs.berkeley.edu
- Copyright (C) 2017 by Jens Mönig and Brian Harvey
+ Copyright (C) 2018 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 = '2017-September-01';
+modules.lists = '2018-March-08';
var List;
var ListWatcherMorph;
@@ -120,7 +120,7 @@ List.prototype.enableTables = false; // default, to not confuse NYC teachers
// List printing
List.prototype.toString = function () {
- return 'a List [' + this.length + ' elements]';
+ return 'a List [' + this.length() + ' elements]';
};
// List updating:
diff --git a/locale.js b/locale.js
index 4c4108b6..aae55631 100644
--- a/locale.js
+++ b/locale.js
@@ -6,7 +6,7 @@
written by Jens Mönig
- Copyright (C) 2017 by Jens Mönig
+ Copyright (C) 2018 by Jens Mönig
This file is part of Snap!.
@@ -42,7 +42,7 @@
/*global modules, contains*/
-modules.locale = '2017-October-28';
+modules.locale = '2018-July-09';
// Global stuff
@@ -156,11 +156,11 @@ SnapTranslator.dict.de = {
'language_name':
'Deutsch',
'language_translator':
- 'Jens M\u00F6nig',
+ 'Jens M\u00F6nig, Jadga H\u00fcgle',
'translator_e-mail':
- 'jens@moenig.org',
+ 'jens@moenig.org, jadga.huegle@sap.com',
'last_changed':
- '2017-10-20'
+ '2018-06-08'
};
SnapTranslator.dict.it = {
@@ -215,7 +215,7 @@ SnapTranslator.dict.pt = {
'translator_e-mail':
'mmsequeira@gmail.com',
'last_changed':
- '2016-10-30'
+ '2018-05-20'
};
SnapTranslator.dict.cs = {
@@ -237,7 +237,7 @@ SnapTranslator.dict.zh_CN = {
'translator_e-mail':
'ubertao@qq.com/djh@rhjxx.cn',
'last_changed':
- '2017-10-28'
+ '2018-01-22'
};
SnapTranslator.dict.eo = {
@@ -281,18 +281,18 @@ SnapTranslator.dict.ru = {
'translator_e-mail':
'svetlanap@berkeley.edu, tema@school830.ru',
'last_changed':
- '2017-09-01'
+ '2018-02-05'
};
SnapTranslator.dict.es = {
'language_name':
'Espa\u00F1ol',
'language_translator':
- 'V\u00EDctor Manuel Muratalla Morales',
+ 'V\u00EDctor Manuel Muratalla Morales / Cristi\u00E1n Rizzi Iribarren / Alfonso Ruzafa',
'translator_e-mail':
- 'victor.muratalla@yahoo.com',
+ 'victor.muratalla@yahoo.com / rizzi.cristian@gmail.com',
'last_changed':
- '2013-03-25'
+ '2018-02-19'
};
SnapTranslator.dict.nl = {
@@ -314,7 +314,7 @@ SnapTranslator.dict.pl = {
'translator_e-mail':
'witek@oeiizk.waw.pl',
'last_changed':
- '2016-11-14'
+ '2017-11-09'
};
SnapTranslator.dict.zh_TW = {
@@ -358,7 +358,7 @@ SnapTranslator.dict.el = {
'translator_e-mail':
'ino.samaras@berkeley.edu',
'last_changed':
- '2013-09-16'
+ '2018-01-19'
};
SnapTranslator.dict.ca = {
@@ -367,9 +367,20 @@ SnapTranslator.dict.ca = {
'language_translator':
'Bernat Romagosa Carrasquer, Joan Guillén i Pelegay',
'translator_e-mail':
- 'bernat@arduino.org, jguille2@xtec.cat',
+ 'bernat@snap4arduino.rocks, jguille2@xtec.cat',
'last_changed':
- '2017-10-28'
+ '2018-06-16'
+};
+
+SnapTranslator.dict.ca_VA = {
+ 'language_name':
+ 'Català - Valencià',
+ 'language_translator':
+ 'Bernat Romagosa Carrasquer, Joan Guillén i Pelegay, Pilar Embid',
+ 'translator_e-mail':
+ 'bernat@snap4arduino.rocks, jguille2@xtec.cat, embid_mar@gva.es',
+ 'last_changed':
+ '2018-02-08'
};
SnapTranslator.dict.fi = {
@@ -468,7 +479,7 @@ SnapTranslator.dict.tr = {
'translator_e-mail':
'hakanatas@gmail.com',
'last_changed':
- '2015-7-27'
+ '2018-01-22'
};
SnapTranslator.dict.hu = {
@@ -569,3 +580,14 @@ SnapTranslator.dict.gl = {
'last_changed':
'2016-11-09'
};
+
+SnapTranslator.dict.eu = {
+ 'language_name':
+ 'Euskara',
+ 'language_translator':
+ 'Asier Iturralde Sarasola',
+ 'translator_e-mail':
+ 'aiturralde@iametza.eus',
+ 'last_changed':
+ '2018-06-26'
+};
diff --git a/morphic.js b/morphic.js
index 6098416a..8916a148 100644
--- a/morphic.js
+++ b/morphic.js
@@ -8,7 +8,7 @@
written by Jens Mönig
jens@moenig.org
- Copyright (C) 2017 by Jens Mönig
+ Copyright (C) 2018 by Jens Mönig
This file is part of Snap!.
@@ -85,6 +85,7 @@
ColorPaletteMorph
GrayPaletteMorph
ColorPickerMorph
+ DialMorph
FrameMorph
ScrollFrameMorph
ListMorph
@@ -126,6 +127,7 @@
CursorMorph
BoxMorph
SpeechBubbleMorph
+ DialMorph
CircleBoxMorph
SliderButtonMorph
SliderMorph
@@ -1161,7 +1163,7 @@
/*global window, HTMLCanvasElement, FileReader, Audio, FileList*/
-var morphicVersion = '2017-September-26';
+var morphicVersion = '2018-March-19';
var modules = {}; // keep track of additional loaded modules
var useBlurredShadows = getBlurredShadowSupport(); // check for Chrome-bug
@@ -1919,6 +1921,19 @@ Color.prototype.toString = function () {
this.a + ')';
};
+Color.prototype.toRGBstring = function () {
+ return 'rgb(' +
+ Math.round(this.r) + ',' +
+ Math.round(this.g) + ',' +
+ Math.round(this.b) + ')';
+};
+
+Color.fromString = function (aString) {
+ // I parse rgb/rgba strings into a Color object
+ var components = aString.split(/[\(),]/).slice(1,5);
+ return new Color(components[0], components[1], components[2], components[3]);
+};
+
// Color copying:
Color.prototype.copy = function () {
@@ -3135,22 +3150,22 @@ Morph.prototype.setFullCenter = function (aPoint) {
Morph.prototype.keepWithin = function (aMorph) {
// make sure I am completely within another Morph's bounds
var leftOff, rightOff, topOff, bottomOff;
- leftOff = this.fullBounds().left() - aMorph.left();
- if (leftOff < 0) {
- this.moveBy(new Point(-leftOff, 0));
- }
rightOff = this.fullBounds().right() - aMorph.right();
if (rightOff > 0) {
this.moveBy(new Point(-rightOff, 0));
}
- topOff = this.fullBounds().top() - aMorph.top();
- if (topOff < 0) {
- this.moveBy(new Point(0, -topOff));
+ leftOff = this.fullBounds().left() - aMorph.left();
+ if (leftOff < 0) {
+ this.moveBy(new Point(-leftOff, 0));
}
bottomOff = this.fullBounds().bottom() - aMorph.bottom();
if (bottomOff > 0) {
this.moveBy(new Point(0, -bottomOff));
}
+ topOff = this.fullBounds().top() - aMorph.top();
+ if (topOff < 0) {
+ this.moveBy(new Point(0, -topOff));
+ }
};
Morph.prototype.scrollIntoView = function () {
@@ -3249,31 +3264,7 @@ Morph.prototype.drawNew = function () {
this.image = newCanvas(this.extent());
var context = this.image.getContext('2d');
context.fillStyle = this.color.toString();
-
- /*
- 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();
-
+ context.fillRect(0, 0, this.width(), this.height());
if (this.cachedTexture) {
this.drawCachedTexture();
} else if (this.texture) {
@@ -4843,6 +4834,52 @@ PenMorph.prototype.setHeading = function (degrees) {
this.changed();
};
+PenMorph.prototype.numericalSetters = function () {
+ // for context menu demo purposes
+ return [
+ 'setLeft',
+ 'setTop',
+ 'setWidth',
+ 'setHeight',
+ 'setAlphaScaled',
+ 'setHeading'
+ ];
+};
+
+// PenMorph menu:
+
+PenMorph.prototype.developersMenu = function () {
+ var menu = PenMorph.uber.developersMenu.call(this);
+ menu.addLine();
+ menu.addItem(
+ 'set rotation',
+ "setRotation",
+ 'interactively turn this morph\nusing a dial widget'
+ );
+ return menu;
+};
+
+PenMorph.prototype.setRotation = function () {
+ var menu, dial,
+ name = this.name || this.constructor.name;
+ if (name.length > 10) {
+ name = name.slice(0, 9) + '...';
+ }
+ menu = new MenuMorph(this, name);
+ dial = new DialMorph(null, null, this.heading);
+ dial.rootForGrab = function () {return this; };
+ dial.target = this;
+ dial.action = 'setHeading';
+ menu.items.push(dial);
+ menu.addLine();
+ menu.addItem('(90) right', function () {this.setHeading(90); });
+ menu.addItem('(-90) left', function () {this.setHeading(-90); });
+ menu.addItem('(0) up', function () {this.setHeading(0); });
+ menu.addItem('(180) down', function () {this.setHeading(180); });
+ menu.isDraggable = true;
+ menu.popUpAtHand(this.world());
+};
+
// PenMorph drawing:
PenMorph.prototype.drawLine = function (start, dest) {
@@ -6245,6 +6282,315 @@ SpeechBubbleMorph.prototype.fixLayout = function () {
this.addShadow(new Point(2, 2), 80);
};
+// DialMorph //////////////////////////////////////////////////////
+
+// I am a knob than can be turned to select a number
+
+var DialMorph;
+
+// DialMorph inherits from Morph:
+
+DialMorph.prototype = new Morph();
+DialMorph.prototype.constructor = DialMorph;
+DialMorph.uber = Morph.prototype;
+
+function DialMorph(min, max, value, tick, radius) {
+ this.init(min, max, value, tick, radius);
+}
+
+DialMorph.prototype.init = function (min, max, value, tick, radius) {
+ this.target = null;
+ this.action = null;
+ this.min = min || 0;
+ this.max = max || 360;
+ this.value = Math.max(this.min, (value || 0) % this.max);
+ this.tick = tick || 15;
+ this.fillColor = null;
+
+ DialMorph.uber.init.call(this);
+
+ this.color = new Color(230, 230, 230);
+ this.noticesTransparentClick = true;
+ this.setRadius(radius || MorphicPreferences.menuFontSize * 4);
+};
+
+DialMorph.prototype.setRadius = function (radius) {
+ this.radius = radius;
+ this.setExtent(new Point(this.radius * 2, this.radius * 2));
+};
+
+DialMorph.prototype.setValue = function (value, snapToTick, noUpdate) {
+ var range = this.max - this.min;
+ value = value || 0;
+ this.value = this.min + (((+value % range) + range) % range);
+ if (snapToTick) {
+ if (this.value < this.tick) {
+ this.value = this.min;
+ } else {
+ this.value -= this.value % this.tick % this.value;
+ }
+ }
+ this.drawNew();
+ this.changed();
+ if (noUpdate) {return; }
+ this.updateTarget();
+};
+
+DialMorph.prototype.getValueOf = function (point) {
+ var range = this.max - this.min,
+ center = this.center(),
+ deltaX = point.x - center.x,
+ deltaY = center.y - point.y,
+ angle = Math.abs(deltaX) < 0.001 ? (deltaY < 0 ? 90 : 270)
+ : Math.round(
+ (deltaX >= 0 ? 0 : 180)
+ - (Math.atan(deltaY / deltaX) * 57.2957795131)
+ ),
+ value = angle + 90 % 360,
+ ratio = value / 360;
+ return range * ratio + this.min;
+};
+
+DialMorph.prototype.setExtent = function (aPoint) {
+ var size = Math.min(aPoint.x, aPoint.y);
+ this.radius = size / 2;
+ DialMorph.uber.setExtent.call(this, new Point(size, size));
+};
+
+DialMorph.prototype.drawNew = function () {
+ var ctx, i, angle, x1, y1, x2, y2,
+ light = this.color.lighter().toString(),
+ range = this.max - this.min,
+ ticks = range / this.tick,
+ face = this.radius * 0.75,
+ inner = face * 0.85,
+ outer = face * 0.95;
+
+ this.image = newCanvas(this.extent());
+ ctx = this.image.getContext('2d');
+
+ // draw a light border:
+ ctx.fillStyle = light;
+ ctx.beginPath();
+ ctx.arc(
+ this.radius,
+ this.radius,
+ face + Math.min(1, this.radius - face),
+ 0,
+ 2 * Math.PI,
+ false
+ );
+ ctx.closePath();
+ ctx.fill();
+
+ // fill circle:
+ ctx.fillStyle = this.color.toString();
+ ctx.beginPath();
+ ctx.arc(
+ this.radius,
+ this.radius,
+ face,
+ 0,
+ 2 * Math.PI,
+ false
+ );
+ ctx.closePath();
+ ctx.fill();
+
+ // fill value
+ angle = (this.value - this.min) * (Math.PI * 2) / range - Math.PI / 2;
+ ctx.fillStyle = (this.fillColor || this.color.darker()).toString();
+ ctx.beginPath();
+ ctx.arc(
+ this.radius,
+ this.radius,
+ face,
+ Math.PI / -2,
+ angle,
+ false
+ );
+ ctx.lineTo(this.radius, this.radius);
+ ctx.closePath();
+ ctx.fill();
+
+ // draw ticks:
+ ctx.strokeStyle = new Color(35, 35, 35).toString();
+ ctx.lineWidth = 1;
+ for (i = 0; i < ticks; i += 1) {
+ angle = (i - 3) * (Math.PI * 2) / ticks - Math.PI / 2;
+ ctx.beginPath();
+ x1 = this.radius + Math.cos(angle) * inner;
+ y1 = this.radius + Math.sin(angle) * inner;
+ x2 = this.radius + Math.cos(angle) * outer;
+ y2 = this.radius + Math.sin(angle) * outer;
+ ctx.moveTo(x1, y1);
+ ctx.lineTo(x2, y2);
+ ctx.stroke();
+ }
+
+ // draw a filled center:
+ inner = face * 0.05;
+ ctx.fillStyle = 'black';
+ ctx.beginPath();
+ ctx.arc(
+ this.radius,
+ this.radius,
+ inner,
+ 0,
+ 2 * Math.PI,
+ false
+ );
+ ctx.closePath();
+ ctx.fill();
+
+ // draw the inner hand:
+ ctx.strokeStyle = 'black';
+ ctx.lineWidth = 1;
+ angle = (this.value - this.min) * (Math.PI * 2) / range - Math.PI / 2;
+ outer = face * 0.8;
+ x1 = this.radius + Math.cos(angle) * inner;
+ y1 = this.radius + Math.sin(angle) * inner;
+ x2 = this.radius + Math.cos(angle) * outer;
+ y2 = this.radius + Math.sin(angle) * outer;
+ ctx.beginPath();
+ ctx.moveTo(x1, y1);
+ ctx.lineTo(x2, y2);
+ ctx.stroke();
+
+ // draw a read-out circle:
+ inner = inner * 2;
+ x2 = this.radius + Math.cos(angle) * (outer + inner);
+ y2 = this.radius + Math.sin(angle) * (outer + inner);
+ ctx.fillStyle = 'black';
+ ctx.beginPath();
+ ctx.arc(
+ x2,
+ y2,
+ inner,
+ 0,
+ 2 * Math.PI,
+ false
+ );
+ ctx.closePath();
+ ctx.stroke();
+
+ // draw the outer hand:
+ angle = (this.value - this.min) * (Math.PI * 2) / range - Math.PI / 2;
+ x1 = this.radius + Math.cos(angle) * face;
+ y1 = this.radius + Math.sin(angle) * face;
+ x2 = this.radius + Math.cos(angle) * (this.radius - 1);
+ y2 = this.radius + Math.sin(angle) * (this.radius - 1);
+ ctx.beginPath();
+ ctx.moveTo(x1, y1);
+ ctx.lineTo(x2, y2);
+ ctx.lineWidth = 3;
+ ctx.strokeStyle = light;
+ ctx.stroke();
+ ctx.lineWidth = 1;
+ ctx.strokeStyle = 'black';
+ ctx.stroke();
+
+ // draw arrow tip:
+ angle = radians(degrees(angle) - 4);
+ x1 = this.radius + Math.cos(angle) * this.radius * 0.9;
+ y1 = this.radius + Math.sin(angle) * this.radius * 0.9;
+ ctx.beginPath();
+ ctx.moveTo(x1, y1);
+ angle = radians(degrees(angle) + 8);
+ x1 = this.radius + Math.cos(angle) * this.radius * 0.9;
+ y1 = this.radius + Math.sin(angle) * this.radius * 0.9;
+ ctx.lineTo(x1, y1);
+ ctx.lineTo(x2, y2);
+ ctx.closePath();
+ ctx.lineWidth = 3;
+ ctx.strokeStyle = light;
+ ctx.stroke();
+ ctx.lineWidth = 1;
+ ctx.strokeStyle = 'black';
+ ctx.stroke();
+ ctx.fill();
+};
+
+// DialMorph stepping:
+
+DialMorph.prototype.step = null;
+
+DialMorph.prototype.mouseDownLeft = function (pos) {
+ var world, myself = this;
+ world = this.root();
+ this.step = function () {
+ if (world.hand.mouseButton) {
+ myself.setValue(
+ myself.getValueOf(world.hand.bounds.origin),
+ world.currentKey !== 16 // snap to tick
+ );
+ } else {
+ this.step = null;
+ }
+ };
+};
+
+// DialMorph menu:
+
+DialMorph.prototype.developersMenu = function () {
+ var menu = DialMorph.uber.developersMenu.call(this);
+ menu.addLine();
+ menu.addItem(
+ 'set target',
+ "setTarget",
+ 'select another morph\nwhose numerical property\nwill be ' +
+ 'controlled by this one'
+ );
+ return menu;
+};
+
+DialMorph.prototype.setTarget = function () {
+ var choices = this.overlappedMorphs(),
+ menu = new MenuMorph(this, 'choose target:'),
+ myself = this;
+
+ choices.push(this.world());
+ choices.forEach(function (each) {
+ menu.addItem(each.toString().slice(0, 50), function () {
+ myself.target = each;
+ myself.setTargetSetter();
+ });
+ });
+ if (choices.length === 1) {
+ this.target = choices[0];
+ this.setTargetSetter();
+ } else if (choices.length > 0) {
+ menu.popUpAtHand(this.world());
+ }
+};
+
+DialMorph.prototype.setTargetSetter = function () {
+ var choices = this.target.numericalSetters(),
+ menu = new MenuMorph(this, 'choose target property:'),
+ myself = this;
+
+ choices.forEach(function (each) {
+ menu.addItem(each, function () {
+ myself.action = each;
+ });
+ });
+ if (choices.length === 1) {
+ this.action = choices[0];
+ } else if (choices.length > 0) {
+ menu.popUpAtHand(this.world());
+ }
+};
+
+DialMorph.prototype.updateTarget = function () {
+ if (this.action) {
+ if (typeof this.action === 'function') {
+ this.action.call(this.target, this.value);
+ } else { // assume it's a String
+ this.target[this.action](this.value);
+ }
+ }
+};
+
// CircleBoxMorph //////////////////////////////////////////////////////
// I can be used for sliders
@@ -7672,7 +8018,8 @@ MenuMorph.prototype.drawNew = function () {
isLine = false;
if (tuple instanceof StringFieldMorph ||
tuple instanceof ColorPickerMorph ||
- tuple instanceof SliderMorph) {
+ tuple instanceof SliderMorph ||
+ tuple instanceof DialMorph) {
item = tuple;
} else if (tuple[0] === 0) {
isLine = true;
@@ -7729,7 +8076,8 @@ MenuMorph.prototype.maxWidth = function () {
);
} else if ((item instanceof StringFieldMorph) ||
(item instanceof ColorPickerMorph) ||
- (item instanceof SliderMorph)) {
+ (item instanceof SliderMorph) ||
+ (item instanceof DialMorph)) {
w = Math.max(w, item.width());
}
});
@@ -7744,7 +8092,9 @@ MenuMorph.prototype.adjustWidths = function () {
isSelected,
myself = this;
this.children.forEach(function (item) {
- item.silentSetWidth(w);
+ if (!(item instanceof DialMorph)) {
+ item.silentSetWidth(w);
+ }
if (item instanceof MenuItemMorph) {
item.fixLayout();
isSelected = (item.image === item.pressImage);
@@ -7769,16 +8119,38 @@ MenuMorph.prototype.unselectAllItems = function () {
this.children.forEach(function (item) {
if (item instanceof MenuItemMorph) {
item.image = item.normalImage;
+ } else if (item instanceof ScrollFrameMorph) {
+ item.contents.children.forEach(function (morph) {
+ if (morph instanceof MenuItemMorph) {
+ morph.image = morph.normalImage;
+ }
+ });
}
});
this.changed();
};
+// MenuMorph popping up
+
MenuMorph.prototype.popup = function (world, pos) {
+ var scroller;
+
this.drawNew();
this.setPosition(pos);
this.addShadow(new Point(2, 2), 80);
this.keepWithin(world);
+
+ if (this.bottom() > world.bottom()) {
+ // scroll menu items if the menu is taller than the world
+ this.removeShadow();
+ scroller = this.scroll();
+ this.bounds.corner.y = world.bottom() - 2;
+ MenuMorph.uber.drawNew.call(this);
+ this.addShadow(new Point(2, 2), 80);
+ scroller.setHeight(world.bottom() - scroller.top() - 6);
+ scroller.adjustScrollBars(); // ?
+ }
+
if (world.activeMenu) {
world.activeMenu.destroy();
}
@@ -7791,6 +8163,21 @@ MenuMorph.prototype.popup = function (world, pos) {
this.fullChanged();
};
+MenuMorph.prototype.scroll = function () {
+ // private - move all items into a scroll frame
+ var scroller = new ScrollFrameMorph(),
+ start = this.label ? 1 : 0,
+ first = this.children[start];
+
+ scroller.setPosition(first.position());
+ this.children.slice(start).forEach(function (morph) {
+ scroller.addContents(morph);
+ });
+ this.add(scroller);
+ scroller.setWidth(first.width());
+ return scroller;
+};
+
MenuMorph.prototype.popUpAtHand = function (world) {
var wrrld = world || this.world;
this.popup(wrrld, wrrld.hand.position());
@@ -7881,21 +8268,31 @@ MenuMorph.prototype.processKeyPress = function (event) {
};
MenuMorph.prototype.selectFirst = function () {
- var i;
- for (i = 0; i < this.children.length; i += 1) {
- if (this.children[i] instanceof MenuItemMorph) {
- this.select(this.children[i]);
+ var scroller, items, i;
+
+ scroller = detect(this.children, function (morph) {
+ return morph instanceof ScrollFrameMorph;
+ });
+ items = scroller ? scroller.contents.children : this.children;
+ for (i = 0; i < items.length; i += 1) {
+ if (items[i] instanceof MenuItemMorph) {
+ this.select(items[i]);
return;
- }
- }
+ }
+ }
};
MenuMorph.prototype.selectUp = function () {
- var triggers, idx;
+ var scroller, triggers, idx;
- triggers = this.children.filter(function (each) {
- return each instanceof MenuItemMorph;
+ scroller = detect(this.children, function (morph) {
+ return morph instanceof ScrollFrameMorph;
});
+ triggers = (scroller ? scroller.contents.children : this.children).filter(
+ function (each) {
+ return each instanceof MenuItemMorph;
+ }
+ );
if (!this.selection) {
if (triggers.length) {
this.select(triggers[0]);
@@ -7910,11 +8307,16 @@ MenuMorph.prototype.selectUp = function () {
};
MenuMorph.prototype.selectDown = function () {
- var triggers, idx;
+ var scroller, triggers, idx;
- triggers = this.children.filter(function (each) {
- return each instanceof MenuItemMorph;
+ scroller = detect(this.children, function (morph) {
+ return morph instanceof ScrollFrameMorph;
});
+ triggers = (scroller ? scroller.contents.children : this.children).filter(
+ function (each) {
+ return each instanceof MenuItemMorph;
+ }
+ );
if (!this.selection) {
if (triggers.length) {
this.select(triggers[0]);
@@ -7951,6 +8353,7 @@ MenuMorph.prototype.select = function (aMenuItem) {
this.unselectAllItems();
aMenuItem.image = aMenuItem.highlightImage;
aMenuItem.changed();
+ aMenuItem.scrollIntoView();
this.selection = aMenuItem;
};
@@ -9620,7 +10023,7 @@ MenuItemMorph.prototype.mouseLeave = function () {
MenuItemMorph.prototype.mouseDownLeft = function (pos) {
if (this.isListItem()) {
- this.parent.unselectAllItems();
+ this.parentThatIsA(MenuMorph).unselectAllItems();
this.escalateEvent('mouseDownLeft', pos);
}
this.image = this.pressImage;
@@ -9638,7 +10041,7 @@ MenuItemMorph.prototype.mouseClickLeft = function () {
this.popUpSubmenu();
} else {
if (!this.isListItem()) {
- this.parent.closeRootMenu();
+ this.parentThatIsA(MenuMorph).closeRootMenu();
this.world().activeMenu = null;
}
this.trigger();
@@ -9646,8 +10049,9 @@ MenuItemMorph.prototype.mouseClickLeft = function () {
};
MenuItemMorph.prototype.isListItem = function () {
- if (this.parent) {
- return this.parent.isListContents;
+ var menu = this.parentThatIsA(MenuMorph);
+ if (menu) {
+ return menu.isListContents;
}
return false;
};
@@ -10691,7 +11095,9 @@ HandMorph.prototype.grab = function (aMorph) {
if (this.children.length === 0) {
this.world.stopEditing();
this.grabOrigin = aMorph.situation();
- aMorph.addShadow();
+ if (!(aMorph instanceof MenuMorph)) {
+ aMorph.addShadow();
+ }
if (aMorph.prepareToBeGrabbed) {
aMorph.prepareToBeGrabbed(this);
}
@@ -10716,7 +11122,9 @@ HandMorph.prototype.drop = function () {
morphToDrop.cachedFullImage = null;
morphToDrop.cachedFullBounds = null;
morphToDrop.changed();
- morphToDrop.removeShadow();
+ if (!(morphToDrop instanceof MenuMorph)) {
+ morphToDrop.removeShadow();
+ }
this.children = [];
this.setExtent(new Point());
if (morphToDrop.justDropped) {
@@ -10923,6 +11331,7 @@ HandMorph.prototype.processMouseMove = function (event) {
this.morphToGrab.selectForEdit() : this.morphToGrab;
this.grab(morph);
} else if (this.morphToGrab.isTemplate) {
+ this.world.stopEditing();
morph = this.morphToGrab.fullCopy();
morph.isTemplate = false;
morph.isDraggable = true;
@@ -11840,6 +12249,10 @@ WorldMorph.prototype.userCreateMorph = function () {
menu.addItem('slider', function () {
create(new SliderMorph());
});
+ menu.addItem('dial', function () {
+ newMorph = new DialMorph();
+ newMorph.pickUp(this);
+ });
menu.addItem('frame', function () {
newMorph = new FrameMorph();
newMorph.setExtent(new Point(350, 250));
diff --git a/objects.js b/objects.js
index 8a4e1389..f31b6ddc 100644
--- a/objects.js
+++ b/objects.js
@@ -9,7 +9,7 @@
written by Jens Mönig
jens@moenig.org
- Copyright (C) 2017 by Jens Mönig
+ Copyright (C) 2018 by Jens Mönig
This file is part of Snap!.
@@ -81,9 +81,9 @@ modules, IDE_Morph, VariableDialogMorph, HTMLCanvasElement, Context, List,
SpeechBubbleMorph, RingMorph, isNil, FileReader, TableDialogMorph,
BlockEditorMorph, BlockDialogMorph, PrototypeHatBlockMorph, localize,
TableMorph, TableFrameMorph, normalizeCanvas, BooleanSlotMorph, HandleMorph,
-AlignmentMorph*/
+AlignmentMorph, Process, XML_Element, VectorPaintEditorMorph*/
-modules.objects = '2017-October-28';
+modules.objects = '2018-July-06';
var SpriteMorph;
var StageMorph;
@@ -573,16 +573,6 @@ SpriteMorph.prototype.initBlocks = function () {
category: 'control',
spec: 'when %keyHat key pressed'
},
-
- /* migrated to a newer block version:
-
- receiveClick: {
- type: 'hat',
- category: 'control',
- spec: 'when I am clicked'
- },
- */
-
receiveInteraction: {
type: 'hat',
category: 'control',
@@ -696,25 +686,6 @@ SpriteMorph.prototype.initBlocks = function () {
category: 'control',
spec: 'call %repRing %inputs'
},
- /*
- doRunWithInputList: {
- type: 'command',
- category: 'control',
- spec: 'run %cmd with input list %l'
- },
-
- forkWithInputList: {
- type: 'command',
- category: 'control',
- spec: 'launch %cmd with input list %l'
- },
-
- evaluateWithInputList: {
- type: 'reporter',
- category: 'control',
- spec: 'call %r with input list %l'
- },
- */
doReport: {
type: 'command',
category: 'control',
@@ -745,15 +716,13 @@ SpriteMorph.prototype.initBlocks = function () {
// Message passing - very experimental
- doTellTo: { // under construction +++
- dev: true,
+ doTellTo: {
type: 'command',
category: 'control',
// spec: 'tell %spr to %cl' // I liked this version much better, -Jens
spec: 'tell %spr to %cmdRing %inputs'
},
reportAskFor: {
- dev: true,
type: 'reporter',
category: 'control',
spec: 'ask %spr for %repRing %inputs'
@@ -872,11 +841,20 @@ SpriteMorph.prototype.initBlocks = function () {
category: 'sensing',
spec: 'key %key pressed?'
},
- reportDistanceTo: {
+ /*
+ reportDistanceTo: { // has been superseded by reportRelationTo
type: 'reporter',
category: 'sensing',
spec: 'distance to %dst'
},
+ */
+ reportRelationTo: {
+ only: SpriteMorph,
+ type: 'reporter',
+ category: 'sensing',
+ spec: '%rel to %dst',
+ defaults: [['distance']]
+ },
doResetTimer: {
type: 'command',
category: 'sensing',
@@ -1042,7 +1020,7 @@ SpriteMorph.prototype.initBlocks = function () {
reportLetter: {
type: 'reporter',
category: 'operators',
- spec: 'letter %n of %s',
+ spec: 'letter %idx of %s',
defaults: [1, localize('world')]
},
reportStringSize: {
@@ -1099,6 +1077,12 @@ SpriteMorph.prototype.initBlocks = function () {
spec: '%txtfun of %s',
defaults: [null, "Abelson & Sussman"]
},
+ reportCompiled: { // experimental
+ type: 'reporter',
+ category: 'operators',
+ spec: 'compile %repRing for %n args',
+ defaults: [null, 0]
+ },
/*
reportScript: {
@@ -1310,6 +1294,11 @@ SpriteMorph.prototype.initBlockMigrations = function () {
selector: 'doMapValueCode',
inputs: [['String'], '<#1>'],
offset: 1
+ },
+ reportDistanceTo: {
+ selector: 'reportRelationTo',
+ inputs: [['distance']],
+ offset: 1
}
};
};
@@ -1766,11 +1755,12 @@ SpriteMorph.prototype.blockForSelector = function (selector, setDefaults) {
return block;
};
-SpriteMorph.prototype.variableBlock = function (varName) {
+SpriteMorph.prototype.variableBlock = function (varName, isLocalTemplate) {
var block = new ReporterBlockMorph(false);
block.selector = 'reportGetVar';
block.color = this.blockColor.variables;
block.category = 'variables';
+ block.isLocalVarTemplate = isLocalTemplate;
block.setSpec(varName);
block.isDraggable = true;
return block;
@@ -1793,8 +1783,8 @@ SpriteMorph.prototype.blockTemplates = function (category) {
return newBlock;
}
- function variableBlock(varName) {
- var newBlock = SpriteMorph.prototype.variableBlock(varName);
+ function variableBlock(varName, isLocal) {
+ var newBlock = SpriteMorph.prototype.variableBlock(varName, isLocal);
newBlock.isDraggable = false;
newBlock.isTemplate = true;
if (contains(inheritedVars, varName)) {
@@ -2058,7 +2048,7 @@ SpriteMorph.prototype.blockTemplates = function (category) {
blocks.push('-');
blocks.push(block('reportKeyPressed'));
blocks.push('-');
- blocks.push(block('reportDistanceTo'));
+ blocks.push(block('reportRelationTo'));
blocks.push('-');
blocks.push(block('doResetTimer'));
blocks.push(watcherToggle('getTimer'));
@@ -2142,6 +2132,9 @@ SpriteMorph.prototype.blockTemplates = function (category) {
if (true) { // (Process.prototype.enableJS) {
blocks.push('-');
blocks.push(block('reportJSFunction'));
+ if (Process.prototype.enableCompiling) {
+ blocks.push(block('reportCompiled'));
+ }
}
// for debugging: ///////////////
@@ -2210,7 +2203,7 @@ SpriteMorph.prototype.blockTemplates = function (category) {
blocks.push('-');
- varNames = this.variables.allNames();
+ varNames = this.reachableGlobalVariableNames(true);
if (varNames.length > 0) {
varNames.forEach(function (name) {
blocks.push(variableWatcherToggle(name));
@@ -2219,6 +2212,15 @@ SpriteMorph.prototype.blockTemplates = function (category) {
blocks.push('-');
}
+ varNames = this.allLocalVariableNames(true);
+ if (varNames.length > 0) {
+ varNames.forEach(function (name) {
+ blocks.push(variableWatcherToggle(name));
+ blocks.push(variableBlock(name, true));
+ });
+ blocks.push('-');
+ }
+
blocks.push(block('doSetVar'));
blocks.push(block('doChangeVar'));
blocks.push(block('doShowVar'));
@@ -2689,7 +2691,7 @@ SpriteMorph.prototype.blocksMatching = function (
[this.customBlocks, stage.globalBlocks].forEach(function (blocksList) {
blocksList.forEach(function (definition) {
if (contains(types, definition.type)) {
- var spec = localize(definition.blockSpec()).toLowerCase(),
+ var spec = definition.localizedSpec().toLowerCase(),
rel = relevance(labelOf(spec), search);
if (rel !== -1) {
blocks.push([definition.templateInstance(), rel + '2']);
@@ -3232,6 +3234,7 @@ SpriteMorph.prototype.userMenu = function () {
}
menu.addItem("delete", 'remove');
menu.addItem("move", 'moveCenter');
+ menu.addItem("rotate", 'setRotation');
if (this.costume) {
menu.addItem(
"pivot",
@@ -3387,10 +3390,15 @@ SpriteMorph.prototype.initClone = function (hats) {
};
SpriteMorph.prototype.removeClone = function () {
- var exemplar = this.exemplar;
+ var exemplar = this.exemplar,
+ myself = this;
if (this.isTemporary) {
// this.stopTalking();
this.parent.threads.stopAllForReceiver(this);
+ this.parts.slice().forEach(function (part) {
+ myself.detachPart(part);
+ part.removeClone();
+ });
this.corpsify();
this.instances.forEach(function (child) {
if (child.isTemporary) {
@@ -4421,9 +4429,20 @@ SpriteMorph.prototype.setPosition = function (aPoint, justMe) {
SpriteMorph.prototype.forward = function (steps) {
var dest,
- dist = steps * this.parent.scale || 0;
+ dist = steps * this.parent.scale || 0,
+ dot = 0.1;
- if (dist >= 0) {
+ if (dist === 0 && this.isDown) { // draw a dot
+ // dot = Math.min(this.size, 1);
+ this.isDown = false;
+ this.forward(dot * -0.5);
+ this.isDown = true;
+ this.forward(dot);
+ this.isDown = false;
+ this.forward(dot * -0.5);
+ this.isDown = true;
+ return;
+ } else if (dist >= 0) {
dest = this.position().distanceAngle(dist, this.heading);
} else {
dest = this.position().distanceAngle(
@@ -4479,6 +4498,10 @@ SpriteMorph.prototype.setHeading = function (degrees, noShadow) {
};
SpriteMorph.prototype.faceToXY = function (x, y) {
+ this.setHeading(this.angleToXY(x, y));
+};
+
+SpriteMorph.prototype.angleToXY = function (x, y) {
var deltaX = (x - this.xPosition()) * this.parent.scale,
deltaY = (y - this.yPosition()) * this.parent.scale,
angle = Math.abs(deltaX) < 0.001 ? (deltaY < 0 ? 90 : 270)
@@ -4486,7 +4509,7 @@ SpriteMorph.prototype.faceToXY = function (x, y) {
(deltaX >= 0 ? 0 : 180)
- (Math.atan(deltaY / deltaX) * 57.2957795131)
);
- this.setHeading(angle + 90);
+ return angle + 90;
};
SpriteMorph.prototype.turn = function (degrees) {
@@ -4830,7 +4853,15 @@ SpriteMorph.prototype.mouseDownLeft = function () {
return this.receiveUserInteraction('pressed');
};
-SpriteMorph.prototype.receiveUserInteraction = function (interaction) {
+SpriteMorph.prototype.mouseScroll = function (y) {
+ return this.receiveUserInteraction('scrolled-' + (y > 0 ? 'up' : 'down'));
+};
+
+SpriteMorph.prototype.receiveUserInteraction = function (
+ interaction,
+ rightAway,
+ threadSafe
+) {
var stage = this.parentThatIsA(StageMorph),
procs = [],
myself = this,
@@ -4841,7 +4872,12 @@ SpriteMorph.prototype.receiveUserInteraction = function (interaction) {
procs.push(stage.threads.startProcess(
block,
myself,
- stage.isThreadSafe
+ threadSafe || stage.isThreadSafe,
+ null, // export result
+ null, // callback
+ null, // is clicked
+ rightAway, // immediately
+ interaction === 'stopped' // atomic
));
});
return procs;
@@ -5748,6 +5784,41 @@ SpriteMorph.prototype.hasSpriteVariable = function (varName) {
return contains(this.variables.names(), varName);
};
+SpriteMorph.prototype.allLocalVariableNames = function (sorted) {
+ var exceptGlobals = this.globalVariables(),
+ globalNames = exceptGlobals.names(),
+ data;
+
+ function alphabetically(x, y) {
+ return x.toLowerCase() < y.toLowerCase() ? -1 : 1;
+ }
+
+ data = this.variables.allNames(exceptGlobals).filter(function (each) {
+ return !contains(globalNames, each);
+ });
+ if (sorted) {
+ data.sort(alphabetically);
+ }
+ return data;
+};
+
+SpriteMorph.prototype.reachableGlobalVariableNames = function (sorted) {
+ var locals = this.allLocalVariableNames(),
+ data;
+
+ function alphabetically(x, y) {
+ return x.toLowerCase() < y.toLowerCase() ? -1 : 1;
+ }
+
+ data = this.globalVariables().names().filter(function (each) {
+ return !contains(locals, each);
+ });
+ if (sorted) {
+ data.sort(alphabetically);
+ }
+ return data;
+};
+
// SpriteMorph inheritance - custom blocks
SpriteMorph.prototype.getMethod = function (spec) {
@@ -6517,6 +6588,26 @@ StageMorph.prototype.colorFiltered = function (aColor, excludedSprite) {
return morph;
};
+// StageMorph pixel access:
+
+StageMorph.prototype.getPixelColor = function (aPoint) {
+ var point, context, data;
+ if (this.trailsCanvas) {
+ point = aPoint.subtract(this.bounds.origin);
+ context = this.trailsCanvas.getContext('2d');
+ data = context.getImageData(point.x, point.y, 1, 1);
+ if (data.data[3] === 0) {
+ return StageMorph.uber.getPixelColor.call(this, aPoint);
+ }
+ return new Color(
+ data.data[0],
+ data.data[1],
+ data.data[2],
+ data.data[3] / 255
+ );
+ }
+};
+
// StageMorph accessing
StageMorph.prototype.watchers = function (leftPos) {
@@ -6613,6 +6704,7 @@ StageMorph.prototype.step = function () {
// handle keyboard events
if (world.keyboardReceiver === null) {
world.keyboardReceiver = this;
+ world.worldCanvas.focus(); // addresses a Safari 11 bug
}
if (world.currentKey === null) {
this.keyPressed = null;
@@ -6763,7 +6855,7 @@ StageMorph.prototype.fireKeyEvent = function (key) {
myself = this;
this.keysPressed[evt] = true;
- if (evt === 'ctrl enter') {
+ if (evt === 'ctrl enter' && !ide.isAppMode) {
return this.fireGreenFlagEvent();
}
if (evt === 'shift enter') {
@@ -6797,7 +6889,7 @@ StageMorph.prototype.fireKeyEvent = function (key) {
if (!ide.isAppMode) {return ide.saveProjectsBrowser(); }
return;
}
- if (evt === 'esc') {
+ if (evt === 'esc' && !ide.isAppMode) {
return this.fireStopAllEvent();
}
this.children.concat(this).forEach(function (morph) {
@@ -6806,7 +6898,7 @@ StageMorph.prototype.fireKeyEvent = function (key) {
procs.push(myself.threads.startProcess(
block,
morph,
- myself.isThreadSafe
+ true // ignore running scripts, was: myself.isThreadSafe
));
});
}
@@ -6849,9 +6941,15 @@ StageMorph.prototype.fireGreenFlagEvent = function () {
StageMorph.prototype.fireStopAllEvent = function () {
var ide = this.parentThatIsA(IDE_Morph);
+
this.threads.resumeAll(this.stage);
+
+ // experimental: run one step of a user-defined script
+ this.runStopScripts();
+
this.keysPressed = {};
this.threads.stopAll();
+
this.stopAllActiveSounds();
this.children.forEach(function (morph) {
if (morph.stopTalking) {
@@ -6867,6 +6965,17 @@ StageMorph.prototype.fireStopAllEvent = function () {
}
};
+StageMorph.prototype.runStopScripts = function () {
+ // experimental: Allow each sprite to run one last step before termination
+ // usage example: Stop a robot or device associated with the sprite
+ this.receiveUserInteraction('stopped', true, true);
+ this.children.forEach(function (morph) {
+ if (morph instanceof SpriteMorph) {
+ morph.receiveUserInteraction('stopped', true, true);
+ }
+ });
+};
+
StageMorph.prototype.removeAllClones = function () {
var myself = this,
clones = this.children.filter(
@@ -6919,13 +7028,14 @@ StageMorph.prototype.blockTemplates = function (category) {
return newBlock;
}
- function variableBlock(varName) {
- var newBlock = SpriteMorph.prototype.variableBlock(varName);
+ function variableBlock(varName, isLocal) {
+ var newBlock = SpriteMorph.prototype.variableBlock(varName, isLocal);
newBlock.isDraggable = false;
newBlock.isTemplate = true;
return newBlock;
}
+
function watcherToggle(selector) {
if (myself.hiddenPrimitives[selector]) {
return null;
@@ -7082,7 +7192,10 @@ StageMorph.prototype.blockTemplates = function (category) {
blocks.push(block('doStopAll'));
*/
blocks.push(block('doStopThis'));
+ /*
+ // migrated to doStopThis, now redundant
blocks.push(block('doStopOthers'));
+ */
blocks.push('-');
blocks.push(block('doRun'));
blocks.push(block('fork'));
@@ -7197,6 +7310,9 @@ StageMorph.prototype.blockTemplates = function (category) {
if (true) { // (Process.prototype.enableJS) {
blocks.push('-');
blocks.push(block('reportJSFunction'));
+ if (Process.prototype.enableCompiling) {
+ blocks.push(block('reportCompiled'));
+ }
}
// for debugging: ///////////////
@@ -7259,7 +7375,7 @@ StageMorph.prototype.blockTemplates = function (category) {
blocks.push('-');
- varNames = this.variables.allNames();
+ varNames = this.reachableGlobalVariableNames(true);
if (varNames.length > 0) {
varNames.forEach(function (name) {
blocks.push(variableWatcherToggle(name));
@@ -7268,6 +7384,15 @@ StageMorph.prototype.blockTemplates = function (category) {
blocks.push('-');
}
+ varNames = this.allLocalVariableNames(true);
+ if (varNames.length > 0) {
+ varNames.forEach(function (name) {
+ blocks.push(variableWatcherToggle(name));
+ blocks.push(variableBlock(name, true));
+ });
+ blocks.push('-');
+ }
+
blocks.push(block('doSetVar'));
blocks.push(block('doChangeVar'));
blocks.push(block('doShowVar'));
@@ -7622,6 +7747,9 @@ StageMorph.prototype.mouseLeave = function () {
StageMorph.prototype.mouseDownLeft
= SpriteMorph.prototype.mouseDownLeft;
+StageMorph.prototype.mouseScroll
+ = SpriteMorph.prototype.mouseScroll;
+
StageMorph.prototype.receiveUserInteraction
= SpriteMorph.prototype.receiveUserInteraction;
@@ -7690,6 +7818,12 @@ StageMorph.prototype.inheritedVariableNames = function () {
return [];
};
+StageMorph.prototype.allLocalVariableNames
+ = SpriteMorph.prototype.allLocalVariableNames;
+
+StageMorph.prototype.reachableGlobalVariableNames
+ = SpriteMorph.prototype.reachableGlobalVariableNames;
+
// StageMorph inheritance - custom blocks
StageMorph.prototype.getMethod
@@ -8186,7 +8320,8 @@ Costume.prototype.edit = function (aWorld, anIDE, isnew, oncancel, onsubmit) {
anIDE.hasChangedMedia = true;
}
(onsubmit || nop)();
- }
+ },
+ anIDE
);
};
@@ -8290,6 +8425,7 @@ SVG_Costume.uber = Costume.prototype;
function SVG_Costume(svgImage, name, rotationCenter) {
this.contents = svgImage;
+ this.shapes = [];
this.shrinkToFit(this.maxExtent());
this.name = name || null;
this.rotationCenter = rotationCenter || this.center();
@@ -8309,6 +8445,7 @@ SVG_Costume.prototype.copy = function () {
img.src = this.contents.src;
cpy = new SVG_Costume(img, this.name ? copy(this.name) : null);
cpy.rotationCenter = this.rotationCenter.copy();
+ cpy.shapes = this.shapes.map(function (shape) { return shape.copy(); });
return cpy;
};
@@ -8329,6 +8466,60 @@ SVG_Costume.prototype.shrinkToFit = function (extentPoint) {
return;
};
+SVG_Costume.prototype.parseShapes = function () {
+ // I try to parse my SVG as an editable collection of shapes
+ var element = new XML_Element(),
+ // remove 'data:image/svg+xml, ' from src
+ contents = this.contents.src.replace(/^data:image\/.*?, */, '');
+
+ if (this.contents.src.indexOf('base64') > -1) {
+ contents = atob(contents);
+ }
+
+ element.parseString(contents);
+
+ if (this.shapes.length === 0 && element.attributes.snap) {
+ this.shapes = element.children.map(function (child) {
+ return window[child.attributes.prototype].fromSVG(child);
+ });
+ }
+};
+
+SVG_Costume.prototype.edit = function (
+ aWorld,
+ anIDE,
+ isnew,
+ oncancel,
+ onsubmit
+) {
+ var myself = this,
+ editor;
+
+ editor = new VectorPaintEditorMorph();
+
+ editor.oncancel = oncancel || nop;
+ editor.openIn(
+ aWorld,
+ isnew ? newCanvas(StageMorph.prototype.dimensions) : this.contents,
+ isnew ? new Point(240, 180) : myself.rotationCenter,
+ function (img, rc, shapes) {
+ myself.contents = img;
+ myself.rotationCenter = rc;
+ myself.shapes = shapes;
+ myself.version = Date.now();
+ aWorld.changed();
+ if (anIDE) {
+ if (isnew) {anIDE.currentSprite.addCostume(myself); }
+ anIDE.currentSprite.wearCostume(myself);
+ anIDE.hasChangedMedia = true;
+ }
+ (onsubmit || nop)();
+ },
+ anIDE,
+ this.shapes || []
+ );
+};
+
// CostumeEditorMorph ////////////////////////////////////////////////////////
// CostumeEditorMorph inherits from Morph:
diff --git a/paint.js b/paint.js
index 3b154380..27644dde 100644
--- a/paint.js
+++ b/paint.js
@@ -5,7 +5,7 @@
inspired by the Scratch paint editor.
written by Kartik Chandra
- Copyright (C) 2016 by Kartik Chandra
+ Copyright (C) 2018 by Kartik Chandra
This file is part of Snap!.
@@ -64,18 +64,21 @@
Mar 22 - fixed automatic rotation center point mechanism (Jens)
May 10 - retina display support adjustments (Jens)
2017
- April 10 - getGlobalPixelColor adjustment for Chrome & retina (Jens)
+ Apr 10 - getGlobalPixelColor adjustment for Chrome & retina (Jens)
+ 2018
+ Jan 22 - floodfill alpha tweak (Bernat)
+ Mar 19 - vector paint editor (Bernat)
*/
/*global Point, Rectangle, DialogBoxMorph, AlignmentMorph, PushButtonMorph,
Color, SymbolMorph, newCanvas, Morph, TextMorph, Costume, SpriteMorph, nop,
localize, InputFieldMorph, SliderMorph, ToggleMorph, ToggleButtonMorph,
BoxMorph, modules, radians, MorphicPreferences, getDocumentPositionOf,
-StageMorph, isNil*/
+StageMorph, isNil, SVG_Costume*/
// Global stuff ////////////////////////////////////////////////////////
-modules.paint = '2017-April-10';
+modules.paint = '2018-March-19';
// Declarations
@@ -217,7 +220,8 @@ PaintEditorMorph.prototype.buildToolbox = function () {
};
PaintEditorMorph.prototype.buildEdits = function () {
- var paper = this.paper;
+ var myself = this,
+ paper = this.paper;
this.edits.add(this.pushButton(
"undo",
@@ -228,6 +232,25 @@ PaintEditorMorph.prototype.buildEdits = function () {
"clear",
function () {paper.clearCanvas(); }
));
+ this.edits.add(this.pushButton(
+ 'Vector',
+ function () {
+ if (myself.paper.undoBuffer.length > 0) {
+ myself.ide.confirm(
+ 'This will erase your current drawing.\n' +
+ 'Are you sure you want to continue?',
+ 'Switch to vector editor?',
+ function () {
+ myself.switchToVector();
+ },
+ nop
+ );
+ } else {
+ myself.switchToVector();
+ }
+ }
+ ));
+
this.edits.fixLayout();
};
@@ -252,10 +275,17 @@ PaintEditorMorph.prototype.buildScaleBox = function () {
this.scaleBox.fixLayout();
};
-PaintEditorMorph.prototype.openIn = function (world, oldim, oldrc, callback) {
+PaintEditorMorph.prototype.openIn = function (
+ world,
+ oldim,
+ oldrc,
+ callback,
+ anIDE
+) {
// Open the editor in a world with an optional image to edit
this.oldim = oldim;
this.callback = callback || nop;
+ this.ide = anIDE;
this.processKeyUp = function () {
this.shift = false;
@@ -324,6 +354,20 @@ PaintEditorMorph.prototype.cancel = function () {
this.destroy();
};
+PaintEditorMorph.prototype.switchToVector = function () {
+ var myself = this;
+ this.object = new SVG_Costume(new Image(), '', new Point(0,0));
+ this.object.edit(
+ this.world(),
+ this.ide,
+ true,
+ this.oncancel,
+ function() {
+ myself.ide.currentSprite.changed();
+ }
+ );
+};
+
PaintEditorMorph.prototype.populatePropertiesMenu = function () {
var c = this.controls,
myself = this,
@@ -609,7 +653,10 @@ PaintCanvasMorph.prototype.calculateCanvasCenter = function(canvas) {
return null;
}
// Can't use canvasBounds.center(), it rounds down.
- return new Point((canvasBounds.origin.x + canvasBounds.corner.x) / 2, (canvasBounds.origin.y + canvasBounds.corner.y) / 2);
+ return new Point(
+ (canvasBounds.origin.x + canvasBounds.corner.x) / 2,
+ (canvasBounds.origin.y + canvasBounds.corner.y) / 2
+ );
};
// If we are in automaticCrosshairs mode, recalculate the rotationCenter.
@@ -772,7 +819,7 @@ PaintCanvasMorph.prototype.floodfill = function (sourcepoint) {
if (sourcecolor[0] === this.settings.primarycolor.r &&
sourcecolor[1] === this.settings.primarycolor.g &&
sourcecolor[2] === this.settings.primarycolor.b &&
- sourcecolor[3] === this.settings.primarycolor.a) {
+ sourcecolor[3] === this.settings.primarycolor.a * 255) {
return;
}
if (sourcecolor[3] === 0 && this.settings.primarycolor.a === 0) {
@@ -945,7 +992,8 @@ PaintCanvasMorph.prototype.mouseMove = function (pos) {
}
break;
case "crosshairs":
- // Disable automatic crosshairs: user has now chosen where they should be.
+ // Disable automatic crosshairs:
+ // user has now chosen where they should be.
this.automaticCrosshairs = false;
this.rotationCenter = relpos.copy();
this.drawcrosshair(mctx);
diff --git a/sketch.js b/sketch.js
new file mode 100644
index 00000000..b8794953
--- /dev/null
+++ b/sketch.js
@@ -0,0 +1,2102 @@
+/*
+ sketch.js
+
+ a vector paint editor for Snap!
+ inspired by the Snap bitmap paint editor and the Scratch vector editor.
+
+ written by Carles Paredes and Bernat Romagosa
+ Copyright (C) 2017 by Carles Paredes and Bernat Romagosa
+
+ This file is part of Snap!.
+
+ Snap! is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as
+ published by the Free Software Foundation, either version 3 of
+ the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see .
+
+ prerequisites:
+ --------------
+ needs paint.js, blocks.js, gui.js, threads.js, objects.js and morphic.js
+
+ toc
+ ---
+ the following list shows the order in which all constructors are
+ defined. Use this list to locate code in this document:
+
+ VectorShape
+ VectorRectangle
+ VectorLine
+ VectorEllipse
+ VectorPolygon
+ VectorSelection
+ VectorPaintEditorMorph
+ VectorPaintCanvasMorph
+
+ credits
+ -------
+ Carles Paredes wrote the first working prototype in 2015
+ Bernat Romagosa rewrote most of the code in 2017
+
+ revision history
+ -----------------
+ 2018, June 5 (Jens):
+ - fixed initial rotation center for an existing costume
+ - fixed initial rendering, so costumes can be re-opened after saving
+ 2018, June 20 (Jens):
+ - select primary color with right-click (in addition to shift-click)
+*/
+
+/*global Point, Object, Rectangle, AlignmentMorph, Morph, XML_Element, nop,
+PaintColorPickerMorph, Color, SliderMorph, InputFieldMorph, ToggleMorph,
+TextMorph, Image, newCanvas, PaintEditorMorph, StageMorph, Costume, isNil,
+localize, PaintCanvasMorph, detect, modules*/
+
+modules.sketch = '2018-June-20';
+
+// Declarations
+
+var VectorShape;
+var VectorRectangle;
+var VectorLine;
+var VectorEllipse;
+var VectorPolygon;
+var VectorSelection;
+var VectorPaintEditorMorph;
+var VectorPaintCanvasMorph;
+
+// VectorShape
+
+VectorShape.prototype = {};
+VectorShape.prototype.constructor = VectorShape;
+VectorShape.uber = Object.prototype;
+
+function VectorShape (borderWidth, borderColor, fillColor) {
+ this.init(borderWidth, borderColor, fillColor);
+}
+
+VectorShape.prototype.init = function (borderWidth, borderColor, fillColor) {
+ this.borderWidth = (borderColor && borderColor.a) ? borderWidth : 0;
+ this.borderColor = borderColor || new Color(0,0,0,0);
+ this.fillColor = fillColor || new Color(0,0,0,0);
+ this.image = newCanvas();
+ this.isPolygon = false;
+ this.isSelection = false;
+ this.isCrosshair = false;
+ this.origin = new Point();
+ this.destination = new Point();
+};
+
+VectorShape.prototype.toString = function () {
+ return 'a ' +
+ (this.constructor.name ||
+ this.constructor.toString().split(' ')[1].split('(')[0]);
+};
+
+VectorShape.prototype.asSVG = function (tagName) {
+ var svg = new XML_Element(tagName);
+
+ if (this.borderColor && this.borderColor.a) {
+ // if border is not transparent
+ svg.attributes.stroke = this.borderColor.toRGBstring();
+ svg.attributes['stroke-linejoin'] = 'miter';
+ svg.attributes['stroke-width'] = this.borderWidth;
+ } else {
+ svg.attributes.stroke = 'none';
+ }
+
+ if (this.fillColor && this.fillColor.a) {
+ // if fill color is not transparent
+ svg.attributes.fill = this.fillColor.toRGBstring();
+ } else {
+ svg.attributes.fill = 'none';
+ }
+
+ svg.attributes.prototype = this.constructor.name;
+
+ return svg;
+};
+
+VectorShape.prototype.imageURL = function () {
+ var svg = new XML_Element('svg'),
+ bounds = this.bounds();
+
+ svg.attributes.xmlns = 'http://www.w3.org/2000/svg';
+ svg.attributes.version = '1.1';
+ svg.attributes.preserveAspectRatio = 'xMinYMin meet';
+ svg.attributes.viewBox =
+ bounds.left() + ' ' + bounds.top() + ' ' +
+ (bounds.right() - bounds.left()) + ' ' +
+ (bounds.bottom() - bounds.top());
+ svg.attributes.width = (bounds.right() - bounds.left());
+ svg.attributes.height = (bounds.bottom() - bounds.top());
+
+ svg.children = [ this.asSVG() ];
+
+ return 'data:image/svg+xml;base64,' + svg;
+};
+
+VectorShape.prototype.copy = function (newShape) {
+ var shape =
+ newShape ||
+ new VectorShape(
+ this.borderWidth,
+ this.borderColor,
+ this.fillColor
+ );
+ shape.image.width = this.image.width;
+ shape.image.height = this.image.height;
+ shape.image.getContext('2d').drawImage(this.image,0,0);
+ return shape;
+};
+
+VectorShape.prototype.bounds = function() {
+ return new Rectangle(
+ Math.min(this.origin.x, this.destination.x) - (this.borderWidth / 2),
+ Math.min(this.origin.y, this.destination.y) - (this.borderWidth / 2),
+ Math.max(this.origin.x, this.destination.x) + (this.borderWidth / 2),
+ Math.max(this.origin.y, this.destination.y) + (this.borderWidth / 2)
+ );
+};
+
+VectorShape.prototype.containsPoint = function (aPoint) {
+ return this.bounds().containsPoint(aPoint);
+};
+
+VectorShape.prototype.update = function (newPoint, constrain) {
+ this.destination = constrain ? this.constraintPoint(newPoint) : newPoint;
+};
+
+VectorShape.prototype.constraintPoint = function (aPoint) {
+ var newPoint = aPoint,
+ delta = newPoint.subtract(this.origin),
+ constraintPos = new Point(
+ Math.max(
+ Math.abs(delta.x),
+ Math.abs(delta.y)) * (delta.x / Math.abs(delta.x)
+ ),
+ Math.max(
+ Math.abs(delta.x),
+ Math.abs(delta.y)) * (delta.y / Math.abs(delta.y)
+ )
+ );
+
+ newPoint = this.origin.add(constraintPos);
+ return newPoint;
+};
+
+VectorShape.prototype.setColor = function (color, isSecondary) {
+ if (isSecondary) {
+ this.borderColor = color;
+ } else {
+ this.fillColor = color;
+ }
+};
+
+VectorShape.prototype.setBorderWidth = function (width) {
+ if (this.borderColor && this.borderColor.a) {
+ this.borderWidth = width;
+ }
+};
+
+VectorShape.prototype.moveBy = function (delta) {
+ this.origin = this.origin.add(delta);
+ this.destination = this.destination.add(delta);
+};
+
+VectorShape.prototype.resizeBy = function (delta, origin) {
+ this.origin = this.origin.subtract(origin).multiplyBy(delta).add(origin);
+ this.destination = this.destination.subtract(origin).multiplyBy(delta).add(
+ origin
+ );
+};
+
+// Generic drawOn method that stamps the shape SVG into its image canvas
+// with position relative to aCanvasMorph's and asks it to redraw itself
+VectorShape.prototype.drawOn = function (aCanvasMorph) {
+ var myself = this,
+ origin = this.bounds().origin.subtract(aCanvasMorph.position()),
+ img = new Image();
+
+ this.image = newCanvas(aCanvasMorph.extent());
+
+ img.onload = function () {
+ myself.image.getContext('2d').drawImage(img, origin.x, origin.y);
+ aCanvasMorph.redraw = true;
+ };
+
+ img.src = this.imageURL();
+};
+
+// VectorRectangle
+
+VectorRectangle.prototype = new VectorShape();
+VectorRectangle.prototype.constructor = VectorRectangle;
+VectorRectangle.uber = VectorShape.prototype;
+
+function VectorRectangle (
+ borderWidth,
+ borderColor,
+ fillColor,
+ origin,
+ destination
+) {
+ VectorRectangle.uber.init.call(this, borderWidth, borderColor, fillColor);
+ this.init(origin, destination);
+}
+
+VectorRectangle.prototype.init = function (origin, destination) {
+ this.origin = origin;
+ this.destination = destination;
+};
+
+VectorRectangle.fromSVG = function (svg) {
+ var attributes = svg.attributes;
+
+ return new VectorRectangle(
+ parseInt(attributes['stroke-width']), // borderWidth
+ attributes.stroke === 'none' ? null :
+ Color.fromString(attributes.stroke), // borderColor
+ attributes.fill === 'none' ? null :
+ Color.fromString(attributes.fill), // fillColor
+ new Point( // origin
+ parseInt(attributes.x), parseInt(attributes.y)
+ ),
+ new Point( // destination
+ parseInt(attributes.x) + parseInt(attributes.width),
+ parseInt(attributes.y) + parseInt(attributes.height)
+ )
+ );
+};
+
+VectorRectangle.prototype.copy = function () {
+ var newRectangle = new VectorRectangle(
+ this.borderWidth,
+ this.borderColor,
+ this.fillColor,
+ this.origin.copy(),
+ this.destination.copy()
+ );
+ return VectorRectangle.uber.copy.call(this, newRectangle);
+};
+
+VectorRectangle.prototype.toString = function () {
+ return VectorRectangle.uber.toString.call(this) + this.bounds().toString();
+};
+
+VectorRectangle.prototype.width = function () {
+ return Math.abs(this.origin.x - this.destination.x);
+};
+
+VectorRectangle.prototype.height = function () {
+ return Math.abs(this.origin.y - this.destination.y);
+};
+
+VectorRectangle.prototype.x = function () {
+ return Math.min(this.origin.x, this.destination.x);
+};
+
+VectorRectangle.prototype.y = function () {
+ return Math.min(this.origin.y, this.destination.y);
+};
+
+VectorRectangle.prototype.asSVG = function () {
+ var svg = VectorRectangle.uber.asSVG.call(this, 'rect');
+ svg.attributes.width = this.width();
+ svg.attributes.height = this.height();
+ svg.attributes.x = this.x();
+ svg.attributes.y = this.y();
+ return svg;
+};
+
+VectorRectangle.prototype.drawOn = function (aCanvasMorph) {
+ var context,
+ canvasPosition = aCanvasMorph.position();
+
+ this.image = newCanvas(aCanvasMorph.extent());
+
+ context = this.image.getContext('2d');
+
+ context.beginPath();
+ context.rect(
+ this.x() - canvasPosition.x,
+ this.y() - canvasPosition.y,
+ this.width(),
+ this.height()
+ );
+
+ if (this.fillColor.a > 0) {
+ context.fillStyle = this.fillColor.toRGBstring();
+ context.fill();
+ }
+
+ if (this.borderColor.a > 0) {
+ context.lineWidth = this.borderWidth;
+ context.strokeStyle = this.borderColor.toRGBstring();
+ context.stroke();
+ }
+
+ aCanvasMorph.redraw = true;
+};
+
+// VectorLine
+
+VectorLine.prototype = new VectorShape();
+VectorLine.prototype.constructor = VectorLine;
+VectorLine.uber = VectorShape.prototype;
+
+function VectorLine (borderWidth, borderColor, origin, destination) {
+ VectorLine.uber.init.call(this, borderWidth, borderColor);
+ this.init(origin, destination);
+}
+
+VectorLine.prototype.init = function(origin, destination) {
+ this.origin = origin;
+ this.destination = destination;
+};
+
+VectorLine.fromSVG = function (svg) {
+ var attributes = svg.attributes;
+ return new VectorLine(
+ parseInt(attributes['stroke-width']), // borderWidth
+ Color.fromString(attributes.stroke), // borderColor
+ new Point(parseInt(attributes.x1), parseInt(attributes.y1)), // origin
+ new Point(parseInt(attributes.x2), parseInt(attributes.y2)) // dest.
+ );
+};
+
+VectorLine.prototype.copy = function () {
+ var newLine = new VectorLine(
+ this.borderWidth,
+ this.borderColor.copy(),
+ this.origin.copy(),
+ this.destination.copy()
+ );
+ return VectorLine.uber.copy.call(this, newLine);
+};
+
+VectorLine.prototype.containsPoint = function (aPoint) {
+ var lineLength = this.origin.distanceTo(this.destination),
+ distancesSum = aPoint.distanceTo(this.origin) +
+ aPoint.distanceTo(this.destination);
+
+ return Math.abs(lineLength - distancesSum) <=
+ Math.sqrt(this.borderWidth / 2) / 2;
+};
+
+VectorLine.prototype.constraintPoint = function (aPoint) {
+ var angle,
+ newPoint = aPoint;
+
+ angle = newPoint.subtract(this.origin).abs().degrees();
+ if (angle < 22.5) {
+ // horizontal line
+ newPoint.y = this.origin.y;
+ } else if (angle > 67.5) {
+ // vertical line
+ newPoint.x = this.origin.x;
+ } else {
+ // line at 45º
+ newPoint = VectorLine.uber.constraintPoint.call(this, aPoint);
+ }
+
+ return newPoint;
+};
+
+VectorLine.prototype.setColor = function (color, isSecondary) {
+ VectorLine.uber.setColor.call(this, color, !isSecondary);
+};
+
+VectorLine.prototype.toString = function () {
+ return VectorLine.uber.toString.call(this) + this.bounds().toString();
+};
+
+VectorLine.prototype.asSVG = function() {
+ var svg = VectorLine.uber.asSVG.call(this, 'line');
+ svg.attributes.x1 = this.origin.x;
+ svg.attributes.y1 = this.origin.y;
+ svg.attributes.x2 = this.destination.x;
+ svg.attributes.y2 = this.destination.y;
+ return svg;
+};
+
+VectorLine.prototype.drawOn = function (aCanvasMorph) {
+ var context,
+ origin = this.origin.subtract(aCanvasMorph.position()),
+ destination = this.destination.subtract(aCanvasMorph.position());
+
+ this.image = newCanvas(aCanvasMorph.extent());
+
+ context = this.image.getContext('2d');
+
+ context.beginPath();
+
+ if (this.borderColor.a > 0) {
+ context.lineWidth = this.borderWidth;
+ context.strokeStyle = this.borderColor.toRGBstring();
+ context.moveTo(origin.x, origin.y);
+ context.lineTo(destination.x, destination.y);
+ context.stroke();
+ }
+
+ aCanvasMorph.redraw = true;
+};
+
+// VectorEllipse
+
+VectorEllipse.prototype = new VectorShape();
+VectorEllipse.prototype.constructor = VectorEllipse;
+VectorEllipse.uber = VectorShape.prototype;
+
+function VectorEllipse (
+ borderWidth,
+ borderColor,
+ fillColor,
+ origin,
+ destination)
+{
+ VectorEllipse.uber.init.call(this, borderWidth, borderColor, fillColor);
+ this.init(origin, destination);
+}
+
+VectorEllipse.prototype.init = function (origin, destination) {
+ this.origin = origin;
+ this.destination = destination;
+};
+
+VectorEllipse.fromSVG = function (svg) {
+ var attributes = svg.attributes;
+
+ return new VectorEllipse(
+ parseInt(attributes['stroke-width']), // borderWidth
+ attributes.stroke === 'none' ? null :
+ Color.fromString(attributes.stroke), // borderColor
+ attributes.fill === 'none' ? null :
+ Color.fromString(attributes.fill), // fillColor
+ new Point(parseInt(attributes.cx), parseInt(attributes.cy)), // origin
+ new Point(
+ parseInt(attributes.cx) + parseInt(attributes.rx),
+ parseInt(attributes.cy) + parseInt(attributes.ry)) // destination
+ );
+};
+
+VectorEllipse.prototype.copy = function () {
+ var newEllipse = new VectorEllipse(
+ this.borderWidth,
+ this.borderColor,
+ this.fillColor,
+ this.origin.copy(),
+ this.destination.copy()
+ );
+ return VectorEllipse.uber.copy.call(this, newEllipse);
+};
+
+VectorEllipse.prototype.hRadius = function () {
+ return Math.abs(this.destination.x - this.origin.x);
+};
+
+VectorEllipse.prototype.vRadius = function () {
+ return Math.abs(this.destination.y - this.origin.y);
+};
+
+VectorEllipse.prototype.toString = function () {
+ return VectorEllipse.uber.toString.call(this) +
+ ' center: ' + this.origin.toString() +
+ ' radii: ' + this.hRadius().toString() + ',' +
+ this.vRadius().toString();
+};
+
+VectorEllipse.prototype.bounds = function () {
+ var hRadius = this.hRadius(),
+ vRadius = this.vRadius();
+
+ return new Rectangle(
+ this.origin.x - hRadius - (this.borderWidth / 2),
+ this.origin.y - vRadius - (this.borderWidth / 2),
+ this.origin.x + hRadius + (this.borderWidth / 2),
+ this.origin.y + vRadius + (this.borderWidth / 2)
+ );
+};
+
+VectorEllipse.prototype.containsPoint = function (aPoint) {
+ return (
+ Math.pow(aPoint.x - this.origin.x, 2) /
+ Math.pow(this.hRadius() + this.borderWidth / 2, 2)
+ +
+ Math.pow(aPoint.y - this.origin.y, 2) /
+ Math.pow(this.vRadius() + this.borderWidth / 2, 2)
+ ) < 1;
+};
+
+VectorEllipse.prototype.asSVG = function () {
+ var svg = VectorEllipse.uber.asSVG.call(this, 'ellipse');
+ svg.attributes.cx = this.origin.x;
+ svg.attributes.cy = this.origin.y;
+ svg.attributes.rx = this.hRadius();
+ svg.attributes.ry = this.vRadius();
+ return svg;
+};
+
+VectorEllipse.prototype.drawOn = function (aCanvasMorph) {
+ var context,
+ canvasPosition = aCanvasMorph.position();
+
+ this.image = newCanvas(aCanvasMorph.extent());
+ context = this.image.getContext('2d');
+ context.beginPath();
+ context.ellipse(
+ this.origin.x - canvasPosition.x,
+ this.origin.y - canvasPosition.y,
+ this.hRadius(),
+ this.vRadius(),
+ 0,
+ 0,
+ 2 * Math.PI,
+ true);
+
+ if (this.fillColor && this.fillColor.a > 0) {
+ context.fillStyle = this.fillColor.toRGBstring();
+ context.fill();
+ }
+
+ if (this.borderColor.a > 0) {
+ context.lineWidth = this.borderWidth;
+ context.strokeStyle = this.borderColor.toRGBstring();
+ context.stroke();
+ }
+
+ aCanvasMorph.redraw = true;
+};
+
+
+// VectorPolygon
+
+VectorPolygon.prototype = new VectorShape();
+VectorPolygon.prototype.constructor = VectorPolygon;
+VectorPolygon.uber = VectorShape.prototype;
+
+function VectorPolygon (
+ borderWidth,
+ borderColor,
+ fillColor,
+ points,
+ isClosed,
+ isFreeHand
+) {
+ VectorPolygon.uber.init.call(this, borderWidth, borderColor, fillColor);
+ this.init(points, isClosed, isFreeHand);
+}
+
+VectorPolygon.prototype.init = function (points, isClosed, isFreeHand) {
+ this.points = points || [ ];
+ this.isClosed = isClosed;
+ this.isFreeHand = isFreeHand;
+ this.isPolygon = true;
+};
+
+VectorPolygon.fromSVG = function (svg) {
+ var attributes = svg.attributes,
+ points = attributes.d.slice(1).split(/L */).map(
+ function (pointString) {
+ var pointArray = pointString.split(' ');
+ return new Point(
+ parseInt(pointArray[0]),
+ parseInt(pointArray[1]));
+ }
+ );
+
+ return new VectorPolygon(
+ parseInt(attributes['stroke-width']), // borderWidth
+ attributes.stroke === 'none' ? null :
+ Color.fromString(attributes.stroke), // borderColor
+ attributes.fill === 'none' ? null :
+ Color.fromString(attributes.fill), // fillColor
+ points, // points
+ points[0].eq(points[points.length - 1]), // isClosed
+ false // isFreeHand, does only matter when drawing it
+ );
+};
+
+VectorPolygon.prototype.copy = function () {
+ var newPolygon = new VectorPolygon(
+ this.borderWidth,
+ this.borderColor,
+ this.fillColor,
+ this.points.map(function (point) { return point.copy(); }),
+ this.isClosed,
+ this.isFreeHand
+ );
+ return VectorPolygon.uber.copy.call(this, newPolygon);
+};
+
+VectorPolygon.prototype.toString = function () {
+ return VectorPolygon.uber.toString.call(this) + this.points;
+};
+
+VectorPolygon.prototype.bounds = function () {
+ var left = this.points[0].x,
+ top = this.points[0].y,
+ right = this.points[this.points.length - 1].x,
+ bottom = this.points[this.points.length - 1].y;
+
+ this.points.forEach(function (point) {
+ left = Math.min(left, point.x);
+ top = Math.min(top, point.y);
+ right = Math.max(right, point.x);
+ bottom = Math.max(bottom, point.y);
+ });
+
+ return new Rectangle(
+ left - (this.borderWidth / 2),
+ top - (this.borderWidth / 2),
+ right + (this.borderWidth / 2),
+ bottom + (this.borderWidth / 2)
+ );
+};
+
+VectorPolygon.prototype.containsPoint = function (aPoint) {
+ var myself = this,
+ pointCount = this.points.length,
+ inside = false,
+ i, j;
+
+ for (i = 1; i < pointCount; i += 1) {
+ if (pointIsBetween(this.points[i - 1], this.points[i])) {
+ return true;
+ }
+ }
+
+ if (this.isClosed) {
+ for (i = 0, j = pointCount - 1; i < pointCount; i += 1) {
+ if (
+ (this.points[i].y > aPoint.y) !==
+ (this.points[j].y > aPoint.y) &&
+ aPoint.x <
+ (this.points[j].x - this.points[i].x) *
+ (aPoint.y - this.points[i].y) /
+ (this.points[j].y - this.points[i].y) + this.points[i].x
+ ) {
+ inside = !inside;
+ }
+ j = i;
+ }
+ return inside;
+ }
+
+ function pointIsBetween (a, b) {
+ return Math.abs(a.distanceTo(b) -
+ (aPoint.distanceTo(a) + aPoint.distanceTo(b))) <=
+ Math.sqrt(myself.borderWidth / 2) / 2;
+ }
+
+ return false;
+};
+
+VectorPolygon.prototype.update = function (newPoint, constrain) {
+ if (this.isFreeHand || this.points.length === 1) {
+ this.points.push(newPoint);
+ } else if (!this.isFreeHand) {
+ if (constrain) {
+ // we reuse origin to store the previous point and perform the
+ // constraint calculations as if we were drawing a single line
+ this.origin = this.points[this.points.length - 2];
+ newPoint = VectorLine.prototype.constraintPoint.call(
+ this,
+ newPoint
+ );
+ }
+ this.points[this.points.length - 1] = newPoint;
+ }
+};
+
+VectorPolygon.prototype.setColor = function (color, isSecondary) {
+ VectorPolygon.uber.setColor.call(
+ this,
+ color,
+ !this.isClosed || isSecondary
+ );
+};
+
+VectorPolygon.prototype.moveBy = function (delta) {
+ this.points.forEach(function (eachPoint) {
+ eachPoint.x += delta.x;
+ eachPoint.y += delta.y;
+ });
+};
+
+VectorPolygon.prototype.resizeBy = function (delta, origin) {
+ this.points = this.points.map(function (point) {
+ return point.subtract(origin).multiplyBy(delta).add(origin);
+ });
+};
+
+VectorPolygon.prototype.close = function () {
+ if (this.isClosed) {
+ this.points.push(this.points[0].copy());
+ }
+};
+
+VectorPolygon.prototype.asSVG = function () {
+ var svg = VectorPolygon.uber.asSVG.call(this, 'path');
+
+ svg.attributes['stroke-linejoin'] = 'round';
+ svg.attributes['stroke-linecap'] = 'round';
+
+ // M stands for MoveTo and defines the starting point
+ svg.attributes.d = 'M' + this.points[0].x + ' ' + this.points[0].y;
+
+ // L stands for LineTo and defines the rest of the points
+ this.points.slice(1).forEach(function (point) {
+ svg.attributes.d += ' L ' + point.x + ' ' + point.y;
+ });
+
+ return svg;
+};
+
+VectorPolygon.prototype.drawOn = function (aCanvasMorph) {
+ var context,
+ points =
+ this.points.map(
+ function (eachPoint) {
+ return eachPoint.subtract(aCanvasMorph.position());
+ }
+ );
+
+ this.image = newCanvas(aCanvasMorph.extent());
+ context = this.image.getContext('2d');
+
+ context.lineCap = 'round';
+ context.lineJoin = 'round';
+
+ context.beginPath();
+ context.moveTo(points[0].x, points[0].y);
+
+ points.slice(1).forEach(function (point) {
+ context.lineTo(point.x, point.y);
+ });
+
+ if (this.fillColor && this.fillColor.a > 0) {
+ context.fillStyle = this.fillColor.toRGBstring();
+ context.fill();
+ }
+
+ if (this.borderColor.a > 0) {
+ context.lineWidth = this.borderWidth;
+ context.strokeStyle = this.borderColor.toRGBstring();
+ context.stroke();
+ } else if (this.points.length === 2) {
+ // This is a polygon in construction, we should at least draw
+ // a thin line between its first two points
+ context.lineWidth = 1;
+ context.strokeStyle = this.fillColor.toRGBstring();
+ context.stroke();
+ }
+
+ aCanvasMorph.redraw = true;
+};
+
+
+// VectorSelection
+
+VectorSelection.prototype = new VectorRectangle();
+VectorSelection.prototype.constructor = VectorSelection;
+VectorSelection.uber = VectorRectangle.prototype;
+
+function VectorSelection (origin, destination) {
+ VectorRectangle.uber.init.call(
+ this,
+ 1, // borderWidth
+ new Color(0, 0, 0, 255), // borderColor
+ null // fillColor
+ );
+ this.init(origin, destination);
+}
+
+VectorSelection.prototype.init = function (origin, destination) {
+ VectorSelection.uber.init.call(this, origin, destination);
+ this.isSelection = true;
+ this.threshold = 5;
+};
+
+VectorSelection.prototype.corners = function () {
+ var bounds = this.bounds();
+ return [
+ bounds.topLeft(),
+ bounds.topRight(),
+ bounds.bottomLeft(),
+ bounds.bottomRight()
+ ];
+};
+
+VectorSelection.prototype.cornerAt = function (aPoint) {
+ var threshold = this.threshold;
+
+ return this.corners().find(function(corner) {
+ return aPoint.distanceTo(corner) <= threshold;
+ });
+};
+
+VectorSelection.prototype.cornerOppositeTo = function (aPoint) {
+ return this.corners().reduce(function(a, b) {
+ return (aPoint.distanceTo(a) > aPoint.distanceTo(b)) ? a : b;
+ });
+};
+
+VectorSelection.prototype.drawOn = function (aCanvasMorph) {
+ var context,
+ bounds = this.bounds(),
+ canvasPosition = aCanvasMorph.position(),
+ origin = bounds.origin.subtract(canvasPosition),
+ circleRadius = this.threshold;
+
+ this.image = newCanvas(aCanvasMorph.extent());
+
+ context = this.image.getContext('2d');
+
+ context.rect(origin.x, origin.y, this.width(), this.height());
+ context.setLineDash([5]);
+ context.stroke();
+
+ context.setLineDash([]);
+
+ function drawCircle (x, y) {
+ context.beginPath();
+ context.arc(
+ x - canvasPosition.x,
+ y - canvasPosition.y,
+ circleRadius,
+ 0,
+ 2 * Math.PI
+ );
+ context.stroke();
+ }
+
+ drawCircle(bounds.left(), bounds.top());
+ drawCircle(bounds.left(), bounds.bottom());
+ drawCircle(bounds.right(), bounds.top());
+ drawCircle(bounds.right(), bounds.bottom());
+
+ aCanvasMorph.redraw = true;
+};
+
+// Crosshair
+// For convenience, we'll inherit from VectorShape
+
+Crosshair.prototype = VectorShape;
+Crosshair.prototype.constructor = Crosshair;
+Crosshair.uber = VectorShape.prototype;
+
+function Crosshair (center, paper) {
+ this.init(center, paper);
+}
+
+Crosshair.prototype.init = function (center, paper) {
+ this.center = center;
+ this.paper = paper;
+ this.image = newCanvas();
+ this.isCrosshair = true;
+};
+
+Crosshair.prototype.update = function (newPosition) {
+ this.center = newPosition.subtract(this.paper.position());
+};
+
+Crosshair.prototype.moveBy = function (delta) {
+ this.center = this.center.add(delta);
+};
+
+Crosshair.prototype.drawOn = function (aCanvasMorph) {
+ this.image = newCanvas(aCanvasMorph.extent());
+ aCanvasMorph.rotationCenter = this.center.copy();
+ aCanvasMorph.drawcrosshair(this.image.getContext('2d'));
+ aCanvasMorph.redraw = true;
+};
+
+/////////// VectorPaintEditorMorph //////////////////////////
+
+VectorPaintEditorMorph.prototype = new PaintEditorMorph();
+VectorPaintEditorMorph.prototype.constructor = VectorPaintEditorMorph;
+VectorPaintEditorMorph.uber = PaintEditorMorph.prototype;
+
+function VectorPaintEditorMorph() {
+ this.init();
+}
+
+VectorPaintEditorMorph.prototype.init = function () {
+ // additional properties:
+ this.paper = null; // paint canvas
+ this.shapes = [];
+ this.selection = []; // currently selected objects
+ this.selecting = false;
+ this.originalSelection = null; // see VectorPaintEditorMorph >> dragSelection
+ this.moving = false;
+ this.resizing = false;
+ this.lastDragPosition = null;
+ this.history = []; // shapes history, for undo purposes
+ this.clipboard = []; // copied objects ready to be pasted
+ this.currentShape = null; // object being currently edited
+
+ VectorPaintEditorMorph.uber.init.call(this);
+
+ this.labelString = 'Vector Paint Editor';
+ this.createLabel();
+ this.fixLayout();
+};
+
+VectorPaintEditorMorph.prototype.buildEdits = function () {
+ var myself = this;
+
+ this.edits.add(
+ this.pushButton(
+ 'undo',
+ function () {
+ myself.undo();
+ }
+ )
+ );
+
+ this.edits.add(
+ this.pushButton(
+ 'clear',
+ function () {
+ myself.paper.clearCanvas();
+ }
+ )
+ );
+
+ this.edits.add(
+ this.pushButton(
+ 'Bitmap',
+ function () {
+ if (myself.shapes.length > 0) {
+ myself.ide.confirm(
+ 'This will convert your vector objects into\n' +
+ 'bitmaps, and you will not be able to convert\n' +
+ 'them back into vector drawings.\n' +
+ 'Are you sure you want to continue?',
+ 'Convert to bitmap?',
+ function () {
+ myself.convertToBitmap();
+ },
+ nop
+ );
+ } else {
+ myself.convertToBitmap();
+ }
+ }
+ )
+ );
+
+ this.edits.fixLayout();
+};
+
+VectorPaintEditorMorph.prototype.convertToBitmap = function () {
+ var canvas = newCanvas(StageMorph.prototype.dimensions);
+
+ this.object = new Costume();
+
+ this.shapes.forEach(function(each) {
+ canvas.getContext('2d').drawImage(each.image, 0, 0);
+ });
+
+ this.object.rotationCenter = this.paper.rotationCenter.copy();
+ this.object.contents = canvas;
+ this.object.edit(
+ this.world(),
+ this.ide,
+ false,
+ this.oncancel
+ );
+
+ this.destroy();
+};
+
+VectorPaintEditorMorph.prototype.buildScaleBox = function () {
+ var myself = this;
+ ['Top', 'Bottom', 'Up', 'Down'].forEach(function (label) {
+ myself.scaleBox.add(
+ myself.pushButton(
+ label,
+ function () {
+ myself.changeSelectionLayer(label.toLowerCase());
+ }
+ )
+ );
+ });
+
+ this.scaleBox.fixLayout();
+};
+
+VectorPaintEditorMorph.prototype.openIn = function (
+ world,
+ oldim,
+ oldrc,
+ callback,
+ anIDE,
+ shapes
+) {
+ var myself = this,
+ isEmpty = isNil(shapes) || shapes.length === 0;
+
+ VectorPaintEditorMorph.uber.openIn.call(this, world, null, oldrc, callback);
+ this.ide = anIDE;
+ this.paper.drawNew();
+ this.paper.changed();
+
+ // make sure shapes are initialized and can be rendered
+ shapes.forEach(function (shape) {
+ shape.drawOn(myself.paper);
+ });
+ // copy the shapes for editing and re-render the copies
+ this.shapes = shapes.map(function (eachShape) {
+ return eachShape.copy();
+ });
+ this.shapes.forEach(function (shape) {
+ shape.drawOn(myself.paper);
+ });
+ // init the rotation center, if any
+ if (oldrc && !isEmpty) {
+ this.paper.automaticCrosshairs = false;
+ this.paper.rotationCenter = this.getBounds(this.shapes).origin.subtract(
+ this.paper.bounds.origin
+ ).add(oldrc);
+ } else {
+ this.paper.automaticCrosshairs = true;
+ }
+
+ this.updateHistory();
+
+ this.processKeyUp = function () {
+ this.shift = false;
+ this.ctrl = false;
+ this.propertiesControls.constrain.refresh();
+ };
+
+ this.processKeyDown = function (event) {
+ var myself = this,
+ pos;
+
+ this.shift = event.shiftKey;
+ this.ctrl = event.ctrlKey;
+
+ switch (this.world().currentKey) {
+ /* Del and backspace keys */
+ case 46:
+ case 8:
+ this.sortSelection();
+ this.selection.slice().reverse().forEach(function (shape) {
+ myself.shapes.splice(myself.shapes.indexOf(shape), 1);
+ });
+ this.clearSelection();
+ this.updateHistory();
+ break;
+ /* Enter key */
+ case 13:
+ if (this.currentShape && this.currentShape.isPolygon) {
+ this.currentShape.close();
+ this.currentShape.drawOn(this.paper);
+ this.shapes.push(this.currentShape);
+ this.currentShape = null;
+ this.updateHistory();
+ }
+ break;
+ /* Page Up key */
+ case 33:
+ this.changeSelectionLayer('up');
+ break;
+ /* Page Down key */
+ case 34:
+ this.changeSelectionLayer('down');
+ break;
+ /* End key */
+ case 35:
+ this.changeSelectionLayer('bottom');
+ break;
+
+ /* Home key */
+ case 36:
+ this.changeSelectionLayer('top');
+ break;
+ case 90:
+ /* Ctrl + Z */
+ if (this.ctrl) {
+ this.undo();
+ }
+ break;
+ case 67:
+ /* Ctrl + C */
+ if (this.ctrl && this.selection.length) {
+ this.clipboard =
+ this.selection.map(function (each) {
+ return each.copy();
+ }
+ );
+ }
+ break;
+ case 86:
+ /* Ctrl + V */
+ pos = this.world().hand.position();
+ if (this.ctrl && this.paper.bounds.containsPoint(pos)) {
+ this.paper.pasteAt(pos);
+ this.updateHistory();
+ }
+ break;
+ case 65:
+ /* Ctrl + A */
+ if (this.ctrl) {
+ this.paper.currentTool = 'selection';
+ this.paper.toolChanged('selection');
+ this.refreshToolButtons();
+ this.paper.selectShapes(this.shapes);
+ }
+ break;
+ case 27:
+ /* Escape key */
+ this.clearSelection();
+ break;
+ case 37:
+ /* Left arrow */
+ this.moveSelectionBy(new Point(-1, 0));
+ this.updateHistory();
+ break;
+ case 38:
+ /* Up arrow */
+ this.moveSelectionBy(new Point(0, -1));
+ this.updateHistory();
+ break;
+ case 39:
+ /* Right arrow */
+ this.moveSelectionBy(new Point(1, 0));
+ this.updateHistory();
+ break;
+ case 40:
+ /* Down arrow */
+ this.moveSelectionBy(new Point(0, 1));
+ this.updateHistory();
+ break;
+ default:
+ nop();
+ }
+ this.propertiesControls.constrain.refresh();
+ this.drawNew();
+ };
+};
+
+VectorPaintEditorMorph.prototype.buildContents = function() {
+ var myself = this;
+
+ VectorPaintEditorMorph.uber.buildContents.call(this);
+
+ this.paper.destroy();
+ this.paper = new VectorPaintCanvasMorph(myself.shift);
+ this.paper.setExtent(StageMorph.prototype.dimensions);
+ this.body.add(this.paper);
+
+ this.refreshToolButtons();
+ this.fixLayout();
+ this.drawNew();
+};
+
+VectorPaintEditorMorph.prototype.buildToolbox = function () {
+ var tools = {
+ brush:
+ 'Paintbrush tool\n(free draw)',
+ rectangle:
+ 'Rectangle\n(shift: square)',
+ ellipse:
+ 'Ellipse\n(shift: circle)',
+ selection:
+ 'Selection tool',
+ crosshairs:
+ 'Set the rotation center',
+ line:
+ 'Line tool\n(shift: constrain to 45º)',
+ closedBrush:
+ 'Closed brush\n(free draw)',
+ polygon:
+ 'Polygon',
+ paintbucket:
+ 'Paint a shape\n(shift: secondary color)',
+ pipette:
+ 'Pipette tool\n(pick a color from anywhere\nshift: secondary color)'
+ },
+ myself = this,
+ left = this.toolbox.left(),
+ top = this.toolbox.top(),
+ padding = 2,
+ inset = 5,
+ x = 0,
+ y = 0;
+
+ Object.keys(tools).forEach(function (toolName) {
+ var button = myself.toolButton(toolName, tools[toolName]);
+ button.setPosition(new Point(
+ left + x,
+ top + y
+ ));
+ x += button.width() + padding;
+ if (toolName === 'crosshairs') { /* this tool marks the newline */
+ x = 0;
+ y += button.height() + padding;
+ myself.paper.drawcrosshair();
+ }
+ myself.toolbox[toolName] = button;
+ myself.toolbox.add(button);
+ });
+
+ this.toolbox.bounds = this.toolbox.fullBounds().expandBy(inset * 2);
+ this.toolbox.drawNew();
+};
+
+// TODO :'(
+VectorPaintEditorMorph.prototype.populatePropertiesMenu = function () {
+ var c = this.controls,
+ myself = this,
+ pc = this.propertiesControls,
+ alpen = new AlignmentMorph("row", this.padding),
+ alignColor = new AlignmentMorph("row", this.padding);
+
+ pc.primaryColorViewer = new Morph();
+ pc.primaryColorViewer.setExtent(new Point(85, 15)); // 40 = height primary & brush size
+ pc.primaryColorViewer.color = new Color(0, 0, 0);
+ pc.secondaryColorViewer = new Morph();
+ pc.secondaryColorViewer.setExtent(new Point(85, 15)); // 20 = height secondaryColor box
+ pc.secondaryColorViewer.color = new Color(0, 0, 0);
+
+ pc.colorpicker = new PaintColorPickerMorph(
+ new Point(180, 100),
+ function (color, isSecondary) {
+ myself.selectColor(color, isSecondary);
+ }
+ );
+
+ // allow right-click on the color picker to select the secondary color
+ pc.colorpicker.mouseDownRight = function (pos) {
+ if ((pos.subtract(this.position()).x > this.width() * 2 / 3) &&
+ (pos.subtract(this.position()).y > this.height() - 10)) {
+ this.action("transparent", true);
+ } else {
+ this.action(this.getPixelColor(pos), true);
+ }
+ };
+
+ pc.colorpicker.action(new Color(0, 0, 0));
+ pc.colorpicker.action('transparent', true); // secondary color
+
+ pc.penSizeSlider = new SliderMorph(0, 20, 5, 5);
+ pc.penSizeSlider.orientation = "horizontal";
+ pc.penSizeSlider.setHeight(15);
+ pc.penSizeSlider.setWidth(150);
+ pc.penSizeSlider.action = function (num) {
+ if (pc.penSizeField) {
+ pc.penSizeField.setContents(num);
+ }
+ myself.paper.settings.lineWidth = num;
+ myself.selection.forEach(function (shape) {
+ shape.setBorderWidth(num);
+ shape.drawOn(myself.paper);
+ myself.paper.updateSelection();
+ });
+ myself.updateHistory();
+ };
+ pc.penSizeField = new InputFieldMorph("3", true, null, false);
+ pc.penSizeField.contents().minWidth = 20;
+ pc.penSizeField.setWidth(25);
+ pc.penSizeField.accept = function (num) {
+ var val = parseFloat(pc.penSizeField.getValue());
+ pc.penSizeSlider.value = val;
+ pc.penSizeSlider.drawNew();
+ pc.penSizeSlider.updateValue();
+ this.setContents(val);
+ myself.paper.settings.lineWidth = val;
+ this.world().keyboardReceiver = myself;
+ //pc.colorpicker.action(myself.paper.settings.primaryColor);
+ myself.selection.forEach(function (shape) {
+ shape.setBorderWidth(num);
+ shape.drawOn(myself.paper);
+ myself.paper.updateSelection();
+ });
+ myself.updateHistory();
+ };
+ alpen.add(pc.penSizeSlider);
+ alpen.add(pc.penSizeField);
+ alpen.color = myself.color;
+ alpen.fixLayout();
+ pc.penSizeField.drawNew();
+
+ pc.constrain = new ToggleMorph(
+ "checkbox",
+ this,
+ function () { myself.shift = !myself.shift; },
+ "Constrain proportions of shapes?\n(you can also hold shift)",
+ function () { return myself.shift; }
+ );
+
+ alignColor.add(pc.primaryColorViewer);
+ alignColor.add(pc.secondaryColorViewer);
+ alignColor.fixLayout();
+
+ c.add(pc.colorpicker);
+ c.add(new TextMorph(localize('Primary color Secondary color')));
+ c.add(alignColor);
+ c.add(new TextMorph(localize('Brush size')));
+ c.add(alpen);
+ c.add(pc.constrain);
+};
+
+VectorPaintEditorMorph.prototype.selectColor = function (color, secondary) {
+ var myself = this,
+ isSecondary = secondary || this.paper.isShiftPressed(),
+ propertyName = (isSecondary ? 'secondary' : 'primary') + 'Color',
+ ni = newCanvas(
+ this.propertiesControls[propertyName + 'Viewer'].extent()
+ ),
+ ctx = ni.getContext('2d'),
+ i, j;
+
+ this.paper.settings[(propertyName)] = color;
+
+ if (this.selection.length) {
+ this.selection.forEach(function (shape) {
+ shape.setColor(color, isSecondary);
+ shape.drawOn(myself.paper);
+ });
+ this.updateHistory();
+ }
+
+ if (color === 'transparent') {
+ for (i = 0; i < 180; i += 5) {
+ for (j = 0; j < 15; j += 5) {
+ ctx.fillStyle =
+ ((j + i) / 5) % 2 === 0 ?
+ 'rgba(0, 0, 0, 0.2)'
+ :'rgba(0, 0, 0, 0.5)';
+ ctx.fillRect(i, j, 5, 5);
+ }
+ }
+ } else {
+ ctx.fillStyle = color.toString();
+ ctx.fillRect(0, 0, 180, 15);
+ }
+
+ //Brush size
+ ctx.strokeStyle = 'black';
+ ctx.lineWidth = Math.min(this.paper.settings.lineWidth, 20);
+ ctx.beginPath();
+ ctx.lineCap = 'round';
+ ctx.moveTo(20, 30);
+ ctx.lineTo(160, 30);
+ ctx.stroke();
+ this.propertiesControls[propertyName + 'Viewer'].image = ni;
+ this.propertiesControls[propertyName + 'Viewer'].changed();
+};
+
+VectorPaintEditorMorph.prototype.changeSelectionLayer = function (destination) {
+ // I move the selected shapes across the z axis
+ var myself = this;
+
+ this.sortSelection();
+
+ switch (destination) {
+ case 'top':
+ this.selection.forEach(function (shape) {
+ myself.shapes.splice(myself.shapes.indexOf(shape), 1);
+ myself.shapes.push(shape);
+ });
+ break;
+ case 'bottom':
+ this.selection.slice().reverse().forEach(function (shape) {
+ myself.shapes.splice(myself.shapes.indexOf(shape), 1);
+ myself.shapes.splice(0, 0, shape);
+ });
+ break;
+ case 'up':
+ this.selection.forEach(function (shape) {
+ var index = myself.shapes.indexOf(shape);
+ myself.shapes.splice(index, 1);
+ myself.shapes.splice(index + myself.selection.length, 0, shape);
+ });
+ break;
+ case 'down':
+ if (this.shapes[0] !== this.selection[0]) {
+ this.selection.forEach(function (shape) {
+ var index = myself.shapes.indexOf(shape);
+ myself.shapes.splice(index, 1);
+ myself.shapes.splice(index - 1, 0, shape);
+ });
+ }
+ break;
+ }
+
+ this.updateHistory();
+ this.paper.redraw = true;
+};
+
+VectorPaintEditorMorph.prototype.dragSelection = function (pos) {
+ var origin,
+ ratio,
+ delta;
+
+ if (this.lastDragPosition) {
+ if (this.moving) {
+ delta = pos.subtract(this.lastDragPosition);
+ this.moveSelectionBy(delta);
+ } else if (this.resizing) {
+ if (this.shift) {
+ // constrain delta if shift is pressed
+ origin = this.originalSelection.origin;
+ ratio = Math.max(
+ (pos.x - origin.x) /
+ (this.originalSelection.destination.x - origin.x),
+ (pos.y - origin.y) /
+ (this.originalSelection.destination.y - origin.y)
+ );
+ pos = this.originalSelection.destination.subtract(
+ origin
+ ).multiplyBy(ratio).add(origin);
+ }
+ // this.currentShape holds the selection shape
+ delta = (pos.subtract(this.currentShape.origin)).divideBy(
+ this.lastDragPosition.subtract(this.currentShape.origin));
+ this.resizeSelectionBy(delta);
+ }
+ } else if (this.resizing) {
+ // we save the selection as it was before we started resizing so that
+ // we can use it to constrain its proportions later
+ this.originalSelection = this.currentShape.copy();
+ }
+
+ this.lastDragPosition = pos;
+};
+
+VectorPaintEditorMorph.prototype.moveSelectionBy = function (delta) {
+ var paper = this.paper;
+
+ this.selection.forEach(function (shape) {
+ shape.moveBy(delta);
+ shape.drawOn(paper);
+ });
+
+ if (this.currentShape && this.currentShape.isSelection) {
+ this.currentShape.moveBy(delta);
+ this.currentShape.drawOn(paper);
+ }
+};
+
+VectorPaintEditorMorph.prototype.resizeSelectionBy = function (delta) {
+ var paper = this.paper,
+ selectionShape;
+
+ if (this.currentShape && this.currentShape.isSelection) {
+ selectionShape = this.currentShape;
+
+ this.selection.forEach(function (shape) {
+ shape.resizeBy(delta, selectionShape.origin);
+ shape.drawOn(paper);
+ });
+
+ selectionShape.resizeBy(delta, selectionShape.origin);
+ selectionShape.drawOn(paper);
+ }
+};
+
+VectorPaintEditorMorph.prototype.sortSelection = function () {
+ var myself = this;
+ this.selection.sort(function (a, b) {
+ return myself.shapes.indexOf(a) > myself.shapes.indexOf(b);
+ });
+};
+
+VectorPaintEditorMorph.prototype.clearSelection = function () {
+ this.currentShape = null;
+ this.selection = [];
+ this.paper.redraw = true;
+};
+
+VectorPaintEditorMorph.prototype.getSVG = function () {
+ var svg = new XML_Element('svg'),
+ bounds = this.getBounds(this.shapes);
+
+ svg.attributes.xmlns = 'http://www.w3.org/2000/svg';
+ svg.attributes.snap = 'http://snap.berkeley.edu/run';
+ svg.attributes.version = '1.1';
+ svg.attributes.preserveAspectRatio = 'xMinYMin meet';
+ svg.attributes.viewBox =
+ bounds.left() + ' ' + bounds.top() + ' ' +
+ (bounds.right() - bounds.left()) + ' ' +
+ (bounds.bottom() - bounds.top());
+ svg.attributes.width = (bounds.right() - bounds.left());
+ svg.attributes.height = (bounds.bottom() - bounds.top());
+
+ svg.children = this.shapes.map(function (shape) { return shape.asSVG(); });
+
+ return window.btoa(svg);
+};
+
+VectorPaintEditorMorph.prototype.getBounds = function (shapeCollection) {
+ var shapeBounds = shapeCollection.map(function(each) {
+ return each.bounds();
+ });
+
+ if (shapeBounds.length === 0) {return null; }
+
+ return shapeBounds.reduce(
+ function(previous, current) {
+ return new Rectangle(
+ Math.min(previous.left(), current.left()),
+ Math.min(current.top(), previous.top()),
+ Math.max(previous.right(), current.right()),
+ Math.max(previous.bottom(), current.bottom())
+ );
+ }
+ );
+};
+
+VectorPaintEditorMorph.prototype.silentMoveBy = function (delta) {
+ VectorPaintEditorMorph.uber.silentMoveBy.call(this, delta);
+ if (this.currentShape) {
+ this.currentShape.moveBy(delta);
+ }
+ this.shapes.forEach(function (shape) {
+ shape.moveBy(delta);
+ });
+};
+
+VectorPaintEditorMorph.prototype.ok = function () {
+ var myself = this,
+ img = new Image(),
+ shapeOrigin,
+ originDelta;
+
+ if (this.shapes.length === 0) {
+ this.cancel();
+ return;
+ }
+
+ shapeOrigin = this.getBounds(this.shapes).origin;
+ originDelta = shapeOrigin.subtract(this.paper.bounds.origin);
+
+ this.paper.updateAutomaticCenter();
+
+ img.src = 'data:image/svg+xml;base64,' + this.getSVG().toString();
+
+ img.onload = function() {
+ myself.callback(
+ img,
+ myself.paper.rotationCenter.subtract(originDelta),
+ myself.shapes
+ );
+ };
+
+ this.destroy();
+};
+
+// Undo support
+
+VectorPaintEditorMorph.prototype.updateHistory = function () {
+ this.history.push(this.shapes.map(function (shape) {
+ return shape.copy();
+ }));
+};
+
+VectorPaintEditorMorph.prototype.undo = function () {
+ var paper = this.paper,
+ oldSum = this.checksum(),
+ newSum = oldSum;
+
+ function draw(shape) {
+ shape.drawOn(paper);
+ }
+
+ while (this.shapes.length && oldSum == newSum) {
+ this.shapes = this.history.pop() || [];
+ this.shapes.forEach(draw);
+ newSum = this.checksum();
+ }
+
+ this.clearSelection();
+};
+
+VectorPaintEditorMorph.prototype.checksum = function () {
+ return JSON.stringify(this.shapes).split('').reduce(
+ function (previousSum, currentChar) {
+ return previousSum + currentChar.charCodeAt(0);
+ },
+ 0);
+};
+
+// VectorPaintCanvasMorph //////////////////////////
+
+VectorPaintCanvasMorph.prototype = new PaintCanvasMorph();
+VectorPaintCanvasMorph.prototype.constructor = VectorPaintCanvasMorph;
+VectorPaintCanvasMorph.uber = PaintCanvasMorph.prototype;
+
+function VectorPaintCanvasMorph (shift) {
+ this.init(shift);
+}
+
+VectorPaintCanvasMorph.prototype.init = function (shift) {
+ VectorPaintCanvasMorph.uber.init.call(this, shift);
+ this.pointBuffer = [];
+ this.currentTool = 'brush';
+ this.settings = {
+ primaryColor: new Color(0, 0, 0, 255),
+ secondaryColor: new Color(0, 0, 0, 0),
+ lineWidth: 3
+ };
+};
+
+VectorPaintCanvasMorph.prototype.calculateCanvasCenter = function () {
+ var canvasBounds = this.bounds;
+
+ // Can't use canvasBounds.center(), it rounds down.
+ return new Point(
+ (canvasBounds.width()) / 2,
+ (canvasBounds.height()) / 2);
+};
+
+VectorPaintCanvasMorph.prototype.updateAutomaticCenter = function () {
+ var editor = this.parentThatIsA(VectorPaintEditorMorph),
+ shapeBounds = editor.getBounds(editor.shapes),
+ relativePosition;
+
+ if (this.automaticCrosshairs && shapeBounds) {
+ relativePosition = shapeBounds.origin.subtract(this.bounds.origin);
+ this.rotationCenter =
+ (new Point(
+ (shapeBounds.width()) / 2,
+ (shapeBounds.height()) / 2)).add(relativePosition);
+ } else if (this.automaticCrosshairs) {
+ this.calculateCanvasCenter();
+ }
+};
+
+VectorPaintCanvasMorph.prototype.clearCanvas = function () {
+ var editor = this.parentThatIsA(VectorPaintEditorMorph);
+ editor.updateHistory();
+ editor.shapes = [];
+ editor.clearSelection();
+ this.mask.getContext('2d').clearRect(
+ 0,
+ 0,
+ this.bounds.width(),
+ this.bounds.height()
+ );
+ this.redraw = true;
+};
+
+VectorPaintCanvasMorph.prototype.toolChanged = function (tool) {
+ var editor = this.parentThatIsA(VectorPaintEditorMorph);
+ VectorPaintCanvasMorph.uber.toolChanged.call(this, tool);
+
+ if (editor.currentShape && editor.currentShape.isPolygon) {
+ editor.currentShape.close();
+ editor.currentShape.drawOn(this);
+ editor.shapes.push(editor.currentShape);
+ }
+
+ if (tool === 'crosshairs') {
+ editor.clearSelection();
+ editor.currentShape = new Crosshair(this.rotationCenter, this);
+ editor.currentShape.drawOn(this);
+ this.automaticCrosshairs = false;
+ } else if (tool === 'pipette' && editor.selection) {
+ return;
+ } else {
+ editor.clearSelection();
+ editor.currentShape = null;
+ }
+};
+
+VectorPaintCanvasMorph.prototype.drawNew = function () {
+ var myself = this,
+ editor = this.parentThatIsA(VectorPaintEditorMorph),
+ canvas = newCanvas(this.extent());
+
+ this.merge(this.background, canvas);
+ this.merge(this.paper, canvas);
+
+ editor.shapes.forEach(function(each) {
+ myself.merge(each.image, canvas);
+ });
+
+ if (editor.currentShape) {
+ this.merge(editor.currentShape.image, canvas);
+ }
+
+ this.image = canvas;
+ this.drawFrame();
+};
+
+VectorPaintCanvasMorph.prototype.step = function () {
+ if (this.redraw) {
+ this.drawNew();
+ this.changed();
+ this.redraw = false;
+ }
+};
+
+VectorPaintCanvasMorph.prototype.mouseMove = function (pos) {
+ var editor = this.parentThatIsA(VectorPaintEditorMorph),
+ primaryColor = this.settings.primaryColor,
+ secondaryColor = this.settings.secondaryColor,
+ borderWidth = this.settings.lineWidth,
+ selectionCorner,
+ oppositeCorner;
+
+ if (this.currentTool === 'paintbucket') {
+ return;
+
+ } else if (editor.currentShape && editor.currentShape.isSelection
+ && !editor.selecting) {
+
+ selectionCorner = editor.currentShape.cornerAt(pos);
+
+ if (editor.resizing || editor.moving) {
+ editor.dragSelection(pos);
+ } else if (selectionCorner) {
+ oppositeCorner = editor.currentShape.cornerOppositeTo(
+ selectionCorner
+ );
+ editor.currentShape = new VectorSelection(
+ oppositeCorner,
+ selectionCorner
+ );
+ editor.currentShape.drawOn(this);
+ editor.resizing = true;
+ document.body.style.cursor = 'move';
+ } else if (editor.currentShape.containsPoint(pos)) {
+ editor.moving = true;
+ document.body.style.cursor = 'move';
+ }
+
+ } else if (!editor.currentShape || editor.currentShape.isSelection
+ && !editor.selecting) {
+ this.beginShape(borderWidth, primaryColor, secondaryColor, pos);
+ editor.currentShape.drawOn(this);
+ } else {
+ editor.currentShape.update(pos, editor.shift);
+ editor.currentShape.drawOn(this);
+ }
+};
+
+VectorPaintCanvasMorph.prototype.mouseEnter = function () {
+ if (this.currentTool === 'selection') {
+ document.body.style.cursor = 'crosshair';
+ } else {
+ document.body.style.cursor = 'default';
+ }
+};
+
+VectorPaintCanvasMorph.prototype.mouseLeave = function () {
+ document.body.style.cursor = 'default';
+};
+
+VectorPaintCanvasMorph.prototype.mouseClickLeft = function (pos) {
+ var editor = this.parentThatIsA(VectorPaintEditorMorph),
+ shape = editor.currentShape;
+
+ if (shape) {
+ if (shape.isPolygon && !shape.isFreeHand) {
+ shape.points.push(shape.points[shape.points.length - 1].copy());
+ } else if (shape.isPolygon) {
+ shape.close();
+ shape.drawOn(this);
+ editor.shapes.push(shape);
+ editor.currentShape = null;
+ } else if (shape.isSelection) {
+ if (editor.selecting) {
+ shape.destination = pos;
+ this.selectInside(shape);
+ editor.selecting = false;
+ } else if (editor.moving || editor.resizing) {
+ editor.moving = false;
+ editor.resizing = false;
+ this.updateSelection();
+ } else {
+ this.selectAtPoint(pos);
+ }
+ } else if (shape.isCrosshair) {
+ this.rotationCenter = pos.subtract(this.bounds.origin);
+ } else {
+ shape.update(pos, editor.shift);
+ editor.shapes.push(shape);
+ editor.currentShape = null;
+ }
+ } else if (this.currentTool === 'selection') {
+ this.selectAtPoint(pos);
+ }
+
+ editor.lastDragPosition = null;
+ this.mouseEnter();
+ editor.updateHistory();
+};
+
+VectorPaintCanvasMorph.prototype.mouseDoubleClick = function (pos) {
+ var editor = this.parentThatIsA(VectorPaintEditorMorph),
+ shape = editor.currentShape;
+
+ if (shape && shape.isPolygon) {
+ shape.close(); // if it applies
+ shape.drawOn(this);
+ editor.shapes.push(shape);
+ editor.currentShape = null;
+ editor.updateHistory();
+ }
+};
+
+VectorPaintCanvasMorph.prototype.beginShape = function (
+ borderWidth,
+ primaryColor,
+ secondaryColor,
+ pos
+) {
+ switch (this.currentTool) {
+ case 'brush':
+ this.beginPolygon( // unclosed, freehanded
+ borderWidth,
+ primaryColor,
+ null,
+ pos,
+ false,
+ true
+ );
+ break;
+ case 'line':
+ this.beginLine(borderWidth, primaryColor, pos);
+ break;
+ case 'rectangle':
+ this.beginRectangle(borderWidth, secondaryColor, primaryColor, pos);
+ break;
+ case 'ellipse':
+ this.beginEllipse(borderWidth, secondaryColor, primaryColor, pos);
+ break;
+ case 'polygon':
+ this.beginPolygon( // closed, point-based
+ borderWidth,
+ secondaryColor,
+ primaryColor,
+ pos,
+ true,
+ false
+ );
+ break;
+ case 'closedBrush':
+ this.beginPolygon( // closed, freehanded
+ borderWidth,
+ secondaryColor,
+ primaryColor,
+ pos,
+ true,
+ true
+ );
+ break;
+ case 'selection':
+ this.beginSelection(pos);
+ break;
+ // pipette is defined in PaintCanvasMorph >> toolButton
+ }
+};
+
+VectorPaintCanvasMorph.prototype.beginPolygon = function (
+ borderWidth,
+ borderColor,
+ fillColor,
+ origin,
+ isClosed,
+ isFreeHand
+) {
+ var editor = this.parentThatIsA(VectorPaintEditorMorph);
+ editor.currentShape = new VectorPolygon(
+ borderWidth,
+ borderColor,
+ fillColor,
+ [origin],
+ isClosed,
+ isFreeHand
+ );
+};
+
+VectorPaintCanvasMorph.prototype.beginLine = function (
+ borderWidth,
+ borderColor,
+ origin
+) {
+ var editor = this.parentThatIsA(VectorPaintEditorMorph);
+ editor.currentShape = new VectorLine(
+ borderWidth,
+ borderColor,
+ origin,
+ origin
+ );
+};
+
+VectorPaintCanvasMorph.prototype.beginRectangle = function (
+ borderWidth,
+ borderColor,
+ fillColor,
+ origin
+) {
+ var editor = this.parentThatIsA(VectorPaintEditorMorph);
+ editor.currentShape = new VectorRectangle(
+ borderWidth,
+ borderColor,
+ fillColor,
+ origin,
+ origin
+ );
+};
+
+VectorPaintCanvasMorph.prototype.beginEllipse = function (
+ borderWidth,
+ borderColor,
+ fillColor,
+ origin
+) {
+ var editor = this.parentThatIsA(VectorPaintEditorMorph);
+ editor.currentShape = new VectorEllipse(
+ borderWidth,
+ borderColor,
+ fillColor,
+ origin,
+ origin
+ );
+};
+
+VectorPaintCanvasMorph.prototype.beginSelection = function (origin) {
+ var editor = this.parentThatIsA(VectorPaintEditorMorph);
+ editor.currentShape = new VectorSelection(origin, origin);
+ editor.selecting = true;
+};
+
+VectorPaintCanvasMorph.prototype.selectInside = function (selectionShape) {
+ // I find and select all shapes contained inside
+ // the bounds of selectionShape
+ var selectionBounds = selectionShape.bounds(),
+ editor = this.parentThatIsA(VectorPaintEditorMorph);
+
+ editor.selection = editor.shapes.filter(function (eachShape) {
+ return selectionBounds.containsRectangle(eachShape.bounds());
+ });
+
+ if (editor.selection.length > 0) {
+ selectionBounds = editor.getBounds(editor.selection);
+ selectionShape.origin = selectionBounds.topLeft();
+ selectionShape.destination = selectionBounds.bottomRight();
+ selectionShape.drawOn(this);
+ } else {
+ editor.currentShape = null;
+ this.redraw = true;
+ }
+};
+
+VectorPaintCanvasMorph.prototype.selectAtPoint = function (position) {
+ // I find and select the topmost shape at position
+ var editor = this.parentThatIsA(VectorPaintEditorMorph),
+ shape = this.shapeAt(position),
+ bounds,
+ index;
+
+ if (shape) {
+ if (editor.shift) {
+ index = editor.selection.indexOf(shape);
+ if (index > -1) {
+ editor.selection.splice(index, 1);
+ } else {
+ editor.selection.push(shape);
+ }
+ } else {
+ editor.selection = [ shape ];
+ }
+ bounds = editor.getBounds(editor.selection);
+ }
+
+ if (bounds) {
+ editor.currentShape = new VectorSelection(
+ bounds.topLeft(),
+ bounds.bottomRight()
+ );
+ editor.currentShape.drawOn(this);
+ } else {
+ editor.clearSelection();
+ }
+};
+
+VectorPaintCanvasMorph.prototype.selectShapes = function (shapes) {
+ var editor = this.parentThatIsA(VectorPaintEditorMorph),
+ bounds;
+
+ if (shapes.length > 0) {
+ bounds = editor.getBounds(shapes);
+ editor.selection = shapes;
+ editor.currentShape = new VectorSelection(
+ bounds.topLeft(),
+ bounds.bottomRight()
+ );
+ editor.currentShape.drawOn(this);
+ }
+};
+
+VectorPaintCanvasMorph.prototype.updateSelection = function () {
+ var editor = this.parentThatIsA(VectorPaintEditorMorph);
+ this.selectShapes(editor.selection);
+};
+
+VectorPaintCanvasMorph.prototype.shapeAt = function (position) {
+ var editor = this.parentThatIsA(VectorPaintEditorMorph);
+ return detect(
+ editor.shapes.slice().reverse(),
+ function (shape) {
+ return shape.containsPoint(position);
+ });
+};
+
+VectorPaintCanvasMorph.prototype.pasteAt = function (position) {
+ var editor = this.parentThatIsA(VectorPaintEditorMorph),
+ myself = this,
+ clipboard = editor.clipboard,
+ delta,
+ copies = [];
+
+ if (clipboard.length > 0) {
+ // Each shape is positioned according to the difference between
+ // the first shape's original position and the paste position
+ delta = position.subtract(clipboard[0].bounds().origin);
+ }
+
+ clipboard.forEach(function (shape) {
+ var copy = shape.copy();
+ copy.moveBy(delta);
+ editor.selection.push(copy);
+ editor.shapes.push(copy);
+ copy.drawOn(myself);
+ copies.push(copy);
+ });
+
+ if (copies.length > 0) {
+ this.selectShapes(copies);
+ editor.updateHistory();
+ }
+};
+
+VectorPaintCanvasMorph.prototype.floodfill = function (sourcepoint) {
+ var editor = this.parentThatIsA(VectorPaintEditorMorph),
+ shape = this.shapeAt(sourcepoint.add(this.position()));
+
+ if (shape) {
+ shape.setColor(
+ editor.shift ?
+ this.settings.secondaryColor
+ : this.settings.primaryColor, editor.shift
+ );
+ shape.drawOn(this);
+ }
+};
+
diff --git a/snap.html b/snap.html
index 499f9be8..303144c0 100755
--- a/snap.html
+++ b/snap.html
@@ -2,35 +2,36 @@
- Snap! Build Your Own Blocks
+ Snap! Build Your Own Blocks 4.2.1
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/store.js b/store.js
index 847e8155..964a7724 100644
--- a/store.js
+++ b/store.js
@@ -7,7 +7,7 @@
written by Jens Mönig
jens@moenig.org
- Copyright (C) 2017 by Jens Mönig
+ Copyright (C) 2018 by Jens Mönig
This file is part of Snap!.
@@ -61,7 +61,7 @@ normalizeCanvas, contains*/
// Global stuff ////////////////////////////////////////////////////////
-modules.store = '2017-October-28';
+modules.store = '2018-July-09';
// XML_Serializer ///////////////////////////////////////////////////////
@@ -255,7 +255,7 @@ SnapSerializer.uber = XML_Serializer.prototype;
// SnapSerializer constants:
-SnapSerializer.prototype.app = 'Snap! 4.1, http://snap.berkeley.edu';
+SnapSerializer.prototype.app = 'Snap! 4.2, http://snap.berkeley.edu';
SnapSerializer.prototype.thumbnailSize = new Point(160, 120);
@@ -407,7 +407,7 @@ SnapSerializer.prototype.rawLoadProjectModel = function (xmlNode) {
StageMorph.prototype.dimensions = new Point(480, 360);
if (model.stage.attributes.width) {
StageMorph.prototype.dimensions.x =
- Math.max(+model.stage.attributes.width, 480);
+ Math.max(+model.stage.attributes.width, 240);
}
if (model.stage.attributes.height) {
StageMorph.prototype.dimensions.y =
@@ -876,7 +876,7 @@ SnapSerializer.prototype.loadCustomBlocks = function (
// private
var myself = this;
element.children.forEach(function (child) {
- var definition, names, inputs, vars, header, code, comment, i;
+ var definition, names, inputs, vars, header, code, trans, comment, i;
if (child.tag !== 'block-definition') {
return;
}
@@ -915,14 +915,17 @@ SnapSerializer.prototype.loadCustomBlocks = function (
return;
}
i += 1;
- definition.declarations[names[i]] = [
- child.attributes.type,
- contains(['%b', '%boolUE'], child.attributes.type) ?
- (child.contents ? child.contents === 'true' : null)
- : child.contents,
- options ? options.contents : undefined,
- child.attributes.readonly === 'true'
- ];
+ definition.declarations.set(
+ names[i],
+ [
+ child.attributes.type,
+ contains(['%b', '%boolUE'], child.attributes.type) ?
+ (child.contents ? child.contents === 'true' : null)
+ : child.contents,
+ options ? options.contents : undefined,
+ child.attributes.readonly === 'true'
+ ]
+ );
});
}
@@ -943,6 +946,11 @@ SnapSerializer.prototype.loadCustomBlocks = function (
definition.codeMapping = code.contents;
}
+ trans = child.childNamed('translations');
+ if (trans) {
+ definition.updateTranslations(trans.contents);
+ }
+
comment = child.childNamed('comment');
if (comment) {
definition.comment = myself.loadComment(comment);
@@ -1097,10 +1105,10 @@ SnapSerializer.prototype.loadBlock = function (model, isReporter, object) {
model.attributes,
'var'
)) {
- return SpriteMorph.prototype.variableBlock(
+ block = SpriteMorph.prototype.variableBlock(
model.attributes['var']
);
- }
+ } else {
/*
// disable JavaScript functions, commented out for now
if (model.attributes.s === 'reportJSFunction' &&
@@ -1112,10 +1120,11 @@ SnapSerializer.prototype.loadBlock = function (model, isReporter, object) {
}
}
*/
- block = SpriteMorph.prototype.blockForSelector(model.attributes.s);
- migration = SpriteMorph.prototype.blockMigrations[model.attributes.s];
- if (migration) {
- migrationOffset = migration.offset;
+ 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;
@@ -1144,7 +1153,10 @@ SnapSerializer.prototype.loadBlock = function (model, isReporter, object) {
: null
);
}
- if (!info) {
+ if (!info || !contains(
+ // catch other forks' blocks
+ SpriteMorph.prototype.categories, info.category
+ )) {
return this.obsoleteBlock(isReporter);
}
block = info.type === 'command' ? new CustomCommandBlockMorph(
@@ -1238,6 +1250,7 @@ SnapSerializer.prototype.loadInput = function (model, input, block, object) {
SnapSerializer.prototype.loadValue = function (model, object) {
// private
var v, i, lst, items, el, center, image, name, audio, option, bool, origin,
+ wish, def,
myself = this;
function record() {
@@ -1276,6 +1289,10 @@ SnapSerializer.prototype.loadValue = function (model, object) {
if (bool) {
return this.loadValue(bool);
}
+ wish = model.childNamed('wish');
+ if (wish) {
+ return this.loadValue(wish);
+ }
return model.contents;
case 'bool':
return model.contents === 'true';
@@ -1493,6 +1510,13 @@ SnapSerializer.prototype.loadValue = function (model, object) {
}
record();
return v;
+ case 'wish':
+ def = new CustomBlockDefinition(model.attributes.s);
+ def.type = model.attributes.type;
+ def.category = model.attributes.category;
+ def.storedSemanticSpec = model.attributes.s;
+ def.updateTranslations(model.contents);
+ return def.blockInstance(true); // include translations
}
return undefined;
};
@@ -1895,10 +1919,20 @@ BlockMorph.prototype.toBlockXML = function (serializer) {
};
ReporterBlockMorph.prototype.toXML = function (serializer) {
- return this.selector === 'reportGetVar' ? serializer.format(
- '',
- this.blockSpec
- ) : this.toBlockXML(serializer);
+ if (this.selector === 'reportGetVar') {
+ if (!this.comment) {
+ return serializer.format(
+ '',
+ this.blockSpec);
+ } else {
+ return serializer.format(
+ '%',
+ this.blockSpec,
+ this.comment.toXML(serializer));
+ }
+ } else {
+ return this.toBlockXML(serializer);
+ }
};
ReporterBlockMorph.prototype.toScriptXML = function (
@@ -1930,7 +1964,7 @@ CustomCommandBlockMorph.prototype.toBlockXML = function (serializer) {
var scope = this.isGlobal ? undefined : 'local';
return serializer.format(
'%%%',
- this.blockSpec,
+ this.semanticSpec,
this.isGlobal ?
'' : serializer.format(' scope="@"', scope),
serializer.store(this.inputs()),
@@ -1951,7 +1985,6 @@ CustomReporterBlockMorph.prototype.toBlockXML
CustomBlockDefinition.prototype.toXML = function (serializer) {
var myself = this;
-
function encodeScripts(array) {
return array.reduce(function (xml, element) {
if (element instanceof BlockMorph) {
@@ -1970,6 +2003,7 @@ CustomBlockDefinition.prototype.toXML = function (serializer) {
(this.variableNames.length ? '%' : '@') +
'' +
'@
' +
+ '@' +
'%%%' +
'',
this.spec,
@@ -1980,15 +2014,17 @@ CustomBlockDefinition.prototype.toXML = function (serializer) {
serializer.store(new List(this.variableNames)) : ''),
this.codeHeader || '',
this.codeMapping || '',
- Object.keys(this.declarations).reduce(function (xml, decl) {
+ this.translationsAsText(),
+ Array.from(this.declarations.keys()).reduce(function (xml, decl) {
+ // to be refactored now that we've moved to ES6 Map:
return xml + serializer.format(
'$%',
- myself.declarations[decl][0],
- myself.declarations[decl][3] ?
+ myself.declarations.get(decl)[0],
+ myself.declarations.get(decl)[3] ?
' readonly="true"' : '',
- myself.declarations[decl][1],
- myself.declarations[decl][2] ?
- '' + myself.declarations[decl][2] +
+ myself.declarations.get(decl)[1],
+ myself.declarations.get(decl)[2] ?
+ '' + myself.declarations.get(decl)[2] +
''
: ''
);
@@ -2014,6 +2050,16 @@ BooleanSlotMorph.prototype.toXML = function () {
};
InputSlotMorph.prototype.toXML = function (serializer) {
+ if (this.selectedBlock) {
+ return serializer.format(
+ '@',
+ this.selectedBlock.semanticSpec,
+ this.selectedBlock instanceof CommandBlockMorph ? 'command'
+ : (this.selectedBlock.isPredicate ? 'predicate' : 'reporter'),
+ this.selectedBlock.category,
+ this.selectedBlock.storedTranslations
+ );
+ }
if (this.constant) {
return serializer.format(
'',
diff --git a/symbols.js b/symbols.js
index eb7fa163..f99d1f55 100644
--- a/symbols.js
+++ b/symbols.js
@@ -7,7 +7,7 @@
written by Jens Mönig
jens@moenig.org
- Copyright (C) 2017 by Jens Mönig
+ Copyright (C) 2018 by Jens Mönig
This file is part of Snap!.
@@ -41,7 +41,7 @@
// Global stuff ////////////////////////////////////////////////////////
-modules.symbols = '2017-September-26';
+modules.symbols = '2018-June-05';
var SymbolMorph;
@@ -110,6 +110,7 @@ SymbolMorph.prototype.names = [
'rectangleSolid',
'circle',
'circleSolid',
+ 'ellipse',
'line',
'cross',
'crosshairs',
@@ -131,6 +132,9 @@ SymbolMorph.prototype.names = [
'robot',
'magnifyingGlass',
'magnifierOutline',
+ 'selection',
+ 'polygon',
+ 'closedBrush',
'notes',
'camera',
'location',
@@ -270,6 +274,8 @@ SymbolMorph.prototype.symbolCanvasColored = function (aColor) {
return this.drawSymbolCircle(canvas, aColor);
case 'circleSolid':
return this.drawSymbolCircleSolid(canvas, aColor);
+ case 'ellipse':
+ return this.drawSymbolCircle(canvas, aColor);
case 'line':
return this.drawSymbolLine(canvas, aColor);
case 'cross':
@@ -312,6 +318,12 @@ SymbolMorph.prototype.symbolCanvasColored = function (aColor) {
return this.drawSymbolMagnifyingGlass(canvas, aColor);
case 'magnifierOutline':
return this.drawSymbolMagnifierOutline(canvas, aColor);
+ case 'selection':
+ return this.drawSymbolSelection(canvas, aColor);
+ case 'polygon':
+ return this.drawSymbolOctagonOutline(canvas, aColor);
+ case 'closedBrush':
+ return this.drawSymbolClosedBrushPath(canvas, aColor);
case 'notes':
return this.drawSymbolNotes(canvas, aColor);
case 'camera':
@@ -1550,6 +1562,57 @@ SymbolMorph.prototype.drawSymbolMagnifierOutline = function (canvas, color) {
return canvas;
};
+
+SymbolMorph.prototype.drawSymbolSelection = function (canvas, color) {
+ // answer a canvas showing a filled arrow and a dashed rectangle
+ var ctx = canvas.getContext('2d'),
+ w = canvas.width,
+ h = canvas.height;
+
+ ctx.save();
+ ctx.setLineDash([3]);
+ this.drawSymbolRectangle(canvas, color);
+ ctx.restore();
+
+ ctx.save();
+ ctx.fillStyle = color.toString();
+ ctx.translate(0.7 * w, 0.4 * h);
+ ctx.scale(0.5, 0.5);
+ ctx.rotate(radians(135));
+ this.drawSymbolArrowDownOutline(canvas, color);
+ ctx.fill();
+ ctx.restore();
+
+ return canvas;
+};
+
+SymbolMorph.prototype.drawSymbolOctagonOutline = function (canvas, color) {
+ // answer a canvas showing an octagon
+ var ctx = canvas.getContext('2d'),
+ side = canvas.width,
+ vert = (side - (side * 0.383)) / 2,
+ l = Math.max(side / 20, 0.5);
+
+ ctx.strokeStyle = color.toString();
+ ctx.lineWidth = l * 2;
+ ctx.beginPath();
+ ctx.moveTo(vert, l);
+ ctx.lineTo(side - vert, l);
+ ctx.lineTo(side - l, vert);
+ ctx.lineTo(side - l, side - vert);
+ ctx.lineTo(side - vert, side - l);
+ ctx.lineTo(vert, side - l);
+ ctx.lineTo(l, side - vert);
+ ctx.lineTo(l, vert);
+ ctx.closePath();
+ ctx.stroke();
+
+ return canvas;
+};
+
+SymbolMorph.prototype.drawSymbolClosedBrushPath =
+ SymbolMorph.prototype.drawSymbolCloudOutline;
+
SymbolMorph.prototype.drawSymbolNotes = function (canvas, color) {
// answer a canvas showing two musical notes
var ctx = canvas.getContext('2d'),
diff --git a/threads.js b/threads.js
index 523c0836..9af685bf 100644
--- a/threads.js
+++ b/threads.js
@@ -9,7 +9,7 @@
written by Jens Mönig
jens@moenig.org
- Copyright (C) 2017 by Jens Mönig
+ Copyright (C) 2018 by Jens Mönig
This file is part of Snap!.
@@ -42,6 +42,7 @@
Context
Variable
VariableFrame
+ JSCompiler
credits
@@ -61,12 +62,14 @@ StageMorph, SpriteMorph, StagePrompterMorph, Note, modules, isString, copy,
isNil, WatcherMorph, List, ListWatcherMorph, alert, console, TableMorph,
TableFrameMorph, ColorSlotMorph, isSnapObject*/
-modules.threads = '2017-October-20';
+modules.threads = '2018-July-09';
var ThreadManager;
var Process;
var Context;
+var Variable;
var VariableFrame;
+var JSCompiler;
function snapEquals(a, b) {
if (a instanceof List || (b instanceof List)) {
@@ -112,7 +115,8 @@ function invoke(
receiver, // sprite or environment, optional for contexts
timeout, // msecs
timeoutErrorMsg, // string
- suppressErrors // bool
+ suppressErrors, // bool
+ callerProcess // optional for JS-functions
) {
// execute the given block or context synchronously without yielding.
// Apply context (not a block) to a list of optional arguments.
@@ -151,6 +155,11 @@ function invoke(
);
} else if (action.evaluate) {
return action.evaluate();
+ } else if (action instanceof Function) {
+ return action.apply(
+ receiver,
+ contextArgs.asArray().concat(callerProcess)
+ );
} else {
throw new Error('expecting a block or ring but getting ' + action);
}
@@ -196,7 +205,8 @@ ThreadManager.prototype.startProcess = function (
exportResult, // bool
callback,
isClicked,
- rightAway
+ rightAway,
+ atomic // special option used (only) for "onStop" scripts
) {
var top = block.topBlock(),
active = this.findProcess(top, receiver),
@@ -212,6 +222,7 @@ ThreadManager.prototype.startProcess = function (
newProc = new Process(top, receiver, callback, isClicked);
newProc.exportResult = exportResult;
newProc.isClicked = isClicked || false;
+ newProc.isAtomic = atomic || false;
// show a highlight around the running stack
// if there are more than one active processes
@@ -525,6 +536,7 @@ Process.prototype.timeout = 500; // msecs after which to force yield
Process.prototype.isCatchingErrors = true;
Process.prototype.enableLiveCoding = false; // experimental
Process.prototype.enableSingleStepping = false; // experimental
+Process.prototype.enableCompiling = false; // experimental
Process.prototype.flashTime = 0; // experimental
// Process.prototype.enableJS = false;
@@ -694,7 +706,9 @@ Process.prototype.evaluateContext = function () {
};
Process.prototype.evaluateBlock = function (block, argCount) {
- var selector = block.selector;
+ var rcvr, inputs,
+ selector = block.selector;
+
// check for special forms
if (selector === 'reportOr' ||
selector === 'reportAnd' ||
@@ -703,8 +717,8 @@ Process.prototype.evaluateBlock = function (block, argCount) {
}
// first evaluate all inputs, then apply the primitive
- var rcvr = this.context.receiver || this.receiver,
- inputs = this.context.inputs;
+ rcvr = this.context.receiver || this.receiver;
+ inputs = this.context.inputs;
if (argCount > inputs.length) {
this.evaluateNextInput(block);
@@ -775,7 +789,7 @@ Process.prototype.doReport = function (block) {
if (this.isClicked && (block.topBlock() === this.topBlock)) {
this.isShowingResult = true;
}
- if (this.context.expression.partOfCustomCommand) {
+ if (block.partOfCustomCommand) {
this.doStopCustomBlock();
this.popContext();
} else {
@@ -798,8 +812,9 @@ Process.prototype.doReport = function (block) {
}
// in any case evaluate (and ignore)
// the input, because it could be
- // and HTTP Request for a hardware extension
+ // an HTTP Request for a hardware extension
this.pushContext(block.inputs()[0], outer);
+ this.context.isCustomCommand = block.partOfCustomCommand;
};
// Process: Non-Block evaluation
@@ -1090,6 +1105,7 @@ Process.prototype.evaluate = function (
outer,
context.receiver
);
+ runnable.isCustomCommand = isCommand; // for short-circuiting HTTP requests
this.context.parentContext = runnable;
if (context.expression instanceof ReporterBlockMorph) {
@@ -1162,16 +1178,17 @@ Process.prototype.evaluate = function (
};
Process.prototype.fork = function (context, args) {
+ if (this.readyToTerminate) {return; }
var proc = new Process(),
stage = this.homeContext.receiver.parentThatIsA(StageMorph);
proc.instrument = this.instrument;
proc.receiver = this.receiver;
- proc.initializeFor(context, args);
+ proc.initializeFor(context, args, this.enableSingleStepping);
// proc.pushContext('doYield');
stage.threads.processes.push(proc);
};
-Process.prototype.initializeFor = function (context, args) {
+Process.prototype.initializeFor = function (context, args, ignoreExit) {
// used by Process.fork() and global invoke()
if (context.isContinuation) {
throw new Error(
@@ -1243,14 +1260,16 @@ Process.prototype.initializeFor = function (context, args) {
// insert a tagged exit context
// which "report" can catch later
// needed for invoke() situations
- exit = new Context(
- runnable.parentContext,
- 'expectReport',
- outer,
- outer.receiver
- );
- exit.tag = 'exit';
- runnable.parentContext = exit;
+ if (!ignoreExit) { // when single stepping LAUNCH
+ exit = new Context(
+ runnable.parentContext,
+ 'expectReport',
+ outer,
+ outer.receiver
+ );
+ exit.tag = 'exit';
+ runnable.parentContext = exit;
+ }
}
this.homeContext = new Context(); // context.outerContext;
@@ -1330,7 +1349,7 @@ Process.prototype.evaluateCustomBlock = function () {
var caller = this.context.parentContext,
block = this.context.expression,
method = block.isGlobal ? block.definition
- : this.blockReceiver().getMethod(block.blockSpec),
+ : this.blockReceiver().getMethod(block.semanticSpec),
context = method.body,
declarations = method.declarations,
args = new List(this.context.inputs),
@@ -1383,7 +1402,7 @@ Process.prototype.evaluateCustomBlock = function () {
// if the parameter is an upvar,
// create a reference to the variable it points to
- if (declarations[context.inputs[i]][0] === '%upvar') {
+ if (declarations.get(context.inputs[i])[0] === '%upvar') {
this.context.outerContext.variables.vars[value] =
outer.variables.vars[context.inputs[i]];
}
@@ -1440,21 +1459,23 @@ Process.prototype.doDeclareVariables = function (varNames) {
Process.prototype.doSetVar = function (varName, value) {
var varFrame = this.context.variables,
- name = varName,
- rcvr;
+ name = varName;
if (name instanceof Context) {
- rcvr = this.blockReceiver();
if (name.expression.selector === 'reportGetVar') {
name.variables.setVar(
name.expression.blockSpec,
value,
- rcvr
+ this.blockReceiver()
);
return;
}
this.doSet(name, value);
return;
}
+ if (name instanceof Array) {
+ this.doSet(name, value);
+ return;
+ }
varFrame.setVar(name, value, this.blockReceiver());
};
@@ -1838,6 +1859,7 @@ Process.prototype.doStopAll = function () {
if (stage) {
stage.threads.resumeAll(stage);
stage.keysPressed = {};
+ stage.runStopScripts();
stage.threads.stopAll();
stage.stopAllActiveSounds();
stage.children.forEach(function (morph) {
@@ -2280,7 +2302,14 @@ Process.prototype.reportURL = function (url) {
}
this.httpRequest = new XMLHttpRequest();
this.httpRequest.open("GET", url, true);
+ this.httpRequest.setRequestHeader('Cache-Control', 'max-age=0');
this.httpRequest.send(null);
+ if (this.context.isCustomCommand) {
+ // special case when ignoring the result, e.g. when
+ // communicating with a robot to control its motors
+ this.httpRequest = null;
+ return null;
+ }
} else if (this.httpRequest.readyState === 4) {
response = this.httpRequest.responseText;
this.httpRequest = null;
@@ -2308,6 +2337,9 @@ Process.prototype.doBroadcast = function (message) {
myself = this,
procs = [];
+ if (this.readyToTerminate) {
+ return [];
+ }
if (message instanceof List && (message.length() === 2)) {
thisObj = this.blockReceiver();
msg = message.at(1);
@@ -2352,6 +2384,10 @@ Process.prototype.doBroadcast = function (message) {
Process.prototype.doBroadcastAndWait = function (message) {
if (!this.context.activeSends) {
this.context.activeSends = this.doBroadcast(message);
+ this.context.activeSends.forEach(function (proc) {
+ // optimize for atomic sub-routines
+ proc.runStep();
+ });
}
this.context.activeSends = this.context.activeSends.filter(
function (proc) {
@@ -2409,15 +2445,15 @@ Process.prototype.reportTypeOf = function (thing) {
if (thing === true || (thing === false)) {
return 'Boolean';
}
+ if (thing instanceof List) {
+ return 'list';
+ }
if (!isNaN(+thing)) {
return 'number';
}
if (isString(thing)) {
return 'text';
}
- if (thing instanceof List) {
- return 'list';
- }
if (thing instanceof SpriteMorph) {
return 'sprite';
}
@@ -2671,11 +2707,18 @@ Process.prototype.reportJoinWords = function (aList) {
// Process string ops
Process.prototype.reportLetter = function (idx, string) {
+ var str, i;
if (string instanceof List) { // catch a common user error
return '';
}
- var i = +(idx || 0),
- str = isNil(string) ? '' : string.toString();
+ if (this.inputOption(idx) === 'any') {
+ idx = this.reportRandom(1, string.length);
+ }
+ if (this.inputOption(idx) === 'last') {
+ idx = string.length;
+ }
+ i = +(idx || 0);
+ str = isNil(string) ? '' : string.toString();
return str[i - 1] || '';
};
@@ -2688,12 +2731,20 @@ Process.prototype.reportStringSize = function (data) {
};
Process.prototype.reportUnicode = function (string) {
- var str = (string || '').toString()[0];
- return str ? str.charCodeAt(0) : 0;
+ var str = isNil(string) ? '\u0000' : string.toString();
+
+ if (str.codePointAt) { // support for Unicode in newer browsers.
+ return str.codePointAt(0);
+ }
+ return str.charCodeAt(0);
};
Process.prototype.reportUnicodeAsLetter = function (num) {
var code = +(num || 0);
+
+ if (String.fromCodePoint) { // support for Unicode in newer browsers.
+ return String.fromCodePoint(code);
+ }
return String.fromCharCode(code);
};
@@ -2878,13 +2929,28 @@ Process.prototype.getObjectsNamed = function (name, thisObj, stageObj) {
return those;
};
+Process.prototype.setHeading = function (direction) {
+ var thisObj = this.blockReceiver();
+
+ if (thisObj) {
+ if (this.inputOption(direction) === 'random') {
+ direction = this.reportRandom(1, 36000) / 100;
+ }
+ thisObj.setHeading(direction);
+ }
+};
+
Process.prototype.doFaceTowards = function (name) {
var thisObj = this.blockReceiver(),
thatObj;
if (thisObj) {
- if (this.inputOption(name) === 'mouse-pointer') {
+ if (this.inputOption(name) === 'center') {
+ thisObj.faceToXY(0, 0);
+ } else if (this.inputOption(name) === 'mouse-pointer') {
thisObj.faceToXY(this.reportMouseX(), this.reportMouseY());
+ } else if (this.inputOption(name) === 'random position') {
+ thisObj.setHeading(this.reportRandom(1, 36000) / 100);
} else {
if (name instanceof List) {
thisObj.faceToXY(
@@ -2906,11 +2972,22 @@ Process.prototype.doFaceTowards = function (name) {
Process.prototype.doGotoObject = function (name) {
var thisObj = this.blockReceiver(),
- thatObj;
+ thatObj,
+ stage;
if (thisObj) {
- if (this.inputOption(name) === 'mouse-pointer') {
+ if (this.inputOption(name) === 'center') {
+ thisObj.gotoXY(0, 0);
+ } else if (this.inputOption(name) === 'mouse-pointer') {
thisObj.gotoXY(this.reportMouseX(), this.reportMouseY());
+ } else if (this.inputOption(name) === 'random position') {
+ stage = thisObj.parentThatIsA(StageMorph);
+ if (stage) {
+ thisObj.setCenter(new Point(
+ this.reportRandom(stage.left(), stage.right()),
+ this.reportRandom(stage.top(), stage.bottom())
+ ));
+ }
} else {
if (name instanceof List) {
thisObj.gotoXY(
@@ -2936,7 +3013,7 @@ Process.prototype.createClone = function (name) {
var thisObj = this.blockReceiver(),
thatObj;
- if (!name) {return; }
+ if (!name || this.readyToTerminate) {return; }
if (thisObj) {
if (this.inputOption(name) === 'myself') {
thisObj.createClone(!this.isFirstStep);
@@ -3009,7 +3086,7 @@ Process.prototype.objectTouchingObject = function (thisObj, name) {
return true;
}
if (isSnapObject(name)) {
- return thisObj.isTouching(name);
+ return name.isVisible && thisObj.isTouching(name);
}
if (name instanceof List) { // assume all elements to be sprites
those = name.itemsArray();
@@ -3017,7 +3094,7 @@ Process.prototype.objectTouchingObject = function (thisObj, name) {
those = this.getObjectsNamed(name, thisObj, stage); // clones
}
if (those.some(function (any) {
- return thisObj.isTouching(any);
+ return any.isVisible && thisObj.isTouching(any);
})) {
return true;
}
@@ -3076,6 +3153,17 @@ Process.prototype.reportColorIsTouchingColor = function (color1, color2) {
return false;
};
+Process.prototype.reportRelationTo = function (relation, name) {
+ var rel = this.inputOption(relation);
+ if (rel === 'distance') {
+ return this.reportDistanceTo(name);
+ }
+ if (rel === 'direction') {
+ return this.reportDirectionTo(name);
+ }
+ return 0;
+};
+
Process.prototype.reportDistanceTo = function (name) {
var thisObj = this.blockReceiver(),
thatObj,
@@ -3088,6 +3176,9 @@ Process.prototype.reportDistanceTo = function (name) {
point = rc;
if (this.inputOption(name) === 'mouse-pointer') {
point = thisObj.world().hand.position();
+ } else if (this.inputOption(name) === 'center') {
+ return new Point(thisObj.xPosition(), thisObj.yPosition())
+ .distanceTo(new Point(0, 0));
} else if (name instanceof List) {
return new Point(thisObj.xPosition(), thisObj.yPosition())
.distanceTo(new Point(name.at(1), name.at(2)));
@@ -3102,6 +3193,35 @@ Process.prototype.reportDistanceTo = function (name) {
return 0;
};
+Process.prototype.reportDirectionTo = function (name) {
+ var thisObj = this.blockReceiver(),
+ thatObj;
+
+ if (thisObj) {
+ if (this.inputOption(name) === 'mouse-pointer') {
+ return thisObj.angleToXY(this.reportMouseX(), this.reportMouseY());
+ }
+ if (this.inputOption(name) === 'center') {
+ return thisObj.angleToXY(0, 0);
+ }
+ if (name instanceof List) {
+ return thisObj.angleToXY(
+ name.at(1),
+ name.at(2)
+ );
+ }
+ thatObj = this.getOtherObject(name, this.homeContext.receiver);
+ if (thatObj) {
+ return thisObj.angleToXY(
+ thatObj.xPosition(),
+ thatObj.yPosition()
+ );
+ }
+ return thisObj.direction();
+ }
+ return 0;
+};
+
Process.prototype.reportAttributeOf = function (attribute, name) {
var thisObj = this.blockReceiver(),
thatObj,
@@ -3117,6 +3237,16 @@ Process.prototype.reportAttributeOf = function (attribute, name) {
}
if (thatObj) {
this.assertAlive(thatObj);
+ if (attribute instanceof BlockMorph) { // a "wish"
+ return this.reportContextFor(
+ this.reify(
+ thatObj.getMethod(attribute.semanticSpec)
+ .blockInstance(),
+ new List()
+ ),
+ thatObj
+ );
+ }
if (attribute instanceof Context) {
return this.reportContextFor(attribute, thatObj);
}
@@ -3226,16 +3356,16 @@ Process.prototype.reportGet = function (query) {
Process.prototype.doSet = function (attribute, value) {
// experimental, manipulate sprites' attributes
var name, rcvr;
- if (!(attribute instanceof Context)) {
- return;
- }
rcvr = this.blockReceiver();
this.assertAlive(rcvr);
- if (!(attribute instanceof Context) ||
- attribute.expression.selector !== 'reportGet') {
+ if (!(attribute instanceof Context || attribute instanceof Array) ||
+ (attribute instanceof Context &&
+ attribute.expression.selector !== 'reportGet')) {
throw new Error(localize('unsupported attribute'));
}
- name = attribute.expression.inputs()[0].evaluate();
+ name = attribute instanceof Context ?
+ attribute.expression.inputs()[0].evaluate()
+ : attribute;
if (name instanceof Array) {
name = name[0];
}
@@ -3675,6 +3805,209 @@ Process.prototype.unflash = function () {
}
};
+// Process: Compile (as of yet simple) block scripts to JS
+
+/*
+ with either only explicit formal parameters or a specified number of
+ implicit formal parameters mapped to empty input slots
+ *** highly experimental and heavily under construction ***
+*/
+
+Process.prototype.reportCompiled = function (context, implicitParamCount) {
+ // implicitParamCount is optional and indicates the number of
+ // expected parameters, if any. This is only used to handle
+ // implicit (empty slot) parameters and can otherwise be
+ // ignored
+ return new JSCompiler(this).compileFunction(context, implicitParamCount);
+};
+
+Process.prototype.getVarNamed = function (name) {
+ // private - special form for compiled expressions
+ // DO NOT use except in compiled methods!
+ // first check script vars, then global ones
+ var frame = this.homeContext.variables.silentFind(name) ||
+ this.context.variables.silentFind(name),
+ value;
+ if (frame) {
+ value = frame.vars[name].value;
+ return (value === 0 ? 0
+ : value === false ? false
+ : value === '' ? ''
+ : value || 0); // don't return null
+ }
+ throw new Error(
+ localize('a variable of name \'')
+ + name
+ + localize('\'\ndoes not exist in this context')
+ );
+};
+
+Process.prototype.setVarNamed = function (name, value) {
+ // private - special form for compiled expressions
+ // incomplete, currently only sets named vars
+ // DO NOT use except in compiled methods!
+ // first check script vars, then global ones
+ var frame = this.homeContext.variables.silentFind(name) ||
+ this.context.variables.silentFind(name);
+ if (isNil(frame)) {
+ throw new Error(
+ localize('a variable of name \'')
+ + name
+ + localize('\'\ndoes not exist in this context')
+ );
+ }
+ frame.vars[name].value = value;
+};
+
+Process.prototype.incrementVarNamed = function (name, delta) {
+ // private - special form for compiled expressions
+ this.setVarNamed(name, this.getVarNamed(name) + (+delta));
+};
+
+// Process: Atomic HOFs using experimental JIT-compilation
+
+Process.prototype.reportAtomicMap = function (reporter, list) {
+ this.assertType(list, 'list');
+ var result = [],
+ src = list.asArray(),
+ len = src.length,
+ func,
+ i;
+
+ // try compiling the reporter into generic JavaScript
+ // fall back to the morphic reporter if unsuccessful
+ try {
+ func = this.reportCompiled(reporter, 1); // a single expected input
+ } catch (err) {
+ console.log(err.message);
+ func = reporter;
+ }
+
+ // iterate over the data in a single frame:
+ // to do: Insert some kind of user escape mechanism
+ for (i = 0; i < len; i += 1) {
+ result.push(
+ invoke(
+ func,
+ new List([src[i]]),
+ null,
+ null,
+ null,
+ null,
+ this // process
+ )
+ );
+ }
+ return new List(result);
+};
+
+Process.prototype.reportAtomicKeep = function (reporter, list) {
+ this.assertType(list, 'list');
+ var result = [],
+ src = list.asArray(),
+ len = src.length,
+ func,
+ i;
+
+ // try compiling the reporter into generic JavaScript
+ // fall back to the morphic reporter if unsuccessful
+ try {
+ func = this.reportCompiled(reporter, 1); // a single expected input
+ } catch (err) {
+ console.log(err.message);
+ func = reporter;
+ }
+
+ // iterate over the data in a single frame:
+ // to do: Insert some kind of user escape mechanism
+ for (i = 0; i < len; i += 1) {
+ if (
+ invoke(
+ func,
+ new List([src[i]]),
+ null,
+ null,
+ null,
+ null,
+ this // process
+ )
+ ) {
+ result.push(src[i]);
+ }
+ }
+ return new List(result);
+};
+
+Process.prototype.reportAtomicCombine = function (reporter, list) {
+ this.assertType(list, 'list');
+ var result = '',
+ src = list.asArray(),
+ len = src.length,
+ func,
+ i;
+
+ if (len === 0) {
+ return result;
+ }
+ result = src[0];
+
+ // try compiling the reporter into generic JavaScript
+ // fall back to the morphic reporter if unsuccessful
+ try {
+ func = this.reportCompiled(reporter, 2); // a single expected input
+ } catch (err) {
+ console.log(err.message);
+ func = reporter;
+ }
+
+ // iterate over the data in a single frame:
+ // to do: Insert some kind of user escape mechanism
+ for (i = 1; i < len; i += 1) {
+ result = invoke(
+ func,
+ new List([result, src[i]]),
+ null,
+ null,
+ null,
+ null,
+ this // process
+ );
+ }
+ return result;
+};
+
+Process.prototype.reportAtomicSort = function (list, reporter) {
+ this.assertType(list, 'list');
+ var myself = this,
+ func;
+
+ // try compiling the reporter into generic JavaScript
+ // fall back to the morphic reporter if unsuccessful
+ try {
+ func = this.reportCompiled(reporter, 2); // two inputs expected
+ } catch (err) {
+ console.log(err.message);
+ func = reporter;
+ }
+
+ // iterate over the data in a single frame:
+ return new List(
+ list.asArray().slice().sort(
+ function (a, b) {
+ return invoke(
+ func,
+ new List([a, b]),
+ null,
+ null,
+ null,
+ null,
+ myself // process
+ ) ? -1 : 1;
+ }
+ )
+ );
+};
+
// Context /////////////////////////////////////////////////////////////
/*
@@ -3703,7 +4036,9 @@ Process.prototype.unflash = function () {
startValue initial value for interpolated operations
activeAudio audio buffer for interpolated operations, don't persist
activeNote audio oscillator for interpolated ops, don't persist
+ activeSends forked processes waiting to be completed
isCustomBlock marker for return ops
+ isCustomCommand marker for interpolated blocking reporters (reportURL)
emptySlots caches the number of empty slots for reification
tag string or number to optionally identify the Context,
as a "return" target (for the "stop block" primitive)
@@ -3730,9 +4065,11 @@ function Context(
this.pc = 0;
this.isContinuation = false;
this.startTime = null;
+ this.activeSends = null;
this.activeAudio = null;
this.activeNote = null;
this.isCustomBlock = false; // marks the end of a custom block's stack
+ this.isCustomCommand = null; // used for ignoring URL reporters' results
this.emptySlots = 0; // used for block reification
this.tag = null; // lexical catch-tag for custom blocks
this.isFlashing = false; // for single-stepping
@@ -4063,7 +4400,8 @@ VariableFrame.prototype.names = function () {
return names;
};
-VariableFrame.prototype.allNamesDict = function () {
+VariableFrame.prototype.allNamesDict = function (upTo) {
+ // "upTo" is an optional parent frame at which to stop, e.g. globals
var dict = {}, current = this;
function addKeysToDict(srcDict, trgtDict) {
@@ -4075,19 +4413,20 @@ VariableFrame.prototype.allNamesDict = function () {
}
}
- while (current) {
+ while (current && (current !== upTo)) {
addKeysToDict(current.vars, dict);
current = current.parentFrame;
}
return dict;
};
-VariableFrame.prototype.allNames = function () {
+VariableFrame.prototype.allNames = function (upTo) {
/*
only show the names of the lexical scope, hybrid scoping is
reserved to the daring ;-)
+ "upTo" is an optional parent frame at which to stop, e.g. globals
*/
- var answer = [], each, dict = this.allNamesDict();
+ var answer = [], each, dict = this.allNamesDict(upTo);
for (each in dict) {
if (Object.prototype.hasOwnProperty.call(dict, each)) {
@@ -4096,3 +4435,243 @@ VariableFrame.prototype.allNames = function () {
}
return answer;
};
+
+// JSCompiler /////////////////////////////////////////////////////////////////
+
+/*
+ Compile simple, side-effect free Reporters
+ with either only explicit formal parameters or a specified number of
+ implicit formal parameters mapped to empty input slots
+ *** highly experimental and heavily under construction ***
+*/
+
+function JSCompiler(aProcess) {
+ this.process = aProcess;
+ this.source = null; // a context
+ this.gensyms = null; // temp dictionary for parameter substitutions
+ this.implicitParams = null;
+ this.paramCount = null;
+}
+
+JSCompiler.prototype.toString = function () {
+ return 'a JSCompiler';
+};
+
+JSCompiler.prototype.compileFunction = function (aContext, implicitParamCount) {
+ var block = aContext.expression,
+ parameters = aContext.inputs,
+ parms = [],
+ hasEmptySlots = false,
+ myself = this,
+ i;
+
+ this.source = aContext;
+ this.implicitParams = implicitParamCount || 1;
+
+ // scan for empty input slots
+ hasEmptySlots = !isNil(detect(
+ block.allChildren(),
+ function (morph) {return morph.isEmptySlot && morph.isEmptySlot(); }
+ ));
+
+ // translate formal parameters into gensyms
+ this.gensyms = {};
+ this.paramCount = 0;
+ if (parameters.length) {
+ // test for conflicts
+ if (hasEmptySlots) {
+ throw new Error(
+ 'compiling does not yet support\n' +
+ 'mixing explicit formal parameters\n' +
+ 'with empty input slots'
+ );
+ }
+ // map explicit formal parameters
+ parameters.forEach(function (pName, idx) {
+ var pn = 'p' + idx;
+ parms.push(pn);
+ myself.gensyms[pName] = pn;
+ });
+ } else if (hasEmptySlots) {
+ if (this.implicitParams > 1) {
+ for (i = 0; i < this.implicitParams; i += 1) {
+ parms.push('p' + i);
+ }
+ } else {
+ // allow for a single implicit formal parameter
+ parms = ['p0'];
+ }
+ }
+
+ // compile using gensyms
+
+ if (block instanceof CommandBlockMorph) {
+ return Function.apply(
+ null,
+ parms.concat([this.compileSequence(block)])
+ );
+ }
+ return Function.apply(
+ null,
+ parms.concat(['return ' + this.compileExpression(block)])
+ );
+};
+
+JSCompiler.prototype.compileExpression = function (block) {
+ var selector = block.selector,
+ inputs = block.inputs(),
+ target,
+ rcvr,
+ args;
+
+ // first check for special forms and infix operators
+ switch (selector) {
+ case 'reportOr':
+ return this.compileInfix('||', inputs);
+ case 'reportAnd':
+ return this.compileInfix('&&', inputs);
+ case 'evaluateCustomBlock':
+ throw new Error(
+ 'compiling does not yet support\n' +
+ 'custom blocks'
+ );
+
+ // special command forms
+ case 'doSetVar': // redirect var to process
+ return 'arguments[arguments.length - 1].setVarNamed(' +
+ this.compileInput(inputs[0]) +
+ ',' +
+ this.compileInput(inputs[1]) +
+ ')';
+ case 'doChangeVar': // redirect var to process
+ return 'arguments[arguments.length - 1].incrementVarNamed(' +
+ this.compileInput(inputs[0]) +
+ ',' +
+ this.compileInput(inputs[1]) +
+ ')';
+ case 'doReport':
+ return 'return ' + this.compileInput(inputs[0]);
+ case 'doIf':
+ return 'if (' +
+ this.compileInput(inputs[0]) +
+ ') {\n' +
+ this.compileSequence(inputs[1].evaluate()) +
+ '}';
+ case 'doIfElse':
+ return 'if (' +
+ this.compileInput(inputs[0]) +
+ ') {\n' +
+ this.compileSequence(inputs[1].evaluate()) +
+ '} else {\n' +
+ this.compileSequence(inputs[2].evaluate()) +
+ '}';
+
+ default:
+ target = this.process[selector] ? this.process
+ : (this.source.receiver || this.process.receiver);
+ rcvr = target.constructor.name + '.prototype';
+ args = this.compileInputs(inputs);
+ if (isSnapObject(target)) {
+ return rcvr + '.' + selector + '.apply('+ rcvr + ', [' + args +'])';
+ } else {
+ return 'arguments[arguments.length - 1].' +
+ selector +
+ '.apply(arguments[arguments.length - 1], [' + args +'])';
+ }
+ }
+};
+
+JSCompiler.prototype.compileSequence = function (commandBlock) {
+ var body = '',
+ myself = this;
+ commandBlock.blockSequence().forEach(function (block) {
+ body += myself.compileExpression(block);
+ body += ';\n';
+ });
+ return body;
+};
+
+JSCompiler.prototype.compileInfix = function (operator, inputs) {
+ return '(' + this.compileInput(inputs[0]) + ' ' + operator + ' ' +
+ this.compileInput(inputs[1]) +')';
+};
+
+JSCompiler.prototype.compileInputs = function (array) {
+ var args = '',
+ myself = this;
+
+ array.forEach(function (inp) {
+ if (args.length) {
+ args += ', ';
+ }
+ args += myself.compileInput(inp);
+ });
+ return args;
+};
+
+JSCompiler.prototype.compileInput = function (inp) {
+ var value, type;
+
+ if (inp.isEmptySlot && inp.isEmptySlot()) {
+ // implicit formal parameter
+ if (this.implicitParams > 1) {
+ if (this.paramCount < this.implicitParams) {
+ this.paramCount += 1;
+ return 'p' + (this.paramCount - 1);
+ }
+ throw new Error(
+ localize('expecting') + ' ' + this.implicitParams + ' '
+ + localize('input(s), but getting') + ' '
+ + this.paramCount
+ );
+ }
+ return 'p0';
+ } else if (inp instanceof MultiArgMorph) {
+ return 'new List([' + this.compileInputs(inp.inputs()) + '])';
+ } else if (inp instanceof ArgLabelMorph) {
+ return this.compileInput(inp.argMorph());
+ } else if (inp instanceof ArgMorph) {
+ // literal - evaluate inline
+ value = inp.evaluate();
+ type = this.process.reportTypeOf(value);
+ switch (type) {
+ case 'number':
+ case 'Boolean':
+ return '' + value;
+ case 'text':
+ // enclose in double quotes
+ return '"' + value + '"';
+ case 'list':
+ return 'new List([' + this.compileInputs(value) + '])';
+ default:
+ if (value instanceof Array) {
+ return '"' + value[0] + '"';
+ }
+ throw new Error(
+ 'compiling does not yet support\n' +
+ 'inputs of type\n' +
+ type
+ );
+ }
+ } else if (inp instanceof BlockMorph) {
+ if (inp.selector === 'reportGetVar') {
+ if (contains(this.source.inputs, inp.blockSpec)) {
+ // un-quoted gensym:
+ return this.gensyms[inp.blockSpec];
+ }
+ // redirect var query to process
+ return 'arguments[arguments.length - 1].getVarNamed("' +
+ inp.blockSpec +
+ '")';
+ }
+ return this.compileExpression(inp);
+ } else {
+ throw new Error(
+ 'compiling does not yet support\n' +
+ 'input slots of type\n' +
+ inp.constructor.name
+ );
+ }
+};
+
+
diff --git a/tools.xml b/tools.xml
index e409fb7b..e5d3a58c 100644
--- a/tools.xml
+++ b/tools.xml
@@ -1 +1 @@
-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.
Hello!12
1
1
110i
121
cont
3
catchtag
cont
catchtag
1101
1
\ No newline at end of file
+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.
de:drucke _ in Größe _
ca:etiqueta _ de mida _
es:etiqueta _ de tamaño _
fr:étiquette _ d'une taille de _
Hello!12
de:ist _ leer?
ca:_ buida?
es:_ vacía?
fr:_ vide?
de:behalte Elemente, die _ aus _
ca:manté els elements on _ de _
es:mantener los elementos donde _ de _
fr:garder les items tels que _ de _
1
de:kombiniere mit _ die Elemente von _
ca:combina amb _ els elements de _
es:combinar con _ los elementos de _
fr:combine avec _ les items de _
1
de:wenn _ dann _ sonst _
ca:si _ llavors _ si no _
es:si _ entonces _ sino _
fr:si _ alors _ sinon _
de:für _ = _ bis _ _
ca:per _ = _ fins _ _
es:para _ = _ hasta _ _
fr:pour _ allant de _ à _ _
110i
de:füge Wörter zusammen _
ca:uneix les paraules _
es:unir las palabras _
fr:fusionne les mots _
121
de:Liste $arrowRight Satz _
ca:llista $arrowRight frase _
es:lista $arrowRight frase _
fr:liste $arrowRight phrase _
de:Satz $arrowRight Liste _
ca:frase $arrowRight llista _
es:frase $arrowRight lista _
fr:phrase $arrowRight liste _
de:fange _ _
ca:agafa _ _
es:atrapar _ _
fr:attrape _ _
cont
3
de:wirf _
ca:llança _
es:lanzar _
fr:lance _
catchtag
de:fange _ _
ca:agafa _ _
es:atrapar _ _
fr:attrape _ _
cont
de:wirf _ _
ca:llança _ _
es:lanzar _ _
fr:lance _ _
catchtag
de:für jedes _ von _ _
ca:per cada _ de _ _
es:para cada _ de _ _
fr:pour chaque _ de _ _
de:falls _ dann _ und pause $pause-1-255-220-0
ca:si _ fes _ i atura-ho tot $pause-1-255-220-0
es:si _ haz _ y páralo todo $pause-1-255-220-0
fr:si _ faire _ et mettre tout en pause $pause-1-255-220-0
de:Wort $arrowRight Liste _
ca:paraula $arrowRight llista _
es:palabra $arrowRight lista _
fr:mot $arrowRight liste _
de:ignoriere _
ca:ignora _
es:ignorar _
fr:ignore _
de:Liste $arrowRight Wort _
ca:llista $arrowRight paraula _
es:lista $arrowRight palabra _
fr:liste $arrowRight mot _
de:Zahlen von _ bis _
ca:nombres des de _ a _
es:números de _ a _
fr:nombres de _ à _
1101
de:wende _ an auf _
ca:mapeja _ sobre _
es:mapear _ sobre _
fr:appliquer _ à _
1
diff --git a/widgets.js b/widgets.js
index 7d15fd1f..ccff05ff 100644
--- a/widgets.js
+++ b/widgets.js
@@ -7,7 +7,7 @@
written by Jens Mönig
jens@moenig.org
- Copyright (C) 2017 by Jens Mönig
+ Copyright (C) 2018 by Jens Mönig
This file is part of Snap!.
@@ -85,7 +85,7 @@ HTMLCanvasElement, fontHeight, SymbolMorph, localize, SpeechBubbleMorph,
ArrowMorph, MenuMorph, isString, isNil, SliderMorph, MorphicPreferences,
ScrollFrameMorph, MenuItemMorph, Note*/
-modules.widgets = '2017-September-25';
+modules.widgets = '2018-February-08';
var PushButtonMorph;
var ToggleButtonMorph;
@@ -2075,6 +2075,10 @@ DialogBoxMorph.prototype.promptCredentials = function (
emlLabel = labelText('foo');
inp.add(emlLabel);
inp.add(eml);
+ inp.add(labelText('Password:'));
+ inp.add(pw1);
+ inp.add(labelText('Repeat Password:'));
+ inp.add(pw2);
}
if (purpose === 'login') {
@@ -2091,7 +2095,7 @@ DialogBoxMorph.prototype.promptCredentials = function (
inp.add(pw2);
}
- if (purpose === 'resetPassword') {
+ if (purpose === 'resetPassword' || purpose === 'resendVerification') {
inp.add(labelText('User name:'));
inp.add(usr);
}
@@ -2182,10 +2186,10 @@ DialogBoxMorph.prototype.promptCredentials = function (
if (purpose === 'login') {
checklist = [usr, pw1];
} else if (purpose === 'signup') {
- checklist = [usr, bmn, byr, eml];
+ checklist = [usr, bmn, byr, eml, pw1, pw2];
} else if (purpose === 'changePassword') {
checklist = [opw, pw1, pw2];
- } else if (purpose === 'resetPassword') {
+ } else if (purpose === 'resetPassword' || purpose === 'resendVerification') {
checklist = [usr];
}
@@ -2205,12 +2209,12 @@ DialogBoxMorph.prototype.promptCredentials = function (
return false;
}
if (em.indexOf(' ') > -1 || em.indexOf('@') === -1
- || em.indexOf('.') === -1) {
+ || em.indexOf('.') === -1 || em.length < 5) {
indicate(eml, 'please provide a valid\nemail address');
return false;
}
}
- if (purpose === 'changePassword') {
+ if (purpose === 'changePassword' || purpose === 'signup') {
if (pw1.getValue().length < 6) {
indicate(pw1, 'password must be six\ncharacters or longer');
return false;
@@ -2238,7 +2242,7 @@ DialogBoxMorph.prototype.promptCredentials = function (
this.edit = function () {
if (purpose === 'changePassword') {
opw.edit();
- } else { // 'signup', 'login', 'resetPassword'
+ } else { // 'signup', 'login', 'resetPassword', 'resendVerification'
usr.edit();
}
};
@@ -2249,6 +2253,7 @@ DialogBoxMorph.prototype.promptCredentials = function (
email: eml.getValue(),
oldpassword: opw.getValue(),
password: pw1.getValue(),
+ passwordRepeat: pw2.getValue(),
choice: agree
};
};
diff --git a/xml.js b/xml.js
index b80f5200..995a042b 100755
--- a/xml.js
+++ b/xml.js
@@ -67,7 +67,7 @@
// Global stuff ////////////////////////////////////////////////////////
-modules.xml = '2015-June-25';
+modules.xml = '2017-November-15';
// Declarations
@@ -238,7 +238,7 @@ XML_Element.prototype.toString = function (isFormatted, indentationLevel) {
for (key in this.attributes) {
if (Object.prototype.hasOwnProperty.call(this.attributes, key)
&& this.attributes[key]) {
- result += ' ' + key + '="' + this.attributes[key] + '"';
+ result += ' ' + key + '="' + this.escape(this.attributes[key]) + '"';
}
}
@@ -247,7 +247,7 @@ XML_Element.prototype.toString = function (isFormatted, indentationLevel) {
result += '/>';
} else {
result += '>';
- result += this.contents;
+ result += this.escape(this.contents);
this.children.forEach(function (element) {
if (isFormatted) {
result += '\n';