Merge pull request #3071 from pavle-goloskokovic/master

Adding fallback for loading files on browsers where URL API is not supported
This commit is contained in:
Richard Davey 2017-10-13 11:18:54 +01:00 committed by GitHub
commit dc592967ae
6 changed files with 62 additions and 335 deletions

View file

@ -162,4 +162,52 @@ var File = new Class({
});
/**
* Static method for creating object URL using URL API and setting it as image 'src' attribute.
* If URL API is not supported (usually on old browsers) it falls back to creating Base64 encoded url using FileReader.
*
* @method createObjectURL
* @static
* @param image {Image} Image object which 'src' attribute should be set to object URL.
* @param blob {Blob} A Blob object to create an object URL for.
* @param defaultType {string} Default mime type used if blob type is not available.
*/
File.createObjectURL = function (image, blob, defaultType)
{
if(URL)
{
image.src = URL.createObjectURL(blob);
}
else
{
var reader = new FileReader();
reader.onload = function()
{
delete image.crossOrigin;
image.src = 'data:' + (blob.type || defaultType) + ';base64,' + reader.result.split(',')[1];
};
reader.onerror = image.onerror;
reader.readAsDataURL(blob);
}
};
/**
* Static method for releasing an existing object URL which was previously created
* by calling {@link File#createObjectURL} method.
*
* @method revokeObjectURL
* @static
* @param image {Image} Image object which 'src' attribute should be revoked.
*/
File.revokeObjectURL = function (image)
{
if(URL)
{
URL.revokeObjectURL(image.src);
}
};
module.exports = File;

View file

