Tim van der Lippe | 706ec96 | 2021-06-04 13:24:42 +0100 | [diff] [blame^] | 1 | var hasOwnProperty = Object.prototype.hasOwnProperty; |
| 2 | |
| 3 | function isEqualSelectors(a, b) { |
| 4 | var cursor1 = a.head; |
| 5 | var cursor2 = b.head; |
| 6 | |
| 7 | while (cursor1 !== null && cursor2 !== null && cursor1.data.id === cursor2.data.id) { |
| 8 | cursor1 = cursor1.next; |
| 9 | cursor2 = cursor2.next; |
| 10 | } |
| 11 | |
| 12 | return cursor1 === null && cursor2 === null; |
| 13 | } |
| 14 | |
| 15 | function isEqualDeclarations(a, b) { |
| 16 | var cursor1 = a.head; |
| 17 | var cursor2 = b.head; |
| 18 | |
| 19 | while (cursor1 !== null && cursor2 !== null && cursor1.data.id === cursor2.data.id) { |
| 20 | cursor1 = cursor1.next; |
| 21 | cursor2 = cursor2.next; |
| 22 | } |
| 23 | |
| 24 | return cursor1 === null && cursor2 === null; |
| 25 | } |
| 26 | |
| 27 | function compareDeclarations(declarations1, declarations2) { |
| 28 | var result = { |
| 29 | eq: [], |
| 30 | ne1: [], |
| 31 | ne2: [], |
| 32 | ne2overrided: [] |
| 33 | }; |
| 34 | |
| 35 | var fingerprints = Object.create(null); |
| 36 | var declarations2hash = Object.create(null); |
| 37 | |
| 38 | for (var cursor = declarations2.head; cursor; cursor = cursor.next) { |
| 39 | declarations2hash[cursor.data.id] = true; |
| 40 | } |
| 41 | |
| 42 | for (var cursor = declarations1.head; cursor; cursor = cursor.next) { |
| 43 | var data = cursor.data; |
| 44 | |
| 45 | if (data.fingerprint) { |
| 46 | fingerprints[data.fingerprint] = data.important; |
| 47 | } |
| 48 | |
| 49 | if (declarations2hash[data.id]) { |
| 50 | declarations2hash[data.id] = false; |
| 51 | result.eq.push(data); |
| 52 | } else { |
| 53 | result.ne1.push(data); |
| 54 | } |
| 55 | } |
| 56 | |
| 57 | for (var cursor = declarations2.head; cursor; cursor = cursor.next) { |
| 58 | var data = cursor.data; |
| 59 | |
| 60 | if (declarations2hash[data.id]) { |
| 61 | // when declarations1 has an overriding declaration, this is not a difference |
| 62 | // unless no !important is used on prev and !important is used on the following |
| 63 | if (!hasOwnProperty.call(fingerprints, data.fingerprint) || |
| 64 | (!fingerprints[data.fingerprint] && data.important)) { |
| 65 | result.ne2.push(data); |
| 66 | } |
| 67 | |
| 68 | result.ne2overrided.push(data); |
| 69 | } |
| 70 | } |
| 71 | |
| 72 | return result; |
| 73 | } |
| 74 | |
| 75 | function addSelectors(dest, source) { |
| 76 | source.each(function(sourceData) { |
| 77 | var newStr = sourceData.id; |
| 78 | var cursor = dest.head; |
| 79 | |
| 80 | while (cursor) { |
| 81 | var nextStr = cursor.data.id; |
| 82 | |
| 83 | if (nextStr === newStr) { |
| 84 | return; |
| 85 | } |
| 86 | |
| 87 | if (nextStr > newStr) { |
| 88 | break; |
| 89 | } |
| 90 | |
| 91 | cursor = cursor.next; |
| 92 | } |
| 93 | |
| 94 | dest.insert(dest.createItem(sourceData), cursor); |
| 95 | }); |
| 96 | |
| 97 | return dest; |
| 98 | } |
| 99 | |
| 100 | // check if simpleselectors has no equal specificity and element selector |
| 101 | function hasSimilarSelectors(selectors1, selectors2) { |
| 102 | var cursor1 = selectors1.head; |
| 103 | |
| 104 | while (cursor1 !== null) { |
| 105 | var cursor2 = selectors2.head; |
| 106 | |
| 107 | while (cursor2 !== null) { |
| 108 | if (cursor1.data.compareMarker === cursor2.data.compareMarker) { |
| 109 | return true; |
| 110 | } |
| 111 | |
| 112 | cursor2 = cursor2.next; |
| 113 | } |
| 114 | |
| 115 | cursor1 = cursor1.next; |
| 116 | } |
| 117 | |
| 118 | return false; |
| 119 | } |
| 120 | |
| 121 | // test node can't to be skipped |
| 122 | function unsafeToSkipNode(node) { |
| 123 | switch (node.type) { |
| 124 | case 'Rule': |
| 125 | // unsafe skip ruleset with selector similarities |
| 126 | return hasSimilarSelectors(node.prelude.children, this); |
| 127 | |
| 128 | case 'Atrule': |
| 129 | // can skip at-rules with blocks |
| 130 | if (node.block) { |
| 131 | // unsafe skip at-rule if block contains something unsafe to skip |
| 132 | return node.block.children.some(unsafeToSkipNode, this); |
| 133 | } |
| 134 | break; |
| 135 | |
| 136 | case 'Declaration': |
| 137 | return false; |
| 138 | } |
| 139 | |
| 140 | // unsafe by default |
| 141 | return true; |
| 142 | } |
| 143 | |
| 144 | module.exports = { |
| 145 | isEqualSelectors: isEqualSelectors, |
| 146 | isEqualDeclarations: isEqualDeclarations, |
| 147 | compareDeclarations: compareDeclarations, |
| 148 | addSelectors: addSelectors, |
| 149 | hasSimilarSelectors: hasSimilarSelectors, |
| 150 | unsafeToSkipNode: unsafeToSkipNode |
| 151 | }; |