Merge pull request #983 from cycomachead/blob-api

Use Blob API to Save Files (to Disk)
dev
Jens Mönig 2015-12-15 12:56:52 +01:00
commit 99784ca436
7 zmienionych plików z 270 dodań i 194 usunięć

2
FileSaver.min.js vendored 100644
Wyświetl plik

@ -0,0 +1,2 @@
/*! @source http://purl.eligrey.com/github/FileSaver.js/blob/master/FileSaver.js */
var saveAs=saveAs||function(e){"use strict";if(typeof navigator!=="undefined"&&/MSIE [1-9]\./.test(navigator.userAgent)){return}var t=e.document,n=function(){return e.URL||e.webkitURL||e},r=t.createElementNS("http://www.w3.org/1999/xhtml","a"),i="download"in r,o=function(e){var t=new MouseEvent("click");e.dispatchEvent(t)},a=/Version\/[\d\.]+.*Safari/.test(navigator.userAgent),f=e.webkitRequestFileSystem,u=e.requestFileSystem||f||e.mozRequestFileSystem,s=function(t){(e.setImmediate||e.setTimeout)(function(){throw t},0)},c="application/octet-stream",d=0,l=500,w=function(t){var r=function(){if(typeof t==="string"){n().revokeObjectURL(t)}else{t.remove()}};if(e.chrome){r()}else{setTimeout(r,l)}},p=function(e,t,n){t=[].concat(t);var r=t.length;while(r--){var i=e["on"+t[r]];if(typeof i==="function"){try{i.call(e,n||e)}catch(o){s(o)}}}},v=function(e){if(/^\s*(?:text\/\S*|application\/xml|\S*\/\S*\+xml)\s*;.*charset\s*=\s*utf-8/i.test(e.type)){return new Blob(["\ufeff",e],{type:e.type})}return e},y=function(t,s,l){if(!l){t=v(t)}var y=this,m=t.type,S=false,h,R,O=function(){p(y,"writestart progress write writeend".split(" "))},g=function(){if(R&&a&&typeof FileReader!=="undefined"){var r=new FileReader;r.onloadend=function(){var e=r.result;R.location.href="data:attachment/file"+e.slice(e.search(/[,;]/));y.readyState=y.DONE;O()};r.readAsDataURL(t);y.readyState=y.INIT;return}if(S||!h){h=n().createObjectURL(t)}if(R){R.location.href=h}else{var i=e.open(h,"_blank");if(i==undefined&&a){e.location.href=h}}y.readyState=y.DONE;O();w(h)},b=function(e){return function(){if(y.readyState!==y.DONE){return e.apply(this,arguments)}}},E={create:true,exclusive:false},N;y.readyState=y.INIT;if(!s){s="download"}if(i){h=n().createObjectURL(t);r.href=h;r.download=s;setTimeout(function(){o(r);O();w(h);y.readyState=y.DONE});return}if(e.chrome&&m&&m!==c){N=t.slice||t.webkitSlice;t=N.call(t,0,t.size,c);S=true}if(f&&s!=="download"){s+=".download"}if(m===c||f){R=e}if(!u){g();return}d+=t.size;u(e.TEMPORARY,d,b(function(e){e.root.getDirectory("saved",E,b(function(e){var n=function(){e.getFile(s,E,b(function(e){e.createWriter(b(function(n){n.onwriteend=function(t){R.location.href=e.toURL();y.readyState=y.DONE;p(y,"writeend",t);w(e)};n.onerror=function(){var e=n.error;if(e.code!==e.ABORT_ERR){g()}};"writestart progress write abort".split(" ").forEach(function(e){n["on"+e]=y["on"+e]});n.write(t);y.abort=function(){n.abort();y.readyState=y.DONE};y.readyState=y.WRITING}),g)}),g)};e.getFile(s,{create:false},b(function(e){e.remove();n()}),b(function(e){if(e.code===e.NOT_FOUND_ERR){n()}else{g()}}))}),g)}),g)},m=y.prototype,S=function(e,t,n){return new y(e,t,n)};if(typeof navigator!=="undefined"&&navigator.msSaveOrOpenBlob){return function(e,t,n){if(!n){e=v(e)}return navigator.msSaveOrOpenBlob(e,t||"download")}}m.abort=function(){var e=this;e.readyState=e.DONE;p(e,"abort")};m.readyState=m.INIT=0;m.WRITING=1;m.DONE=2;m.error=m.onwritestart=m.onprogress=m.onwrite=m.onabort=m.onerror=m.onwriteend=null;return S}(typeof self!=="undefined"&&self||typeof window!=="undefined"&&window||this.content);if(typeof module!=="undefined"&&module.exports){module.exports.saveAs=saveAs}else if(typeof define!=="undefined"&&define!==null&&define.amd!=null){define([],function(){return saveAs})}

