github.com/derat/nup@v0.0.0-20230418113745-15592ba7c620/web/construct-style-sheets-polyfill.js (about) 1 // Downloaded from https://unpkg.com/construct-style-sheets-polyfill 2 // (https://unpkg.com/construct-style-sheets-polyfill@3.1.0/dist/adoptedStyleSheets.js) 3 // on 20220525. 4 // 5 // https://github.com/calebdwilliams/construct-style-sheets/blob/main/LICENSE.md 6 // ("git st" in original): 7 // 8 // Copyright 2019 Caleb Williamsgit st 9 // 10 // Permission is hereby granted, free of charge, to any person obtaining a copy 11 // of this software and associated documentation files (the "Software"), to deal 12 // in the Software without restriction, including without limitation the rights 13 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 // copies of the Software, and to permit persons to whom the Software is 15 // furnished to do so, subject to the following conditions: 16 // 17 // The above copyright notice and this permission notice shall be included in 18 // all copies or substantial portions of the Software. 19 // 20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 // SOFTWARE. 27 28 (function () { 29 'use strict'; 30 31 if (typeof document === 'undefined' || 'adoptedStyleSheets' in document) { return; } 32 33 var hasShadyCss = 'ShadyCSS' in window && !ShadyCSS.nativeShadow; 34 var bootstrapper = document.implementation.createHTMLDocument(''); 35 var closedShadowRootRegistry = new WeakMap(); 36 var _DOMException = typeof DOMException === 'object' ? Error : DOMException; 37 var defineProperty = Object.defineProperty; 38 var forEach = Array.prototype.forEach; 39 40 var importPattern = /@import.+?;?$/gm; 41 function rejectImports(contents) { 42 var _contents = contents.replace(importPattern, ''); 43 if (_contents !== contents) { 44 console.warn('@import rules are not allowed here. See https://github.com/WICG/construct-stylesheets/issues/119#issuecomment-588352418'); 45 } 46 return _contents.trim(); 47 } 48 function isElementConnected(element) { 49 return 'isConnected' in element 50 ? element.isConnected 51 : document.contains(element); 52 } 53 function unique(arr) { 54 return arr.filter(function (value, index) { return arr.indexOf(value) === index; }); 55 } 56 function diff(arr1, arr2) { 57 return arr1.filter(function (value) { return arr2.indexOf(value) === -1; }); 58 } 59 function removeNode(node) { 60 node.parentNode.removeChild(node); 61 } 62 function getShadowRoot(element) { 63 return element.shadowRoot || closedShadowRootRegistry.get(element); 64 } 65 66 var cssStyleSheetMethods = [ 67 'addRule', 68 'deleteRule', 69 'insertRule', 70 'removeRule', 71 ]; 72 var NonConstructedStyleSheet = CSSStyleSheet; 73 var nonConstructedProto = NonConstructedStyleSheet.prototype; 74 nonConstructedProto.replace = function () { 75 return Promise.reject(new _DOMException("Can't call replace on non-constructed CSSStyleSheets.")); 76 }; 77 nonConstructedProto.replaceSync = function () { 78 throw new _DOMException("Failed to execute 'replaceSync' on 'CSSStyleSheet': Can't call replaceSync on non-constructed CSSStyleSheets."); 79 }; 80 function isCSSStyleSheetInstance(instance) { 81 return typeof instance === 'object' 82 ? proto$1.isPrototypeOf(instance) || 83 nonConstructedProto.isPrototypeOf(instance) 84 : false; 85 } 86 function isNonConstructedStyleSheetInstance(instance) { 87 return typeof instance === 'object' 88 ? nonConstructedProto.isPrototypeOf(instance) 89 : false; 90 } 91 var $basicStyleElement = new WeakMap(); 92 var $locations = new WeakMap(); 93 var $adoptersByLocation = new WeakMap(); 94 var $appliedMethods = new WeakMap(); 95 function addAdopterLocation(sheet, location) { 96 var adopter = document.createElement('style'); 97 $adoptersByLocation.get(sheet).set(location, adopter); 98 $locations.get(sheet).push(location); 99 return adopter; 100 } 101 function getAdopterByLocation(sheet, location) { 102 return $adoptersByLocation.get(sheet).get(location); 103 } 104 function removeAdopterLocation(sheet, location) { 105 $adoptersByLocation.get(sheet).delete(location); 106 $locations.set(sheet, $locations.get(sheet).filter(function (_location) { return _location !== location; })); 107 } 108 function restyleAdopter(sheet, adopter) { 109 requestAnimationFrame(function () { 110 adopter.textContent = $basicStyleElement.get(sheet).textContent; 111 $appliedMethods 112 .get(sheet) 113 .forEach(function (command) { 114 return adopter.sheet[command.method].apply(adopter.sheet, command.args); 115 }); 116 }); 117 } 118 function checkInvocationCorrectness(self) { 119 if (!$basicStyleElement.has(self)) { 120 throw new TypeError('Illegal invocation'); 121 } 122 } 123 function ConstructedStyleSheet() { 124 var self = this; 125 var style = document.createElement('style'); 126 bootstrapper.body.appendChild(style); 127 $basicStyleElement.set(self, style); 128 $locations.set(self, []); 129 $adoptersByLocation.set(self, new WeakMap()); 130 $appliedMethods.set(self, []); 131 } 132 var proto$1 = ConstructedStyleSheet.prototype; 133 proto$1.replace = function replace(contents) { 134 try { 135 this.replaceSync(contents); 136 return Promise.resolve(this); 137 } 138 catch (e) { 139 return Promise.reject(e); 140 } 141 }; 142 proto$1.replaceSync = function replaceSync(contents) { 143 checkInvocationCorrectness(this); 144 if (typeof contents === 'string') { 145 var self_1 = this; 146 $basicStyleElement.get(self_1).textContent = rejectImports(contents); 147 $appliedMethods.set(self_1, []); 148 $locations.get(self_1).forEach(function (location) { 149 if (location.isConnected()) { 150 restyleAdopter(self_1, getAdopterByLocation(self_1, location)); 151 } 152 }); 153 } 154 }; 155 defineProperty(proto$1, 'cssRules', { 156 configurable: true, 157 enumerable: true, 158 get: function cssRules() { 159 checkInvocationCorrectness(this); 160 return $basicStyleElement.get(this).sheet.cssRules; 161 }, 162 }); 163 defineProperty(proto$1, 'media', { 164 configurable: true, 165 enumerable: true, 166 get: function media() { 167 checkInvocationCorrectness(this); 168 return $basicStyleElement.get(this).sheet.media; 169 }, 170 }); 171 cssStyleSheetMethods.forEach(function (method) { 172 proto$1[method] = function () { 173 var self = this; 174 checkInvocationCorrectness(self); 175 var args = arguments; 176 $appliedMethods.get(self).push({ method: method, args: args }); 177 $locations.get(self).forEach(function (location) { 178 if (location.isConnected()) { 179 var sheet = getAdopterByLocation(self, location).sheet; 180 sheet[method].apply(sheet, args); 181 } 182 }); 183 var basicSheet = $basicStyleElement.get(self).sheet; 184 return basicSheet[method].apply(basicSheet, args); 185 }; 186 }); 187 defineProperty(ConstructedStyleSheet, Symbol.hasInstance, { 188 configurable: true, 189 value: isCSSStyleSheetInstance, 190 }); 191 192 var defaultObserverOptions = { 193 childList: true, 194 subtree: true, 195 }; 196 var locations = new WeakMap(); 197 function getAssociatedLocation(element) { 198 var location = locations.get(element); 199 if (!location) { 200 location = new Location(element); 201 locations.set(element, location); 202 } 203 return location; 204 } 205 function attachAdoptedStyleSheetProperty(constructor) { 206 defineProperty(constructor.prototype, 'adoptedStyleSheets', { 207 configurable: true, 208 enumerable: true, 209 get: function () { 210 return getAssociatedLocation(this).sheets; 211 }, 212 set: function (sheets) { 213 getAssociatedLocation(this).update(sheets); 214 }, 215 }); 216 } 217 function traverseWebComponents(node, callback) { 218 var iter = document.createNodeIterator(node, NodeFilter.SHOW_ELEMENT, function (foundNode) { 219 return getShadowRoot(foundNode) 220 ? NodeFilter.FILTER_ACCEPT 221 : NodeFilter.FILTER_REJECT; 222 }, 223 null, false); 224 for (var next = void 0; (next = iter.nextNode());) { 225 callback(getShadowRoot(next)); 226 } 227 } 228 var $element = new WeakMap(); 229 var $uniqueSheets = new WeakMap(); 230 var $observer = new WeakMap(); 231 function isExistingAdopter(self, element) { 232 return (element instanceof HTMLStyleElement && 233 $uniqueSheets.get(self).some(function (sheet) { return getAdopterByLocation(sheet, self); })); 234 } 235 function getAdopterContainer(self) { 236 var element = $element.get(self); 237 return element instanceof Document ? element.body : element; 238 } 239 function adopt(self) { 240 var styleList = document.createDocumentFragment(); 241 var sheets = $uniqueSheets.get(self); 242 var observer = $observer.get(self); 243 var container = getAdopterContainer(self); 244 observer.disconnect(); 245 sheets.forEach(function (sheet) { 246 styleList.appendChild(getAdopterByLocation(sheet, self) || addAdopterLocation(sheet, self)); 247 }); 248 container.insertBefore(styleList, null); 249 observer.observe(container, defaultObserverOptions); 250 sheets.forEach(function (sheet) { 251 restyleAdopter(sheet, getAdopterByLocation(sheet, self)); 252 }); 253 } 254 function Location(element) { 255 var self = this; 256 self.sheets = []; 257 $element.set(self, element); 258 $uniqueSheets.set(self, []); 259 $observer.set(self, new MutationObserver(function (mutations, observer) { 260 if (!document) { 261 observer.disconnect(); 262 return; 263 } 264 mutations.forEach(function (mutation) { 265 if (!hasShadyCss) { 266 forEach.call(mutation.addedNodes, function (node) { 267 if (!(node instanceof Element)) { 268 return; 269 } 270 traverseWebComponents(node, function (root) { 271 getAssociatedLocation(root).connect(); 272 }); 273 }); 274 } 275 forEach.call(mutation.removedNodes, function (node) { 276 if (!(node instanceof Element)) { 277 return; 278 } 279 if (isExistingAdopter(self, node)) { 280 adopt(self); 281 } 282 if (!hasShadyCss) { 283 traverseWebComponents(node, function (root) { 284 getAssociatedLocation(root).disconnect(); 285 }); 286 } 287 }); 288 }); 289 })); 290 } 291 Location.prototype = { 292 isConnected: function () { 293 var element = $element.get(this); 294 return element instanceof Document 295 ? element.readyState !== 'loading' 296 : isElementConnected(element.host); 297 }, 298 connect: function () { 299 var container = getAdopterContainer(this); 300 $observer.get(this).observe(container, defaultObserverOptions); 301 if ($uniqueSheets.get(this).length > 0) { 302 adopt(this); 303 } 304 traverseWebComponents(container, function (root) { 305 getAssociatedLocation(root).connect(); 306 }); 307 }, 308 disconnect: function () { 309 $observer.get(this).disconnect(); 310 }, 311 update: function (sheets) { 312 var self = this; 313 var locationType = $element.get(self) === document ? 'Document' : 'ShadowRoot'; 314 if (!Array.isArray(sheets)) { 315 throw new TypeError("Failed to set the 'adoptedStyleSheets' property on " + locationType + ": Iterator getter is not callable."); 316 } 317 if (!sheets.every(isCSSStyleSheetInstance)) { 318 throw new TypeError("Failed to set the 'adoptedStyleSheets' property on " + locationType + ": Failed to convert value to 'CSSStyleSheet'"); 319 } 320 if (sheets.some(isNonConstructedStyleSheetInstance)) { 321 throw new TypeError("Failed to set the 'adoptedStyleSheets' property on " + locationType + ": Can't adopt non-constructed stylesheets"); 322 } 323 self.sheets = sheets; 324 var oldUniqueSheets = $uniqueSheets.get(self); 325 var uniqueSheets = unique(sheets); 326 var removedSheets = diff(oldUniqueSheets, uniqueSheets); 327 removedSheets.forEach(function (sheet) { 328 removeNode(getAdopterByLocation(sheet, self)); 329 removeAdopterLocation(sheet, self); 330 }); 331 $uniqueSheets.set(self, uniqueSheets); 332 if (self.isConnected() && uniqueSheets.length > 0) { 333 adopt(self); 334 } 335 }, 336 }; 337 338 window.CSSStyleSheet = ConstructedStyleSheet; 339 attachAdoptedStyleSheetProperty(Document); 340 if ('ShadowRoot' in window) { 341 attachAdoptedStyleSheetProperty(ShadowRoot); 342 var proto = Element.prototype; 343 var attach_1 = proto.attachShadow; 344 proto.attachShadow = function attachShadow(init) { 345 var root = attach_1.call(this, init); 346 if (init.mode === 'closed') { 347 closedShadowRootRegistry.set(this, root); 348 } 349 return root; 350 }; 351 } 352 var documentLocation = getAssociatedLocation(document); 353 if (documentLocation.isConnected()) { 354 documentLocation.connect(); 355 } 356 else { 357 document.addEventListener('DOMContentLoaded', documentLocation.connect.bind(documentLocation)); 358 } 359 360 }());