SvgImport: Implement onError() callback

Closes #969
This commit is contained in:
Jürg Lehni 2016-02-14 14:52:37 +01:00
parent 79d446136a
commit 2025bd1a77
8 changed files with 92 additions and 67 deletions

View file

@ -525,10 +525,13 @@ Base.exports.PaperScript = (function() {
// same order the script tags appear. // same order the script tags appear.
// If the async attribute is specified on the script element, // If the async attribute is specified on the script element,
// request the source asynchronously and execute as soon as // request the source asynchronously and execute as soon as
// it is retreived. // it is retrieved.
Http.request('get', src, function(code) { Http.request({
execute(code, scope, src); url: src, async: async,
}, async); onLoad: function(code) {
execute(code, scope, src);
}
});
} else { } else {
// We can simply get the code form the script tag. // We can simply get the code form the script tag.
execute(script.innerHTML, scope, script.baseURI); execute(script.innerHTML, scope, script.baseURI);

View file

@ -2223,7 +2223,10 @@ new function() { // Injection scope for hit-test functions shared with project
* @option options.onLoad {Function} the callback function to call once the * @option options.onLoad {Function} the callback function to call once the
* SVG content is loaded from the given URL receiving two arguments: the * SVG content is loaded from the given URL receiving two arguments: the
* converted `item` and the original `svg` data as a string. Only * converted `item` and the original `svg` data as a string. Only
* required when loading from external files. * required when loading from external resources.
* @option options.onError {Function} the callback function to call if an
* error occurs during loading. Only required when loading from external
* resources.
* @option [options.applyMatrix={@link PaperScope#settings}.applyMatrix] * @option [options.applyMatrix={@link PaperScope#settings}.applyMatrix]
* {Boolean} whether imported items should have their transformation * {Boolean} whether imported items should have their transformation
* matrices applied to their contents or not * matrices applied to their contents or not

View file

@ -775,10 +775,13 @@ var Project = PaperScopeItem.extend(/** @lends Project# */{
* *
* @option [options.expandShapes=false] {Boolean} whether imported shape * @option [options.expandShapes=false] {Boolean} whether imported shape
* items should be expanded to path items * items should be expanded to path items
* @option options.onLoad(item, svg) {Function} the callback function to * @option options.onLoad {Function} the callback function to call once the
* call once the SVG content is loaded from the given URL receiving two * SVG content is loaded from the given URL receiving two arguments: the
* arguments: the converted `item` and the original `svg` data as a * converted `item` and the original `svg` data as a string. Only
* string. Only required when loading from external files. * required when loading from external resources.
* @option options.onError {Function} the callback function to call if an
* error occurs during loading. Only required when loading from external
* resources.
* @option [options.applyMatrix={@link PaperScope#settings}.applyMatrix] * @option [options.applyMatrix={@link PaperScope#settings}.applyMatrix]
* {Boolean} whether imported items should have their transformation * {Boolean} whether imported items should have their transformation
* matrices applied to their contents or not * matrices applied to their contents or not

View file

@ -11,23 +11,32 @@
*/ */
var Http = { var Http = {
request: function(method, url, callback, async) { request: function(options) {
// Code borrowed from Coffee Script and extended: // Code borrowed from Coffee Script and extended:
async = (async === undefined) ? true : async;
var ctor = window.ActiveXObject || window.XMLHttpRequest, var ctor = window.ActiveXObject || window.XMLHttpRequest,
xhr = new ctor('Microsoft.XMLHTTP'); xhr = new ctor('Microsoft.XMLHTTP');
xhr.open(method.toUpperCase(), url, async); xhr.open((options.method || 'get').toUpperCase(), options.url,
Base.pick(options.async, true));
if ('overrideMimeType' in xhr) if ('overrideMimeType' in xhr)
xhr.overrideMimeType('text/plain'); xhr.overrideMimeType('text/plain');
xhr.onreadystatechange = function() { xhr.onload = function() {
if (xhr.readyState === 4) { var status = xhr.status;
var status = xhr.status; if (status === 0 || status === 200) {
if (status === 0 || status === 200) { if (options.onLoad) {
callback.call(xhr, xhr.responseText); options.onLoad.call(xhr, xhr.responseText);
} else {
throw new Error('Could not load ' + url + ' (Error '
+ status + ')');
} }
} else {
xhr.onerror();
}
};
xhr.onerror = function() {
var status = xhr.status,
message = 'Could not load "' + options.url + '" (Status: '
+ status + ')';
if (options.onError) {
options.onError(message, status);
} else {
throw new Error(message);
} }
}; };
return xhr.send(null); return xhr.send(null);

View file

@ -635,7 +635,7 @@ new function() {
return item; return item;
} }
function importSVG(source, options, isRoot) { function importSVG(source, options) {
if (!source) if (!source)
return null; return null;
options = typeof options === 'function' ? { onLoad: options } options = typeof options === 'function' ? { onLoad: options }
@ -644,51 +644,65 @@ new function() {
// Remember current scope so we can restore it in onLoad. // Remember current scope so we can restore it in onLoad.
scope = paper; scope = paper;
function onLoadCallback(svg) { function onLoad(svg) {
paper = scope; paper = scope;
var item = importSVG(svg, options, isRoot), var item = importSVG(svg, options, true),
onLoad = options.onLoad; onLoad = options.onLoad;
if (onLoad) if (onLoad)
onLoad.call(this, item, svg); onLoad.call(this, item, svg);
} }
if (isRoot) { function onError(message, status) {
// Have the group not pass on all transformations to its children, var onError = options.onError;
// as this is how SVG works too. if (onError) {
// See if it's a string but handle markup separately onError.call(this, message, status);
if (typeof source === 'string' && !/^.*</.test(source)) { } else {
// First see if we're meant to import an element with the given throw new Error(message);
// id.
node = document.getElementById(source);
// Check if the string does not represent SVG data, in which
// case it must be the URL of a SVG to be loaded.
if (node) {
source = null;
} else {
return Http.request('get', source, onLoadCallback);
}
} else if (typeof File !== 'undefined' && source instanceof File) {
// Load local file through FileReader
var reader = new FileReader();
reader.onload = function() {
onLoadCallback(reader.result);
};
return reader.readAsText(source);
} }
} }
// Have the group not pass on all transformations to its children,
// as this is how SVG works too.
// See if it's a string but handle markup separately
if (typeof source === 'string' && !/^.*</.test(source)) {
// First see if we're meant to import an element with the given
// id.
node = document.getElementById(source);
// Check if the string does not represent SVG data, in which
// case it must be the URL of a SVG to be loaded.
if (node) {
source = null;
} else {
Http.request({
url: source, async: true,
onLoad: onLoad, onError: onError
});
}
} else if (typeof File !== 'undefined' && source instanceof File) {
// Load local file through FileReader
var reader = new FileReader();
reader.onload = function() {
onLoad(reader.result);
};
reader.onerror = function() {
onError(reader.error);
};
return reader.readAsText(source);
}
if (typeof source === 'string') { if (typeof source === 'string') {
node = new window.DOMParser().parseFromString(source, node = new window.DOMParser().parseFromString(source,
'image/svg+xml'); 'image/svg+xml');
} }
if (!node.nodeName) if (!node.nodeName)
throw new Error('Unsupported SVG source: ' + source); throw new Error('Unsupported SVG source: ' + source);
return importNode(node, options, isRoot); return importNode(node, options, true);
} }
// NOTE: Documentation is in Item#importSVG() // NOTE: Documentation is in Item#importSVG()
Item.inject({ Item.inject({
importSVG: function(node, options) { importSVG: function(node, options) {
return this.addChild(importSVG(node, options, true)); return this.addChild(importSVG(node, options));
} }
}); });
@ -696,7 +710,7 @@ new function() {
Project.inject({ Project.inject({
importSVG: function(node, options) { importSVG: function(node, options) {
this.activate(); this.activate();
return importSVG(node, options, true); return importSVG(node, options);
} }
}); });
}; };

View file

@ -122,31 +122,18 @@ test('Import complex CompoundPath and clone', function() {
function importSVG(assert, url, message, options) { function importSVG(assert, url, message, options) {
var done = assert.async(); var done = assert.async();
if (!message)
message = 'The imported SVG "' + url + '" should visually be the same '
+ 'as the rasterized original SVG data.';
project.importSVG(url, { project.importSVG(url, {
onLoad: function(item, svg) { onLoad: function(item, svg) {
function getValue(name) { if (!message) {
return parseFloat(svg.getAttribute(name)); message = 'The imported SVG "' + url + '" should visually be '
+ 'the same as the rasterized original SVG data.';
} }
/*
var size = new Size(getValue('width'), getValue('height'));
var group = new Group({
children: [
new Shape.Rectangle({
clipMask: true,
size: size
}),
item
]
});
*/
compareSVG(done, item, svg, message, options); compareSVG(done, item, svg, message, options);
}, },
onError: function(error) { onError: function(error) {
// TODO: Implement in SvgImport first! var ok = !!(options && options.expectError);
pushFailure('Loading SVG from a valid URL should not give an error.'); QUnit.push(ok, false, !ok, ok && message
|| 'Loading SVG from a valid URL should not give an error.');
done(); done();
} }
}); });
@ -164,4 +151,10 @@ if (!isNode) {
importSVG(assert, 'assets/' + name); importSVG(assert, 'assets/' + name);
}); });
}); });
test('Import inexistent file', function(assert) {
importSVG(assert, 'assets/inexistent.svg',
'Load an inexistent SVG file should trigger an error',
{ expectError: true });
});
} }