File size: 2,385 Bytes
271613e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
import each from 'licia/each'
import sortKeys from 'licia/sortKeys'

function formatStyle(style) {
  const ret = {}

  for (let i = 0, len = style.length; i < len; i++) {
    const name = style[i]

    if (style[name] === 'initial') continue

    ret[name] = style[name]
  }

  return sortStyleKeys(ret)
}

const elProto = Element.prototype

let matchesSel = function () {
  return false
}

if (elProto.webkitMatchesSelector) {
  matchesSel = (el, selText) => el.webkitMatchesSelector(selText)
} else if (elProto.mozMatchesSelector) {
  matchesSel = (el, selText) => el.mozMatchesSelector(selText)
}

export default class CssStore {
  constructor(el) {
    this._el = el
  }
  getComputedStyle() {
    const computedStyle = window.getComputedStyle(this._el)

    return formatStyle(computedStyle)
  }
  getMatchedCSSRules() {
    const ret = []

    each(document.styleSheets, (styleSheet) => {
      try {
        // Started with version 64, Chrome does not allow cross origin script to access this property.
        if (!styleSheet.cssRules) return
      } catch {
        return
      }

      each(styleSheet.cssRules, (cssRule) => {
        let matchesEl = false

        // Mobile safari will throw DOM Exception 12 error, need to try catch it.
        try {
          matchesEl = this._elMatchesSel(cssRule.selectorText)
        } catch {
          // No op
        }

        if (!matchesEl) return

        ret.push({
          selectorText: cssRule.selectorText,
          style: formatStyle(cssRule.style),
        })
      })
    })

    return ret
  }
  _elMatchesSel(selText) {
    return matchesSel(this._el, selText)
  }
}

function sortStyleKeys(style) {
  return sortKeys(style, {
    comparator: (a, b) => {
      const lenA = a.length
      const lenB = b.length
      const len = lenA > lenB ? lenB : lenA

      for (let i = 0; i < len; i++) {
        const codeA = a.charCodeAt(i)
        const codeB = b.charCodeAt(i)
        const cmpResult = cmpCode(codeA, codeB)

        if (cmpResult !== 0) return cmpResult
      }

      if (lenA > lenB) return 1
      if (lenA < lenB) return -1

      return 0
    },
  })
}

function cmpCode(a, b) {
  a = transCode(a)
  b = transCode(b)

  if (a > b) return 1
  if (a < b) return -1
  return 0
}

function transCode(code) {
  // - should be placed after lowercase chars.
  if (code === 45) return 123
  return code
}