Declaration.js 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. var isCustomProperty = require('../../utils/names').isCustomProperty;
  2. var TYPE = require('../../tokenizer').TYPE;
  3. var CHARCODE = require('../../tokenizer').CHARCODE;
  4. var rawMode = require('./Raw').mode;
  5. var IDENT = TYPE.Ident;
  6. var HASH = TYPE.Hash;
  7. var COLON = TYPE.Colon;
  8. var SEMICOLON = TYPE.Semicolon;
  9. var DELIM = TYPE.Delim;
  10. var EXCLAMATIONMARK = CHARCODE.ExclamationMark;
  11. var SOLIDUS = CHARCODE.Solidus;
  12. var ASTERISK = CHARCODE.Asterisk;
  13. var AMPERSAND = CHARCODE.Ampersand;
  14. var DOLLARSIGN = CHARCODE.DollarSign;
  15. var PLUSSIGN = CHARCODE.PlusSign;
  16. var NUMBERSIGN = CHARCODE.NumberSign;
  17. function consumeValueRaw(startToken) {
  18. return this.Raw(startToken, rawMode.exclamationMarkOrSemicolon, true);
  19. }
  20. function consumeCustomPropertyRaw(startToken) {
  21. return this.Raw(startToken, rawMode.exclamationMarkOrSemicolon, false);
  22. }
  23. function consumeValue() {
  24. var startValueToken = this.scanner.tokenIndex;
  25. var value = this.Value();
  26. if (value.type !== 'Raw' &&
  27. this.scanner.eof === false &&
  28. this.scanner.tokenType !== SEMICOLON &&
  29. this.scanner.isDelim(EXCLAMATIONMARK) === false &&
  30. this.scanner.isBalanceEdge(startValueToken) === false) {
  31. this.error();
  32. }
  33. return value;
  34. }
  35. module.exports = {
  36. name: 'Declaration',
  37. structure: {
  38. important: [Boolean, String],
  39. property: String,
  40. value: ['Value', 'Raw']
  41. },
  42. parse: function() {
  43. var start = this.scanner.tokenStart;
  44. var startToken = this.scanner.tokenIndex;
  45. var property = readProperty.call(this);
  46. var customProperty = isCustomProperty(property);
  47. var parseValue = customProperty ? this.parseCustomProperty : this.parseValue;
  48. var consumeRaw = customProperty ? consumeCustomPropertyRaw : consumeValueRaw;
  49. var important = false;
  50. var value;
  51. this.scanner.skipSC();
  52. this.eat(COLON);
  53. if (!customProperty) {
  54. this.scanner.skipSC();
  55. }
  56. if (parseValue) {
  57. value = this.parseWithFallback(consumeValue, consumeRaw);
  58. } else {
  59. value = consumeRaw.call(this, this.scanner.tokenIndex);
  60. }
  61. if (this.scanner.isDelim(EXCLAMATIONMARK)) {
  62. important = getImportant.call(this);
  63. this.scanner.skipSC();
  64. }
  65. // Do not include semicolon to range per spec
  66. // https://drafts.csswg.org/css-syntax/#declaration-diagram
  67. if (this.scanner.eof === false &&
  68. this.scanner.tokenType !== SEMICOLON &&
  69. this.scanner.isBalanceEdge(startToken) === false) {
  70. this.error();
  71. }
  72. return {
  73. type: 'Declaration',
  74. loc: this.getLocation(start, this.scanner.tokenStart),
  75. important: important,
  76. property: property,
  77. value: value
  78. };
  79. },
  80. generate: function(node) {
  81. this.chunk(node.property);
  82. this.chunk(':');
  83. this.node(node.value);
  84. if (node.important) {
  85. this.chunk(node.important === true ? '!important' : '!' + node.important);
  86. }
  87. },
  88. walkContext: 'declaration'
  89. };
  90. function readProperty() {
  91. var start = this.scanner.tokenStart;
  92. var prefix = 0;
  93. // hacks
  94. if (this.scanner.tokenType === DELIM) {
  95. switch (this.scanner.source.charCodeAt(this.scanner.tokenStart)) {
  96. case ASTERISK:
  97. case DOLLARSIGN:
  98. case PLUSSIGN:
  99. case NUMBERSIGN:
  100. case AMPERSAND:
  101. this.scanner.next();
  102. break;
  103. // TODO: not sure we should support this hack
  104. case SOLIDUS:
  105. this.scanner.next();
  106. if (this.scanner.isDelim(SOLIDUS)) {
  107. this.scanner.next();
  108. }
  109. break;
  110. }
  111. }
  112. if (prefix) {
  113. this.scanner.skip(prefix);
  114. }
  115. if (this.scanner.tokenType === HASH) {
  116. this.eat(HASH);
  117. } else {
  118. this.eat(IDENT);
  119. }
  120. return this.scanner.substrToCursor(start);
  121. }
  122. // ! ws* important
  123. function getImportant() {
  124. this.eat(DELIM);
  125. this.scanner.skipSC();
  126. var important = this.consume(IDENT);
  127. // store original value in case it differ from `important`
  128. // for better original source restoring and hacks like `!ie` support
  129. return important === 'important' ? true : important;
  130. }