cssSelector.ometa 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  1. ometa CssSelector <: Parser {
  2. crChar = '\r',
  3. ffChar = '\f',
  4. nlChar = '\n',
  5. tabChar = '\t',
  6. lineEnding = crChar | ffChar | nlChar,
  7. tabOrLineEnding = tabChar | lineEnding,
  8. ident = '-' nmstart:s nmchar*:cs -> { '-' + s + cs.join('') }
  9. | nmstart:s nmchar*:cs -> { s + cs.join('') },
  10. name = nmchar+:n -> { n.join('') },
  11. nmstart = ('_' | letter | nonascii | escape):n -> { n },
  12. nonascii = '',
  13. unicode = '',
  14. escape = unicode | '',
  15. nmchar = '_' | '-' | letterOrDigit | nonascii | escape,
  16. num = digit+: d -> d.join('')
  17. | digit* '.' digit+,
  18. string = (string1 | string2):s -> { s },
  19. //string1 = '\"' (~(lineEnding | '\"') | '\\' nl | nonascii | escape)*:s '\"' -> { '\"' + s.join('') + '\"' },
  20. string1 = '"' letter*:s '"' -> { '"' + s.join('') + '"' },
  21. string2 = '\'' (~(lineEnding | '\'') | '\\' nl | nonascii | escape)*:s '\'' -> { '\'' + s.join('') + '\'' },
  22. //invalid = invalid1 | invalid2,
  23. //invalid1 = '\"' (~(lineEnding | '\"') | '\\' nl | nonascii | escape)*,
  24. //invalid2 = '\'' (~(lineEnding | '\'') | '\\' nl | nonascii | escape)*,
  25. nl = crChar nlChar
  26. | lineEnding,
  27. D = 'd' | 'D',
  28. E = 'e' | 'E',
  29. N = 'n' | 'N',
  30. O = 'o' | 'O',
  31. T = 't' | 'T',
  32. V = 'v' | 'V',
  33. S = ' ' -> { ' ' }
  34. | '\n' -> { '\n' },
  35. INCLUDES = '~' '=' -> { '~=' },
  36. DASHMATCH = '|' '=' -> { '|=' },
  37. PREFIXMATCH = '^' '=' -> { '^=' },
  38. SUFFIXMATCH = '$' '=' -> { '$=' },
  39. SUBSTRINGMATCH = '*' '=' -> { '*=' },
  40. IDENT = ident,
  41. STRING = string:s -> { s },
  42. FUNCTION = ident:i '(' -> { i + '(' },
  43. NUMBER = num:n -> { n },
  44. HASH = '#' name:n -> { '#' + n },
  45. PLUS = S+ '+' -> { ' +' }
  46. | '+' -> { '+' },
  47. // First line of next selector is a Css Hack
  48. GREATER = '>' '>' -> { '> >' }
  49. | S+ '>' -> { ' >' }
  50. | '>' -> { '>' },
  51. COMMA = S+ ',' -> { ' ,' }
  52. | ',' -> { ',' },
  53. TILDE = S+ '~' -> { ' ~' }
  54. | '~' -> { '~' },
  55. NOT = ':' N O T '(' -> { ':not(' },
  56. ATKEYWORD = '@' ident,
  57. INVALID = invalid,
  58. PERCENTAGE = num:n '%' -> { n + '%' },
  59. DIMENSION = num:n ident:i -> { n + i },
  60. CDO = '<' '!' '-' '-',
  61. CDC = '-' '-' '>',
  62. selectors_group = selector:pre comma_separated_selector* -> { self.addSelector(pre); self },
  63. comma_separated_selector = COMMA:com S*:spacing selector:sel -> { self.addCommaSeparatedSelector(com + spacing.join(''), sel) },
  64. selector = simple_selector_sequence:sim (combined_sequence)*:additional -> { sim + additional.join('') }
  65. // Css Hack
  66. | combined_sequence*:comb -> { comb.join('') },
  67. combinator = PLUS:p S+ -> { p + ' ' }
  68. | PLUS:p -> { p }
  69. | GREATER:g S+ -> { g + ' ' }
  70. | GREATER:g -> { g }
  71. | TILDE:t S+ -> { t + ' ' }
  72. | TILDE:t -> { t }
  73. | S+:spacing -> { spacing.join('') },
  74. combined_sequence = combinator+:comb simple_selector_sequence:sel -> { comb.join('') + sel },
  75. non_namespaced_selector = (HASH | class | attrib | negation | pseudo):sel -> { sel },
  76. simple_selector_sequence = namespace_prefix:pre '*' non_namespaced_selector*:post -> { pre + '*' + post.join('') }
  77. | namespace_prefix:pre element_name:ele non_namespaced_selector*:post -> { pre + ele + post.join('') }
  78. | '*' non_namespaced_selector*:post -> { '*' + post.join('') }
  79. | element_name:ele non_namespaced_selector*:post -> { ele + post.join('') }
  80. | non_namespaced_selector+:sels -> { sels.join('') }
  81. // Css Hack
  82. | expression:ex -> { ex },
  83. namespace_prefix = ('*' | IDENT):pre '|' -> { pre + '|' }
  84. | '|' -> { '|' },
  85. // First line of the next selector is a Css Hack
  86. element_name = IDENT:i '*' -> { i + '*' }
  87. | IDENT:i -> { i },
  88. class = '.' IDENT:i -> { '.' + i },
  89. attrib = '[' S* possible_namespaced_attrib:att ']' -> { '[' + att + ']' },
  90. possible_namespaced_attrib = namespace_prefix:pre ident_with_possible_postfix:post -> { pre + post }
  91. | ident_with_possible_postfix:post -> { post },
  92. ident_with_possible_postfix = IDENT:left S* attrib_match:match S* (IDENT | STRING):right S* -> { left + match + right }
  93. | IDENT:i S* -> { i },
  94. attrib_match = (PREFIXMATCH | SUFFIXMATCH | SUBSTRINGMATCH | equals_match | INCLUDES | DASHMATCH):m -> { m },
  95. equals_match = '=' -> { '=' },
  96. pseudo = ':' ':' (functional_pseudo | IDENT):i -> { '::' + i }
  97. | ':' (functional_pseudo | IDENT):i -> { ':' + i },
  98. functional_pseudo = FUNCTION:f S* full_expression:e ')' -> { f + e + ')' }
  99. // Css Hack for :-moz-any(...)
  100. | FUNCTION:f S* selectors_group:sel ')' -> { f + sel.toString() + ')' },
  101. expression_content = (PLUS | '-' | PERCENTAGE | DIMENSION | NUMBER | STRING | IDENT):e -> { e },
  102. expression = expression_content:ec S+ expression:e -> { ec + ' ' + e }
  103. | expression_content:ec expression:e -> { ec + e }
  104. | expression_content:ec S* -> { ec },
  105. full_expression = (expression)+:ea -> { ea.join('') },
  106. negation = NOT:n S* negation_arg:na S* ')' -> { n + na + ')' },
  107. //negation_arg = (type_selector | universal | HASH | class | attrib | pseudo):na -> { na }
  108. //Technically not allowed, but here for scss compatibility
  109. negation_arg = selectors_group:sg -> { sg.toString() }
  110. }
  111. CssSelector.initialize = function() {
  112. var hasBeenStringified = false,
  113. self = this;
  114. this.selector;
  115. this.selectors = [];
  116. this.commaSeparatedSelectors = [];
  117. var resetFields = function() {
  118. self.selector = null;
  119. self.selectors = [];
  120. self.commaSeparatedSelectors = [];
  121. hasBeenStrigified = false;
  122. };
  123. this.addSelector = function(sel) {
  124. if(hasBeenStringified) {
  125. resetFields();
  126. }
  127. this.selector = sel;
  128. };
  129. this.addCommaSeparatedSelector = function(commaAndSpacing, selector) {
  130. if(hasBeenStringified) {
  131. resetFields();
  132. }
  133. this.selectors.push(selector);
  134. this.commaSeparatedSelectors.push({
  135. commaAndSpacing: commaAndSpacing,
  136. selector: selector
  137. });
  138. };
  139. this.toString = function() {
  140. hasBeenStringified = true;
  141. return this.selector + this.commaSeparatedSelectors.map(function(csSel) {
  142. return csSel.commaAndSpacing + csSel.selector;
  143. }).join('');
  144. };
  145. };
  146. CssSelector