diff --git a/blocks.js b/blocks.js index f30b8e41..77aa8eb5 100644 --- a/blocks.js +++ b/blocks.js @@ -1853,6 +1853,7 @@ SyntaxElementMorph.prototype.exportPictureWithResult = function (aBubble) { ctx = pic.getContext('2d'); ctx.drawImage(scr, 0, pic.height - scr.height); ctx.drawImage(bub, scr.width + 2, 0); + // TODO: modify this call! window.open(pic.toDataURL()); }; @@ -2312,6 +2313,7 @@ BlockMorph.prototype.userMenu = function () { ); menu.addItem( "script pic...", + // TODO: modify this function to use saveAs function () { window.open(myself.topBlock().scriptPic().toDataURL()); }, @@ -5376,6 +5378,7 @@ ScriptsMorph.prototype.cleanUp = function () { ScriptsMorph.prototype.exportScriptsPicture = function () { var pic = this.scriptsPicture(); if (pic) { + // TODO: modify this to use a saveAs window.open(pic.toDataURL()); } }; @@ -11039,6 +11042,7 @@ CommentMorph.prototype.userMenu = function () { menu.addItem("delete", 'destroy'); menu.addItem( "comment pic...", + // TODO: modify this to use save as function () { window.open(myself.fullImageClassic().toDataURL()); }, diff --git a/byob.js b/byob.js index f4d19f98..4db43c0e 100644 --- a/byob.js +++ b/byob.js @@ -762,6 +762,7 @@ CustomCommandBlockMorph.prototype.userMenu = function () { menu = new MenuMorph(this); menu.addItem( "script pic...", + // TODO: modify this to use saveAs function () { window.open(this.topBlock().fullImage().toDataURL()); }, @@ -784,6 +785,7 @@ CustomCommandBlockMorph.prototype.userMenu = function () { CustomCommandBlockMorph.prototype.exportBlockDefinition = function () { var xml = new SnapSerializer().serialize(this.definition); + // TODO: modify this to use saveAs window.open('data:text/xml,' + encodeURIComponent(xml)); }; @@ -3326,6 +3328,7 @@ BlockExportDialogMorph.prototype.selectNone = function () { // BlockExportDialogMorph ops BlockExportDialogMorph.prototype.exportBlocks = function () { + // TODO: modify this code to use saveAs var str = encodeURIComponent( this.serializer.serialize(this.blocks) ); diff --git a/gui.js b/gui.js index 01621db7..ccb0d1c9 100644 --- a/gui.js +++ b/gui.js @@ -67,7 +67,7 @@ BlockImportDialogMorph, SnapTranslator, localize, List, InputSlotMorph, SnapCloud, Uint8Array, HandleMorph, SVG_Costume, fontHeight, hex_sha512, sb, CommentMorph, CommandBlockMorph, BlockLabelPlaceHolderMorph, Audio, SpeechBubbleMorph, ScriptFocusMorph, XML_Element, WatcherMorph, -BlockRemovalDialogMorph*/ +BlockRemovalDialogMorph, saveAs*/ // Global stuff //////////////////////////////////////////////////////// @@ -3647,56 +3647,48 @@ IDE_Morph.prototype.setURL = function (str) { }; // Allow for downloading a file to a disk or open in a new tab. -// This function relies the FileSaver.js library which exports saveAs() -// https://github.com/eligrey/FileSaver.js -// This function is the primary way to get any data (image or text) out of Snap! -IDE_Morph.prototype.saveFileAs = function (contents, fileName, newWindow) { +// This relies the FileSaver.js library which exports saveAs() +// See: https://github.com/eligrey/FileSaver.js +// There are two utility methods saveImageAs and saveXMLAs that should be used +// over this method, if appropriate. +IDE_Morph.prototype.saveFileAs = function (contents, type, fileName, newWindow) { // TODO: handle Process.isCatchingErrors easily? // TODO: Add confirmations // TODO: Properly handle extensions. // TODO: Check for URI encoding? - var isFileSaverSupported, - blobData, fileType, fileExt, + var isFileSaverSupported = false, + fileExt, encodedData, world, dlg, errorMessage; - // File type for blobs, text for XML, image/png for images. - fileType = 'text/xml;charset=utf-8'; - fileExt = '.xml'; // .txt, when? + // type is a /; format. + fileExt = '.' + type.split('/')[1].split(';')[0] // Error Message Handling world = this.world(); errorMessage = 'Longer Message is coming soon!'; // This is a workaround for a known Chrome crash with large URLs function exhibitsChomeBug(contents) { - var MAX_LENGTH = 2e6; + var MAX_LENGTH = 2e6, + isChrome = navigator.userAgent.indexOf('Chrome') !== -1, + isTooLong = contents.length > MAX_LENGTH; - return false; + return isChrome && isTooLong }; + function dataURLFormat (text) { + return 'data:' + fileType + ',' + text; + } + try { isFileSaverSupported = !!new Blob; } catch (e) {} - // Handle rendering Cavas elements - if (contents instanceof HTMLCanvasElement) { - if (contents.toBlob && isFileSaverSupported) { - contents.toBlob(function(blob) { - saveAs(blobData, fileName, false); - }); - return; - } else { - contents = contents.toDataURL(); - fileType = 'image/png'; - fileExt = '.png'; - } - } - // Force open in a new window, or use as a fallback. if (!isFileSaverSupported || newWindow) { // Prevent crashing errors in Chrome encodedData = encodeURIComponent(contents); if (!exhibitsChomeBug(encodedData)) { - window.open('data:' + fileType + ',' + encodedData, '_blank'); + window.open(dataURLFormat(encodedData), '_blank'); } else { dlg = new DialogBoxMorph(); dlg.inform('Uh oh!', errorMessage, world); @@ -3704,11 +3696,29 @@ IDE_Morph.prototype.saveFileAs = function (contents, fileName, newWindow) { dlg.fixLayout(); dlg.drawNew(); } - } else { - console.log('FILENAME: ', fileName); - blobData = new Blob([contents], {type: fileType}); - saveAs(blobData, fileName + fileExt, false); + } else if (!(contents instanceof Blob)){ + contents = new Blob([contents], {type: type }); } + + saveAs(contents, fileName + fileExt, false); +} + +// This helps exporting a canvas as an image +// cavas.toBlob() is only supported in Firefox and IE, but is faster. +IDE_Morph.prototype.saveCanvasAs = function (canvas, fileName, newWindow) { + var myself = this; + if (canvas.toBlob) { + canvas.toBlob(function(blob) { + this.saveFileAs(blob, 'image/png', fileName, newWindow); + }); + } else { + this.saveFileAs(canvas.toDataURL(), 'image/png', fileName, newWindow); + } +} + +IDE_Morph.prototype.saveXMLAs = function(text, fileName, newWindow) { + var typeTag = 'text/xml;chartset=utf-8'; + this.saveFileAs(text, typeTag, fileName, newWindow); } IDE_Morph.prototype.switchToUserMode = function () { @@ -5908,6 +5918,7 @@ SpriteIconMorph.prototype.fixLayout = function () { // SpriteIconMorph menu +// TODO: fix this function SpriteIconMorph.prototype.userMenu = function () { var menu = new MenuMorph(this), myself = this; @@ -6270,6 +6281,7 @@ CostumeIconMorph.prototype.removeCostume = function () { } }; +// TODO: Modify and fix this CostumeIconMorph.prototype.exportCostume = function () { if (this.object instanceof SVG_Costume) { window.open(this.object.contents.src); diff --git a/objects.js b/objects.js index e51fe07f..5710584b 100644 --- a/objects.js +++ b/objects.js @@ -583,7 +583,7 @@ SpriteMorph.prototype.initBlocks = function () { }, /* migrated to a newer block version: - + receiveClick: { type: 'hat', category: 'control', @@ -5735,8 +5735,13 @@ StageMorph.prototype.userMenu = function () { menu.addItem("show all", 'showAll'); menu.addItem( "pic...", +<<<<<<< Local Changes + // TODO: replace this function. +======= +>>>>>>> External Changes function () { - window.open(myself.fullImageClassic().toDataURL()); + // pass a canvas to be opened as a new window. + ide.saveCanvasAs(myself.fullImageClassic(), 'stage', true); }, 'open a new window\nwith a picture of the stage' ); @@ -7614,9 +7619,11 @@ WatcherMorph.prototype.userMenu = function () { menu.addItem( 'export...', function () { - window.open( - 'data:text/plain;charset=utf-8,' + - encodeURIComponent(this.currentValue.toString()) + ide.saveFileAs( + this.currentValue.toString(), + 'text/plain;charset=utf-8', + 'variable', + false ); } );