From a02ffdaac02a71f67bf0bd9dce1c9b637646f0c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Herv=C3=A9=20=20MENAGER?= <herve.menager@pasteur.fr> Date: Sat, 5 May 2018 22:05:35 +0200 Subject: [PATCH] add URL polyfill for older browsers Former-commit-id: c6f710178efd92ab3847b2518eda34eacc1e7374 --- .../static/url-polyfill/url-polyfill.js | 348 ++++++++++++++++++ ippisite/ippidb/templates/base.html | 1 + 2 files changed, 349 insertions(+) create mode 100644 ippisite/ippidb/static/url-polyfill/url-polyfill.js diff --git a/ippisite/ippidb/static/url-polyfill/url-polyfill.js b/ippisite/ippidb/static/url-polyfill/url-polyfill.js new file mode 100644 index 00000000..e38c1202 --- /dev/null +++ b/ippisite/ippidb/static/url-polyfill/url-polyfill.js @@ -0,0 +1,348 @@ +(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(value).replace(/\+/g, ' '); + }; + + 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( + deserializeParam(attribute[0]), + (attribute.length > 1) ? deserializeParam(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 += serializeParam(name) + '=' + serializeParam(value); + }); + return searchString; + }; + + global.URLSearchParams = URLSearchParams; + }; + + if(!('URLSearchParams' in global) || (new URLSearchParams('?a=1').toString() !== 'a=1')) { + polyfillURLSearchParams(); + } + + // 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 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); + + 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() { + // 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 + }, + + '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); + } + } + +})( + (typeof global !== 'undefined') ? global + : ((typeof window !== 'undefined') ? window + : ((typeof self !== 'undefined') ? self : this)) +); diff --git a/ippisite/ippidb/templates/base.html b/ippisite/ippidb/templates/base.html index 4364ad63..e62186fa 100644 --- a/ippisite/ippidb/templates/base.html +++ b/ippisite/ippidb/templates/base.html @@ -9,6 +9,7 @@ <link rel="stylesheet" href="/static/css/ippidb.css"> <link rel="stylesheet" href="/static/css/ippidb-typeahead.css"> + <script src="/static/url-polyfill/url-polyfill.js"></script> <script src="/static/jquery/jquery-3.3.1.min.js"></script> <script src="/static/bootstrap/js/bootstrap.bundle.min.js"></script> <script src="/static/typeahead/typeahead.bundle.min.js"></script> -- GitLab