helpers.js 6.7 KB

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