1var emptyCharacter = ''; 2 3var Breaks = require('../options/format').Breaks; 4var Spaces = require('../options/format').Spaces; 5 6var Marker = require('../tokenizer/marker'); 7var Token = require('../tokenizer/token'); 8 9function supportsAfterClosingBrace(token) { 10 return token[1][1] == 'background' || token[1][1] == 'transform' || token[1][1] == 'src'; 11} 12 13function afterClosingBrace(token, valueIndex) { 14 return token[valueIndex][1][token[valueIndex][1].length - 1] == Marker.CLOSE_ROUND_BRACKET; 15} 16 17function afterComma(token, valueIndex) { 18 return token[valueIndex][1] == Marker.COMMA; 19} 20 21function afterSlash(token, valueIndex) { 22 return token[valueIndex][1] == Marker.FORWARD_SLASH; 23} 24 25function beforeComma(token, valueIndex) { 26 return token[valueIndex + 1] && token[valueIndex + 1][1] == Marker.COMMA; 27} 28 29function beforeSlash(token, valueIndex) { 30 return token[valueIndex + 1] && token[valueIndex + 1][1] == Marker.FORWARD_SLASH; 31} 32 33function inFilter(token) { 34 return token[1][1] == 'filter' || token[1][1] == '-ms-filter'; 35} 36 37function disallowsSpace(context, token, valueIndex) { 38 return !context.spaceAfterClosingBrace && supportsAfterClosingBrace(token) && afterClosingBrace(token, valueIndex) || 39 beforeSlash(token, valueIndex) || 40 afterSlash(token, valueIndex) || 41 beforeComma(token, valueIndex) || 42 afterComma(token, valueIndex); 43} 44 45function rules(context, tokens) { 46 var store = context.store; 47 48 for (var i = 0, l = tokens.length; i < l; i++) { 49 store(context, tokens[i]); 50 51 if (i < l - 1) { 52 store(context, comma(context)); 53 } 54 } 55} 56 57function body(context, tokens) { 58 var lastPropertyAt = lastPropertyIndex(tokens); 59 60 for (var i = 0, l = tokens.length; i < l; i++) { 61 property(context, tokens, i, lastPropertyAt); 62 } 63} 64 65function lastPropertyIndex(tokens) { 66 var index = tokens.length - 1; 67 68 for (; index >= 0; index--) { 69 if (tokens[index][0] != Token.COMMENT) { 70 break; 71 } 72 } 73 74 return index; 75} 76 77function property(context, tokens, position, lastPropertyAt) { 78 var store = context.store; 79 var token = tokens[position]; 80 81 var propertyValue = token[2]; 82 var isPropertyBlock = propertyValue && propertyValue[0] === Token.PROPERTY_BLOCK; 83 84 var needsSemicolon; 85 if ( context.format ) { 86 if ( context.format.semicolonAfterLastProperty || isPropertyBlock ) { 87 needsSemicolon = true; 88 } else if ( position < lastPropertyAt ) { 89 needsSemicolon = true; 90 } else { 91 needsSemicolon = false; 92 } 93 } else { 94 needsSemicolon = position < lastPropertyAt || isPropertyBlock; 95 } 96 97 var isLast = position === lastPropertyAt; 98 99 switch (token[0]) { 100 case Token.AT_RULE: 101 store(context, token); 102 store(context, semicolon(context, Breaks.AfterProperty, false)); 103 break; 104 case Token.AT_RULE_BLOCK: 105 rules(context, token[1]); 106 store(context, openBrace(context, Breaks.AfterRuleBegins, true)); 107 body(context, token[2]); 108 store(context, closeBrace(context, Breaks.AfterRuleEnds, false, isLast)); 109 break; 110 case Token.COMMENT: 111 store(context, token); 112 break; 113 case Token.PROPERTY: 114 store(context, token[1]); 115 store(context, colon(context)); 116 if (propertyValue) { 117 value(context, token); 118 } 119 store(context, needsSemicolon ? semicolon(context, Breaks.AfterProperty, isLast) : emptyCharacter); 120 break; 121 case Token.RAW: 122 store(context, token); 123 } 124} 125 126function value(context, token) { 127 var store = context.store; 128 var j, m; 129 130 if (token[2][0] == Token.PROPERTY_BLOCK) { 131 store(context, openBrace(context, Breaks.AfterBlockBegins, false)); 132 body(context, token[2][1]); 133 store(context, closeBrace(context, Breaks.AfterBlockEnds, false, true)); 134 } else { 135 for (j = 2, m = token.length; j < m; j++) { 136 store(context, token[j]); 137 138 if (j < m - 1 && (inFilter(token) || !disallowsSpace(context, token, j))) { 139 store(context, Marker.SPACE); 140 } 141 } 142 } 143} 144 145function allowsBreak(context, where) { 146 return context.format && context.format.breaks[where]; 147} 148 149function allowsSpace(context, where) { 150 return context.format && context.format.spaces[where]; 151} 152 153function openBrace(context, where, needsPrefixSpace) { 154 if (context.format) { 155 context.indentBy += context.format.indentBy; 156 context.indentWith = context.format.indentWith.repeat(context.indentBy); 157 return (needsPrefixSpace && allowsSpace(context, Spaces.BeforeBlockBegins) ? Marker.SPACE : emptyCharacter) + 158 Marker.OPEN_CURLY_BRACKET + 159 (allowsBreak(context, where) ? context.format.breakWith : emptyCharacter) + 160 context.indentWith; 161 } else { 162 return Marker.OPEN_CURLY_BRACKET; 163 } 164} 165 166function closeBrace(context, where, beforeBlockEnd, isLast) { 167 if (context.format) { 168 context.indentBy -= context.format.indentBy; 169 context.indentWith = context.format.indentWith.repeat(context.indentBy); 170 return (allowsBreak(context, Breaks.AfterProperty) || beforeBlockEnd && allowsBreak(context, Breaks.BeforeBlockEnds) ? context.format.breakWith : emptyCharacter) + 171 context.indentWith + 172 Marker.CLOSE_CURLY_BRACKET + 173 (isLast ? emptyCharacter : (allowsBreak(context, where) ? context.format.breakWith : emptyCharacter) + context.indentWith); 174 } else { 175 return Marker.CLOSE_CURLY_BRACKET; 176 } 177} 178 179function colon(context) { 180 return context.format ? 181 Marker.COLON + (allowsSpace(context, Spaces.BeforeValue) ? Marker.SPACE : emptyCharacter) : 182 Marker.COLON; 183} 184 185function semicolon(context, where, isLast) { 186 return context.format ? 187 Marker.SEMICOLON + (isLast || !allowsBreak(context, where) ? emptyCharacter : context.format.breakWith + context.indentWith) : 188 Marker.SEMICOLON; 189} 190 191function comma(context) { 192 return context.format ? 193 Marker.COMMA + (allowsBreak(context, Breaks.BetweenSelectors) ? context.format.breakWith : emptyCharacter) + context.indentWith : 194 Marker.COMMA; 195} 196 197function all(context, tokens) { 198 var store = context.store; 199 var token; 200 var isLast; 201 var i, l; 202 203 for (i = 0, l = tokens.length; i < l; i++) { 204 token = tokens[i]; 205 isLast = i == l - 1; 206 207 switch (token[0]) { 208 case Token.AT_RULE: 209 store(context, token); 210 store(context, semicolon(context, Breaks.AfterAtRule, isLast)); 211 break; 212 case Token.AT_RULE_BLOCK: 213 rules(context, token[1]); 214 store(context, openBrace(context, Breaks.AfterRuleBegins, true)); 215 body(context, token[2]); 216 store(context, closeBrace(context, Breaks.AfterRuleEnds, false, isLast)); 217 break; 218 case Token.NESTED_BLOCK: 219 rules(context, token[1]); 220 store(context, openBrace(context, Breaks.AfterBlockBegins, true)); 221 all(context, token[2]); 222 store(context, closeBrace(context, Breaks.AfterBlockEnds, true, isLast)); 223 break; 224 case Token.COMMENT: 225 store(context, token); 226 store(context, allowsBreak(context, Breaks.AfterComment) ? context.format.breakWith : emptyCharacter); 227 break; 228 case Token.RAW: 229 store(context, token); 230 break; 231 case Token.RULE: 232 rules(context, token[1]); 233 store(context, openBrace(context, Breaks.AfterRuleBegins, true)); 234 body(context, token[2]); 235 store(context, closeBrace(context, Breaks.AfterRuleEnds, false, isLast)); 236 break; 237 } 238 } 239} 240 241module.exports = { 242 all: all, 243 body: body, 244 property: property, 245 rules: rules, 246 value: value 247}; 248