1var Hack = require('./hack'); 2 3var Marker = require('../tokenizer/marker'); 4var Token = require('../tokenizer/token'); 5 6var Match = { 7 ASTERISK: '*', 8 BACKSLASH: '\\', 9 BANG: '!', 10 BANG_SUFFIX_PATTERN: /!\w+$/, 11 IMPORTANT_TOKEN: '!important', 12 IMPORTANT_TOKEN_PATTERN: new RegExp('!important$', 'i'), 13 IMPORTANT_WORD: 'important', 14 IMPORTANT_WORD_PATTERN: new RegExp('important$', 'i'), 15 SUFFIX_BANG_PATTERN: /!$/, 16 UNDERSCORE: '_', 17 VARIABLE_REFERENCE_PATTERN: /var\(--.+\)$/ 18}; 19 20function wrapAll(properties, includeVariable, skipProperties) { 21 var wrapped = []; 22 var single; 23 var property; 24 var i; 25 26 for (i = properties.length - 1; i >= 0; i--) { 27 property = properties[i]; 28 29 if (property[0] != Token.PROPERTY) { 30 continue; 31 } 32 33 if (!includeVariable && someVariableReferences(property)) { 34 continue; 35 } 36 37 if (skipProperties && skipProperties.indexOf(property[1][1]) > -1) { 38 continue; 39 } 40 41 single = wrapSingle(property); 42 single.all = properties; 43 single.position = i; 44 wrapped.unshift(single); 45 } 46 47 return wrapped; 48} 49 50function someVariableReferences(property) { 51 var i, l; 52 var value; 53 54 // skipping `property` and property name tokens 55 for (i = 2, l = property.length; i < l; i++) { 56 value = property[i]; 57 58 if (value[0] != Token.PROPERTY_VALUE) { 59 continue; 60 } 61 62 if (isVariableReference(value[1])) { 63 return true; 64 } 65 } 66 67 return false; 68} 69 70function isVariableReference(value) { 71 return Match.VARIABLE_REFERENCE_PATTERN.test(value); 72} 73 74function isMultiplex(property) { 75 var value; 76 var i, l; 77 78 for (i = 3, l = property.length; i < l; i++) { 79 value = property[i]; 80 81 if (value[0] == Token.PROPERTY_VALUE && (value[1] == Marker.COMMA || value[1] == Marker.FORWARD_SLASH)) { 82 return true; 83 } 84 } 85 86 return false; 87} 88 89function hackFrom(property) { 90 var match = false; 91 var name = property[1][1]; 92 var lastValue = property[property.length - 1]; 93 94 if (name[0] == Match.UNDERSCORE) { 95 match = [Hack.UNDERSCORE]; 96 } else if (name[0] == Match.ASTERISK) { 97 match = [Hack.ASTERISK]; 98 } else if (lastValue[1][0] == Match.BANG && !lastValue[1].match(Match.IMPORTANT_WORD_PATTERN)) { 99 match = [Hack.BANG]; 100 } else if (lastValue[1].indexOf(Match.BANG) > 0 && !lastValue[1].match(Match.IMPORTANT_WORD_PATTERN) && Match.BANG_SUFFIX_PATTERN.test(lastValue[1])) { 101 match = [Hack.BANG]; 102 } else if (lastValue[1].indexOf(Match.BACKSLASH) > 0 && lastValue[1].indexOf(Match.BACKSLASH) == lastValue[1].length - Match.BACKSLASH.length - 1) { 103 match = [Hack.BACKSLASH, lastValue[1].substring(lastValue[1].indexOf(Match.BACKSLASH) + 1)]; 104 } else if (lastValue[1].indexOf(Match.BACKSLASH) === 0 && lastValue[1].length == 2) { 105 match = [Hack.BACKSLASH, lastValue[1].substring(1)]; 106 } 107 108 return match; 109} 110 111function isImportant(property) { 112 if (property.length < 3) 113 return false; 114 115 var lastValue = property[property.length - 1]; 116 if (Match.IMPORTANT_TOKEN_PATTERN.test(lastValue[1])) { 117 return true; 118 } else if (Match.IMPORTANT_WORD_PATTERN.test(lastValue[1]) && Match.SUFFIX_BANG_PATTERN.test(property[property.length - 2][1])) { 119 return true; 120 } 121 122 return false; 123} 124 125function stripImportant(property) { 126 var lastValue = property[property.length - 1]; 127 var oneButLastValue = property[property.length - 2]; 128 129 if (Match.IMPORTANT_TOKEN_PATTERN.test(lastValue[1])) { 130 lastValue[1] = lastValue[1].replace(Match.IMPORTANT_TOKEN_PATTERN, ''); 131 } else { 132 lastValue[1] = lastValue[1].replace(Match.IMPORTANT_WORD_PATTERN, ''); 133 oneButLastValue[1] = oneButLastValue[1].replace(Match.SUFFIX_BANG_PATTERN, ''); 134 } 135 136 if (lastValue[1].length === 0) { 137 property.pop(); 138 } 139 140 if (oneButLastValue[1].length === 0) { 141 property.pop(); 142 } 143} 144 145function stripPrefixHack(property) { 146 property[1][1] = property[1][1].substring(1); 147} 148 149function stripSuffixHack(property, hackFrom) { 150 var lastValue = property[property.length - 1]; 151 lastValue[1] = lastValue[1] 152 .substring(0, lastValue[1].indexOf(hackFrom[0] == Hack.BACKSLASH ? Match.BACKSLASH : Match.BANG)) 153 .trim(); 154 155 if (lastValue[1].length === 0) { 156 property.pop(); 157 } 158} 159 160function wrapSingle(property) { 161 var importantProperty = isImportant(property); 162 if (importantProperty) { 163 stripImportant(property); 164 } 165 166 var whichHack = hackFrom(property); 167 if (whichHack[0] == Hack.ASTERISK || whichHack[0] == Hack.UNDERSCORE) { 168 stripPrefixHack(property); 169 } else if (whichHack[0] == Hack.BACKSLASH || whichHack[0] == Hack.BANG) { 170 stripSuffixHack(property, whichHack); 171 } 172 173 return { 174 block: property[2] && property[2][0] == Token.PROPERTY_BLOCK, 175 components: [], 176 dirty: false, 177 hack: whichHack, 178 important: importantProperty, 179 name: property[1][1], 180 multiplex: property.length > 3 ? isMultiplex(property) : false, 181 position: 0, 182 shorthand: false, 183 unused: false, 184 value: property.slice(2) 185 }; 186} 187 188module.exports = { 189 all: wrapAll, 190 single: wrapSingle 191}; 192