helpers.js 7.3 KB

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