Нет описания

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. // Helper for tokenizing the contents of a CSS selector block
  2. module.exports = (function() {
  3. var createTokenPrototype = function (processable) {
  4. var important = '!important';
  5. // Constructor for tokens
  6. function Token (prop, p2, p3) {
  7. this.prop = prop;
  8. if (typeof(p2) === 'string') {
  9. this.value = p2;
  10. this.isImportant = p3;
  11. }
  12. else {
  13. this.value = processable[prop].defaultValue;
  14. this.isImportant = p2;
  15. }
  16. }
  17. Token.prototype.prop = null;
  18. Token.prototype.value = null;
  19. Token.prototype.granularValues = null;
  20. Token.prototype.components = null;
  21. Token.prototype.position = null;
  22. Token.prototype.isImportant = false;
  23. Token.prototype.isDirty = false;
  24. Token.prototype.isShorthand = false;
  25. Token.prototype.isIrrelevant = false;
  26. Token.prototype.isReal = true;
  27. Token.prototype.isMarkedForDeletion = false;
  28. // Tells if this token is a component of the other one
  29. Token.prototype.isComponentOf = function (other) {
  30. if (!processable[this.prop] || !processable[other.prop])
  31. return false;
  32. if (!(processable[other.prop].components instanceof Array) || !processable[other.prop].components.length)
  33. return false;
  34. return processable[other.prop].components.indexOf(this.prop) >= 0;
  35. };
  36. // Clones a token
  37. Token.prototype.clone = function (isImportant) {
  38. var token = new Token(this.prop, this.value, (typeof(isImportant) !== 'undefined' ? isImportant : this.isImportant));
  39. return token;
  40. };
  41. // Creates an irrelevant token with the same prop
  42. Token.prototype.cloneIrrelevant = function (isImportant) {
  43. var token = Token.makeDefault(this.prop, (typeof(isImportant) !== 'undefined' ? isImportant : this.isImportant));
  44. token.isIrrelevant = true;
  45. return token;
  46. };
  47. // Creates an array of property tokens with their default values
  48. Token.makeDefaults = function (props, important) {
  49. return props.map(function(prop) {
  50. return new Token(prop, important);
  51. });
  52. };
  53. // Parses one CSS property declaration into a token
  54. Token.tokenizeOne = function (fullProp) {
  55. // Find first colon
  56. var colonPos = fullProp.indexOf(':');
  57. if (colonPos < 0) {
  58. // This property doesn't have a colon, it's invalid. Let's keep it intact anyway.
  59. return new Token('', fullProp);
  60. }
  61. // Parse parts of the property
  62. var prop = fullProp.substr(0, colonPos).trim();
  63. var value = fullProp.substr(colonPos + 1).trim();
  64. var isImportant = false;
  65. var importantPos = value.indexOf(important);
  66. // Check if the property is important
  67. if (importantPos >= 1 && importantPos === value.length - important.length) {
  68. value = value.substr(0, importantPos).trim();
  69. isImportant = true;
  70. }
  71. // Return result
  72. var result = new Token(prop, value, isImportant);
  73. // If this is a shorthand, break up its values
  74. // NOTE: we need to do this for all shorthands because otherwise we couldn't remove default values from them
  75. if (processable[prop] && processable[prop].isShorthand) {
  76. result.isShorthand = true;
  77. result.components = processable[prop].breakUp(result);
  78. result.isDirty = true;
  79. }
  80. return result;
  81. };
  82. // Breaks up a string of CSS property declarations into tokens so that they can be handled more easily
  83. Token.tokenize = function (input) {
  84. // Split the input by semicolons and parse the parts
  85. var tokens = input.split(';').map(Token.tokenizeOne);
  86. return tokens;
  87. };
  88. // Transforms tokens back into CSS properties
  89. Token.detokenize = function (tokens) {
  90. // If by mistake the input is not an array, make it an array
  91. if (!(tokens instanceof Array)) {
  92. tokens = [tokens];
  93. }
  94. var result = '';
  95. // This step takes care of putting together the components of shorthands
  96. // NOTE: this is necessary to do for every shorthand, otherwise we couldn't remove their default values
  97. for (var i = 0; i < tokens.length; i++) {
  98. var t = tokens[i];
  99. if (t.isShorthand && t.isDirty) {
  100. var news = processable[t.prop].putTogether(t.prop, t.components, t.isImportant);
  101. Array.prototype.splice.apply(tokens, [i, 1].concat(news));
  102. t.isDirty = false;
  103. i--;
  104. continue;
  105. }
  106. if (t.prop)
  107. result += t.prop + ':';
  108. if (t.value)
  109. result += t.value;
  110. if (t.isImportant)
  111. result += important;
  112. result += ';';
  113. }
  114. return result.substr(0, result.length - 1);
  115. };
  116. // Gets the final (detokenized) length of the given tokens
  117. Token.getDetokenizedLength = function (tokens) {
  118. // If by mistake the input is not an array, make it an array
  119. if (!(tokens instanceof Array)) {
  120. tokens = [tokens];
  121. }
  122. var result = 0;
  123. // This step takes care of putting together the components of shorthands
  124. // NOTE: this is necessary to do for every shorthand, otherwise we couldn't remove their default values
  125. for (var i = 0; i < tokens.length; i++) {
  126. var t = tokens[i];
  127. if (t.isShorthand && t.isDirty) {
  128. var news = processable[t.prop].putTogether(t.prop, t.components, t.isImportant);
  129. Array.prototype.splice.apply(tokens, [i, 1].concat(news));
  130. t.isDirty = false;
  131. i--;
  132. continue;
  133. }
  134. if (t.prop) {
  135. result += t.prop.length + 1;
  136. }
  137. if (t.value) {
  138. result += t.value.length;
  139. }
  140. if (t.isImportant) {
  141. result += important.length;
  142. }
  143. }
  144. return result;
  145. };
  146. return Token;
  147. };
  148. return {
  149. createTokenPrototype: createTokenPrototype
  150. };
  151. })();