You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
483 lines
14 KiB
483 lines
14 KiB
(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; |
|
}; |
|
|
|
/** |
|
* Search param name and values should be encoded according to https://url.spec.whatwg.org/#urlencoded-serializing |
|
* encodeURIComponent() produces the same result except encoding spaces as `%20` instead of `+`. |
|
*/ |
|
var serializeParam = function(value) { |
|
return encodeURIComponent(value).replace(/%20/g, '+'); |
|
}; |
|
|
|
var deserializeParam = function(value) { |
|
return decodeURIComponent(String(value).replace(/\+/g, ' ')); |
|
}; |
|
|
|
var polyfillURLSearchParams = function() { |
|
|
|
var URLSearchParams = function(searchString) { |
|
Object.defineProperty(this, '_entries', { writable: true, value: {} }); |
|
var typeofSearchString = typeof searchString; |
|
|
|
if (typeofSearchString === 'undefined') { |
|
// do nothing |
|
} else if (typeofSearchString === 'string') { |
|
if (searchString !== '') { |
|
this._fromString(searchString); |
|
} |
|
} else if (searchString instanceof URLSearchParams) { |
|
var _this = this; |
|
searchString.forEach(function(value, name) { |
|
_this.append(name, value); |
|
}); |
|
} else if ((searchString !== null) && (typeofSearchString === 'object')) { |
|
if (Object.prototype.toString.call(searchString) === '[object Array]') { |
|
for (var i = 0; i < searchString.length; i++) { |
|
var entry = searchString[i]; |
|
if ((Object.prototype.toString.call(entry) === '[object Array]') || (entry.length !== 2)) { |
|
this.append(entry[0], entry[1]); |
|
} else { |
|
throw new TypeError('Expected [string, any] as entry at index ' + i + ' of URLSearchParams\'s input'); |
|
} |
|
} |
|
} else { |
|
for (var key in searchString) { |
|
if (searchString.hasOwnProperty(key)) { |
|
this.append(key, searchString[key]); |
|
} |
|
} |
|
} |
|
} else { |
|
throw new TypeError('Unsupported input\'s type for URLSearchParams'); |
|
} |
|
}; |
|
|
|
var proto = URLSearchParams.prototype; |
|
|
|
proto.append = function(name, value) { |
|
if (name in this._entries) { |
|
this._entries[name].push(String(value)); |
|
} else { |
|
this._entries[name] = [String(value)]; |
|
} |
|
}; |
|
|
|
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] = [String(value)]; |
|
}; |
|
|
|
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 searchArray = []; |
|
this.forEach(function(value, name) { |
|
searchArray.push(serializeParam(name) + '=' + serializeParam(value)); |
|
}); |
|
return searchArray.join('&'); |
|
}; |
|
|
|
|
|
global.URLSearchParams = URLSearchParams; |
|
}; |
|
|
|
var checkIfURLSearchParamsSupported = function() { |
|
try { |
|
var URLSearchParams = global.URLSearchParams; |
|
|
|
return (new URLSearchParams('?a=1').toString() === 'a=1') && (typeof URLSearchParams.prototype.set === 'function'); |
|
} catch (e) { |
|
return false; |
|
} |
|
}; |
|
|
|
if (!checkIfURLSearchParamsSupported()) { |
|
polyfillURLSearchParams(); |
|
} |
|
|
|
var proto = global.URLSearchParams.prototype; |
|
|
|
if (typeof proto.sort !== 'function') { |
|
proto.sort = function() { |
|
var _this = this; |
|
var items = []; |
|
this.forEach(function(value, name) { |
|
items.push([name, value]); |
|
if (!_this._entries) { |
|
_this.delete(name); |
|
} |
|
}); |
|
items.sort(function(a, b) { |
|
if (a[0] < b[0]) { |
|
return -1; |
|
} else if (a[0] > b[0]) { |
|
return +1; |
|
} else { |
|
return 0; |
|
} |
|
}); |
|
if (_this._entries) { // force reset because IE keeps keys index |
|
_this._entries = {}; |
|
} |
|
for (var i = 0; i < items.length; i++) { |
|
this.append(items[i][0], items[i][1]); |
|
} |
|
}; |
|
} |
|
|
|
if (typeof proto._fromString !== 'function') { |
|
Object.defineProperty(proto, '_fromString', { |
|
enumerable: false, |
|
configurable: false, |
|
writable: false, |
|
value: function(searchString) { |
|
if (this._entries) { |
|
this._entries = {}; |
|
} else { |
|
var keys = []; |
|
this.forEach(function(value, name) { |
|
keys.push(name); |
|
}); |
|
for (var i = 0; i < keys.length; i++) { |
|
this.delete(keys[i]); |
|
} |
|
} |
|
|
|
searchString = searchString.replace(/^\?/, ''); |
|
var attributes = searchString.split('&'); |
|
var attribute; |
|
for (var i = 0; i < attributes.length; i++) { |
|
attribute = attributes[i].split('='); |
|
this.append( |
|
deserializeParam(attribute[0]), |
|
(attribute.length > 1) ? deserializeParam(attribute[1]) : '' |
|
); |
|
} |
|
} |
|
}); |
|
} |
|
|
|
// HTMLAnchorElement |
|
|
|
})( |
|
(typeof global !== 'undefined') ? global |
|
: ((typeof window !== 'undefined') ? window |
|
: ((typeof self !== 'undefined') ? self : this)) |
|
); |
|
|
|
(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 global.URL('b', 'http://a'); |
|
u.pathname = 'c d'; |
|
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') url = String(url); |
|
|
|
// Only create another document if the base is different from current location. |
|
var doc = document, baseElement; |
|
if (base && (global.location === void 0 || base !== global.location.href)) { |
|
doc = document.implementation.createHTMLDocument(''); |
|
baseElement = doc.createElement('base'); |
|
baseElement.href = base; |
|
doc.head.appendChild(baseElement); |
|
try { |
|
if (baseElement.href.indexOf(base) !== 0) throw new Error(baseElement.href); |
|
} catch (err) { |
|
throw new Error('URL unable to set base ' + base + ' due to ' + err); |
|
} |
|
} |
|
|
|
var anchorElement = doc.createElement('a'); |
|
anchorElement.href = url; |
|
if (baseElement) { |
|
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 |
|
}); |
|
|
|
|
|
// create a linked searchParams which reflect its changes on URL |
|
var searchParams = new global.URLSearchParams(this.search); |
|
var enableSearchUpdate = true; |
|
var enableSearchParamsUpdate = true; |
|
var _this = this; |
|
['append', 'delete', 'set'].forEach(function(methodName) { |
|
var method = searchParams[methodName]; |
|
searchParams[methodName] = function() { |
|
method.apply(searchParams, arguments); |
|
if (enableSearchUpdate) { |
|
enableSearchParamsUpdate = false; |
|
_this.search = searchParams.toString(); |
|
enableSearchParamsUpdate = true; |
|
} |
|
}; |
|
}); |
|
|
|
Object.defineProperty(this, 'searchParams', { |
|
value: searchParams, |
|
enumerable: true |
|
}); |
|
|
|
var search = void 0; |
|
Object.defineProperty(this, '_updateSearchParams', { |
|
enumerable: false, |
|
configurable: false, |
|
writable: false, |
|
value: function() { |
|
if (this.search !== search) { |
|
search = this.search; |
|
if (enableSearchParamsUpdate) { |
|
enableSearchUpdate = false; |
|
this.searchParams._fromString(this.search); |
|
enableSearchUpdate = true; |
|
} |
|
} |
|
} |
|
}); |
|
}; |
|
|
|
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'] |
|
.forEach(function(attributeName) { |
|
linkURLWithAnchorAttribute(attributeName); |
|
}); |
|
|
|
Object.defineProperty(proto, 'search', { |
|
get: function() { |
|
return this._anchorElement['search']; |
|
}, |
|
set: function(value) { |
|
this._anchorElement['search'] = value; |
|
this._updateSearchParams(); |
|
}, |
|
enumerable: true |
|
}); |
|
|
|
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; |
|
this._updateSearchParams(); |
|
}, |
|
enumerable: true |
|
}, |
|
|
|
'pathname': { |
|
get: function() { |
|
return this._anchorElement.pathname.replace(/(^\/?)/, '/'); |
|
}, |
|
set: function(value) { |
|
this._anchorElement.pathname = value; |
|
}, |
|
enumerable: true |
|
}, |
|
|
|
'origin': { |
|
get: function() { |
|
// get expected port from protocol |
|
var expectedPort = { 'http:': 80, 'https:': 443, 'ftp:': 21 }[this._anchorElement.protocol]; |
|
// add port to origin if, expected port is different than actual port |
|
// and it is not empty f.e http://foo:8080 |
|
// 8080 != 80 && 8080 != '' |
|
var addPortToOrigin = this._anchorElement.port != expectedPort && |
|
this._anchorElement.port !== ''; |
|
|
|
return this._anchorElement.protocol + |
|
'//' + |
|
this._anchorElement.hostname + |
|
(addPortToOrigin ? (':' + 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 |
|
}, |
|
}); |
|
|
|
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); |
|
} |
|
} |
|
|
|
})( |
|
(typeof global !== 'undefined') ? global |
|
: ((typeof window !== 'undefined') ? window |
|
: ((typeof self !== 'undefined') ? self : this)) |
|
);
|
|
|