turtlestitch/stitchcode/store.js

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;
};