kopia lustrzana https://github.com/backface/turtlestitch
453 wiersze
15 KiB
JavaScript
453 wiersze
15 KiB
JavaScript
SnapSerializer.prototype.app = 'TurtleStitch 2.0, http://www.turtlestitch.org';
|
|
SnapSerializer.prototype.thumbnailSize = new Point(480, 360);
|
|
|
|
SnapSerializer.prototype.openProject = function (project, ide) {
|
|
var stage = ide.stage,
|
|
sprites = [],
|
|
sprite;
|
|
if (!project || !project.stage) {
|
|
return;
|
|
}
|
|
ide.projectName = project.name;
|
|
ide.projectNotes = project.notes || '';
|
|
ide.origName = project.origName || '';
|
|
ide.origCreator = project.origCreator || '';
|
|
ide.creator = project.creator || '';
|
|
ide.remixHistory = project.remixHistorycreat || '';
|
|
//console.log("name: " + ide.projectName + ", creator: " + ide.creator + ", origName: " + ide.origName + ", origCreator: " + ide.origCreator);
|
|
if (ide.globalVariables) {
|
|
ide.globalVariables = project.globalVariables;
|
|
}
|
|
if (stage) {
|
|
stage.destroy();
|
|
}
|
|
ide.add(project.stage);
|
|
ide.stage = project.stage;
|
|
sprites = ide.stage.children.filter(function (child) {
|
|
return child instanceof SpriteMorph;
|
|
});
|
|
sprites.sort(function (x, y) {
|
|
return x.idx - y.idx;
|
|
});
|
|
|
|
ide.sprites = new List(sprites);
|
|
sprite = sprites[0] || project.stage;
|
|
|
|
if (sizeOf(this.mediaDict) > 0) {
|
|
ide.hasChangedMedia = false;
|
|
this.mediaDict = {};
|
|
} else {
|
|
ide.hasChangedMedia = true;
|
|
}
|
|
project.stage.drawNew();
|
|
ide.createCorral();
|
|
ide.selectSprite(sprite);
|
|
ide.fixLayout();
|
|
|
|
// force watchers to update
|
|
//project.stage.watchers().forEach(function (watcher) {
|
|
// watcher.onNextStep = function () {this.currentValue = null;};
|
|
//})
|
|
|
|
ide.world().keyboardReceiver = project.stage;
|
|
ide.stage.initCamera();
|
|
};
|
|
|
|
SnapSerializer.prototype.rawLoadProjectModel = function (xmlNode) {
|
|
// private
|
|
var myself = this,
|
|
project = {sprites: {}},
|
|
model,
|
|
nameID;
|
|
|
|
this.project = project;
|
|
|
|
model = {project: xmlNode };
|
|
if (+xmlNode.attributes.version > this.version) {
|
|
throw 'Project uses newer version of Serializer';
|
|
}
|
|
|
|
/* Project Info */
|
|
|
|
this.objects = {};
|
|
project.name = model.project.attributes.name;
|
|
project.origName = model.project.childNamed('origName') ? model.project.childNamed('origName').contents : ""
|
|
project.creator = model.project.childNamed('creator') ? model.project.childNamed('creator').contents : ""
|
|
project.origCreator = model.project.childNamed('origCreator') ? model.project.childNamed('origCreator').contents : ""
|
|
project.remixHistory = model.project.childNamed('origCreator') ? model.project.childNamed('origCreator').contents : ""
|
|
|
|
if (!project.name) {
|
|
nameID = 1;
|
|
while (
|
|
Object.prototype.hasOwnProperty.call(
|
|
localStorage,
|
|
'-snap-project-Untitled ' + nameID
|
|
)
|
|
) {
|
|
nameID += 1;
|
|
}
|
|
project.name = 'Untitled ' + nameID;
|
|
}
|
|
model.notes = model.project.childNamed('notes');
|
|
if (model.notes) {
|
|
project.notes = model.notes.contents;
|
|
}
|
|
model.globalVariables = model.project.childNamed('variables');
|
|
project.globalVariables = new VariableFrame();
|
|
|
|
/* Stage */
|
|
|
|
model.stage = model.project.require('stage');
|
|
StageMorph.prototype.frameRate = 0;
|
|
project.stage = new StageMorph(project.globalVariables);
|
|
if (Object.prototype.hasOwnProperty.call(
|
|
model.stage.attributes,
|
|
'id'
|
|
)) {
|
|
this.objects[model.stage.attributes.id] = project.stage;
|
|
}
|
|
if (model.stage.attributes.name) {
|
|
project.stage.name = model.stage.attributes.name;
|
|
}
|
|
if (model.stage.attributes.scheduled === 'true') {
|
|
project.stage.fps = 30;
|
|
StageMorph.prototype.frameRate = 30;
|
|
}
|
|
model.pentrails = model.stage.childNamed('pentrails');
|
|
if (model.pentrails) {
|
|
project.pentrails = new Image();
|
|
project.pentrails.onload = function () {
|
|
if (project.stage.trailsCanvas) { // work-around a bug in FF
|
|
normalizeCanvas(project.stage.trailsCanvas);
|
|
var context = project.stage.trailsCanvas.getContext('2d');
|
|
if (context) context.drawImage(project.pentrails, 0, 0);
|
|
project.stage.changed();
|
|
}
|
|
};
|
|
project.pentrails.src = model.pentrails.contents;
|
|
}
|
|
project.stage.setTempo(model.stage.attributes.tempo);
|
|
StageMorph.prototype.dimensions = new Point(480, 360);
|
|
if (model.stage.attributes.width) {
|
|
StageMorph.prototype.dimensions.x =
|
|
Math.max(+model.stage.attributes.width, 480);
|
|
}
|
|
if (model.stage.attributes.height) {
|
|
StageMorph.prototype.dimensions.y =
|
|
Math.max(+model.stage.attributes.height, 180);
|
|
}
|
|
project.stage.setExtent(StageMorph.prototype.dimensions);
|
|
SpriteMorph.prototype.useFlatLineEnds =
|
|
model.stage.attributes.lines === 'flat';
|
|
BooleanSlotMorph.prototype.isTernary =
|
|
model.stage.attributes.ternary !== 'false';
|
|
project.stage.isThreadSafe =
|
|
model.stage.attributes.threadsafe === 'true';
|
|
StageMorph.prototype.enableCodeMapping =
|
|
model.stage.attributes.codify === 'true';
|
|
StageMorph.prototype.enableInheritance =
|
|
model.stage.attributes.inheritance !== 'false';
|
|
StageMorph.prototype.enableSublistIDs =
|
|
model.stage.attributes.sublistIDs === 'true';
|
|
|
|
model.hiddenPrimitives = model.project.childNamed('hidden');
|
|
if (model.hiddenPrimitives) {
|
|
model.hiddenPrimitives.contents.split(' ').forEach(
|
|
function (sel) {
|
|
if (sel) {
|
|
StageMorph.prototype.hiddenPrimitives[sel] = true;
|
|
}
|
|
}
|
|
);
|
|
}
|
|
|
|
model.codeHeaders = model.project.childNamed('headers');
|
|
if (model.codeHeaders) {
|
|
model.codeHeaders.children.forEach(function (xml) {
|
|
StageMorph.prototype.codeHeaders[xml.tag] = xml.contents;
|
|
});
|
|
}
|
|
|
|
model.codeMappings = model.project.childNamed('code');
|
|
if (model.codeMappings) {
|
|
model.codeMappings.children.forEach(function (xml) {
|
|
StageMorph.prototype.codeMappings[xml.tag] = xml.contents;
|
|
});
|
|
}
|
|
|
|
model.globalBlocks = model.project.childNamed('blocks');
|
|
if (model.globalBlocks) {
|
|
this.loadCustomBlocks(project.stage, model.globalBlocks, true);
|
|
this.populateCustomBlocks(
|
|
project.stage,
|
|
model.globalBlocks,
|
|
true
|
|
);
|
|
}
|
|
this.loadObject(project.stage, model.stage);
|
|
|
|
/* Sprites */
|
|
|
|
model.sprites = model.stage.require('sprites');
|
|
project.sprites[project.stage.name] = project.stage;
|
|
|
|
model.sprites.childrenNamed('sprite').forEach(function (model) {
|
|
myself.loadValue(model);
|
|
});
|
|
|
|
// restore inheritance and nesting associations
|
|
myself.project.stage.children.forEach(function (sprite) {
|
|
var exemplar, anchor;
|
|
if (sprite.inheritanceInfo) { // only sprites can inherit
|
|
exemplar = myself.project.sprites[
|
|
sprite.inheritanceInfo.exemplar
|
|
];
|
|
if (exemplar) {
|
|
sprite.setExemplar(exemplar);
|
|
}
|
|
sprite.inheritedAttributes = sprite.inheritanceInfo.delegated || [];
|
|
}
|
|
if (sprite.nestingInfo) { // only sprites may have nesting info
|
|
anchor = myself.project.sprites[sprite.nestingInfo.anchor];
|
|
if (anchor) {
|
|
anchor.attachPart(sprite);
|
|
}
|
|
sprite.rotatesWithAnchor = (sprite.nestingInfo.synch === 'true');
|
|
}
|
|
});
|
|
myself.project.stage.children.forEach(function (sprite) {
|
|
var costume;
|
|
if (sprite.nestingInfo) { // only sprites may have nesting info
|
|
sprite.nestingScale = +(sprite.nestingInfo.scale || sprite.scale);
|
|
delete sprite.nestingInfo;
|
|
}
|
|
['scripts', 'costumes', 'sounds'].forEach(function (att) {
|
|
if (sprite.inheritsAttribute(att)) {
|
|
sprite.refreshInheritedAttribute(att);
|
|
}
|
|
});
|
|
if (sprite.inheritsAttribute('costumes')) {
|
|
costume = sprite.costumes.asArray()[
|
|
sprite.inheritanceInfo.costumeNumber - 1
|
|
];
|
|
if (costume) {
|
|
if (costume.loaded) {
|
|
sprite.wearCostume(costume, true);
|
|
} else {
|
|
costume.loaded = function () {
|
|
sprite.wearCostume(costume, true);
|
|
this.loaded = true;
|
|
};
|
|
}
|
|
}
|
|
}
|
|
delete sprite.inheritanceInfo;
|
|
});
|
|
|
|
/* Global Variables */
|
|
|
|
if (model.globalVariables) {
|
|
this.loadVariables(
|
|
project.globalVariables,
|
|
model.globalVariables
|
|
);
|
|
}
|
|
|
|
this.objects = {};
|
|
|
|
/* Watchers */
|
|
|
|
model.sprites.childrenNamed('watcher').forEach(function (model) {
|
|
var watcher, color, target, hidden, extX, extY;
|
|
|
|
color = myself.loadColor(model.attributes.color);
|
|
target = Object.prototype.hasOwnProperty.call(
|
|
model.attributes,
|
|
'scope'
|
|
) ? project.sprites[model.attributes.scope] : null;
|
|
|
|
// determine whether the watcher is hidden, slightly
|
|
// complicated to retain backward compatibility
|
|
// with former tag format: hidden="hidden"
|
|
// now it's: hidden="true"
|
|
hidden = Object.prototype.hasOwnProperty.call(
|
|
model.attributes,
|
|
'hidden'
|
|
) && (model.attributes.hidden !== 'false');
|
|
|
|
if (Object.prototype.hasOwnProperty.call(
|
|
model.attributes,
|
|
'var'
|
|
)) {
|
|
watcher = new WatcherMorph(
|
|
model.attributes['var'],
|
|
color,
|
|
isNil(target) ? project.globalVariables
|
|
: target.variables,
|
|
model.attributes['var'],
|
|
hidden
|
|
);
|
|
} else {
|
|
watcher = new WatcherMorph(
|
|
localize(myself.watcherLabels[model.attributes.s]),
|
|
color,
|
|
target,
|
|
model.attributes.s,
|
|
hidden
|
|
);
|
|
}
|
|
watcher.setStyle(model.attributes.style || 'normal');
|
|
if (watcher.style === 'slider') {
|
|
watcher.setSliderMin(model.attributes.min || '1', true);
|
|
watcher.setSliderMax(model.attributes.max || '100', true);
|
|
}
|
|
watcher.setPosition(
|
|
project.stage.topLeft().add(new Point(
|
|
+model.attributes.x || 0,
|
|
+model.attributes.y || 0
|
|
))
|
|
);
|
|
project.stage.add(watcher);
|
|
watcher.onNextStep = function () {this.currentValue = null; };
|
|
|
|
// set watcher's contentsMorph's extent if it is showing a list and
|
|
// its monitor dimensions are given
|
|
if (watcher.currentValue instanceof List) {
|
|
extX = model.attributes.extX;
|
|
if (extX) {
|
|
watcher.cellMorph.contentsMorph.setWidth(+extX);
|
|
}
|
|
extY = model.attributes.extY;
|
|
if (extY) {
|
|
watcher.cellMorph.contentsMorph.setHeight(+extY);
|
|
}
|
|
// adjust my contentsMorph's handle position
|
|
watcher.cellMorph.contentsMorph.handle.drawNew();
|
|
}
|
|
});
|
|
|
|
// clear sprites' inherited methods caches, if any
|
|
myself.project.stage.children.forEach(function (sprite) {
|
|
sprite.inheritedMethodsCache = [];
|
|
});
|
|
|
|
this.objects = {};
|
|
return project;
|
|
};
|
|
|
|
|
|
StageMorph.prototype.toXML = function (serializer) {
|
|
var thumbnail = normalizeCanvas(
|
|
this.thumbnail(SnapSerializer.prototype.thumbnailSize),
|
|
true
|
|
),
|
|
thumbdata,
|
|
ide = this.parentThatIsA(IDE_Morph);
|
|
|
|
// catch cross-origin tainting exception when using SVG costumes
|
|
try {
|
|
thumbdata = thumbnail.toDataURL('image/png');
|
|
} catch (error) {
|
|
thumbdata = null;
|
|
}
|
|
|
|
function code(key) {
|
|
var str = '';
|
|
Object.keys(StageMorph.prototype[key]).forEach(
|
|
function (selector) {
|
|
str += (
|
|
'<' + selector + '>' +
|
|
XML_Element.prototype.escape(
|
|
StageMorph.prototype[key][selector]
|
|
) +
|
|
'</' + selector + '>'
|
|
);
|
|
}
|
|
);
|
|
return str;
|
|
}
|
|
|
|
this.removeAllClones();
|
|
return serializer.format(
|
|
'<project name="@" app="@" version="@">\n' +
|
|
'<notes>$</notes>\n' +
|
|
'<creator>$</creator>\n' +
|
|
'<origCreator>$</origCreator>\n' +
|
|
'<origName>$</origName>\n' +
|
|
'<remixHistory>$</remixHistory>\n' +
|
|
'<thumbnail>$</thumbnail>\n' +
|
|
'<stage name="@" width="@" height="@" ' +
|
|
'costume="@" tempo="@" threadsafe="@" ' +
|
|
'%' +
|
|
'lines="@" ' +
|
|
'ternary="@" ' +
|
|
'codify="@" ' +
|
|
'inheritance="@" ' +
|
|
'sublistIDs="@" ' +
|
|
'scheduled="@" ~>' +
|
|
'<pentrails>$</pentrails>' +
|
|
'<costumes>%</costumes>' +
|
|
'<sounds>%</sounds>' +
|
|
'<variables>%</variables>' +
|
|
'<blocks>%</blocks>' +
|
|
'<scripts>%</scripts><sprites>%</sprites>' +
|
|
'</stage>\n' +
|
|
'<hidden>$</hidden>' +
|
|
'<headers>%</headers>' +
|
|
'<code>%</code>' +
|
|
'<blocks>%</blocks>' +
|
|
'<variables>%</variables>\n' +
|
|
'</project>',
|
|
(ide && ide.projectName) ? ide.projectName : localize('Untitled'),
|
|
serializer.app,
|
|
serializer.version,
|
|
(ide && ide.projectNotes) ? ide.projectNotes : '',
|
|
(ide && ide.creator) ? ide.creator : '',
|
|
(ide && ide.origCreator) ? ide.origCreator : '',
|
|
(ide && ide.origName) ? ide.origName : '',
|
|
(ide && ide.remixHistory) ? ide.remixHistory : '',
|
|
thumbdata,
|
|
this.name,
|
|
StageMorph.prototype.dimensions.x,
|
|
StageMorph.prototype.dimensions.y,
|
|
this.getCostumeIdx(),
|
|
this.getTempo(),
|
|
this.isThreadSafe,
|
|
this.instrument ?
|
|
' instrument="' + parseInt(this.instrument) + '" ' : '',
|
|
SpriteMorph.prototype.useFlatLineEnds ? 'flat' : 'round',
|
|
BooleanSlotMorph.prototype.isTernary,
|
|
this.enableCodeMapping,
|
|
this.enableInheritance,
|
|
this.enableSublistIDs,
|
|
StageMorph.prototype.frameRate !== 0,
|
|
normalizeCanvas(this.trailsCanvas, true).toDataURL('image/png'),
|
|
serializer.store(this.costumes, this.name + '_cst'),
|
|
serializer.store(this.sounds, this.name + '_snd'),
|
|
serializer.store(this.variables),
|
|
serializer.store(this.customBlocks),
|
|
serializer.store(this.scripts),
|
|
serializer.store(this.children),
|
|
Object.keys(StageMorph.prototype.hiddenPrimitives).reduce(
|
|
function (a, b) {return a + ' ' + b; },
|
|
''
|
|
),
|
|
code('codeHeaders'),
|
|
code('codeMappings'),
|
|
serializer.store(this.globalBlocks),
|
|
(ide && ide.globalVariables) ?
|
|
serializer.store(ide.globalVariables) : ''
|
|
);
|
|
};
|
|
|
|
XML_Serializer.prototype.serialize = function (object, forBlocksLibrary) {
|
|
// public: answer an XML string representing the given object
|
|
var xml;
|
|
this.flush(); // in case an error occurred in an earlier attempt
|
|
this.flushMedia();
|
|
this.isExportingBlocksLibrary = forBlocksLibrary;
|
|
xml = this.store(object);
|
|
this.flush();
|
|
return xml;
|
|
};
|