github.com/iqoqo/nomad@v0.11.3-0.20200911112621-d7021c74d101/website/public/ie-custom-properties.js (about)

     1  /*! ie11CustomProperties.js v2.7.2 | MIT License | https://git.io/fjXMN */
     2  // c1.onElement helper
     3  !(function() {
     4    'use strict'
     5  
     6    if (!Element.prototype.matches)
     7      Element.prototype.matches = Element.prototype.msMatchesSelector
     8  
     9    var w = window
    10    if (!w.c1) w.c1 = {}
    11    var listeners = [],
    12      root = document,
    13      Observer
    14  
    15    c1.onElement = function(selector, options /*, disconnectedCallback*/) {
    16      if (typeof options === 'function') {
    17        options = { parsed: options }
    18      }
    19      var listener = {
    20        selector: selector,
    21        immediate: options.immediate,
    22        //disconnectedCallback: disconnectedCallback,
    23        elements: new WeakMap()
    24      }
    25  
    26      if (options.parsed) {
    27        listener.parsed = function(el) {
    28          requestAnimationFrame(function() {
    29            options.parsed(el)
    30          })
    31        }
    32      }
    33  
    34      var els = root.querySelectorAll(listener.selector),
    35        i = 0,
    36        el
    37      while ((el = els[i++])) {
    38        listener.elements.set(el, true)
    39        listener.parsed && listener.parsed.call(el, el)
    40        listener.immediate && listener.immediate.call(el, el)
    41      }
    42  
    43      listeners.push(listener)
    44      if (!Observer) {
    45        Observer = new MutationObserver(checkMutations)
    46        Observer.observe(root, {
    47          childList: true,
    48          subtree: true
    49        })
    50      }
    51      checkListener(listener)
    52    }
    53    function checkListener(listener, target) {
    54      var i = 0,
    55        el,
    56        els = []
    57      target && target.matches(listener.selector) && els.push(target)
    58      if (loaded) {
    59        // ok? check inside node on innerHTML - only when loaded
    60        Array.prototype.push.apply(
    61          els,
    62          (target || root).querySelectorAll(listener.selector)
    63        )
    64      }
    65      while ((el = els[i++])) {
    66        if (listener.elements.has(el)) continue
    67        listener.elements.set(el, true)
    68        //listener.connectedCallback.call(el, el);
    69        listener.parsed && listener.parsed.call(el, el)
    70        listener.immediate && listener.immediate.call(el, el)
    71      }
    72    }
    73    function checkListeners(inside) {
    74      var i = 0,
    75        listener
    76      while ((listener = listeners[i++])) checkListener(listener, inside)
    77    }
    78    function checkMutations(mutations) {
    79      var j = 0,
    80        i,
    81        mutation,
    82        nodes,
    83        target
    84      while ((mutation = mutations[j++])) {
    85        ;(nodes = mutation.addedNodes), (i = 0)
    86        while ((target = nodes[i++]))
    87          target.nodeType === 1 && checkListeners(target)
    88      }
    89    }
    90  
    91    var loaded = false
    92    document.addEventListener('DOMContentLoaded', function() {
    93      loaded = true
    94    })
    95  
    96    // svg polyfills
    97    function copyProperty(prop, from, to) {
    98      var desc = Object.getOwnPropertyDescriptor(from, prop)
    99      Object.defineProperty(to, prop, desc)
   100    }
   101    if (!('classList' in Element.prototype)) {
   102      copyProperty('classList', HTMLElement.prototype, Element.prototype)
   103    }
   104    if (!('innerHTML' in Element.prototype)) {
   105      copyProperty('innerHTML', HTMLElement.prototype, Element.prototype)
   106    }
   107    if (!('sheet' in SVGStyleElement.prototype)) {
   108      Object.defineProperty(SVGStyleElement.prototype, 'sheet', {
   109        get: function() {
   110          var all = document.styleSheets
   111          for (var i = 0, sheet; (sheet = all[i++]); ) {
   112            if (sheet.ownerNode === this) return sheet
   113          }
   114        }
   115      })
   116    }
   117    // if ('children' in HTMLElement.prototype && !('children' in Element.prototype)) {
   118    // 	copyProperty('children', HTMLElement.prototype, Element.prototype);
   119    // }
   120    // if ('contains' in HTMLElement.prototype && !('contains' in Element.prototype)) {
   121    // 	copyProperty('contains', HTMLElement.prototype, Element.prototype);
   122    // }
   123    // if ('getElementsByClassName' in HTMLElement.prototype && !('getElementsByClassName' in Element.prototype)) {
   124    // 	copyProperty('getElementsByClassName', HTMLElement.prototype, Element.prototype);
   125    // }
   126  })()
   127  
   128  // main logic
   129  !(function() {
   130    'use strict'
   131    var testEl = document.createElement('i')
   132    testEl.style.setProperty('--x', 'y')
   133    if (testEl.style.getPropertyValue('--x') === 'y' || !testEl.msMatchesSelector)
   134      return
   135  
   136    // cached regexps, better performance
   137    const regFindSetters = /([\s{;])(--([A-Za-z0-9-_]+\s*:[^;!}{]+)(!important)?)(?=\s*([;}]|$))/g
   138    const regFindGetters = /([{;]\s*)([A-Za-z0-9-_]+\s*:[^;}{]*var\([^!;}{]+)(!important)?(?=\s*([;}$]|$))/g
   139    const regRuleIEGetters = /-ieVar-([^:]+):/g
   140    const regRuleIESetters = /-ie-([^};]+)/g
   141    const regHasVar = /var\(/
   142    const regPseudos = /:(hover|active|focus|target|:before|:after)/
   143  
   144    c1.onElement('link[rel="stylesheet"]', {
   145      immediate: function(el) {
   146        fetchCss(el.href, function(css) {
   147          var newCss = rewriteCss(css)
   148          if (css === newCss) return
   149          newCss = relToAbs(el.href, newCss)
   150          el.disabled = true
   151          var style = document.createElement('style')
   152          el.parentNode.insertBefore(style, el)
   153          activateStyleElement(style, newCss)
   154        })
   155      }
   156    })
   157    c1.onElement('style', {
   158      immediate: function(el) {
   159        if (el.hasAttribute('ie-polyfilled')) return
   160        if (el.ieCP_elementSheet) return
   161        var css = el.innerHTML
   162        var newCss = rewriteCss(css)
   163        if (css === newCss) return
   164        activateStyleElement(el, newCss)
   165      }
   166    })
   167    c1.onElement('[ie-style]', {
   168      immediate: function(el) {
   169        var newCss = rewriteCss('{' + el.getAttribute('ie-style')).substr(1)
   170        el.style.cssText += ';' + newCss
   171        var found = parseRewrittenStyle(el.style)
   172        if (found.getters) addGetterElement(el, found.getters, '%styleAttr')
   173        if (found.setters) addSetterElement(el, found.setters)
   174      }
   175    })
   176  
   177    function relToAbs(base, css) {
   178      return css.replace(/url\(([^)]+)\)/g, function($0, $1) {
   179        $1 = $1.trim().replace(/(^['"]|['"]$)/g, '')
   180        if ($1.match(/^([a-z]+:|\/)/)) return $0
   181        base = base.replace(/\?.*/, '')
   182        return 'url(' + base + './../' + $1 + ')'
   183      })
   184    }
   185  
   186    // ie has a bug, where unknown properties at pseudo-selectors are computed at the element
   187    // #el::after { -content:'x'; } => getComputedStyle(el)['-content'] == 'x'
   188    // should we add something like -ieVar-pseudo_after-content:'x'?
   189    function rewriteCss(css) {
   190      /* uncomment if spec finished and needed by someone
   191    css = css.replace(/@property ([^{]+){([^}]+)}/, function($0, prop, body){
   192      prop = prop.trim();
   193      const declaration = {name:prop};
   194      body.split(';').forEach(function(pair){
   195        const x = pair.split(':');
   196        if (x[1]) declaration[ x[0].trim() ] = x[1];
   197      });
   198      declaration['inherits'] = declaration['inherits'].trim()==='true' ? true : false;
   199      declaration['initialValue'] = declaration['initial-value'];
   200      CSS.registerProperty(declaration)
   201      return '/*\n @property ... removed \n*'+'/';
   202    });
   203    */
   204  
   205      return css
   206        .replace(regFindSetters, function($0, $1, $2, $3, important) {
   207          return $1 + '-ie-' + (important ? '❗' : '') + $3
   208        })
   209        .replace(regFindGetters, function($0, $1, $2, important) {
   210          return $1 + '-ieVar-' + (important ? '❗' : '') + $2 + '; ' + $2 // keep the original, so chaining works "--x:var(--y)"
   211        })
   212    }
   213  
   214    // beta
   215    const styles_of_getter_properties = {}
   216  
   217    function parseRewrittenStyle(style) {
   218      // less memory then parameter cssText?
   219  
   220      // beta
   221      style['z-index'] // ie11 can access unknown properties in stylesheets only if accessed a dashed known property
   222  
   223      const cssText = style.cssText
   224      var matchesGetters = cssText.match(regRuleIEGetters),
   225        j,
   226        match
   227      if (matchesGetters) {
   228        var getters = [] // eg. [border,color]
   229        for (j = 0; (match = matchesGetters[j++]); ) {
   230          let propName = match.slice(7, -1)
   231          if (propName[0] === '❗') propName = propName.substr(1)
   232          getters.push(propName)
   233  
   234          // beta
   235          if (!styles_of_getter_properties[propName])
   236            styles_of_getter_properties[propName] = []
   237          styles_of_getter_properties[propName].push(style)
   238        }
   239      }
   240      var matchesSetters = cssText.match(regRuleIESetters)
   241      if (matchesSetters) {
   242        var setters = {} // eg. [--color:#fff, --padding:10px];
   243        for (j = 0; (match = matchesSetters[j++]); ) {
   244          let x = match.substr(4).split(':')
   245          let propName = x[0]
   246          let propValue = x[1]
   247          if (propName[0] === '❗') propName = propName.substr(1)
   248          setters[propName] = propValue
   249        }
   250      }
   251      return { getters: getters, setters: setters }
   252    }
   253    function activateStyleElement(style, css) {
   254      style.innerHTML = css
   255      style.setAttribute('ie-polyfilled', true)
   256      var rules = style.sheet.rules,
   257        i = 0,
   258        rule // cssRules = CSSRuleList, rules = MSCSSRuleList
   259      while ((rule = rules[i++])) {
   260        const found = parseRewrittenStyle(rule.style)
   261        if (found.getters) addGettersSelector(rule.selectorText, found.getters)
   262        if (found.setters) addSettersSelector(rule.selectorText, found.setters)
   263  
   264        // mediaQueries: redraw the hole document
   265        // better add events for each element?
   266        const media =
   267          rule.parentRule &&
   268          rule.parentRule.media &&
   269          rule.parentRule.media.mediaText
   270        if (media && (found.getters || found.setters)) {
   271          matchMedia(media).addListener(function() {
   272            drawTree(document.documentElement)
   273          })
   274        }
   275      }
   276  
   277      // beta
   278      redrawStyleSheets()
   279    }
   280  
   281    function addGettersSelector(selector, properties) {
   282      selectorAddPseudoListeners(selector)
   283      c1.onElement(unPseudo(selector), {
   284        immediate: function(el) {
   285          addGetterElement(el, properties, selector)
   286          drawElement(el)
   287        }
   288      })
   289    }
   290    function addGetterElement(el, properties, selector) {
   291      var i = 0,
   292        prop,
   293        j
   294      const selectors = selector.split(',') // split grouped selectors
   295      el.setAttribute('iecp-needed', true)
   296      if (!el.ieCPSelectors) el.ieCPSelectors = {}
   297      while ((prop = properties[i++])) {
   298        for (j = 0; (selector = selectors[j++]); ) {
   299          const parts = selector.trim().split('::')
   300          if (!el.ieCPSelectors[prop]) el.ieCPSelectors[prop] = []
   301          el.ieCPSelectors[prop].push({
   302            selector: parts[0],
   303            pseudo: parts[1] ? '::' + parts[1] : ''
   304          })
   305        }
   306      }
   307    }
   308    function addSettersSelector(selector, propVals) {
   309      selectorAddPseudoListeners(selector)
   310      c1.onElement(unPseudo(selector), {
   311        immediate: function(el) {
   312          addSetterElement(el, propVals)
   313        }
   314      })
   315    }
   316    function addSetterElement(el, propVals) {
   317      if (!el.ieCP_setters) el.ieCP_setters = {}
   318      for (var prop in propVals) {
   319        // eg. {foo:#fff, bar:baz}
   320        el.ieCP_setters['--' + prop] = 1
   321      }
   322      drawTree(el)
   323    }
   324  
   325    //beta
   326    function redrawStyleSheets() {
   327      for (var prop in styles_of_getter_properties) {
   328        let styles = styles_of_getter_properties[prop]
   329        for (var i = 0, style; (style = styles[i++]); ) {
   330          if (style.owningElement) continue
   331          var value = style['-ieVar-' + prop]
   332          if (!value) continue
   333          var value = styleComputeValueWidthVars(
   334            getComputedStyle(document.documentElement),
   335            value
   336          )
   337          if (value === '') continue
   338          style[prop] = value
   339        }
   340      }
   341    }
   342  
   343    const pseudos = {
   344      hover: {
   345        on: 'mouseenter',
   346        off: 'mouseleave'
   347      },
   348      focus: {
   349        on: 'focusin',
   350        off: 'focusout'
   351      },
   352      active: {
   353        on: 'CSSActivate',
   354        off: 'CSSDeactivate'
   355      }
   356    }
   357    function selectorAddPseudoListeners(selector) {
   358      // ie11 has the strange behavoir, that groups of selectors are individual rules, but starting with the full selector:
   359      // td, th, button { color:red } results in this rules:
   360      // "td, th, button" | "th, th" | "th"
   361      selector = selector.split(',')[0]
   362      for (var pseudo in pseudos) {
   363        var parts = selector.split(':' + pseudo)
   364        if (parts.length > 1) {
   365          var ending = parts[1].match(/^[^\s]*/) // ending elementpart of selector (used for not(:active))
   366          let sel = unPseudo(parts[0] + ending)
   367          const listeners = pseudos[pseudo]
   368          c1.onElement(sel, function(el) {
   369            el.addEventListener(listeners.on, drawTreeEvent)
   370            el.addEventListener(listeners.off, drawTreeEvent)
   371          })
   372        }
   373      }
   374    }
   375    let CSSActive = null
   376    document.addEventListener('mousedown', function(e) {
   377      setTimeout(function() {
   378        if (e.target === document.activeElement) {
   379          var evt = document.createEvent('Event')
   380          evt.initEvent('CSSActivate', true, true)
   381          CSSActive = e.target
   382          CSSActive.dispatchEvent(evt)
   383        }
   384      })
   385    })
   386    document.addEventListener('mouseup', function() {
   387      if (CSSActive) {
   388        var evt = document.createEvent('Event')
   389        evt.initEvent('CSSDeactivate', true, true)
   390        CSSActive.dispatchEvent(evt)
   391        CSSActive = null
   392      }
   393    })
   394  
   395    function unPseudo(selector) {
   396      return selector.replace(regPseudos, '').replace(':not()', '')
   397    }
   398  
   399    var uniqueCounter = 0
   400  
   401    function _drawElement(el) {
   402      if (!el.ieCP_unique) {
   403        // use el.uniqueNumber? but needs class for the css-selector => test performance
   404        el.ieCP_unique = ++uniqueCounter
   405        el.classList.add('iecp-u' + el.ieCP_unique)
   406      }
   407      var style = getComputedStyle(el)
   408      if (el.ieCP_sheet)
   409        while (el.ieCP_sheet.rules[0]) el.ieCP_sheet.deleteRule(0)
   410      for (var prop in el.ieCPSelectors) {
   411        var important = style['-ieVar-❗' + prop]
   412        let valueWithVar = important || style['-ieVar-' + prop]
   413        if (!valueWithVar) continue // todo, what if '0'
   414  
   415        var details = {}
   416        var value = styleComputeValueWidthVars(style, valueWithVar, details)
   417  
   418        if (important) value += ' !important'
   419        for (var i = 0, item; (item = el.ieCPSelectors[prop][i++]); ) {
   420          // todo: split and use requestAnimationFrame?
   421          if (item.selector === '%styleAttr') {
   422            el.style[prop] = value
   423          } else {
   424            // beta
   425            if (!important && details.allByRoot !== false) continue // dont have to draw root-properties
   426  
   427            //let selector = item.selector.replace(/>? \.[^ ]+/, ' ', item.selector); // todo: try to equalize specificity
   428            let selector = item.selector
   429            elementStyleSheet(el).insertRule(
   430              selector +
   431                '.iecp-u' +
   432                el.ieCP_unique +
   433                item.pseudo +
   434                ' {' +
   435                prop +
   436                ':' +
   437                value +
   438                '}',
   439              0
   440            ) // faster then innerHTML
   441          }
   442        }
   443      }
   444    }
   445    function elementStyleSheet(el) {
   446      if (!el.ieCP_sheet) {
   447        var tag = document.createElement('style')
   448        tag.ieCP_elementSheet = 1
   449        //el.appendChild(tag); // yes! self-closing tags can have style as children, but - if i set innerHTML, the stylesheet is lost
   450        document.head.appendChild(tag)
   451        el.ieCP_sheet = tag.sheet
   452      }
   453      return el.ieCP_sheet
   454    }
   455    function drawTree(target) {
   456      if (!target) return
   457      var els = target.querySelectorAll('[iecp-needed]')
   458      if (target.hasAttribute && target.hasAttribute('iecp-needed'))
   459        drawElement(target) // self
   460      for (var i = 0, el; (el = els[i++]); ) drawElement(el) // tree
   461    }
   462    // draw queue
   463    let drawQueue = new Set()
   464    let collecting = false
   465    let drawing = false
   466    function drawElement(el) {
   467      drawQueue.add(el)
   468      if (collecting) return
   469      collecting = true
   470      requestAnimationFrame(function() {
   471        collecting = false
   472        drawing = true
   473        drawQueue.forEach(_drawElement)
   474        requestAnimationFrame(function() {
   475          // mutationObserver will trigger delayed
   476          drawing = false
   477        })
   478        drawQueue.clear()
   479      })
   480    }
   481  
   482    function drawTreeEvent(e) {
   483      drawTree(e.target)
   484    }
   485  
   486    const regValueGetters = /var\(([^),]+)(\,(.+))?\)/g
   487    function styleComputeValueWidthVars(style, valueWithVar, details) {
   488      return valueWithVar.replace(regValueGetters, function(
   489        full,
   490        variable,
   491        x,
   492        fallback
   493      ) {
   494        variable = variable.trim()
   495        var pValue = style.getPropertyValue(variable)
   496        if (details && style.lastPropertyServedBy !== document.documentElement)
   497          details.allByRoot = false
   498        if (pValue === '' && fallback !== undefined) pValue = fallback.trim() // fallback
   499        return pValue
   500      })
   501    }
   502  
   503    // mutation listener
   504    var observer = new MutationObserver(function(mutations) {
   505      if (drawing) return
   506      for (var i = 0, mutation; (mutation = mutations[i++]); ) {
   507        if (mutation.attributeName === 'ie-polyfilled') continue
   508        if (mutation.attributeName === 'iecp-needed') continue
   509        // recheck all selectors if it targets new elements?
   510        drawTree(mutation.target)
   511      }
   512    })
   513    setTimeout(function() {
   514      observer.observe(document, { attributes: true, subtree: true })
   515    })
   516  
   517    // :target listener
   518    var oldHash = location.hash
   519    addEventListener('hashchange', function(e) {
   520      var newEl = document.getElementById(location.hash.substr(1))
   521      if (newEl) {
   522        var oldEl = document.getElementById(oldHash.substr(1))
   523        drawTree(newEl)
   524        drawTree(oldEl)
   525      } else {
   526        drawTree(document)
   527      }
   528      oldHash = location.hash
   529    })
   530  
   531    // add owningElement to Element.style
   532    var descriptor = Object.getOwnPropertyDescriptor(
   533      HTMLElement.prototype,
   534      'style'
   535    )
   536    var styleGetter = descriptor.get
   537    descriptor.get = function() {
   538      const style = styleGetter.call(this)
   539      style.owningElement = this
   540      return style
   541    }
   542    Object.defineProperty(HTMLElement.prototype, 'style', descriptor)
   543  
   544    // add computedFor to computed style-objects
   545    var originalGetComputed = getComputedStyle
   546    window.getComputedStyle = function(el) {
   547      var style = originalGetComputed.apply(this, arguments)
   548      style.computedFor = el
   549      //style.pseudoElt = pseudoElt; //not needed at the moment
   550      return style
   551    }
   552  
   553    // getPropertyValue / setProperty hooks
   554    const StyleProto = CSSStyleDeclaration.prototype
   555  
   556    const oldGetP = StyleProto.getPropertyValue
   557    StyleProto.getPropertyValue = function(property) {
   558      this.lastPropertyServedBy = false
   559      if (property[0] !== '-' || property[1] !== '-')
   560        return oldGetP.apply(this, arguments)
   561      const undashed = property.substr(2)
   562      const ieProperty = '-ie-' + undashed
   563      const iePropertyImportant = '-ie-❗' + undashed
   564      let value = this[iePropertyImportant] || this[ieProperty]
   565      if (this.computedFor) {
   566        // computedStyle
   567        if (value !== undefined) {
   568          if (regHasVar.test(value)) {
   569            value = styleComputeValueWidthVars(this, value)
   570          }
   571          this.lastPropertyServedBy = this.computedFor
   572        } else {
   573          if (!register[property] || register[property].inherits) {
   574            // inherited
   575            //let el = this.pseudoElt ? this.computedFor : this.computedFor.parentNode;
   576            let el = this.computedFor.parentNode
   577            while (el.nodeType === 1) {
   578              // how slower would it be to getComputedStyle for every element, not just with defined ieCP_setters
   579              if (el.ieCP_setters && el.ieCP_setters[property]) {
   580                // i could make
   581                // value = el.nodeType ? getComputedStyle(this.computedFor.parentNode).getPropertyValue(property)
   582                // but i fear performance, stupid?
   583                var style = getComputedStyle(el)
   584                var tmpVal = style[iePropertyImportant] || style[ieProperty]
   585                if (tmpVal !== undefined) {
   586                  value = tmpVal
   587                  if (regHasVar.test(value)) {
   588                    // calculated style from current element not from the element the value was inherited from! (style, value)
   589                    value = styleComputeValueWidthVars(this, value)
   590                  }
   591                  this.lastPropertyServedBy = el
   592                  break
   593                }
   594              }
   595              el = el.parentNode
   596            }
   597          }
   598        }
   599      }
   600      if (value === undefined && register[property])
   601        value = register[property].initialValue
   602      if (value === undefined) value = ''
   603      return value
   604    }
   605  
   606    const oldSetP = StyleProto.setProperty
   607    StyleProto.setProperty = function(property, value, prio) {
   608      if (property[0] !== '-' || property[1] !== '-')
   609        return oldSetP.apply(this, arguments)
   610      if (this.owningElement) {
   611        const el = this.owningElement
   612        if (!el.ieCP_setters) el.ieCP_setters = {}
   613        el.ieCP_setters[property] = 1
   614        drawTree(el)
   615      }
   616      property = '-ie-' + (prio === 'important' ? '❗' : '') + property.substr(2)
   617      this.cssText += '; ' + property + ':' + value + ';'
   618      //this[property] = value;
   619    }
   620  
   621    if (!window.CSS) window.CSS = {}
   622    const register = {}
   623    CSS.registerProperty = function(options) {
   624      register[options.name] = options
   625    }
   626  
   627    // utils
   628    function fetchCss(url, callback) {
   629      var request = new XMLHttpRequest()
   630      request.open('GET', url)
   631      request.overrideMimeType('text/css')
   632      request.onload = function() {
   633        if (request.status >= 200 && request.status < 400) {
   634          callback(request.responseText)
   635        }
   636      }
   637      request.send()
   638    }
   639  })()