Improve saveAs refactoring (WIP across all Snap!)

- added TODO comments to all functions that need updating (byob, blocks)
- separate out the code into 3 new functions for readability:
  - `saveFileAs` is a generic method
  - `saveCanvasAs` and `saveXMLAs` are wrappers which aid in exporting images and all XML files which together cover almost all export cases.
  - In other cases, you can call `saveFileAs` directly with some additional input.
- `saveCanvasAs` in particular uses a new `Canvas.toBlob()` option when it is available (FF and IE currently) that should greatly reduce memory.
(Chrome and Safari both have open bugs and may at somepoint implement this function.)
- I have added in code to detect bugs in chrome, including when a new window is being opened. If a new window cannot be opened, the a (to be written) warning message will appear.
- a few functions (namely those in morphic.js) have not touched. However, these are functions which should only occur in a small subset of cases and are behind hidden features.
dev
Michael Ball 2015-11-09 15:14:23 -08:00
rodzic 35c1826be3
commit e47dcb0ebf
4 zmienionych plików z 62 dodań i 36 usunięć

Wyświetl plik

@ -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());
},

Wyświetl plik

@ -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)
);

74
gui.js
Wyświetl plik

@ -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 <kind>/<ext>;<meta> 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);

Wyświetl plik

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