Atrule.js 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107
  1. var TYPE = require('../../tokenizer').TYPE;
  2. var ATRULE = TYPE.Atrule;
  3. var SEMICOLON = TYPE.Semicolon;
  4. var LEFTCURLYBRACKET = TYPE.LeftCurlyBracket;
  5. var RIGHTCURLYBRACKET = TYPE.RightCurlyBracket;
  6. function consumeRaw(startToken) {
  7. return this.Raw(startToken, SEMICOLON, LEFTCURLYBRACKET, false, true);
  8. }
  9. function isDeclarationBlockAtrule() {
  10. for (var offset = 1, type; type = this.scanner.lookupType(offset); offset++) {
  11. if (type === RIGHTCURLYBRACKET) {
  12. return true;
  13. }
  14. if (type === LEFTCURLYBRACKET ||
  15. type === ATRULE) {
  16. return false;
  17. }
  18. }
  19. return false;
  20. }
  21. module.exports = {
  22. name: 'Atrule',
  23. structure: {
  24. name: String,
  25. prelude: ['AtrulePrelude', 'Raw', null],
  26. block: ['Block', null]
  27. },
  28. parse: function() {
  29. var start = this.scanner.tokenStart;
  30. var name;
  31. var nameLowerCase;
  32. var prelude = null;
  33. var block = null;
  34. this.scanner.eat(ATRULE);
  35. name = this.scanner.substrToCursor(start + 1);
  36. nameLowerCase = name.toLowerCase();
  37. this.scanner.skipSC();
  38. // parse prelude
  39. if (this.scanner.eof === false &&
  40. this.scanner.tokenType !== LEFTCURLYBRACKET &&
  41. this.scanner.tokenType !== SEMICOLON) {
  42. if (this.parseAtrulePrelude) {
  43. prelude = this.parseWithFallback(this.AtrulePrelude.bind(this, name), consumeRaw);
  44. // turn empty AtrulePrelude into null
  45. if (prelude.type === 'AtrulePrelude' && prelude.children.head === null) {
  46. prelude = null;
  47. }
  48. } else {
  49. prelude = consumeRaw.call(this, this.scanner.currentToken);
  50. }
  51. this.scanner.skipSC();
  52. }
  53. switch (this.scanner.tokenType) {
  54. case SEMICOLON:
  55. this.scanner.next();
  56. break;
  57. case LEFTCURLYBRACKET:
  58. if (this.atrule.hasOwnProperty(nameLowerCase) &&
  59. typeof this.atrule[nameLowerCase].block === 'function') {
  60. block = this.atrule[nameLowerCase].block.call(this);
  61. } else {
  62. // TODO: should consume block content as Raw?
  63. block = this.Block(isDeclarationBlockAtrule.call(this));
  64. }
  65. break;
  66. }
  67. return {
  68. type: 'Atrule',
  69. loc: this.getLocation(start, this.scanner.tokenStart),
  70. name: name,
  71. prelude: prelude,
  72. block: block
  73. };
  74. },
  75. generate: function(node) {
  76. this.chunk('@');
  77. this.chunk(node.name);
  78. if (node.prelude !== null) {
  79. this.chunk(' ');
  80. this.node(node.prelude);
  81. }
  82. if (node.block) {
  83. this.node(node.block);
  84. } else {
  85. this.chunk(';');
  86. }
  87. },
  88. walkContext: 'atrule'
  89. };