484 lines
14 KiB
JavaScript
484 lines
14 KiB
JavaScript
(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))
|
|
);
|