UNPKG

6.15 kBJavaScriptView Raw
1/**
2 * @license
3 * Copyright (c) 2018 The Polymer Project Authors. All rights reserved.
4 * This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
5 * The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
6 * The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
7 * Code distributed by Google as part of the polymer project is also
8 * subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
9 */
10
11(function() {
12 'use strict';
13
14 /**
15 * Basic flow of the loader process
16 *
17 * There are 4 flows the loader can take when booting up
18 *
19 * - Synchronous script, no polyfills needed
20 * - wait for `DOMContentLoaded`
21 * - run callbacks passed to `waitFor`
22 * - fire WCR event
23 *
24 * - Synchronous script, polyfills needed
25 * - document.write the polyfill bundle
26 * - wait on the `load` event of the bundle to batch Custom Element upgrades
27 * - wait for `DOMContentLoaded`
28 * - run callbacks passed to `waitFor`
29 * - fire WCR event
30 *
31 * - Asynchronous script, no polyfills needed
32 * - fire WCR event, as there could not be any callbacks passed to `waitFor`
33 *
34 * - Asynchronous script, polyfills needed
35 * - Append the polyfill bundle script
36 * - wait for `load` event of the bundle
37 * - batch Custom Element Upgrades
38 * - run callbacks pass to `waitFor`
39 * - fire WCR event
40 */
41
42 var polyfillsLoaded = false;
43 var whenLoadedFns = [];
44 var allowUpgrades = false;
45 var flushFn;
46
47 function fireEvent() {
48 window.WebComponents.ready = true;
49 document.dispatchEvent(new CustomEvent('WebComponentsReady', { bubbles: true }));
50 }
51
52 function batchCustomElements() {
53 if (window.customElements && customElements.polyfillWrapFlushCallback) {
54 customElements.polyfillWrapFlushCallback(function (flushCallback) {
55 flushFn = flushCallback;
56 if (allowUpgrades) {
57 flushFn();
58 }
59 });
60 }
61 }
62
63 function asyncReady() {
64 batchCustomElements();
65 ready();
66 }
67
68 function ready() {
69 // bootstrap <template> elements before custom elements
70 if (window.HTMLTemplateElement && HTMLTemplateElement.bootstrap) {
71 HTMLTemplateElement.bootstrap(window.document);
72 }
73 polyfillsLoaded = true;
74 runWhenLoadedFns().then(fireEvent);
75 }
76
77 function runWhenLoadedFns() {
78 allowUpgrades = false;
79 var done = function() {
80 allowUpgrades = true;
81 whenLoadedFns.length = 0;
82 flushFn && flushFn();
83 };
84 return Promise.all(whenLoadedFns.map(function(fn) {
85 return fn instanceof Function ? fn() : fn;
86 })).then(function() {
87 done();
88 }).catch(function(err) {
89 console.error(err);
90 });
91 }
92
93 window.WebComponents = window.WebComponents || {};
94 window.WebComponents.ready = window.WebComponents.ready || false;
95 window.WebComponents.waitFor = window.WebComponents.waitFor || function(waitFn) {
96 if (!waitFn) {
97 return;
98 }
99 whenLoadedFns.push(waitFn);
100 if (polyfillsLoaded) {
101 runWhenLoadedFns();
102 }
103 };
104 window.WebComponents._batchCustomElements = batchCustomElements;
105
106 var name = 'webcomponents-loader.js';
107 // Feature detect which polyfill needs to be imported.
108 var polyfills = [];
109 if (!('attachShadow' in Element.prototype && 'getRootNode' in Element.prototype) ||
110 (window.ShadyDOM && window.ShadyDOM.force)) {
111 polyfills.push('sd');
112 }
113 if (!window.customElements || window.customElements.forcePolyfill) {
114 polyfills.push('ce');
115 }
116
117 var needsTemplate = (function() {
118 // no real <template> because no `content` property (IE and older browsers)
119 var t = document.createElement('template');
120 if (!('content' in t)) {
121 return true;
122 }
123 // broken doc fragment (older Edge)
124 if (!(t.content.cloneNode() instanceof DocumentFragment)) {
125 return true;
126 }
127 // broken <template> cloning (Edge up to at least version 17)
128 var t2 = document.createElement('template');
129 t2.content.appendChild(document.createElement('div'));
130 t.content.appendChild(t2);
131 var clone = t.cloneNode(true);
132 return (clone.content.childNodes.length === 0 ||
133 clone.content.firstChild.content.childNodes.length === 0);
134 })();
135
136 // NOTE: any browser that does not have template or ES6 features
137 // must load the full suite of polyfills.
138 if (!window.Promise || !Array.from || !window.URL || !window.Symbol || needsTemplate) {
139 polyfills = ['sd-ce-pf'];
140 }
141
142 if (polyfills.length) {
143 var url;
144 var polyfillFile = 'bundles/webcomponents-' + polyfills.join('-') + '.js';
145
146 // Load it from the right place.
147 if (window.WebComponents.root) {
148 url = window.WebComponents.root + polyfillFile;
149 } else {
150 var script = document.querySelector('script[src*="' + name +'"]');
151 // Load it from the right place.
152 url = script.src.replace(name, polyfillFile);
153 }
154
155 var newScript = document.createElement('script');
156 newScript.src = url;
157 // if readyState is 'loading', this script is synchronous
158 if (document.readyState === 'loading') {
159 // make sure custom elements are batched whenever parser gets to the injected script
160 newScript.setAttribute('onload', 'window.WebComponents._batchCustomElements()');
161 document.write(newScript.outerHTML);
162 document.addEventListener('DOMContentLoaded', ready);
163 } else {
164 newScript.addEventListener('load', function () {
165 asyncReady();
166 });
167 newScript.addEventListener('error', function () {
168 throw new Error('Could not load polyfill bundle' + url);
169 });
170 document.head.appendChild(newScript);
171 }
172 } else {
173 polyfillsLoaded = true;
174 if (document.readyState === 'complete') {
175 fireEvent()
176 } else {
177 // this script may come between DCL and load, so listen for both, and cancel load listener if DCL fires
178 window.addEventListener('load', ready);
179 window.addEventListener('DOMContentLoaded', function() {
180 window.removeEventListener('load', ready);
181 ready();
182 })
183 }
184 }
185})();