helpers.js 7.0 KB

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