blob: 54c683fe168614263cfeec970f93e6958127b7c6 [file] [log] [blame]
(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;
};
if (!('URLSearchParams' in global) || (new global.URLSearchParams('?a=1').toString() !== 'a=1')) {
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%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') 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))
);