From 5bc2e1b687d9550ca0801e7241f57b068beb80a1 Mon Sep 17 00:00:00 2001 From: valdrinkoshi Date: Thu, 13 Jul 2017 18:07:31 -0700 Subject: [PATCH 1/2] forEach method --- src/html-imports.js | 68 ++++++++++++++++++++++----------------------- 1 file changed, 33 insertions(+), 35 deletions(-) diff --git a/src/html-imports.js b/src/html-imports.js index ef5e2ba..bc19ac1 100644 --- a/src/html-imports.js +++ b/src/html-imports.js @@ -29,6 +29,20 @@ }); } + /** + * @param {Array|NodeList|NamedNodeMap} list + * @param {!Function} callback + * @param {boolean=} inverseOrder + */ + const forEach = (list, callback, inverseOrder) => { + const length = list ? list.length : 0; + const increment = inverseOrder ? -1 : 1; + let i = inverseOrder ? length - 1 : 0; + for (; i < length && i >= 0; i = i + increment) { + callback(list[i], i); + } + }; + /********************* path fixup *********************/ const ABS_URL_TEST = /(^\/)|(^#)|(^[\w-\d]*:)/; const CSS_URL_REGEXP = /(url\()([^)]*)(\))/g; @@ -212,9 +226,7 @@ loadImports(doc) { const links = /** @type {!NodeList} */ (doc.querySelectorAll(importSelector)); - for (let i = 0, l = links.length; i < l; i++) { - this.loadImport(links[i]); - } + forEach(links, link => this.loadImport(link)); } /** @@ -301,7 +313,7 @@ (content.querySelectorAll(importDependenciesSelector)); // For source map hints. let inlineScriptIndex = 0; - for (let i = 0, l = n$.length, n; i < l && (n = n$[i]); i++) { + forEach(n$, n => { // Listen for load/error events, then fix urls. whenElementLoaded(n); Path.fixUrls(n, url); @@ -317,7 +329,7 @@ n.textContent = ''; inlineScriptIndex++; } - } + }); return content; } @@ -372,7 +384,7 @@ flatten(doc) { const n$ = /** @type {!NodeList} */ (doc.querySelectorAll(importSelector)); - for (let i = 0, l = n$.length, n; i < l && (n = n$[i]); i++) { + forEach(n$, n => { const imp = this.documents[n.href]; n.import = /** @type {!Document} */ (imp); if (imp && imp.nodeType === Node.DOCUMENT_FRAGMENT_NODE) { @@ -385,7 +397,7 @@ this.flatten(imp); n.appendChild(imp); } - } + }); } /** @@ -407,9 +419,7 @@ (document.createElement('script')); // Remove import-dependency attribute to avoid double cloning. s.removeAttribute(importDependencyAttr); - for (let j = 0, ll = s.attributes.length; j < ll; j++) { - clone.setAttribute(s.attributes[j].name, s.attributes[j].value); - } + forEach(s.attributes, attr => clone.setAttribute(attr.name, attr.value)); // Update currentScript and replace original with clone script. currentScript = clone; s.parentNode.replaceChild(clone, s); @@ -442,7 +452,7 @@ // If there is one imported, we must move all imported // links and styles to . const needsMove = isIE && !!document.querySelector(disabledLinkSelector); - for (let i = 0, l = s$.length, s; i < l && (s = s$[i]); i++) { + forEach(s$, s => { // Listen for load/error events, remove selector once is done loading. whenElementLoaded(s, () => { s.removeAttribute(importDependencyAttr); @@ -472,7 +482,7 @@ // Enable the loading of . s.removeAttribute('type'); } - } + }); } /** @@ -482,9 +492,7 @@ const n$ = /** @type {!NodeList} */ (document.querySelectorAll(importSelector)); // Inverse order to have events firing bottom-up. - for (let i = n$.length - 1, n; i >= 0 && (n = n$[i]); i--) { - this.fireEventIfNeeded(n); - } + forEach(n$, n => this.fireEventIfNeeded(n), true); } /** @@ -510,16 +518,8 @@ * @param {Array} mutations */ handleMutations(mutations) { - for (let i = 0; i < mutations.length; i++) { - const m = mutations[i]; - if (!m.addedNodes) { - continue; - } - for (let ii = 0; ii < m.addedNodes.length; ii++) { - const elem = m.addedNodes[ii]; - if (!elem || elem.nodeType !== Node.ELEMENT_NODE) { - continue; - } + forEach(mutations, m => forEach(m.addedNodes, elem => { + if (elem && elem.nodeType === Node.ELEMENT_NODE) { // NOTE: added scripts are not updating currentScript in IE. if (isImportLink(elem)) { this.loadImport( /** @type {!HTMLLinkElement} */ (elem)); @@ -527,7 +527,7 @@ this.loadImports( /** @type {!Element} */ (elem)); } } - } + })); } } @@ -615,13 +615,11 @@ callback(); return; } - for (let i = 0, l = imports.length, imp; i < l && (imp = imports[i]); i++) { - whenElementLoaded(imp, () => { - if (--pending === 0) { - callback(); - } - }); - } + forEach(imports, imp => whenElementLoaded(imp, () => { + if (--pending === 0) { + callback(); + } + })); } /** @@ -666,11 +664,11 @@ // or have .import defined. const imps = /** @type {!NodeList} */ (document.querySelectorAll(importSelector)); - for (let i = 0, l = imps.length, imp; i < l && (imp = imps[i]); i++) { + forEach(imps, imp => { if (!imp.import || imp.import.readyState !== 'loading') { imp['__loaded'] = true; } - } + }); // Listen for load/error events to capture dynamically added scripts. /** * @type {!function(!Event)} From 0f35731a71c2418c64929a5eaad3525c4580bb1d Mon Sep 17 00:00:00 2001 From: valdrinkoshi Date: Fri, 14 Jul 2017 12:32:07 -0700 Subject: [PATCH 2/2] build --- html-imports.min.js | 29 ++++++++++++++--------------- html-imports.min.js.map | 2 +- 2 files changed, 15 insertions(+), 16 deletions(-) diff --git a/html-imports.min.js b/html-imports.min.js index ab5a6dc..b93c407 100644 --- a/html-imports.min.js +++ b/html-imports.min.js @@ -7,20 +7,19 @@ Code distributed by Google as part of the polymer project is also subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt */ -'use strict';(function(u){function z(a,b){if("function"===typeof window.CustomEvent)return new CustomEvent(a,b);var c=document.createEvent("CustomEvent");c.initCustomEvent(a,!!b.bubbles,!!b.cancelable,b.detail);return c}function l(a){if(v)return a.ownerDocument!==document?a.ownerDocument:null;var b=a.__importDoc;if(!b&&a.parentNode){b=a.parentNode;if("function"===typeof b.closest)b=b.closest("link[rel=import]");else for(;!p(b)&&(b=b.parentNode););a.__importDoc=b}return b}function D(a){var b=document.querySelectorAll("link[rel=import]:not(import-dependency)"), -c=b.length;if(c)for(var e=0,d=b.length,h;e]*)(rel=['|"]?stylesheet['|"]?[^>]*>)/g,f={A:function(a,b){a.href&&a.setAttribute("href",f.i(a.getAttribute("href"),b));a.src&&a.setAttribute("src",f.i(a.getAttribute("src"),b));if("style"===a.localName){var c=f.u(a.textContent,b,F);a.textContent=f.u(c,b,G)}},u:function(a, -b,c){return a.replace(c,function(a,c,h,g){a=h.replace(/["']/g,"");b&&(a=f.v(a,b));return c+"'"+a+"'"+g})},i:function(a,b){return a&&E.test(a)?a:f.v(a,b)},v:function(a,b){if(void 0===f.g){f.g=!1;try{var c=new URL("b","http://a");c.pathname="c%20d";f.g="http://a/c%20d"===c.href}catch(e){}}if(f.g)return(new URL(a,b)).href;c=f.w;c||(c=document.implementation.createHTMLDocument("temp"),f.w=c,c.l=c.createElement("base"),c.head.appendChild(c.l),c.j=c.createElement("a"));c.l.href=b;c.j.href=a;return c.j.href|| -a}},C={async:!0,load:function(a,b,c){if(a)if(a.match(/^data:/)){a=a.split(",");var e=a[1],e=-1d.status?b(e,a):c(e)};d.send()}else c("error: href must be specified")}}, -w=/Trident/.test(navigator.userAgent)||/Edge\/\d./i.test(navigator.userAgent);k.prototype.f=function(a){a=a.querySelectorAll("link[rel=import]");for(var b=0,c=a.length;b]*)(rel=['|"]?stylesheet['|"]?[^>]*>)/g,d={w:function(a,b){a.href&&a.setAttribute("href",d.h(a.getAttribute("href"),b));a.src&&a.setAttribute("src",d.h(a.getAttribute("src"),b)); +if("style"===a.localName){var c=d.s(a.textContent,b,C);a.textContent=d.s(c,b,D)}},s:function(a,b,c){return a.replace(c,function(a,c,k,g){a=k.replace(/["']/g,"");b&&(a=d.u(a,b));return c+"'"+a+"'"+g})},h:function(a,b){return a&&B.test(a)?a:d.u(a,b)},u:function(a,b){if(void 0===d.f){d.f=!1;try{var c=new URL("b","http://a");c.pathname="c%20d";d.f="http://a/c%20d"===c.href}catch(e){}}if(d.f)return(new URL(a,b)).href;c=d.v;c||(c=document.implementation.createHTMLDocument("temp"),d.v=c,c.j=c.createElement("base"), +c.head.appendChild(c.j),c.i=c.createElement("a"));c.j.href=b;c.i.href=a;return c.i.href||a}},y={async:!0,load:function(a,b,c){if(a)if(a.match(/^data:/)){a=a.split(",");var e=a[1],e=-1f.status?b(e,a):c(e)};f.send()}else c("error: href must be specified")}},t=/Trident/.test(navigator.userAgent)||/Edge\/\d./i.test(navigator.userAgent);g.prototype.c=function(a){var b=this;h(a.querySelectorAll("link[rel=import]"),function(a){return b.m(a)})};g.prototype.m=function(a){var b=this,c=a.href;if(void 0!==this.a[c]){var e=this.a[c];e&&e.__loaded&&(a.import=e,this.l(a))}else this.b++,this.a[c]="pending",y.load(c,function(a,e){a=b.C(a,e||c);b.a[c]=a;b.b--;b.c(a); +b.o()},function(){b.a[c]=null;b.b--;b.o()})};g.prototype.C=function(a,b){if(!a)return document.createDocumentFragment();t&&(a=a.replace(E,function(a,b,c){return-1===a.indexOf("type=")?b+" type=import-disable "+c:a}));var c=document.createElement("template");c.innerHTML=a;if(c.content)a=c.content;else for(a=document.createDocumentFragment();c.firstChild;)a.appendChild(c.firstChild);if(c=a.querySelector("base"))b=d.h(c.getAttribute("href"),b),c.removeAttribute("href");var e=0;h(a.querySelectorAll('link[rel=import], link[rel=stylesheet][href][type=import-disable],\n style:not([type]), link[rel=stylesheet][href]:not([type]),\n script:not([type]), script[type="application/javascript"],\n script[type="text/javascript"]'), +function(a){p(a);d.w(a,b);a.setAttribute("import-dependency","");"script"===a.localName&&!a.src&&a.textContent&&(a.setAttribute("src","data:text/javascript;charset=utf-8,"+encodeURIComponent(a.textContent+("\n//# sourceURL="+b+(e?"-"+e:"")+".js\n"))),a.textContent="",e++)});return a};g.prototype.o=function(){var a=this;if(!this.b){this.g.disconnect();this.flatten(document);var b=!1,c=!1,e=function(){c&&b&&(a.c(document),a.b||(a.g.observe(document.head,{childList:!0,subtree:!0}),a.A()))};this.F(function(){c= +!0;e()});this.D(function(){b=!0;e()})}};g.prototype.flatten=function(a){var b=this;h(a.querySelectorAll("link[rel=import]"),function(a){var c=b.a[a.href];(a.import=c)&&c.nodeType===Node.DOCUMENT_FRAGMENT_NODE&&(b.a[a.href]=a,a.readyState="loading",a.import=a,b.flatten(c),a.appendChild(c))})};g.prototype.D=function(a){function b(f){if(f {\r\n\r\n /********************* base setup *********************/\r\n const useNative = Boolean('import' in document.createElement('link'));\r\n\r\n // Polyfill `currentScript` for browsers without it.\r\n let currentScript = null;\r\n if ('currentScript' in document === false) {\r\n Object.defineProperty(document, 'currentScript', {\r\n get() {\r\n return currentScript ||\r\n // NOTE: only works when called in synchronously executing code.\r\n // readyState should check if `loading` but IE10 is\r\n // interactive when scripts run so we cheat. This is not needed by\r\n // html-imports polyfill but helps generally polyfill `currentScript`.\r\n (document.readyState !== 'complete' ?\r\n document.scripts[document.scripts.length - 1] : null);\r\n },\r\n configurable: true\r\n });\r\n }\r\n\r\n /********************* path fixup *********************/\r\n const ABS_URL_TEST = /(^\\/)|(^#)|(^[\\w-\\d]*:)/;\r\n const CSS_URL_REGEXP = /(url\\()([^)]*)(\\))/g;\r\n const CSS_IMPORT_REGEXP = /(@import[\\s]+(?!url\\())([^;]*)(;)/g;\r\n const STYLESHEET_REGEXP = /(]*)(rel=['|\"]?stylesheet['|\"]?[^>]*>)/g;\r\n\r\n // path fixup: style elements in imports must be made relative to the main\r\n // document. We fixup url's in url() and @import.\r\n const Path = {\r\n\r\n fixUrls(element, base) {\r\n if (element.href) {\r\n element.setAttribute('href',\r\n Path.replaceAttrUrl(element.getAttribute('href'), base));\r\n }\r\n if (element.src) {\r\n element.setAttribute('src',\r\n Path.replaceAttrUrl(element.getAttribute('src'), base));\r\n }\r\n if (element.localName === 'style') {\r\n const r = Path.replaceUrls(element.textContent, base, CSS_URL_REGEXP);\r\n element.textContent = Path.replaceUrls(r, base, CSS_IMPORT_REGEXP);\r\n }\r\n },\r\n\r\n replaceUrls(text, linkUrl, regexp) {\r\n return text.replace(regexp, (m, pre, url, post) => {\r\n let urlPath = url.replace(/[\"']/g, '');\r\n if (linkUrl) {\r\n urlPath = Path.resolveUrl(urlPath, linkUrl);\r\n }\r\n return pre + '\\'' + urlPath + '\\'' + post;\r\n });\r\n },\r\n\r\n replaceAttrUrl(text, linkUrl) {\r\n if (text && ABS_URL_TEST.test(text)) {\r\n return text;\r\n } else {\r\n return Path.resolveUrl(text, linkUrl);\r\n }\r\n },\r\n\r\n resolveUrl(url, base) {\r\n // Lazy feature detection.\r\n if (Path.__workingURL === undefined) {\r\n Path.__workingURL = false;\r\n try {\r\n const u = new URL('b', 'http://a');\r\n u.pathname = 'c%20d';\r\n Path.__workingURL = (u.href === 'http://a/c%20d');\r\n } catch (e) {}\r\n }\r\n\r\n if (Path.__workingURL) {\r\n return (new URL(url, base)).href;\r\n }\r\n\r\n // Fallback to creating an anchor into a disconnected document.\r\n let doc = Path.__tempDoc;\r\n if (!doc) {\r\n doc = document.implementation.createHTMLDocument('temp');\r\n Path.__tempDoc = doc;\r\n doc.__base = doc.createElement('base');\r\n doc.head.appendChild(doc.__base);\r\n doc.__anchor = doc.createElement('a');\r\n }\r\n doc.__base.href = base;\r\n doc.__anchor.href = url;\r\n return doc.__anchor.href || url;\r\n }\r\n };\r\n\r\n /********************* Xhr processor *********************/\r\n const Xhr = {\r\n\r\n async: true,\r\n\r\n /**\r\n * @param {!string} url\r\n * @param {!function(!string, string=)} success\r\n * @param {!function(!string)} fail\r\n */\r\n load(url, success, fail) {\r\n if (!url) {\r\n fail('error: href must be specified');\r\n } else if (url.match(/^data:/)) {\r\n // Handle Data URI Scheme\r\n const pieces = url.split(',');\r\n const header = pieces[0];\r\n let resource = pieces[1];\r\n if (header.indexOf(';base64') > -1) {\r\n resource = atob(resource);\r\n } else {\r\n resource = decodeURIComponent(resource);\r\n }\r\n success(resource);\r\n } else {\r\n const request = new XMLHttpRequest();\r\n request.open('GET', url, Xhr.async);\r\n request.onload = () => {\r\n // Servers redirecting an import can add a Location header to help us\r\n // polyfill correctly. Handle relative and full paths.\r\n // Prefer responseURL which already resolves redirects\r\n // https://xhr.spec.whatwg.org/#the-responseurl-attribute\r\n let redirectedUrl = request.responseURL || request.getResponseHeader('Location');\r\n if (redirectedUrl && redirectedUrl.indexOf('/') === 0) {\r\n // In IE location.origin might not work\r\n // https://connect.microsoft.com/IE/feedback/details/1763802/location-origin-is-undefined-in-ie-11-on-windows-10-but-works-on-windows-7\r\n const origin = (location.origin || location.protocol + '//' + location.host);\r\n redirectedUrl = origin + redirectedUrl;\r\n }\r\n const resource = /** @type {string} */ (request.response || request.responseText);\r\n if (request.status === 304 || request.status === 0 ||\r\n request.status >= 200 && request.status < 300) {\r\n success(resource, redirectedUrl);\r\n } else {\r\n fail(resource);\r\n }\r\n };\r\n request.send();\r\n }\r\n }\r\n };\r\n\r\n /********************* importer *********************/\r\n\r\n const isIE = /Trident/.test(navigator.userAgent) ||\r\n /Edge\\/\\d./i.test(navigator.userAgent);\r\n\r\n const importSelector = 'link[rel=import]';\r\n\r\n // Used to disable loading of resources.\r\n const importDisableType = 'import-disable';\r\n\r\n const disabledLinkSelector = `link[rel=stylesheet][href][type=${importDisableType}]`;\r\n\r\n const importDependenciesSelector = `${importSelector}, ${disabledLinkSelector},\r\n style:not([type]), link[rel=stylesheet][href]:not([type]),\r\n script:not([type]), script[type=\"application/javascript\"],\r\n script[type=\"text/javascript\"]`;\r\n\r\n const importDependencyAttr = 'import-dependency';\r\n\r\n const rootImportSelector = `${importSelector}:not(${importDependencyAttr})`;\r\n\r\n const pendingScriptsSelector = `script[${importDependencyAttr}]`;\r\n\r\n const pendingStylesSelector = `style[${importDependencyAttr}],\r\n link[rel=stylesheet][${importDependencyAttr}]`;\r\n\r\n /**\r\n * Importer will:\r\n * - load any linked import documents (with deduping)\r\n * - whenever an import is loaded, prompt the parser to try to parse\r\n * - observe imported documents for new elements (these are handled via the\r\n * dynamic importer)\r\n */\r\n class Importer {\r\n constructor() {\r\n this.documents = {};\r\n // Used to keep track of pending loads, so that flattening and firing of\r\n // events can be done when all resources are ready.\r\n this.inflight = 0;\r\n this.dynamicImportsMO = new MutationObserver(m => this.handleMutations(m));\r\n // Observe changes on .\r\n this.dynamicImportsMO.observe(document.head, {\r\n childList: true,\r\n subtree: true\r\n });\r\n // 1. Load imports contents\r\n // 2. Assign them to first import links on the document\r\n // 3. Wait for import styles & scripts to be done loading/running\r\n // 4. Fire load/error events\r\n this.loadImports(document);\r\n }\r\n\r\n /**\r\n * @param {!(HTMLDocument|DocumentFragment|Element)} doc\r\n */\r\n loadImports(doc) {\r\n const links = /** @type {!NodeList} */\r\n (doc.querySelectorAll(importSelector));\r\n for (let i = 0, l = links.length; i < l; i++) {\r\n this.loadImport(links[i]);\r\n }\r\n }\r\n\r\n /**\r\n * @param {!HTMLLinkElement} link\r\n */\r\n loadImport(link) {\r\n const url = link.href;\r\n // This resource is already being handled by another import.\r\n if (this.documents[url] !== undefined) {\r\n // If import is already loaded, we can safely associate it to the link\r\n // and fire the load/error event.\r\n const imp = this.documents[url];\r\n if (imp && imp['__loaded']) {\r\n link.import = imp;\r\n this.fireEventIfNeeded(link);\r\n }\r\n return;\r\n }\r\n this.inflight++;\r\n // Mark it as pending to notify others this url is being loaded.\r\n this.documents[url] = 'pending';\r\n Xhr.load(url, (resource, redirectedUrl) => {\r\n const doc = this.makeDocument(resource, redirectedUrl || url);\r\n this.documents[url] = doc;\r\n this.inflight--;\r\n // Load subtree.\r\n this.loadImports(doc);\r\n this.processImportsIfLoadingDone();\r\n }, () => {\r\n // If load fails, handle error.\r\n this.documents[url] = null;\r\n this.inflight--;\r\n this.processImportsIfLoadingDone();\r\n });\r\n }\r\n\r\n /**\r\n * Creates a new document containing resource and normalizes urls accordingly.\r\n * @param {string=} resource\r\n * @param {string=} url\r\n * @return {!DocumentFragment}\r\n */\r\n makeDocument(resource, url) {\r\n if (!resource) {\r\n return document.createDocumentFragment();\r\n }\r\n\r\n if (isIE) {\r\n // should be appended to . Not doing so\r\n // in IE/Edge breaks the cascading order. We disable the loading by\r\n // setting the type before setting innerHTML to avoid loading\r\n // resources twice.\r\n resource = resource.replace(STYLESHEET_REGEXP, (match, p1, p2) => {\r\n if (match.indexOf('type=') === -1) {\r\n return `${p1} type=${importDisableType} ${p2}`;\r\n }\r\n return match;\r\n });\r\n }\r\n\r\n let content;\r\n const template = /** @type {!HTMLTemplateElement} */\r\n (document.createElement('template'));\r\n template.innerHTML = resource;\r\n if (template.content) {\r\n // This creates issues in Safari10 when used with shadydom (see #12).\r\n content = template.content;\r\n } else {\r\n //