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  }());