Wyświetl plik

@ -1855,7 +1855,8 @@ SyntaxElementMorph.prototype.showBubble = function (value, exportPic) {
};
SyntaxElementMorph.prototype.exportPictureWithResult = function (aBubble) {
var scr = this.fullImage(),
var ide = this.parentThatIsA(IDE_Morph),
scr = this.fullImage(),
bub = aBubble.fullImageClassic(),
taller = Math.max(0, bub.height - scr.height),
pic = newCanvas(new Point(
@ -1865,7 +1866,12 @@ SyntaxElementMorph.prototype.exportPictureWithResult = function (aBubble) {
ctx = pic.getContext('2d');
ctx.drawImage(scr, 0, pic.height - scr.height);
ctx.drawImage(bub, scr.width + 2, 0);
window.open(pic.toDataURL());
// request to open pic in new window.
ide.saveCanvasAs(
pic,
ide.projetName || localize('Untitled') + ' ' + localize('script pic'),
true
);
};
// SyntaxElementMorph code mapping
@ -2332,7 +2338,13 @@ BlockMorph.prototype.userMenu = function () {
menu.addItem(
"script pic...",
function () {
window.open(myself.topBlock().scriptPic().toDataURL());
var ide = myself.parentThatIsA(IDE_Morph);
ide.saveCanvasAs(
myself.topBlock().scriptPic(),
ide.projetName || localize('Untitled') + ' ' +
localize('script pic'),
true // request new window
);
},
'open a new window\nwith a picture of this script'
);
@ -5489,9 +5501,15 @@ ScriptsMorph.prototype.cleanUp = function () {
};
ScriptsMorph.prototype.exportScriptsPicture = function () {
var pic = this.scriptsPicture();
var pic = this.scriptsPicture(),
ide = this.world().children[0];
if (pic) {
window.open(pic.toDataURL());
ide.saveCanvasAs(
pic,
ide.projetName || localize('Untitled') + ' ' +
localize('script pic'),
true // request new window
);
}
};
@ -11166,7 +11184,13 @@ CommentMorph.prototype.userMenu = function () {
menu.addItem(
"comment pic...",
function () {
window.open(myself.fullImageClassic().toDataURL());
var ide = myself.parentThatIsA(IDE_Morph);
ide.saveCanvasAs(
myself.fullImageClassic(),
ide.projetName || localize('Untitled') + ' ' +
localize('comment pic'),
true // request new window
);
},
'open a new window\nwith a picture of this comment'
);

28
byob.js
Wyświetl plik

@ -821,7 +821,13 @@ CustomCommandBlockMorph.prototype.userMenu = function () {
menu.addItem(
"script pic...",
function () {
window.open(this.topBlock().scriptPic().toDataURL());
var ide = this.world().children[0];
ide.saveCanvasAs(
this.topBlock().scriptPic(),
ide.projectName || localize('Untitled') + ' ' +
localize('script pic'),
true // request opening a new window
);
},
'open a new window\nwith a picture of this script'
);
@ -862,8 +868,10 @@ CustomCommandBlockMorph.prototype.userMenu = function () {
};
CustomCommandBlockMorph.prototype.exportBlockDefinition = function () {
var xml = new SnapSerializer().serialize(this.definition);
window.open('data:text/xml,' + encodeURIComponent(xml));
var xml = new SnapSerializer().serialize(this.definition),
ide = this.parentThatIsA(IDE_Morph);
ide.saveXMLAs(xml, this.spec);
};
CustomCommandBlockMorph.prototype.deleteBlockDefinition = function () {
@ -3497,17 +3505,21 @@ BlockExportDialogMorph.prototype.selectNone = function () {
// BlockExportDialogMorph ops
BlockExportDialogMorph.prototype.exportBlocks = function () {
var str = encodeURIComponent(
this.serializer.serialize(this.blocks)
);
var str = this.serializer.serialize(this.blocks),
ide = this.world().children[0];
if (this.blocks.length > 0) {
window.open('data:text/xml,<blocks app="'
str = '<blocks app="'
+ this.serializer.app
+ '" version="'
+ this.serializer.version
+ '">'
+ str
+ '</blocks>');
+ '</blocks>';
ide.saveXMLAs(
str,
ide.projectName || localize('Untitled') + ' ' + localize('blocks')
);
} else {
new DialogBoxMorph().inform(
'Export blocks',

371
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 ////////////////////////////////////////////////////////
@ -85,7 +85,6 @@ var SoundIconMorph;
var JukeboxMorph;
var StageHandleMorph;
// IDE_Morph ///////////////////////////////////////////////////////////
// I am SNAP's top-level frame, the Editor window
@ -2519,15 +2518,6 @@ IDE_Morph.prototype.projectMenu = function () {
menu.addItem('New', 'createNewProject');
menu.addItem('Open...', 'openProjectsBrowser');
menu.addItem('Save', "save");
if (shiftClicked) {
menu.addItem(
'Save to disk',
'saveProjectToDisk',
'store this project\nin the downloads folder\n'
+ '(in supporting browsers)',
new Color(100, 0, 0)
);
}
menu.addItem('Save As...', 'saveProjectsBrowser');
menu.addLine();
menu.addItem(
@ -2564,6 +2554,24 @@ IDE_Morph.prototype.projectMenu = function () {
'file menu import hint' // looks up the actual text in the translator
);
if (shiftClicked) {
menu.addItem(
localize('Export project...') + ' ' + localize('(in a new window)'),
function () {
if (myself.projectName) {
myself.exportProject(myself.projectName, shiftClicked);
} else {
myself.prompt('Export Project As...', function (name) {
// false - override the shiftClick setting to use XML
// true - open XML in a new tab
myself.exportProject(name, false, true);
}, null, 'exportProject');
}
},
'show project data as XML\nin a new browser window',
new Color(100, 0, 0)
);
}
menu.addItem(
shiftClicked ?
'Export project as plain text...' : 'Export project...',
@ -2572,11 +2580,11 @@ IDE_Morph.prototype.projectMenu = function () {
myself.exportProject(myself.projectName, shiftClicked);
} else {
myself.prompt('Export Project As...', function (name) {
myself.exportProject(name);
myself.exportProject(name, shiftClicked);
}, null, 'exportProject');
}
},
'show project data as XML\nin a new browser window',
'save project data as XML\nto your downloads folder',
shiftClicked ? new Color(100, 0, 0) : null
);
@ -2606,7 +2614,7 @@ IDE_Morph.prototype.projectMenu = function () {
'Export summary with drop-shadows...',
function () {myself.exportProjectSummary(true); },
'open a new browser browser window' +
'\n with a summary of this project' +
'\nwith a summary of this project' +
'\nwith drop-shadows on all pictures.' +
'\nnot supported by all browsers',
new Color(100, 0, 0)
@ -2984,7 +2992,6 @@ IDE_Morph.prototype.save = function () {
}
};
IDE_Morph.prototype.saveProject = function (name) {
var myself = this;
this.nextSteps([
@ -2997,6 +3004,7 @@ IDE_Morph.prototype.saveProject = function (name) {
]);
};
// Serialize a project and save to the browser.
IDE_Morph.prototype.rawSaveProject = function (name) {
var str;
if (name) {
@ -3019,104 +3027,28 @@ IDE_Morph.prototype.rawSaveProject = function (name) {
}
};
IDE_Morph.prototype.saveProjectToDisk = function () {
var data,
link = document.createElement('a');
if (Process.prototype.isCatchingErrors) {
try {
data = this.serializer.serialize(this.stage);
link.setAttribute('href', 'data:text/xml,' + data);
link.setAttribute('download', this.projectName + '.xml');
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
} catch (err) {
this.showMessage('Saving failed: ' + err);
}
} else {
data = this.serializer.serialize(this.stage);
link.setAttribute('href', 'data:text/xml,' + data);
link.setAttribute('download', this.projectName + '.xml');
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
}
};
IDE_Morph.prototype.exportProject = function (name, plain, newWindow) {
// Export project XML, saving a file to disk
// newWindow requests displaying the project in a new tab.
var menu, str, dataPrefix;
/* alternative download mechanism, commented out in favor of blob-uris
IDE_Morph.prototype.saveProjectToDisk = function () {
var data;
if (Process.prototype.isCatchingErrors) {
try {
data = this.serializer.serialize(this.stage);
} catch (err) {
this.showMessage('Serialization failed: ' + err);
}
} else {
data = this.serializer.serialize(this.stage);
}
this.download(data, this.projectName);
};
IDE_Morph.prototype.download = function (data, fileName, mime, suffix) {
var link = document.createElement('a'),
myself = this;
mime = mime || 'data:text/xml';
fileName = fileName || 'download';
suffix = suffix || 'xml';
function saveToDisk() {
var msg = myself.showMessage('Downloading to disk...');
link.setAttribute('href', mime + ',' + data);
link.setAttribute('download', fileName + '.' + suffix);
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
msg.destroy();
}
if (Process.prototype.isCatchingErrors) {
try {
saveToDisk();
} catch (err) {
this.showMessage('Saving failed: ' + err);
}
} else {
saveToDisk();
}
};
*/
IDE_Morph.prototype.exportProject = function (name, plain) {
var menu, str;
if (name) {
this.setProjectName(name);
if (Process.prototype.isCatchingErrors) {
try {
menu = this.showMessage('Exporting');
str = encodeURIComponent(
this.serializer.serialize(this.stage)
);
this.setURL('#open:' + str);
window.open('data:text/'
+ (plain ? 'plain,' + str : 'xml,' + str));
menu.destroy();
this.showMessage('Exported!', 1);
} catch (err) {
this.showMessage('Export failed: ' + err);
}
} else {
dataPrefix = 'data:text/' + plain ? 'plain,' : 'xml,';
try {
menu = this.showMessage('Exporting');
str = encodeURIComponent(
this.serializer.serialize(this.stage)
);
this.setURL('#open:' + str);
window.open('data:text/'
+ (plain ? 'plain,' + str : 'xml,' + str));
str = this.serializer.serialize(this.stage)
this.setURL('#open:' + dataPrefix + encodeURIComponent(str));
this.saveXMLAs(str, name, newWindow);
menu.destroy();
this.showMessage('Exported!', 1);
} catch (err) {
if (Process.prototype.isCatchingErrors) {
this.showMessage('Export failed: ' + err);
} else {
throw err;
}
}
}
};
@ -3175,16 +3107,15 @@ IDE_Morph.prototype.removeUnusedBlocks = function () {
};
IDE_Morph.prototype.exportSprite = function (sprite) {
var str = encodeURIComponent(
this.serializer.serialize(sprite.allParts())
);
window.open('data:text/xml,<sprites app="'
var str = this.serializer.serialize(sprite.allParts());
str = '<sprites app="'
+ this.serializer.app
+ '" version="'
+ this.serializer.version
+ '">'
+ str
+ '</sprites>');
+ '</sprites>';
this.saveXMLAs(str, sprite.name);
};
IDE_Morph.prototype.exportScriptsPicture = function () {
@ -3233,8 +3164,7 @@ IDE_Morph.prototype.exportScriptsPicture = function () {
y += padding;
y += each.height;
});
window.open(pic.toDataURL());
this.saveCanvasAs(pic, this.projectName || localize('Untitled'), true);
};
IDE_Morph.prototype.exportProjectSummary = function (useDropShadows) {
@ -3511,9 +3441,12 @@ IDE_Morph.prototype.exportProjectSummary = function (useDropShadows) {
addBlocks(stage.globalBlocks);
}
window.open('data:text/html;charset=utf-8,' + encodeURIComponent(
'<!DOCTYPE html>' + html.toString()
));
this.saveFileAs(
'<!DOCTYPE html>' + html.toString(),
'text/html;charset=utf-8,',
pname,
true // request opening a new window.
);
};
IDE_Morph.prototype.openProjectString = function (str) {
@ -3713,11 +3646,126 @@ IDE_Morph.prototype.openProject = function (name) {
};
IDE_Morph.prototype.setURL = function (str) {
// Set the URL to a project's XML contents
if (this.projectsInURLs) {
location.hash = str;
}
};
IDE_Morph.prototype.saveFileAs = function (
contents,
fileType,
fileName,
newWindow // (optional) defaults to false.
) {
/** Allow for downloading a file to a disk or open in a new tab.
This relies the FileSaver.js library which exports saveAs()
Two utility methods saveImageAs and saveXMLAs should be used first.
1. Opening a new window uses standard URI encoding.
2. downloading a file uses Blobs.
- every other combo is unsupposed.
*/
var blobIsSupported = false,
world = this.world(),
fileExt,
dataURI, dialog;
// fileType is a <kind>/<ext>;<charset> format.
fileExt = fileType.split('/')[1].split(';')[0];
// handle text/plain as a .txt file
fileExt = '.' + (fileExt === 'plain' ? 'txt' : fileExt);
// This is a workaround for a known Chrome crash with large URLs
function exhibitsChomeBug(contents) {
var MAX_LENGTH = 2e6,
isChrome = navigator.userAgent.indexOf('Chrome') !== -1;
return isChrome && contents.length > MAX_LENGTH;
}
function dataURItoBlob(text, mimeType) {
var i,
data = text,
components = text.split(','),
hasTypeStr = text.indexOf('data:') === 0;
// Convert to binary data, in format Blob() can use.
if (hasTypeStr && components[0].indexOf('base64') > -1) {
text = atob(components[1]);
data = new Uint8Array(text.length),
i = text.length;
while (i--) {
data[i] = text.charCodeAt(i);
}
}
return new Blob([data], {type: mimeType });
}
function dataURLFormat(text) {
var hasTypeStr = text.indexOf('data:') === 0;
if (hasTypeStr) {return text; }
return 'data:' + fileType + ',' + encodeURIComponent(text);
}
try {
blobIsSupported = !!new Blob;
} catch (e) {}
if (newWindow) {
// Blob URIs need a custom URL to be displayed in a new window
if (contents instanceof Blob) {
dataURI = URL.createObjectURL(contents);
} else {
dataURI = dataURLFormat(contents);
}
// Detect crashing errors - fallback to downloading if necessary
if (!exhibitsChomeBug(dataURI)) {
window.open(dataURI, fileName);
// Blob URIs should be "cleaned up" to reduce memory.
if (contents instanceof Blob) {
URL.revokeObjectURL(dataURI);
}
} else {
// (recursively) call this defauling newWindow to false
this.showMessage('download to disk text');
this.saveFileAs(contents, fileType, fileName);
}
} else if (blobIsSupported) {
if (!(contents instanceof Blob)) {
contents = dataURItoBlob(contents, fileType);
}
// download a file and delegate to FileSaver
// false: Do not preprend a BOM to the file.
saveAs(contents, fileName + fileExt, false);
} else {
dialog = new DialogBoxMorph();
dialog.inform(
localize('Could not export') + ' ' + fileName,
'unable to export text',
world
);
dialog.fixLayout();
dialog.drawNew();
}
}
IDE_Morph.prototype.saveCanvasAs = function (canvas, fileName, newWindow) {
// Export a Canvas object as a PNG image
// cavas.toBlob() is currently only supported in Firefox and IE
var myself = this;
if (canvas.toBlob) {
canvas.toBlob(function (blob) {
myself.saveFileAs(blob, 'image/png', fileName, newWindow);
});
} else {
this.saveFileAs(canvas.toDataURL(), 'image/png', fileName, newWindow);
}
}
IDE_Morph.prototype.saveXMLAs = function(xml, fileName, newWindow) {
// wrapper to saving XML files with a proper type tag.
this.saveFileAs(xml, 'text/xml;chartset=utf-8', fileName, newWindow);
}
IDE_Morph.prototype.switchToUserMode = function () {
var world = this.world();
@ -4487,33 +4535,19 @@ IDE_Morph.prototype.exportProjectMedia = function (name) {
this.serializer.isCollectingMedia = true;
if (name) {
this.setProjectName(name);
if (Process.prototype.isCatchingErrors) {
try {
menu = this.showMessage('Exporting');
encodeURIComponent(
this.serializer.serialize(this.stage)
);
media = encodeURIComponent(
this.serializer.mediaXML(name)
);
window.open('data:text/xml,' + media);
menu.destroy();
this.showMessage('Exported!', 1);
} catch (err) {
this.serializer.isCollectingMedia = false;
this.showMessage('Export failed: ' + err);
}
} else {
try {
menu = this.showMessage('Exporting');
encodeURIComponent(
this.serializer.serialize(this.stage)
);
media = encodeURIComponent(
this.serializer.mediaXML()
);
window.open('data:text/xml,' + media);
media = this.serializer.mediaXML(name);
this.saveXMLAs(media, this.projectName + ' media');
menu.destroy();
this.showMessage('Exported!', 1);
} catch (err) {
if (Process.prototype.isCatchingErrors) {
this.serializer.isCollectingMedia = false;
this.showMessage('Export failed: ' + err);
} else {
throw err;
}
}
}
this.serializer.isCollectingMedia = false;
@ -4529,10 +4563,8 @@ IDE_Morph.prototype.exportProjectNoMedia = function (name) {
if (Process.prototype.isCatchingErrors) {
try {
menu = this.showMessage('Exporting');
str = encodeURIComponent(
this.serializer.serialize(this.stage)
);
window.open('data:text/xml,' + str);
str = this.serializer.serialize(this.stage);
this.saveXMLAs(str, this.projectName);
menu.destroy();
this.showMessage('Exported!', 1);
} catch (err) {
@ -4541,10 +4573,8 @@ IDE_Morph.prototype.exportProjectNoMedia = function (name) {
}
} else {
menu = this.showMessage('Exporting');
str = encodeURIComponent(
this.serializer.serialize(this.stage)
);
window.open('data:text/xml,' + str);
str = this.serializer.serialize(this.stage);
this.saveXMLAs(str, this.projectName);
menu.destroy();
this.showMessage('Exported!', 1);
}
@ -4561,17 +4591,10 @@ IDE_Morph.prototype.exportProjectAsCloudData = function (name) {
if (Process.prototype.isCatchingErrors) {
try {
menu = this.showMessage('Exporting');
str = encodeURIComponent(
this.serializer.serialize(this.stage)
);
media = encodeURIComponent(
this.serializer.mediaXML(name)
);
dta = encodeURIComponent('<snapdata>')
+ str
+ media
+ encodeURIComponent('</snapdata>');
window.open('data:text/xml,' + dta);
str = this.serializer.serialize(this.stage);
media = this.serializer.mediaXML(name);
dta = '<snapdata>' + str + media + '</snapdata>';
this.saveXMLAs(str, this.projectName);
menu.destroy();
this.showMessage('Exported!', 1);
} catch (err) {
@ -4580,17 +4603,10 @@ IDE_Morph.prototype.exportProjectAsCloudData = function (name) {
}
} else {
menu = this.showMessage('Exporting');
str = encodeURIComponent(
this.serializer.serialize(this.stage)
);
media = encodeURIComponent(
this.serializer.mediaXML()
);
dta = encodeURIComponent('<snapdata>')
+ str
+ media
+ encodeURIComponent('</snapdata>');
window.open('data:text/xml,' + dta);
str = this.serializer.serialize(this.stage);
media = this.serializer.mediaXML(name);
dta = '<snapdata>' + str + media + '</snapdata>';
this.saveXMLAs(str, this.projectName);
menu.destroy();
this.showMessage('Exported!', 1);
}
@ -5908,7 +5924,12 @@ SpriteIconMorph.prototype.userMenu = function () {
menu.addItem(
'pic...',
function () {
window.open(myself.object.fullImageClassic().toDataURL());
var ide = myself.parentThatIsA(IDE_Morph);
ide.saveCanvasAs(
myself.object.fullImageClassic(),
this.object.name,
true
);
},
'open a new window\nwith a picture of the stage'
);
@ -6269,10 +6290,12 @@ CostumeIconMorph.prototype.removeCostume = function () {
};
CostumeIconMorph.prototype.exportCostume = function () {
var ide = this.parentThatIsA(IDE_Morph);
if (this.object instanceof SVG_Costume) {
window.open(this.object.contents.src);
} else { // rastered Costume
window.open(this.object.contents.toDataURL());
// don't show SVG costumes in a new tab (shows text)
ide.saveFileAs(this.object.contents.src, 'text/svg', this.object.name);
} else { // rasterized Costume
ide.saveCanvasAs(this.object.object.contents, this.object.name, true);
}
};

Wyświetl plik

@ -141,7 +141,15 @@ SnapTranslator.dict.en = {
+ 'a file on your computer by dropping it here\n',
'block deletion dialog text':
'Are you sure you want to delete this\n'
+ 'custom block and all its instances?'
+ 'custom block and all its instances?',
'download to disk text':
'This item could not be opened in a new tab.\n' +
'It has been saved to your browser\'s downloads folder.',
'unable to export text':
'This item could not be exported from Snap!.\n' +
'It\'s likely that your project may contain a lot of media ' +
'(sounds and images) or that you are using an older browser.' +
'Please try using a recent version of Chrome, Firefox, or Safari.'
};
SnapTranslator.dict.de = {

Wyświetl plik

@ -589,7 +589,7 @@ SpriteMorph.prototype.initBlocks = function () {
},
/* migrated to a newer block version:
receiveClick: {
type: 'hat',
category: 'control',
@ -5839,7 +5839,11 @@ StageMorph.prototype.userMenu = function () {
menu.addItem(
"pic...",
function () {
window.open(myself.fullImageClassic().toDataURL());
ide.saveCanvasAs(
myself.fullImageClassic(),
myself.name,
true // open as new window
);
},
'open a new window\nwith a picture of the stage'
);
@ -7729,9 +7733,11 @@ WatcherMorph.prototype.userMenu = function () {
menu.addItem(
'export...',
function () {
window.open(
'data:text/plain;charset=utf-8,' +
encodeURIComponent(this.currentValue.toString())
var ide = myself.parentThatIsA(IDE_Morph);
ide.saveFileAs(
myself.currentValue.toString(),
'text/plain;charset=utf-8',
myself.getter // variable name
);
}
);

Wyświetl plik

@ -18,6 +18,7 @@
<script type="text/javascript" src="locale.js"></script>
<script type="text/javascript" src="cloud.js"></script>
<script type="text/javascript" src="sha512.js"></script>
<script type="text/javascript" src="FileSaver.min.js"></script>
<script type="text/javascript">
var world;
window.onload = function () {