Declaration.js 4.4 KB

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