@ -74,7 +74,7 @@ var HTMLFile = new Class({
this.data.onload = function ()
{
URL.revokeObjectURL(_this.data.src);
File.revokeObjectURL(_this.data);
_this.onComplete();
@ -83,14 +83,14 @@ var HTMLFile = new Class({
this.data.onerror = function ()
{
URL.revokeObjectURL(_this.data.src);
File.revokeObjectURL(_this.data);
_this.state = CONST.FILE_ERRORED;
callback(_this);
};
this.data.src = URL.createObjectURL(blob);
File.createObjectURL(this.data, blob, 'image/svg+xml');
}
});

View file

@ -56,7 +56,7 @@ var ImageFile = new Class({
this.data.onload = function ()
{
URL.revokeObjectURL(_this.data.src);
File.revokeObjectURL(_this.data);
_this.onComplete();
@ -65,14 +65,15 @@ var ImageFile = new Class({
this.data.onerror = function ()
{
URL.revokeObjectURL(_this.data.src);
File.revokeObjectURL(_this.data);
_this.state = CONST.FILE_ERRORED;
callback(_this);
};
this.data.src = URL.createObjectURL(this.xhrLoader.response);
File.createObjectURL(this.data, this.xhrLoader.response, 'image/png');
}
});

View file

@ -56,7 +56,10 @@ var SVGFile = new Class({
this.data.onload = function ()
{
URL.revokeObjectURL(_this.data.src);
if(!retry)
{
File.revokeObjectURL(_this.data);
}
_this.onComplete();
@ -65,16 +68,14 @@ var SVGFile = new Class({
this.data.onerror = function ()
{
URL.revokeObjectURL(_this.data.src);
// Safari 8 re-try
if (!retry)
{
retry = true;
var url = 'data:image/svg+xml,' + encodeURIComponent(svg.join(''));
File.revokeObjectURL(_this.data);
_this.data.src = URL.createObjectURL(url);
_this.data.src = 'data:image/svg+xml,' + encodeURIComponent(svg.join(''));
}
else
{
@ -84,7 +85,7 @@ var SVGFile = new Class({
}
};
this.data.src = URL.createObjectURL(blob);
File.createObjectURL(this.data, blob, 'image/svg+xml');
}
});

View file

@ -1,322 +0,0 @@
var g = (typeof global !== 'undefined') ? global
: ((typeof window !== 'undefined') ? window
: ((typeof self !== 'undefined') ? self : this));
(function(global) {
/**
* Polyfill URLSearchParams
*
* Inspired from : https://github.com/WebReflection/url-search-params/blob/master/src/url-search-params.js
*/
var checkIfIteratorIsSupported = function() {
try {
return !!Symbol.iterator;
} catch(error) {
return false;
}
};
var iteratorSupported = checkIfIteratorIsSupported();
var createIterator = function(items) {
var iterator = {
next: function() {
var value = items.shift();
return { done: value === void 0, value: value };
}
};
if(iteratorSupported) {
iterator[Symbol.iterator] = function() {
return iterator;
};
}
return iterator;
};
var polyfillURLSearchParams= function() {
var URLSearchParams = function(searchString) {
Object.defineProperty(this, '_entries', { value: {} });
if(typeof searchString === 'string') {
if(searchString !== '') {
searchString = searchString.replace(/^\?/, '');
var attributes = searchString.split('&');
var attribute;
for(var i = 0; i < attributes.length; i++) {
attribute = attributes[i].split('=');
this.append(
decodeURIComponent(attribute[0]),
(attribute.length > 1) ? decodeURIComponent(attribute[1]) : ''
);
}
}
} else if(searchString instanceof URLSearchParams) {
var _this = this;
searchString.forEach(function(value, name) {
_this.append(value, name);
});
}
};
var proto = URLSearchParams.prototype;
proto.append = function(name, value) {
if(name in this._entries) {
this._entries[name].push(value.toString());
} else {
this._entries[name] = [value.toString()];
}
};
proto.delete = function(name) {
delete this._entries[name];
};
proto.get = function(name) {
return (name in this._entries) ? this._entries[name][0] : null;
};
proto.getAll = function(name) {
return (name in this._entries) ? this._entries[name].slice(0) : [];
};
proto.has = function(name) {
return (name in this._entries);
};
proto.set = function(name, value) {
this._entries[name] = [value.toString()];
};
proto.forEach = function(callback, thisArg) {
var entries;
for(var name in this._entries) {
if(this._entries.hasOwnProperty(name)) {
entries = this._entries[name];
for(var i = 0; i < entries.length; i++) {
callback.call(thisArg, entries[i], name, this);
}
}
}
};
proto.keys = function() {
var items = [];
this.forEach(function(value, name) { items.push(name); });
return createIterator(items);
};
proto.values = function() {
var items = [];
this.forEach(function(value) { items.push(value); });
return createIterator(items);
};
proto.entries = function() {
var items = [];
this.forEach(function(value, name) { items.push([name, value]); });
return createIterator(items);
};
if(iteratorSupported) {
proto[Symbol.iterator] = proto.entries;
}
proto.toString = function() {
var searchString = '';
this.forEach(function(value, name) {
if(searchString.length > 0) searchString+= '&';
searchString += encodeURIComponent(name) + '=' + encodeURIComponent(value);
});
return searchString;
};
global.URLSearchParams = URLSearchParams;
};
if(!('URLSearchParams' in global)) {
polyfillURLSearchParams();
}
// HTMLAnchorElement
})(g);
(function(global) {
/**
* Polyfill URL
*
* Inspired from : https://github.com/arv/DOM-URL-Polyfill/blob/master/src/url.js
*/
var checkIfURLIsSupported = function() {
try {
var u = new URL('b', 'http://a');
u.pathname = 'c%20d';
return (u.href === 'http://a/c%20d') && u.searchParams;
} catch(e) {
return false;
}
};
var polyfillURL = function() {
var _URL = global.URL;
var URL = function(url, base) {
if(typeof url !== 'string') throw new TypeError('Failed to construct \'URL\': Invalid URL');
var doc = document.implementation.createHTMLDocument('');
window.doc = doc;
if(base) {
var baseElement = doc.createElement('base');
baseElement.href = base;
doc.head.appendChild(baseElement);
}
var anchorElement = doc.createElement('a');
anchorElement.href = url;
doc.body.appendChild(anchorElement);
anchorElement.href = anchorElement.href; // force href to refresh
if(anchorElement.protocol === ':' || !/:/.test(anchorElement.href)) {
throw new TypeError('Invalid URL');
}
Object.defineProperty(this, '_anchorElement', {
value: anchorElement
});
};
var proto = URL.prototype;
var linkURLWithAnchorAttribute = function(attributeName) {
Object.defineProperty(proto, attributeName, {
get: function() {
return this._anchorElement[attributeName];
},
set: function(value) {
this._anchorElement[attributeName] = value;
},
enumerable: true
});
};
['hash', 'host', 'hostname', 'port', 'protocol', 'search']
.forEach(function(attributeName) {
linkURLWithAnchorAttribute(attributeName);
});
Object.defineProperties(proto, {
'toString': {
get: function() {
var _this = this;
return function() {
return _this.href;
};
}
},
'href' : {
get: function() {
return this._anchorElement.href.replace(/\?$/,'');
},
set: function(value) {
this._anchorElement.href = value;
},
enumerable: true
},
'pathname' : {
get: function() {
return this._anchorElement.pathname.replace(/(^\/?)/,'/');
},
set: function(value) {
this._anchorElement.pathname = value;
},
enumerable: true
},
'origin': {
get: function() {
return this._anchorElement.protocol + '//' + this._anchorElement.hostname + (this._anchorElement.port ? (':' + this._anchorElement.port) : '');
},
enumerable: true
},
'password': { // TODO
get: function() {
return '';
},
set: function(value) {
},
enumerable: true
},
'username': { // TODO
get: function() {
return '';
},
set: function(value) {
},
enumerable: true
},
'searchParams': {
get: function() {
var searchParams = new URLSearchParams(this.search);
var _this = this;
['append', 'delete', 'set'].forEach(function(methodName) {
var method = searchParams[methodName];
searchParams[methodName] = function() {
method.apply(searchParams, arguments);
_this.search = searchParams.toString();
};
});
return searchParams;
},
enumerable: true
}
});
URL.createObjectURL = function(blob) {
return _URL.createObjectURL.apply(_URL, arguments);
};
URL.revokeObjectURL = function(url) {
return _URL.revokeObjectURL.apply(_URL, arguments);
};
global.URL = URL;
};
if(!checkIfURLIsSupported()) {
polyfillURL();
}
if((global.location !== void 0) && !('origin' in global.location)) {
var getOrigin = function() {
return global.location.protocol + '//' + global.location.hostname + (global.location.port ? (':' + global.location.port) : '');
};
try {
Object.defineProperty(global.location, 'origin', {
get: getOrigin,
enumerable: true
});
} catch(e) {
setInterval(function() {
global.location.origin = getOrigin();
}, 100);
}
}
})(g);

View file

@ -7,4 +7,3 @@ require('./Math.trunc');
require('./performance.now');
require('./requestAnimationFrame');
require('./Uint32Array');
require('./URL');