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] ) + '' ); } ); return str; } this.removeAllClones(); return serializer.format( '\n' + '$\n' + '$\n' + '$\n' + '$\n' + '$\n' + '$\n' + '' + '$' + '%' + '%' + '%' + '%' + '%%' + '\n' + '$' + '%' + '%' + '%' + '%\n' + '', (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; };