kopia lustrzana https://github.com/backface/turtlestitch
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
rodzic
35c1826be3
commit
e47dcb0ebf
|
@ -1853,6 +1853,7 @@ SyntaxElementMorph.prototype.exportPictureWithResult = function (aBubble) {
|
||||||
ctx = pic.getContext('2d');
|
ctx = pic.getContext('2d');
|
||||||
ctx.drawImage(scr, 0, pic.height - scr.height);
|
ctx.drawImage(scr, 0, pic.height - scr.height);
|
||||||
ctx.drawImage(bub, scr.width + 2, 0);
|
ctx.drawImage(bub, scr.width + 2, 0);
|
||||||
|
// TODO: modify this call!
|
||||||
window.open(pic.toDataURL());
|
window.open(pic.toDataURL());
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -2312,6 +2313,7 @@ BlockMorph.prototype.userMenu = function () {
|
||||||
);
|
);
|
||||||
menu.addItem(
|
menu.addItem(
|
||||||
"script pic...",
|
"script pic...",
|
||||||
|
// TODO: modify this function to use saveAs
|
||||||
function () {
|
function () {
|
||||||
window.open(myself.topBlock().scriptPic().toDataURL());
|
window.open(myself.topBlock().scriptPic().toDataURL());
|
||||||
},
|
},
|
||||||
|
@ -5376,6 +5378,7 @@ ScriptsMorph.prototype.cleanUp = function () {
|
||||||
ScriptsMorph.prototype.exportScriptsPicture = function () {
|
ScriptsMorph.prototype.exportScriptsPicture = function () {
|
||||||
var pic = this.scriptsPicture();
|
var pic = this.scriptsPicture();
|
||||||
if (pic) {
|
if (pic) {
|
||||||
|
// TODO: modify this to use a saveAs
|
||||||
window.open(pic.toDataURL());
|
window.open(pic.toDataURL());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -11039,6 +11042,7 @@ CommentMorph.prototype.userMenu = function () {
|
||||||
menu.addItem("delete", 'destroy');
|
menu.addItem("delete", 'destroy');
|
||||||
menu.addItem(
|
menu.addItem(
|
||||||
"comment pic...",
|
"comment pic...",
|
||||||
|
// TODO: modify this to use save as
|
||||||
function () {
|
function () {
|
||||||
window.open(myself.fullImageClassic().toDataURL());
|
window.open(myself.fullImageClassic().toDataURL());
|
||||||
},
|
},
|
||||||
|
|
3
byob.js
3
byob.js
|
@ -762,6 +762,7 @@ CustomCommandBlockMorph.prototype.userMenu = function () {
|
||||||
menu = new MenuMorph(this);
|
menu = new MenuMorph(this);
|
||||||
menu.addItem(
|
menu.addItem(
|
||||||
"script pic...",
|
"script pic...",
|
||||||
|
// TODO: modify this to use saveAs
|
||||||
function () {
|
function () {
|
||||||
window.open(this.topBlock().fullImage().toDataURL());
|
window.open(this.topBlock().fullImage().toDataURL());
|
||||||
},
|
},
|
||||||
|
@ -784,6 +785,7 @@ CustomCommandBlockMorph.prototype.userMenu = function () {
|
||||||
|
|
||||||
CustomCommandBlockMorph.prototype.exportBlockDefinition = function () {
|
CustomCommandBlockMorph.prototype.exportBlockDefinition = function () {
|
||||||
var xml = new SnapSerializer().serialize(this.definition);
|
var xml = new SnapSerializer().serialize(this.definition);
|
||||||
|
// TODO: modify this to use saveAs
|
||||||
window.open('data:text/xml,' + encodeURIComponent(xml));
|
window.open('data:text/xml,' + encodeURIComponent(xml));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -3326,6 +3328,7 @@ BlockExportDialogMorph.prototype.selectNone = function () {
|
||||||
// BlockExportDialogMorph ops
|
// BlockExportDialogMorph ops
|
||||||
|
|
||||||
BlockExportDialogMorph.prototype.exportBlocks = function () {
|
BlockExportDialogMorph.prototype.exportBlocks = function () {
|
||||||
|
// TODO: modify this code to use saveAs
|
||||||
var str = encodeURIComponent(
|
var str = encodeURIComponent(
|
||||||
this.serializer.serialize(this.blocks)
|
this.serializer.serialize(this.blocks)
|
||||||
);
|
);
|
||||||
|
|
74
gui.js
74
gui.js
|
@ -67,7 +67,7 @@ BlockImportDialogMorph, SnapTranslator, localize, List, InputSlotMorph,
|
||||||
SnapCloud, Uint8Array, HandleMorph, SVG_Costume, fontHeight, hex_sha512,
|
SnapCloud, Uint8Array, HandleMorph, SVG_Costume, fontHeight, hex_sha512,
|
||||||
sb, CommentMorph, CommandBlockMorph, BlockLabelPlaceHolderMorph, Audio,
|
sb, CommentMorph, CommandBlockMorph, BlockLabelPlaceHolderMorph, Audio,
|
||||||
SpeechBubbleMorph, ScriptFocusMorph, XML_Element, WatcherMorph,
|
SpeechBubbleMorph, ScriptFocusMorph, XML_Element, WatcherMorph,
|
||||||
BlockRemovalDialogMorph*/
|
BlockRemovalDialogMorph, saveAs*/
|
||||||
|
|
||||||
// Global stuff ////////////////////////////////////////////////////////
|
// 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.
|
// Allow for downloading a file to a disk or open in a new tab.
|
||||||
// This function relies the FileSaver.js library which exports saveAs()
|
// This relies the FileSaver.js library which exports saveAs()
|
||||||
// https://github.com/eligrey/FileSaver.js
|
// See: https://github.com/eligrey/FileSaver.js
|
||||||
// This function is the primary way to get any data (image or text) out of Snap!
|
// There are two utility methods saveImageAs and saveXMLAs that should be used
|
||||||
IDE_Morph.prototype.saveFileAs = function (contents, fileName, newWindow) {
|
// over this method, if appropriate.
|
||||||
|
IDE_Morph.prototype.saveFileAs = function (contents, type, fileName, newWindow) {
|
||||||
// TODO: handle Process.isCatchingErrors easily?
|
// TODO: handle Process.isCatchingErrors easily?
|
||||||
// TODO: Add confirmations
|
// TODO: Add confirmations
|
||||||
// TODO: Properly handle extensions.
|
// TODO: Properly handle extensions.
|
||||||
// TODO: Check for URI encoding?
|
// TODO: Check for URI encoding?
|
||||||
var isFileSaverSupported,
|
var isFileSaverSupported = false,
|
||||||
blobData, fileType, fileExt,
|
fileExt,
|
||||||
encodedData, world, dlg, errorMessage;
|
encodedData, world, dlg, errorMessage;
|
||||||
|
|
||||||
// File type for blobs, text for XML, image/png for images.
|
// type is a <kind>/<ext>;<meta> format.
|
||||||
fileType = 'text/xml;charset=utf-8';
|
fileExt = '.' + type.split('/')[1].split(';')[0]
|
||||||
fileExt = '.xml'; // .txt, when?
|
|
||||||
// Error Message Handling
|
// Error Message Handling
|
||||||
world = this.world();
|
world = this.world();
|
||||||
errorMessage = 'Longer Message is coming soon!';
|
errorMessage = 'Longer Message is coming soon!';
|
||||||
|
|
||||||
// This is a workaround for a known Chrome crash with large URLs
|
// This is a workaround for a known Chrome crash with large URLs
|
||||||
function exhibitsChomeBug(contents) {
|
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 {
|
try {
|
||||||
isFileSaverSupported = !!new Blob;
|
isFileSaverSupported = !!new Blob;
|
||||||
} catch (e) {}
|
} 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.
|
// Force open in a new window, or use as a fallback.
|
||||||
if (!isFileSaverSupported || newWindow) {
|
if (!isFileSaverSupported || newWindow) {
|
||||||
// Prevent crashing errors in Chrome
|
// Prevent crashing errors in Chrome
|
||||||
encodedData = encodeURIComponent(contents);
|
encodedData = encodeURIComponent(contents);
|
||||||
if (!exhibitsChomeBug(encodedData)) {
|
if (!exhibitsChomeBug(encodedData)) {
|
||||||
window.open('data:' + fileType + ',' + encodedData, '_blank');
|
window.open(dataURLFormat(encodedData), '_blank');
|
||||||
} else {
|
} else {
|
||||||
dlg = new DialogBoxMorph();
|
dlg = new DialogBoxMorph();
|
||||||
dlg.inform('Uh oh!', errorMessage, world);
|
dlg.inform('Uh oh!', errorMessage, world);
|
||||||
|
@ -3704,11 +3696,29 @@ IDE_Morph.prototype.saveFileAs = function (contents, fileName, newWindow) {
|
||||||
dlg.fixLayout();
|
dlg.fixLayout();
|
||||||
dlg.drawNew();
|
dlg.drawNew();
|
||||||
}
|
}
|
||||||
} else {
|
} else if (!(contents instanceof Blob)){
|
||||||
console.log('FILENAME: ', fileName);
|
contents = new Blob([contents], {type: type });
|
||||||
blobData = new Blob([contents], {type: fileType});
|
|
||||||
saveAs(blobData, fileName + fileExt, false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 () {
|
IDE_Morph.prototype.switchToUserMode = function () {
|
||||||
|
@ -5908,6 +5918,7 @@ SpriteIconMorph.prototype.fixLayout = function () {
|
||||||
|
|
||||||
// SpriteIconMorph menu
|
// SpriteIconMorph menu
|
||||||
|
|
||||||
|
// TODO: fix this function
|
||||||
SpriteIconMorph.prototype.userMenu = function () {
|
SpriteIconMorph.prototype.userMenu = function () {
|
||||||
var menu = new MenuMorph(this),
|
var menu = new MenuMorph(this),
|
||||||
myself = this;
|
myself = this;
|
||||||
|
@ -6270,6 +6281,7 @@ CostumeIconMorph.prototype.removeCostume = function () {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// TODO: Modify and fix this
|
||||||
CostumeIconMorph.prototype.exportCostume = function () {
|
CostumeIconMorph.prototype.exportCostume = function () {
|
||||||
if (this.object instanceof SVG_Costume) {
|
if (this.object instanceof SVG_Costume) {
|
||||||
window.open(this.object.contents.src);
|
window.open(this.object.contents.src);
|
||||||
|
|
17
objects.js
17
objects.js
|
@ -583,7 +583,7 @@ SpriteMorph.prototype.initBlocks = function () {
|
||||||
},
|
},
|
||||||
|
|
||||||
/* migrated to a newer block version:
|
/* migrated to a newer block version:
|
||||||
|
|
||||||
receiveClick: {
|
receiveClick: {
|
||||||
type: 'hat',
|
type: 'hat',
|
||||||
category: 'control',
|
category: 'control',
|
||||||
|
@ -5735,8 +5735,13 @@ StageMorph.prototype.userMenu = function () {
|
||||||
menu.addItem("show all", 'showAll');
|
menu.addItem("show all", 'showAll');
|
||||||
menu.addItem(
|
menu.addItem(
|
||||||
"pic...",
|
"pic...",
|
||||||
|
<<<<<<< Local Changes
|
||||||
|
// TODO: replace this function.
|
||||||
|
=======
|
||||||
|
>>>>>>> External Changes
|
||||||
function () {
|
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'
|
'open a new window\nwith a picture of the stage'
|
||||||
);
|
);
|
||||||
|
@ -7614,9 +7619,11 @@ WatcherMorph.prototype.userMenu = function () {
|
||||||
menu.addItem(
|
menu.addItem(
|
||||||
'export...',
|
'export...',
|
||||||
function () {
|
function () {
|
||||||
window.open(
|
ide.saveFileAs(
|
||||||
'data:text/plain;charset=utf-8,' +
|
this.currentValue.toString(),
|
||||||
encodeURIComponent(this.currentValue.toString())
|
'text/plain;charset=utf-8',
|
||||||
|
'variable',
|
||||||
|
false
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
Ładowanie…
Reference in New Issue