first-class sprites

dev
Jens Mönig 2016-05-02 12:52:58 +02:00
rodzic 94b63dde74
commit ed3b56a610
12 zmienionych plików z 677 dodań i 129 usunięć

127
blocks.js 100755 → 100644
Wyświetl plik

@ -137,19 +137,19 @@
*/
/*global Array, BoxMorph,
Color, ColorPaletteMorph, CursorMorph, FrameMorph, Function, HandleMorph,
Math, MenuMorph, Morph, MorphicPreferences, Object, Point, ScrollFrameMorph,
ShadowMorph, String, StringMorph, TextMorph, WorldMorph, contains, degrees,
detect, document, getDocumentPositionOf, isNaN, isString, newCanvas, nop,
parseFloat, radians, useBlurredShadows, SpeechBubbleMorph, modules,
StageMorph, fontHeight, TableFrameMorph, SpriteMorph, Context,
ListWatcherMorph, CellMorph, DialogBoxMorph, BlockInputFragmentMorph,
PrototypeHatBlockMorph, Costume, IDE_Morph, BlockDialogMorph,
BlockEditorMorph, localize, isNil*/
Color, ColorPaletteMorph, FrameMorph, Function, HandleMorph, Math, MenuMorph,
Morph, MorphicPreferences, Object, Point, ScrollFrameMorph, ShadowMorph,
String, StringMorph, TextMorph, WorldMorph, contains, degrees, detect,
document, getDocumentPositionOf, isNaN, isString, newCanvas, nop, parseFloat,
radians, useBlurredShadows, SpeechBubbleMorph, modules, StageMorph,
fontHeight, TableFrameMorph, SpriteMorph, Context, ListWatcherMorph,
CellMorph, DialogBoxMorph, BlockInputFragmentMorph, PrototypeHatBlockMorph,
Costume, IDE_Morph, BlockDialogMorph, BlockEditorMorph, localize, isNil,
isSnapObject*/
// Global stuff ////////////////////////////////////////////////////////
modules.blocks = '2016-March-16';
modules.blocks = '2016-May-02';
var SyntaxElementMorph;
var BlockMorph;
@ -1031,6 +1031,15 @@ SyntaxElementMorph.prototype.labelPart = function (spec) {
true
);
break;
case '%get': // sprites, parts, speciment, clones
part = new InputSlotMorph(
null,
false,
'gettablesMenu',
true
);
part.isStatic = true;
break;
case '%cst':
part = new InputSlotMorph(
null,
@ -1049,7 +1058,7 @@ SyntaxElementMorph.prototype.labelPart = function (spec) {
comic: ['comic'],
duplicate: ['duplicate'],
confetti: ['confetti']
},
},
true
);
part.setContents(['ghost']);
@ -1215,17 +1224,7 @@ SyntaxElementMorph.prototype.labelPart = function (spec) {
part = new InputSlotMorph(
null,
false,
{
number : ['number'],
text : ['text'],
Boolean : ['Boolean'],
list : ['list'],
command : ['command'],
reporter : ['reporter'],
predicate : ['predicate']
// ring : 'ring'
// object : 'object'
},
'typesMenu',
true
);
part.setContents(['number']);
@ -1789,11 +1788,28 @@ SyntaxElementMorph.prototype.showBubble = function (value, exportPic) {
morphToShow.expand(this.parentThatIsA(ScrollFrameMorph).extent());
isClickable = true;
} else if (value instanceof Morph) {
img = value.fullImage();
morphToShow = new Morph();
morphToShow.silentSetWidth(img.width);
morphToShow.silentSetHeight(img.height);
morphToShow.image = img;
if (isSnapObject(value)) {
img = value.thumbnail(new Point(40, 40));
morphToShow = new Morph();
morphToShow.silentSetWidth(img.width);
morphToShow.silentSetHeight(img.height);
morphToShow.image = img;
morphToShow.version = value.version;
morphToShow.step = function () {
if (this.version !== value.version) {
img = value.thumbnail(new Point(40, 40));
this.image = img;
this.version = value.version;
this.changed();
}
};
} else {
img = value.fullImage();
morphToShow = new Morph();
morphToShow.silentSetWidth(img.width);
morphToShow.silentSetHeight(img.height);
morphToShow.image = img;
}
} else if (value instanceof Costume) {
img = value.thumbnail(new Point(40, 40));
morphToShow = new Morph();
@ -3245,13 +3261,14 @@ BlockMorph.prototype.fullCopy = function () {
ans.allChildren().filter(function (block) {
if (block instanceof SyntaxElementMorph) {
block.cachedInputs = null;
if (block instanceof InputSlotMorph) {
block.contents().clearSelection();
} else if (block.definition) {
// if (block instanceof InputSlotMorph) {
// block.contents().clearSelection();
// } else
if (block.definition) {
block.initializeVariables();
}
} else if (block instanceof CursorMorph) {
block.destroy();
// } else if (block instanceof CursorMorph) {
// block.destroy();
}
return !isNil(block.comment);
}).forEach(function (block) {
@ -7023,7 +7040,7 @@ InputSlotMorph.prototype.messagesMenu = function () {
allNames = [];
stage.children.concat(stage).forEach(function (morph) {
if (morph instanceof SpriteMorph || morph instanceof StageMorph) {
if (isSnapObject(morph)) {
allNames = allNames.concat(morph.allMessageNames());
}
});
@ -7057,7 +7074,7 @@ InputSlotMorph.prototype.messagesReceivedMenu = function () {
allNames = [];
stage.children.concat(stage).forEach(function (morph) {
if (morph instanceof SpriteMorph || morph instanceof StageMorph) {
if (isSnapObject(morph)) {
allNames = allNames.concat(morph.allMessageNames());
}
});
@ -7175,6 +7192,48 @@ InputSlotMorph.prototype.objectsMenu = function () {
return dict;
};
InputSlotMorph.prototype.typesMenu = function () {
var dict = {
number : ['number'],
text : ['text'],
Boolean : ['Boolean'],
list : ['list']
};
if (SpriteMorph.prototype.enableFirstClass) {
dict.sprite = ['sprite'];
}
dict.command = ['command'];
dict.reporter = ['reporter'];
dict.predicate = ['predicate'];
return dict;
};
InputSlotMorph.prototype.gettablesMenu = function () {
var dict = {
neighbors : ['neighbors'],
self : ['self'],
'other sprites' : ['other sprites'],
clones : ['clones'],
'other clones' : ['other clones']
};
if (SpriteMorph.prototype.enableNesting) {
dict.parts = ['parts'];
dict.anchor = ['anchor'];
}
dict.stage = ['stage'];
if (StageMorph.prototype.enableInheritance) {
dict.children = ['children'];
dict.parent = ['parent'];
}
dict.name = ['name'];
dict['dangling?'] = ['dangling?'];
dict['rotation x'] = ['rotation x'];
dict['rotation y'] = ['rotation y'];
dict['center x'] = ['center x'];
dict['center y'] = ['center y'];
return dict;
};
InputSlotMorph.prototype.attributesMenu = function () {
var block = this.parentThatIsA(BlockMorph),
objName = block.inputs()[1].evaluate(),

48
gui.js 100755 → 100644
Wyświetl plik

@ -65,11 +65,12 @@ BlockExportDialogMorph, BlockImportDialogMorph, SnapTranslator, localize,
List, InputSlotMorph, SnapCloud, Uint8Array, HandleMorph, SVG_Costume,
fontHeight, hex_sha512, sb, CommentMorph, CommandBlockMorph,
BlockLabelPlaceHolderMorph, Audio, SpeechBubbleMorph, ScriptFocusMorph,
XML_Element, WatcherMorph, BlockRemovalDialogMorph, saveAs, TableMorph*/
XML_Element, WatcherMorph, BlockRemovalDialogMorph, saveAs, TableMorph,
isSnapObject*/
// Global stuff ////////////////////////////////////////////////////////
modules.gui = '2016-March-16';
modules.gui = '2016-May-02';
// Declarations
@ -1399,8 +1400,10 @@ IDE_Morph.prototype.createCorral = function () {
frame.alpha = 0;
this.sprites.asArray().forEach(function (morph) {
template = new SpriteIconMorph(morph, template);
frame.contents.add(template);
if (!morph.isClone) {
template = new SpriteIconMorph(morph, template);
frame.contents.add(template);
}
});
this.corral.frame = frame;
@ -1988,6 +1991,7 @@ IDE_Morph.prototype.removeSprite = function (sprite) {
sprite.parts.forEach(function (part) {myself.removeSprite(part); });
idx = this.sprites.asArray().indexOf(sprite) + 1;
this.stage.threads.stopAllForReceiver(sprite);
sprite.corpsify();
sprite.destroy();
this.stage.watchers().forEach(function (watcher) {
if (watcher.object() === sprite) {
@ -2421,6 +2425,20 @@ IDE_Morph.prototype.settingsMenu = function () {
'check to enable\nsprite composition',
true
);
addPreference(
'First-Class Sprites',
function () {
SpriteMorph.prototype.enableFirstClass =
!SpriteMorph.prototype.enableFirstClass;
myself.currentSprite.blocksCache.sensing = null;
myself.currentSprite.paletteCache.sensing = null;
myself.refreshPalette();
},
SpriteMorph.prototype.enableFirstClass,
'uncheck to disable support\nfor first-class sprites',
'check to enable support\n for first-class sprite',
true
);
addPreference(
'Keyboard Editing',
function () {
@ -2471,6 +2489,17 @@ IDE_Morph.prototype.settingsMenu = function () {
false
);
}
addPreference(
'Live coding support',
function () {
Process.prototype.enableLiveCoding =
!Process.prototype.enableLiveCoding;
},
Process.prototype.enableLiveCoding,
'EXPERIMENTAL! uncheck to disable live\ncustom control structures',
'EXPERIMENTAL! check to enable\n live custom control structures',
true
);
menu.addLine(); // everything below this line is stored in the project
addPreference(
'Thread safe scripts',
@ -2799,7 +2828,6 @@ IDE_Morph.prototype.parseResourceFile = function (text) {
return items;
};
// IDE_Morph menu actions
IDE_Morph.prototype.aboutSnap = function () {
@ -2807,7 +2835,7 @@ IDE_Morph.prototype.aboutSnap = function () {
module, btn1, btn2, btn3, btn4, licenseBtn, translatorsBtn,
world = this.world();
aboutTxt = 'Snap! 4.0.6\nBuild Your Own Blocks\n\n'
aboutTxt = 'Snap! 4.0.7\nBuild Your Own Blocks\n\n'
+ 'Copyright \u24B8 2016 Jens M\u00F6nig and '
+ 'Brian Harvey\n'
+ 'jens@moenig.org, bh@cs.berkeley.edu\n\n'
@ -3027,6 +3055,7 @@ IDE_Morph.prototype.newProject = function () {
StageMorph.prototype.enableInheritance = false;
StageMorph.prototype.enableSublistIDs = false;
SpriteMorph.prototype.useFlatLineEnds = false;
Process.prototype.enableLiveCoding = false;
this.setProjectName('');
this.projectNotes = '';
this.createStage();
@ -3534,6 +3563,7 @@ IDE_Morph.prototype.rawOpenProjectString = function (str) {
StageMorph.prototype.enableCodeMapping = false;
StageMorph.prototype.enableInheritance = false;
StageMorph.prototype.enableSublistIDs = false;
Process.prototype.enableLiveCoding = false;
if (Process.prototype.isCatchingErrors) {
try {
this.serializer.openProject(
@ -3578,6 +3608,7 @@ IDE_Morph.prototype.rawOpenCloudDataString = function (str) {
StageMorph.prototype.enableCodeMapping = false;
StageMorph.prototype.enableInheritance = false;
StageMorph.prototype.enableSublistIDs = false;
Process.prototype.enableLiveCoding = false;
if (Process.prototype.isCatchingErrors) {
try {
model = this.serializer.parse(str);
@ -3929,7 +3960,7 @@ IDE_Morph.prototype.toggleZebraColoring = function () {
// select all scripts:
this.stage.children.concat(this.stage).forEach(function (morph) {
if (morph instanceof SpriteMorph || morph instanceof StageMorph) {
if (isSnapObject(morph)) {
scripts = scripts.concat(
morph.scripts.children.filter(function (morph) {
return morph instanceof BlockMorph;
@ -6298,7 +6329,8 @@ CostumeIconMorph.prototype.editCostume = function () {
} else {
this.object.edit(
this.world(),
this.parentThatIsA(IDE_Morph)
this.parentThatIsA(IDE_Morph),
false // not a new costume, retain existing rotation center
);
}
};

Wyświetl plik

@ -2882,3 +2882,36 @@ end - bulk of 151215
* added web api / https reporter library
* Blocks, Store: New “transient variable” feature
* German translation update
== v4.0.6 ==== - saving linked lists
160502
------
* first class sprites, new MY reporter block and extended functionality of TOUCHING
* fixed switching from list watcher to table view inside sprite speech bubbles
* fixed paint editor automatic rotation center issue
* enhanced functionality to SET a sprites attributes
* execute clone initialization scripts first step in the same frame as the clone command
* auto-repair (sorta) certain broken project files
* Threads: More aggressive emergency yielding
* “corpsify” deleted sprites, which might still be referred to by variables and lists
* Blocks: simplify block copying
* experimental hidden “live-coding support” preference
* updated penTrails library to shrinkWrap generated costumes
demos from the documentation:
http://snap.berkeley.edu/run#cloud:Username=jens&ProjectName=population
http://snap.berkeley.edu/run#present:Username=jens&ProjectName=Woodworm
http://snap.berkeley.edu/run#present:Username=jens&ProjectName=Ferris%20Wheel%202016
http://snap.berkeley.edu/run#cloud:Username=jens&ProjectName=PathFollower
http://snap.berkeley.edu/run#present:Username=jens&ProjectName=cartwheel
http://snap.berkeley.edu/run#cloud:Username=jens&ProjectName=rotation
* Translation updates: Slovenian, Portuguese, Chinese
* minor bug fixes
== v4.0.7 ==== - first class sprites

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

@ -185,7 +185,7 @@ SnapTranslator.dict.de = {
'translator_e-mail':
'jens@moenig.org', // optional
'last_changed':
'2016-03-16', // this, too, will appear in the Translators tab
'2016-05-02', // this, too, will appear in the Translators tab
// GUI
// control bar:
@ -527,6 +527,8 @@ SnapTranslator.dict.de = {
'Stoppuhr',
'%att of %spr':
'%att von %spr',
'my %get':
'Attribut %get',
'http:// %s':
'http:// %s',
'turbo mode?':
@ -1352,10 +1354,46 @@ SnapTranslator.dict.de = {
'Funktionsblock',
'predicate':
'Pr\u00e4dikat',
'sprite':
'Objekt',
// list indices
'last':
'letztes',
'any':
'beliebig'
'beliebig',
// attributes
'neighbors':
'Nachbarn',
'self':
'selbst',
'other sprites':
'andere Objekte',
'parts':
'Teile',
'anchor':
'Verankerung',
'parent':
'Vorfahr',
'children':
'Abkömmlinge',
'clones':
'Klone',
'other clones':
'andere Klone',
'dangling?':
'Baumeln?',
'rotation x':
'Drehpunkt x',
'rotation y':
'Drehpunkt y',
'center x':
'Mittelpunkt x',
'center y':
'Mittelpunkt y',
'name':
'Name',
'stage':
'B\u00fchne',
};

Wyświetl plik

@ -1 +1 @@
<blocks app="Snap! 4.0, http://snap.berkeley.edu" version="1"><block-definition s="pen trails" type="reporter" category="pen"><header></header><code></code><inputs></inputs><script><block s="doReport"><block s="evaluate"><block s="reportJSFunction"><list></list><l>return new Costume(&#xD; this.parentThatIsA(StageMorph).trailsCanvas&#xD;);</l></block><list></list></block></block></script></block-definition><block-definition s="set pen trails to: %&apos;costume&apos;" type="command" category="pen"><header></header><code></code><inputs><input type="%s" readonly="true"></input></inputs><script><block s="doRun"><block s="reportJSFunction"><list><l>cst</l></list><l>var stage = this.parentThatIsA(StageMorph);&#xD;stage.trailsCanvas = cst.contents;&#xD;stage.changed();</l></block><list><block var="costume"/></list></block></script></block-definition></blocks>
<blocks app="Snap! 4.0, http://snap.berkeley.edu" version="1"><block-definition s="pen trails" type="reporter" category="pen"><header></header><code></code><inputs></inputs><script><block s="doReport"><block s="evaluate"><block s="reportJSFunction"><list></list><l>var cst = new Costume(&#xD; this.parentThatIsA(StageMorph).trailsCanvas&#xD;);&#xD;cst.shrinkWrap();&#xD;return cst;</l></block><list></list></block></block></script></block-definition><block-definition s="set pen trails to: %&apos;costume&apos;" type="command" category="pen"><header></header><code></code><inputs><input type="%s" readonly="true"></input></inputs><script><block s="doRun"><block s="reportJSFunction"><list><l>cst</l></list><l>var stage = this.parentThatIsA(StageMorph);&#xD;stage.trailsCanvas = cst.contents;&#xD;stage.changed();</l></block><list><block var="costume"/></list></block></script></block-definition></blocks>

49
lists.js 100755 → 100644
Wyświetl plik

@ -60,9 +60,9 @@
Color, Point, WatcherMorph, StringMorph, SpriteMorph, ScrollFrameMorph,
CellMorph, ArrowMorph, MenuMorph, snapEquals, Morph, isNil, localize,
MorphicPreferences, TableDialogMorph, SpriteBubbleMorph, SpeechBubbleMorph,
TableFrameMorph, TableMorph, Variable*/
TableFrameMorph, TableMorph, Variable, isSnapObject*/
modules.lists = '2016-February-24';
modules.lists = '2016-May-02';
var List;
var ListWatcherMorph;
@ -322,6 +322,27 @@ List.prototype.asArray = function () {
return this.contents;
};
List.prototype.itemsArray = function () {
// answer an array containing my elements
// don't convert linked lists to arrays
if (this.isLinked) {
var next = this,
result = [],
i;
while (next && next.isLinked) {
result.push(next.first);
next = next.rest;
}
if (next) {
for (i = 1; i <= next.contents.length; i += 1) {
result.push(next.at(i));
}
}
return result;
}
return this.contents;
};
List.prototype.asText = function () {
var result = '',
length,
@ -353,17 +374,7 @@ List.prototype.asText = function () {
List.prototype.becomeArray = function () {
if (this.isLinked) {
var next = this, i;
this.contents = [];
while (next && next.isLinked) {
this.contents.push(next.first);
next = next.rest;
}
if (next) {
for (i = 1; i <= next.contents.length; i += 1) {
this.contents.push(next.at(i));
}
}
this.contents = this.itemsArray();
this.isLinked = false;
this.first = null;
this.rest = null;
@ -543,10 +554,12 @@ ListWatcherMorph.prototype.update = function (anyway) {
starttime, maxtime = 1000;
this.frame.contents.children.forEach(function (m) {
if (m instanceof CellMorph
&& m.contentsMorph instanceof ListWatcherMorph) {
m.contentsMorph.update();
if (m instanceof CellMorph) {
if (m.contentsMorph instanceof ListWatcherMorph) {
m.contentsMorph.update();
} else if (isSnapObject(m.contents)) {
m.update();
}
}
});
@ -802,7 +815,7 @@ ListWatcherMorph.prototype.showTableView = function () {
if (!view) {return; }
if (view instanceof SpriteBubbleMorph) {
view.changed();
view.drawNew();
view.drawNew(true);
} else if (view instanceof SpeechBubbleMorph) {
view.contents = new TableFrameMorph(new TableMorph(this.list, 10));
view.contents.expand(this.extent());

4
locale.js 100755 → 100644
Wyświetl plik

@ -42,7 +42,7 @@
/*global modules, contains*/
modules.locale = '2016-March-16';
modules.locale = '2016-May-02';
// Global stuff
@ -161,7 +161,7 @@ SnapTranslator.dict.de = {
'translator_e-mail':
'jens@moenig.org',
'last_changed':
'2016-03-16'
'2016-05-02'
};
SnapTranslator.dict.it = {

204
objects.js 100755 → 100644
Wyświetl plik

@ -82,7 +82,7 @@ SpeechBubbleMorph, RingMorph, isNil, FileReader, TableDialogMorph,
BlockEditorMorph, BlockDialogMorph, PrototypeHatBlockMorph, localize,
TableMorph, TableFrameMorph*/
modules.objects = '2016-March-16';
modules.objects = '2016-May-02';
var SpriteMorph;
var StageMorph;
@ -98,6 +98,10 @@ var StagePrompterMorph;
var Note;
var SpriteHighlightMorph;
function isSnapObject(thing) {
return thing instanceof SpriteMorph || (thing instanceof StageMorph);
}
// SpriteMorph /////////////////////////////////////////////////////////
// I am a scriptable object
@ -144,6 +148,7 @@ SpriteMorph.prototype.sliderColor
SpriteMorph.prototype.isCachingPrimitives = true;
SpriteMorph.prototype.enableNesting = true;
SpriteMorph.prototype.enableFirstClass = true;
SpriteMorph.prototype.useFlatLineEnds = false;
SpriteMorph.prototype.highlightColor = new Color(250, 200, 130);
SpriteMorph.prototype.highlightBorder = 8;
@ -863,6 +868,12 @@ SpriteMorph.prototype.initBlocks = function () {
category: 'sensing',
spec: 'current %dates'
},
reportGet:{
type: 'reporter',
category: 'sensing',
spec: 'my %get',
defaults: [['neighbors']]
},
// Operators
reifyScript: {
@ -1310,6 +1321,7 @@ SpriteMorph.prototype.init = function (globals) {
this.rotationStyle = 1; // 1 = full, 2 = left/right, 0 = off
this.version = Date.now(); // for observer optimization
this.isClone = false; // indicate a "temporary" Scratch-style clone
this.isCorpse = false; // indicate whether a sprite/clone has been deleted
this.cloneOriginName = '';
// sprite nesting properties
@ -1356,7 +1368,6 @@ SpriteMorph.prototype.fullCopy = function () {
var c = SpriteMorph.uber.fullCopy.call(this),
myself = this,
arr = [],
dp,
cb;
c.stopTalking();
@ -1393,7 +1404,7 @@ SpriteMorph.prototype.fullCopy = function () {
c.anchor = null;
c.parts = [];
this.parts.forEach(function (part) {
dp = part.fullCopy();
var dp = part.fullCopy();
dp.nestingScale = part.nestingScale;
dp.rotatesWithAnchor = part.rotatesWithAnchor;
c.attachPart(dp);
@ -1404,10 +1415,12 @@ SpriteMorph.prototype.fullCopy = function () {
SpriteMorph.prototype.appearIn = function (ide) {
// private - used in IDE_Morph.duplicateSprite()
this.name = ide.newSpriteName(this.name);
if (!this.isClone) {
this.name = ide.newSpriteName(this.name);
ide.corral.addSprite(this);
ide.sprites.add(this);
}
ide.stage.add(this);
ide.sprites.add(this);
ide.corral.addSprite(this);
this.parts.forEach(function (part) {
part.appearIn(ide);
});
@ -1531,7 +1544,7 @@ SpriteMorph.prototype.drawNew = function () {
}
}
this.version = Date.now();
this.version = Date.now(); // for observer optimization
};
SpriteMorph.prototype.endWarp = function () {
@ -1921,7 +1934,12 @@ SpriteMorph.prototype.blockTemplates = function (category) {
blocks.push(block('getTimer'));
blocks.push('-');
blocks.push(block('reportAttributeOf'));
if (SpriteMorph.prototype.enableFirstClass) {
blocks.push(block('reportGet'));
}
blocks.push('-');
blocks.push(block('reportURL'));
blocks.push('-');
blocks.push(block('reportIsFastTracking'));
@ -2756,7 +2774,9 @@ SpriteMorph.prototype.userMenu = function () {
// menu.addItem('help', 'nop');
return menu;
}
menu.addItem("duplicate", 'duplicate');
if (!this.isClone) {
menu.addItem("duplicate", 'duplicate');
}
menu.addItem("delete", 'remove');
menu.addItem("move", 'moveCenter');
if (!this.isClone) {
@ -2836,19 +2856,35 @@ SpriteMorph.prototype.clonify = function (stage) {
stage.add(this);
hats = this.allHatBlocksFor('__clone__init__');
hats.forEach(function (block) {
stage.threads.startProcess(block, stage.isThreadSafe);
stage.threads.startProcess(
block,
stage.isThreadSafe,
null, // export result
null, // callback
null, // is clicked
true // right away
);
});
this.endWarp();
};
SpriteMorph.prototype.removeClone = function () {
if (this.isClone) {
// this.stopTalking();
this.parent.threads.stopAllForReceiver(this);
this.corpsify();
this.destroy();
this.parent.cloneCount -= 1;
}
};
// SpriteMorph deleting
SpriteMorph.prototype.corpsify = function () {
this.isCorpse = true;
this.version = Date.now();
};
// SpriteMorph primitives
// SpriteMorph hiding and showing:
@ -3666,6 +3702,53 @@ SpriteMorph.prototype.bounceOffEdge = function () {
this.positionTalkBubble();
};
// SpriteMorph rotation center / fixation point manipulation
SpriteMorph.prototype.setRotationX = function (absoluteX) {
this.setRotationCenter(new Point(absoluteX, this.yPosition()));
};
SpriteMorph.prototype.setRotationY = function (absoluteY) {
this.setRotationCenter(new Point(this.xPosition(), absoluteY));
};
SpriteMorph.prototype.setRotationCenter = function (absoluteCoordinate) {
var delta, normal;
if (!this.costume) {
throw new Error('setting the rotation center requires a costume');
}
delta = absoluteCoordinate.subtract(
new Point(this.xPosition(), this.yPosition())
).divideBy(this.scale).rotateBy(radians(90 - this.heading));
normal = this.costume.rotationCenter.add(new Point(delta.x, -delta.y));
this.costume.rotationCenter = normal;
this.drawNew();
};
SpriteMorph.prototype.xCenter = function () {
var stage = this.parentThatIsA(StageMorph);
if (!stage && this.parent.grabOrigin) { // I'm currently being dragged
stage = this.parent.grabOrigin.origin;
}
if (stage) {
return (this.center().x - stage.center().x) / stage.scale;
}
return this.center().x;
};
SpriteMorph.prototype.yCenter = function () {
var stage = this.parentThatIsA(StageMorph);
if (!stage && this.parent.grabOrigin) { // I'm currently being dragged
stage = this.parent.grabOrigin.origin;
}
if (stage) {
return (stage.center().y - this.center().y) / stage.scale;
}
return this.center().y;
};
// SpriteMorph message broadcasting
SpriteMorph.prototype.allMessageNames = function () {
@ -4363,7 +4446,23 @@ SpriteMorph.prototype.thumbnail = function (extentPoint) {
trg = newCanvas(extentPoint),
ctx = trg.getContext('2d');
function xOut(style, alpha, width) {
var inset = Math.min(extentPoint.x, extentPoint.y) / 10;
ctx.strokeStyle = style;
ctx.globalAlpha = alpha;
ctx.compositeOperation = 'lighter';
ctx.lineWidth = width || 1;
ctx.moveTo(inset, inset);
ctx.lineTo(trg.width - inset, trg.height - inset);
ctx.moveTo(inset, trg.height - inset);
ctx.lineTo(trg.width - inset, inset);
ctx.stroke();
}
ctx.save();
if (this.isCorpse) {
ctx.globalAlpha = 0.3;
}
if (src.width && src.height) {
ctx.scale(scale, scale);
ctx.drawImage(
@ -4372,6 +4471,11 @@ SpriteMorph.prototype.thumbnail = function (extentPoint) {
Math.floor(yOffset / scale)
);
}
if (this.isCorpse) {
ctx.restore();
xOut('white', 0.8, 6);
xOut('black', 0.8, 1);
}
return trg;
};
@ -4709,7 +4813,6 @@ StageMorph.uber = FrameMorph.prototype;
// StageMorph preferences settings
StageMorph.prototype.dimensions = new Point(480, 360); // unscaled extent
StageMorph.prototype.frameRate = 0; // unscheduled per default
StageMorph.prototype.isCachingPrimitives
@ -4845,6 +4948,7 @@ StageMorph.prototype.drawNew = function () {
);
this.image = this.applyGraphicsEffects(this.image);
}
this.version = Date.now(); // for observer optimization
};
StageMorph.prototype.drawOn = function (aCanvas, aRect) {
@ -5256,7 +5360,7 @@ StageMorph.prototype.fireKeyEvent = function (key) {
return this.fireStopAllEvent();
}
this.children.concat(this).forEach(function (morph) {
if (morph instanceof SpriteMorph || morph instanceof StageMorph) {
if (isSnapObject(morph)) {
hats = hats.concat(morph.allHatBlocksForKey(evt));
}
});
@ -5284,7 +5388,7 @@ StageMorph.prototype.fireGreenFlagEvent = function () {
myself = this;
this.children.concat(this).forEach(function (morph) {
if (morph instanceof SpriteMorph || morph instanceof StageMorph) {
if (isSnapObject(morph)) {
hats = hats.concat(morph.allHatBlocksFor('__shout__go__'));
}
});
@ -5327,6 +5431,8 @@ StageMorph.prototype.removeAllClones = function () {
);
clones.forEach(function (clone) {
myself.threads.stopAllForReceiver(clone);
clone.detachFromAnchor();
clone.corpsify();
clone.destroy();
});
this.cloneCount = 0;
@ -5575,7 +5681,12 @@ StageMorph.prototype.blockTemplates = function (category) {
blocks.push(block('getTimer'));
blocks.push('-');
blocks.push(block('reportAttributeOf'));
if (SpriteMorph.prototype.enableFirstClass) {
blocks.push(block('reportGet'));
}
blocks.push('-');
blocks.push(block('reportURL'));
blocks.push('-');
blocks.push(block('reportIsFastTracking'));
@ -5848,9 +5959,16 @@ StageMorph.prototype.userMenu = function () {
StageMorph.prototype.showAll = function () {
var myself = this;
this.children.forEach(function (m) {
m.show();
m.keepWithin(myself);
if (m.fixLayout) {m.fixLayout(); }
if (m instanceof SpriteMorph) {
if (!m.anchor) {
m.show();
m.keepWithin(myself);
}
} else {
m.show();
m.keepWithin(myself);
if (m.fixLayout) {m.fixLayout(); }
}
});
};
@ -6173,14 +6291,31 @@ SpriteBubbleMorph.prototype.init = function (
SpriteBubbleMorph.prototype.dataAsMorph = function (data, toggle) {
var contents,
isTable,
sprite = SpriteMorph.prototype,
isText,
img,
scaledImg,
width;
if (data instanceof Morph) {
contents = data;
if (isSnapObject(data)) {
img = data.thumbnail(new Point(40, 40));
contents = new Morph();
contents.silentSetWidth(img.width);
contents.silentSetHeight(img.height);
contents.image = img;
contents.version = data.version;
contents.step = function () {
if (this.version !== data.version) {
img = data.thumbnail(new Point(40, 40));
this.image = img;
this.version = data.version;
this.changed();
}
};
} else {
contents = data;
}
} else if (isString(data)) {
isText = true;
contents = new TextMorph(
@ -6209,7 +6344,13 @@ SpriteBubbleMorph.prototype.dataAsMorph = function (data, toggle) {
contents.silentSetHeight(data.height);
contents.image = data;
} else if (data instanceof List) {
if (!toggle && data.isTable()) {
if (toggle && this.contentsMorph) {
isTable = (this.contentsMorph instanceof ListWatcherMorph);
} else {
isTable = data.isTable();
}
if (isTable) { // (!toggle && data.isTable()) {
contents = new TableFrameMorph(new TableMorph(data, 10));
if (this.stage) {
contents.expand(this.stage.extent().translateBy(
@ -6540,7 +6681,7 @@ Costume.prototype.edit = function (aWorld, anIDE, isnew, oncancel, onsubmit) {
newCanvas(StageMorph.prototype.dimensions) :
this.contents,
isnew ?
new Point(240, 180) :
null :
this.rotationCenter,
function (img, rc) {
myself.contents = img;
@ -6963,6 +7104,7 @@ CellMorph.prototype.init = function (contents, color, idx, parentCell) {
);
this.color = color || new Color(255, 140, 0);
this.isBig = false;
this.version = null; // only for observing sprites
this.drawNew();
};
@ -7012,6 +7154,16 @@ CellMorph.prototype.fixLayout = function () {
// CellMorph drawing:
CellMorph.prototype.update = function () {
// special case for observing sprites
if (!isSnapObject(this.contents)) {
return;
}
if (this.version !== this.contents.version) {
this.drawNew();
}
};
CellMorph.prototype.drawNew = function (toggle, type) {
var context,
txt,
@ -7029,11 +7181,21 @@ CellMorph.prototype.drawNew = function (toggle, type) {
// re-build my contents
if (toggle || (this.contentsMorph && !isSameList && !isSameTable)) {
this.contentsMorph.destroy();
this.version = null;
}
if (toggle || (!isSameList && !isSameTable)) {
if (this.contents instanceof Morph) {
this.contentsMorph = this.contents;
if (isSnapObject(this.contents)) {
img = this.contents.thumbnail(new Point(40, 40));
this.contentsMorph = new Morph();
this.contentsMorph.silentSetWidth(img.width);
this.contentsMorph.silentSetHeight(img.height);
this.contentsMorph.image = img;
this.version = this.contents.version;
} else {
this.contentsMorph = this.contents;
}
} else if (isString(this.contents)) {
txt = this.contents.length > 500 ?
this.contents.slice(0, 500) + '...' : this.contents;
@ -7444,6 +7606,8 @@ WatcherMorph.prototype.update = function () {
}
if (this.cellMorph.contentsMorph instanceof ListWatcherMorph) {
this.cellMorph.contentsMorph.update();
} else if (isSnapObject(this.cellMorph.contents)) {
this.cellMorph.update();
}
};

21
paint.js 100755 → 100644
Wyświetl plik

@ -61,19 +61,18 @@
Oct 02 - revert disable smoothing (Jens)
Dec 15 - center rotation point on costume creating (Craxic)
Jan 18 - avoid pixel collision detection in PaintCanvas (Jens)
*/
Mar 22 - fixed automatic rotation center point mechanism (Jens)
*/
/*global Point, Rectangle, DialogBoxMorph, fontHeight, AlignmentMorph,
FrameMorph, PushButtonMorph, Color, SymbolMorph, newCanvas, Morph, TextMorph,
CostumeIconMorph, IDE_Morph, Costume, SpriteMorph, nop, Image, WardrobeMorph,
TurtleIconMorph, localize, MenuMorph, InputFieldMorph, SliderMorph,
ToggleMorph, ToggleButtonMorph, BoxMorph, modules, radians,
MorphicPreferences, getDocumentPositionOf, StageMorph
*/
/*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*/
// Global stuff ////////////////////////////////////////////////////////
modules.paint = '2016-January-18';
modules.paint = '2016-May-02';
// Declarations
@ -253,7 +252,6 @@ PaintEditorMorph.prototype.buildScaleBox = function () {
PaintEditorMorph.prototype.openIn = function (world, oldim, oldrc, callback) {
// Open the editor in a world with an optional image to edit
this.oldim = oldim;
this.oldrc = oldrc.copy();
this.callback = callback || nop;
this.processKeyUp = function () {
@ -268,9 +266,10 @@ PaintEditorMorph.prototype.openIn = function (world, oldim, oldrc, callback) {
//merge oldim:
if (this.oldim) {
this.paper.automaticCrosshairs = isNil(oldrc);
this.paper.centermerge(this.oldim, this.paper.paper);
this.paper.rotationCenter =
this.oldrc.add(
(oldrc || new Point(0, 0)).add(
new Point(
(this.paper.paper.width - this.oldim.width) / 2,
(this.paper.paper.height - this.oldim.height) / 2

36
store.js 100755 → 100644
Wyświetl plik

@ -56,11 +56,11 @@ Color, List, newCanvas, Costume, Sound, Audio, IDE_Morph, ScriptsMorph,
BlockMorph, ArgMorph, InputSlotMorph, TemplateSlotMorph, CommandSlotMorph,
FunctionSlotMorph, MultiArgMorph, ColorSlotMorph, nop, CommentMorph, isNil,
localize, sizeOf, ArgLabelMorph, SVG_Costume, MorphicPreferences,
SyntaxElementMorph, Variable*/
SyntaxElementMorph, Variable, isSnapObject, console*/
// Global stuff ////////////////////////////////////////////////////////
modules.store = '2016-March-16';
modules.store = '2016-May-02';
// XML_Serializer ///////////////////////////////////////////////////////
@ -974,7 +974,16 @@ SnapSerializer.prototype.loadScript = function (model) {
return;
}
if (block) {
block.nextBlock(nextBlock);
if (block.nextBlock && (nextBlock instanceof CommandBlockMorph)) {
block.nextBlock(nextBlock);
} else { // +++
console.log(
'SNAP: expecting a command but getting a reporter:\n' +
' ' + block.blockSpec + '\n' +
' ' + nextBlock.blockSpec
);
return topBlock;
}
} else {
topBlock = nextBlock;
}
@ -1605,10 +1614,14 @@ VariableFrame.prototype.toXML = function (serializer) {
dta = serializer.format(
'<variable name="@">%</variable>',
v,
typeof val === 'object' ? serializer.store(val)
: typeof val === 'boolean' ?
serializer.format('<bool>$</bool>', val)
: serializer.format('<l>$</l>', val)
typeof val === 'object' ?
(isSnapObject(val) ? ''
: serializer.store(val))
: typeof val === 'boolean' ?
serializer.format(
'<bool>$</bool>', val
)
: serializer.format('<l>$</l>', val)
);
}
return vars + dta;
@ -1897,7 +1910,8 @@ List.prototype.toXML = function (serializer, mediaContext) {
xml += serializer.format(
'<item>%</item>',
typeof value === 'object' ?
serializer.store(value, mediaContext)
(isSnapObject(value) ? ''
: serializer.store(value, mediaContext))
: typeof value === 'boolean' ?
serializer.format('<bool>$</bool>', value)
: serializer.format('<l>$</l>', value)
@ -1916,7 +1930,8 @@ List.prototype.toXML = function (serializer, mediaContext) {
xml += serializer.format(
'<item>%</item>',
typeof value === 'object' ?
serializer.store(value, mediaContext)
(isSnapObject(value) ? ''
: serializer.store(value, mediaContext))
: typeof value === 'boolean' ?
serializer.format('<bool>$</bool>', value)
: serializer.format('<l>$</l>', value)
@ -1933,7 +1948,8 @@ List.prototype.toXML = function (serializer, mediaContext) {
return xml + serializer.format(
'<item>%</item>',
typeof item === 'object' ?
serializer.store(item, mediaContext)
(isSnapObject(item) ? ''
: serializer.store(item, mediaContext))
: typeof item === 'boolean' ?
serializer.format('<bool>$</bool>', item)
: serializer.format('<l>$</l>', item)

29
tables.js 100755 → 100644
Wyświetl plik

@ -68,9 +68,9 @@
MorphicPreferences, FrameMorph, HandleMorph, DialogBoxMorph, isString,
SpriteMorph, Context, Costume, ArgMorph, BlockEditorMorph,
SyntaxElementMorph, MenuMorph, SpriteBubbleMorph, SpeechBubbleMorph,
CellMorph, ListWatcherMorph, isNil, BoxMorph, Variable*/
CellMorph, ListWatcherMorph, isNil, BoxMorph, Variable, isSnapObject*/
modules.tables = '2016-February-24';
modules.tables = '2016-May-02';
var Table;
var TableCellMorph;
@ -307,6 +307,10 @@ TableCellMorph.prototype.setData = function (data, extent) {
// note: don't call changed(), let the TableMorph handle it instead
};
TableCellMorph.prototype.getData = function () {
return this.data instanceof Array ? this.data[0] : this.data;
};
TableCellMorph.prototype.drawNew = function () {
this.image = newCanvas(this.extent());
this.drawData();
@ -373,7 +377,11 @@ TableCellMorph.prototype.drawData = function (lbl, bg) {
TableCellMorph.prototype.dataRepresentation = function (dta) {
if (dta instanceof Morph) {
return dta.fullImageClassic();
if (isSnapObject(dta)) {
return dta.thumbnail(new Point(40, 40));
} else {
return dta.fullImageClassic();
}
} else if (isString(dta)) {
return dta.length > 100 ? dta.slice(0, 100) + '...' : dta;
} else if (typeof dta === 'number') {
@ -544,6 +552,9 @@ TableMorph.prototype.init = function (
this.resizeCol = null;
this.resizeRow = null;
// cached property for updating (don not persist):
this.wantsUpdate = false;
// initialize inherited properties:
// make sure not to draw anything just yet
// therefore omit FrameMorph's properties (not needed here)
@ -697,6 +708,9 @@ TableMorph.prototype.buildCells = function () {
).add(pos)
);
this.add(cell);
if (isSnapObject(cell.getData())) {
this.wantsUpdate = true;
}
}
}
this.add(this.hBar);
@ -718,6 +732,9 @@ TableMorph.prototype.drawData = function (noScrollUpdate) {
!r ? r : r + this.startRow - 1
)
);
if (isSnapObject(cell.getData())) {
this.wantsUpdate = true;
}
}
}
if (!noScrollUpdate) {this.updateScrollBars(); }
@ -777,7 +794,10 @@ TableMorph.prototype.update = function () {
version = this.table instanceof List ?
this.table.version(this.startRow, this.rows)
: this.table.lastChanged;
if (this.tableVersion === version) { return; }
if (this.tableVersion === version && !this.wantsUpdate) {
return;
}
this.wantsUpdate = false;
if (this.table instanceof List) {
oldCols = this.columns.length;
oldRows = this.rows;
@ -1048,6 +1068,7 @@ TableMorph.prototype.showListView = function () {
view.drawNew(true);
} else if (view instanceof SpeechBubbleMorph) {
view.contents = new ListWatcherMorph(this.table);
view.contents.step = view.contents.update;
view.contents.expand(this.extent());
view.drawNew(true);
} else { // watcher cell

211
threads.js 100755 → 100644
Wyświetl plik

@ -59,9 +59,9 @@ degrees, detect, nop, radians, ReporterSlotMorph, CSlotMorph, RingMorph,
IDE_Morph, ArgLabelMorph, localize, XML_Element, hex_sha512, TableDialogMorph,
StageMorph, SpriteMorph, StagePrompterMorph, Note, modules, isString, copy,
isNil, WatcherMorph, List, ListWatcherMorph, alert, console, TableMorph,
TableFrameMorph*/
TableFrameMorph, isSnapObject*/
modules.threads = '2016-March-16';
modules.threads = '2016-May-02';
var ThreadManager;
var Process;
@ -191,7 +191,8 @@ ThreadManager.prototype.startProcess = function (
isThreadSafe,
exportResult,
callback,
isClicked
isClicked,
rightAway
) {
var active = this.findProcess(block),
top = block.topBlock(),
@ -203,13 +204,16 @@ ThreadManager.prototype.startProcess = function (
active.stop();
this.removeTerminatedProcesses();
}
newProc = new Process(block.topBlock(), callback);
newProc = new Process(block.topBlock(), callback, rightAway);
newProc.exportResult = exportResult;
newProc.isClicked = isClicked || false;
if (!newProc.homeContext.receiver.isClone) {
top.addHighlight();
}
this.processes.push(newProc);
if (rightAway) {
newProc.runStep();
}
return newProc;
};
@ -351,7 +355,7 @@ ThreadManager.prototype.doWhen = function (block, stopIt) {
pred,
null,
null,
20,
50,
'the predicate takes\ntoo long for a\ncustom hat block',
true // suppress errors => handle them right here instead
) === true) {
@ -430,8 +434,9 @@ Process.prototype = {};
Process.prototype.constructor = Process;
Process.prototype.timeout = 500; // msecs after which to force yield
Process.prototype.isCatchingErrors = true;
Process.prototype.enableLiveCoding = false; // experimental
function Process(topBlock, onComplete) {
function Process(topBlock, onComplete, rightAway) {
this.topBlock = topBlock || null;
this.readyToYield = false;
@ -462,7 +467,9 @@ function Process(topBlock, onComplete) {
topBlock.blockSequence(),
this.homeContext
);
this.pushContext('doYield'); // highlight top block
if (!rightAway) {
this.pushContext('doYield'); // highlight top block
}
}
}
@ -485,8 +492,9 @@ Process.prototype.runStep = function (deadline) {
this.readyToYield = false;
while (!this.readyToYield
&& this.context
&& (this.isAtomic ?
(Date.now() - this.lastYield < this.timeout) : true)
&& // (this.isAtomic ?
(Date.now() - this.lastYield < this.timeout)
// : true)
) {
// also allow pausing inside atomic steps - for PAUSE block primitive:
if (this.isPaused) {
@ -870,7 +878,8 @@ Process.prototype.reify = function (topBlock, parameterNames, isCustomBlock) {
i = 0;
if (topBlock) {
context.expression = topBlock.fullCopy();
context.expression = this.enableLiveCoding ?
topBlock : topBlock.fullCopy();
context.expression.show(); // be sure to make visible if in app mode
if (!isCustomBlock) {
@ -888,7 +897,8 @@ Process.prototype.reify = function (topBlock, parameterNames, isCustomBlock) {
}
} else {
context.expression = [this.context.expression.fullCopy()];
context.expression = this.enableLiveCoding ? [this.context.expression]
: [this.context.expression.fullCopy()];
}
context.inputs = parameterNames.asArray();
@ -1294,16 +1304,20 @@ Process.prototype.doDeclareVariables = function (varNames) {
Process.prototype.doSetVar = function (varName, value) {
var varFrame = this.context.variables,
name = varName;
name = varName,
rcvr;
if (name instanceof Context) {
rcvr = this.blockReceiver();
if (name.expression.selector === 'reportGetVar') {
name.variables.setVar(
name.expression.blockSpec,
value,
this.blockReceiver()
rcvr
);
return;
}
this.doSet(name, value);
return;
}
varFrame.setVar(name, value, this.blockReceiver());
};
@ -1959,7 +1973,7 @@ Process.prototype.blockReceiver = function () {
// Process sound primitives (interpolated)
Process.prototype.doPlaySoundUntilDone = function (name) {
var sprite = this.homeContext.receiver;
var sprite = this.blockReceiver();
if (this.context.activeAudio === null) {
this.context.activeAudio = sprite.playSound(name);
}
@ -2060,7 +2074,7 @@ Process.prototype.doBroadcast = function (message) {
if (message !== '') {
stage.lastMessage = message;
stage.children.concat(stage).forEach(function (morph) {
if (morph instanceof SpriteMorph || morph instanceof StageMorph) {
if (isSnapObject(morph)) {
hats = hats.concat(morph.allHatBlocksFor(message));
}
});
@ -2116,6 +2130,12 @@ Process.prototype.assertType = function (thing, typeString) {
throw new Error('expecting ' + typeString + ' but getting ' + thingType);
};
Process.prototype.assertAlive = function (thing) {
if (thing && thing.isCorpse) {
throw new Error('cannot operate on a deleted sprite');
}
};
Process.prototype.reportTypeOf = function (thing) {
// answer a string denoting the argument's type
var exp;
@ -2134,6 +2154,12 @@ Process.prototype.reportTypeOf = function (thing) {
if (thing instanceof List) {
return 'list';
}
if (thing instanceof SpriteMorph) {
return 'sprite';
}
if (thing instanceof StageMorph) {
return 'stage';
}
if (thing instanceof Context) {
if (thing.expression instanceof RingMorph) {
return thing.expression.dataType();
@ -2473,6 +2499,11 @@ Process.prototype.getOtherObject = function (name, thisObj, stageObj) {
// private, find the sprite indicated by the given name
// either onstage or in the World's hand
// experimental: deal with first-class sprites
if (isSnapObject(name)) {
return name;
}
var stage = isNil(stageObj) ?
thisObj.parentThatIsA(StageMorph) : stageObj,
thatObj = null;
@ -2564,7 +2595,7 @@ Process.prototype.doGotoObject = function (name) {
// Process temporary cloning (Scratch-style)
Process.prototype.createClone = function (name) {
var thisObj = this.homeContext.receiver,
var thisObj = this.blockReceiver(),
thatObj;
if (!name) {return; }
@ -2623,7 +2654,14 @@ Process.prototype.objectTouchingObject = function (thisObj, name) {
thisObj.isTouching(stage.penTrailsMorph())) {
return true;
}
those = this.getObjectsNamed(name, thisObj, stage); // clones
if (isSnapObject(name)) {
return thisObj.isTouching(name);
}
if (name instanceof List) { // assume all elements to be sprites
those = name.itemsArray();
} else {
those = this.getObjectsNamed(name, thisObj, stage); // clones
}
if (those.some(function (any) {
return thisObj.isTouching(any);
})) {
@ -2640,7 +2678,7 @@ Process.prototype.objectTouchingObject = function (thisObj, name) {
Process.prototype.reportTouchingColor = function (aColor) {
// also check for any parts (subsprites)
var thisObj = this.homeContext.receiver,
var thisObj = this.blockReceiver(),
stage;
if (thisObj) {
@ -2661,7 +2699,7 @@ Process.prototype.reportTouchingColor = function (aColor) {
Process.prototype.reportColorIsTouchingColor = function (color1, color2) {
// also check for any parts (subsprites)
var thisObj = this.homeContext.receiver,
var thisObj = this.blockReceiver(),
stage;
if (thisObj) {
@ -2713,6 +2751,7 @@ Process.prototype.reportAttributeOf = function (attribute, name) {
stage;
if (thisObj) {
this.assertAlive(thisObj);
stage = thisObj.parentThatIsA(StageMorph);
if (stage.name === name) {
thatObj = stage;
@ -2720,6 +2759,7 @@ Process.prototype.reportAttributeOf = function (attribute, name) {
thatObj = this.getOtherObject(name, thisObj, stage);
}
if (thatObj) {
this.assertAlive(thatObj);
if (attribute instanceof Context) {
return this.reportContextFor(attribute, thatObj);
}
@ -2747,6 +2787,139 @@ Process.prototype.reportAttributeOf = function (attribute, name) {
return '';
};
Process.prototype.reportGet = function (query) {
// experimental, answer a reference to a first-class member
// or a list of first-class members
var thisObj = this.blockReceiver(),
neighborhood,
stage,
objName;
if (thisObj) {
switch (this.inputOption(query)) {
case 'self' :
return thisObj;
case 'other sprites':
stage = thisObj.parentThatIsA(StageMorph);
return new List(
stage.children.filter(function (each) {
return each instanceof SpriteMorph &&
each !== thisObj;
})
);
case 'parts':
return new List(thisObj.parts || []);
case 'anchor':
return thisObj.anchor || '';
case 'parent':
return thisObj.exemplar || '';
case 'children':
return new List(thisObj.specimens ? thisObj.specimens() : []);
case 'clones':
stage = thisObj.parentThatIsA(StageMorph);
objName = thisObj.name || thisObj.cloneOriginName;
return new List(
stage.children.filter(function (each) {
return each.isClone &&
(each !== thisObj) &&
(each.cloneOriginName === objName);
})
);
case 'other clones':
return thisObj.isClone ? this.reportGet(['clones']) : new List();
case 'neighbors':
stage = thisObj.parentThatIsA(StageMorph);
neighborhood = thisObj.bounds.expandBy(new Point(
thisObj.width(),
thisObj.height()
));
return new List(
stage.children.filter(function (each) {
return each instanceof SpriteMorph &&
(each !== thisObj) &&
each.bounds.intersects(neighborhood);
})
);
case 'dangling?':
return !thisObj.rotatesWithAnchor;
case 'rotation x':
return thisObj.xPosition();
case 'rotation y':
return thisObj.yPosition();
case 'center x':
return thisObj.xCenter();
case 'center y':
return thisObj.yCenter();
case 'name':
return thisObj.name;
case 'stage':
return thisObj.parentThatIsA(StageMorph);
}
}
return '';
};
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') {
throw new Error(localize('unsupported attribute'));
}
name = attribute.expression.inputs()[0].evaluate();
if (name instanceof Array) {
name = name[0];
}
switch (name) {
case 'anchor':
this.assertType(rcvr, 'sprite');
if (value instanceof SpriteMorph) {
// avoid circularity here, because the GUI already checks for
// conflicts while the user drags parts over prospective targets
if (!rcvr.enableNesting || contains(rcvr.allParts(), value)) {
throw new Error(
localize('unable to nest\n(disabled or circular?)')
);
}
value.attachPart(rcvr);
} else {
rcvr.detachFromAnchor();
}
break;
case 'parent':
this.assertType(rcvr, 'sprite');
value = value instanceof SpriteMorph ? value : null;
// needed: circularity avoidance
rcvr.setExemplar(value);
break;
case 'dangling?':
this.assertType(rcvr, 'sprite');
this.assertType(value, 'Boolean');
rcvr.rotatesWithAnchor = !value;
rcvr.version = Date.now();
break;
case 'rotation x':
this.assertType(rcvr, 'sprite');
this.assertType(value, 'number');
rcvr.setRotationX(value);
break;
case 'rotation y':
this.assertType(rcvr, 'sprite');
this.assertType(value, 'number');
rcvr.setRotationY(value);
break;
default:
throw new Error(
'"' + localize(name) + '" ' + localize('is read-only')
);
}
};
Process.prototype.reportContextFor = function (context, otherObj) {
// Private - return a copy of the context
// and bind it to another receiver