generic-an-plus-b.js 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236
  1. var isDigit = require('../tokenizer').isDigit;
  2. var cmpChar = require('../tokenizer').cmpChar;
  3. var TYPE = require('../tokenizer').TYPE;
  4. var DELIM = TYPE.Delim;
  5. var WHITESPACE = TYPE.WhiteSpace;
  6. var COMMENT = TYPE.Comment;
  7. var IDENT = TYPE.Ident;
  8. var NUMBER = TYPE.Number;
  9. var DIMENSION = TYPE.Dimension;
  10. var PLUSSIGN = 0x002B; // U+002B PLUS SIGN (+)
  11. var HYPHENMINUS = 0x002D; // U+002D HYPHEN-MINUS (-)
  12. var N = 0x006E; // U+006E LATIN SMALL LETTER N (n)
  13. var DISALLOW_SIGN = true;
  14. var ALLOW_SIGN = false;
  15. function isDelim(token, code) {
  16. return token !== null && token.type === DELIM && token.value.charCodeAt(0) === code;
  17. }
  18. function skipSC(token, offset, getNextToken) {
  19. while (token !== null && (token.type === WHITESPACE || token.type === COMMENT)) {
  20. token = getNextToken(++offset);
  21. }
  22. return offset;
  23. }
  24. function checkInteger(token, valueOffset, disallowSign, offset) {
  25. if (!token) {
  26. return 0;
  27. }
  28. var code = token.value.charCodeAt(valueOffset);
  29. if (code === PLUSSIGN || code === HYPHENMINUS) {
  30. if (disallowSign) {
  31. // Number sign is not allowed
  32. return 0;
  33. }
  34. valueOffset++;
  35. }
  36. for (; valueOffset < token.value.length; valueOffset++) {
  37. if (!isDigit(token.value.charCodeAt(valueOffset))) {
  38. // Integer is expected
  39. return 0;
  40. }
  41. }
  42. return offset + 1;
  43. }
  44. // ... <signed-integer>
  45. // ... ['+' | '-'] <signless-integer>
  46. function consumeB(token, offset_, getNextToken) {
  47. var sign = false;
  48. var offset = skipSC(token, offset_, getNextToken);
  49. token = getNextToken(offset);
  50. if (token === null) {
  51. return offset_;
  52. }
  53. if (token.type !== NUMBER) {
  54. if (isDelim(token, PLUSSIGN) || isDelim(token, HYPHENMINUS)) {
  55. sign = true;
  56. offset = skipSC(getNextToken(++offset), offset, getNextToken);
  57. token = getNextToken(offset);
  58. if (token === null && token.type !== NUMBER) {
  59. return 0;
  60. }
  61. } else {
  62. return offset_;
  63. }
  64. }
  65. if (!sign) {
  66. var code = token.value.charCodeAt(0);
  67. if (code !== PLUSSIGN && code !== HYPHENMINUS) {
  68. // Number sign is expected
  69. return 0;
  70. }
  71. }
  72. return checkInteger(token, sign ? 0 : 1, sign, offset);
  73. }
  74. // An+B microsyntax https://www.w3.org/TR/css-syntax-3/#anb
  75. module.exports = function anPlusB(token, getNextToken) {
  76. /* eslint-disable brace-style*/
  77. var offset = 0;
  78. if (!token) {
  79. return 0;
  80. }
  81. // <integer>
  82. if (token.type === NUMBER) {
  83. return checkInteger(token, 0, ALLOW_SIGN, offset); // b
  84. }
  85. // -n
  86. // -n <signed-integer>
  87. // -n ['+' | '-'] <signless-integer>
  88. // -n- <signless-integer>
  89. // <dashndashdigit-ident>
  90. else if (token.type === IDENT && token.value.charCodeAt(0) === HYPHENMINUS) {
  91. // expect 1st char is N
  92. if (!cmpChar(token.value, 1, N)) {
  93. return 0;
  94. }
  95. switch (token.value.length) {
  96. // -n
  97. // -n <signed-integer>
  98. // -n ['+' | '-'] <signless-integer>
  99. case 2:
  100. return consumeB(getNextToken(++offset), offset, getNextToken);
  101. // -n- <signless-integer>
  102. case 3:
  103. if (token.value.charCodeAt(2) !== HYPHENMINUS) {
  104. return 0;
  105. }
  106. offset = skipSC(getNextToken(++offset), offset, getNextToken);
  107. token = getNextToken(offset);
  108. return checkInteger(token, 0, DISALLOW_SIGN, offset);
  109. // <dashndashdigit-ident>
  110. default:
  111. if (token.value.charCodeAt(2) !== HYPHENMINUS) {
  112. return 0;
  113. }
  114. return checkInteger(token, 3, DISALLOW_SIGN, offset);
  115. }
  116. }
  117. // '+'? n
  118. // '+'? n <signed-integer>
  119. // '+'? n ['+' | '-'] <signless-integer>
  120. // '+'? n- <signless-integer>
  121. // '+'? <ndashdigit-ident>
  122. else if (token.type === IDENT || (isDelim(token, PLUSSIGN) && getNextToken(offset + 1).type === IDENT)) {
  123. // just ignore a plus
  124. if (token.type !== IDENT) {
  125. token = getNextToken(++offset);
  126. }
  127. if (token === null || !cmpChar(token.value, 0, N)) {
  128. return 0;
  129. }
  130. switch (token.value.length) {
  131. // '+'? n
  132. // '+'? n <signed-integer>
  133. // '+'? n ['+' | '-'] <signless-integer>
  134. case 1:
  135. return consumeB(getNextToken(++offset), offset, getNextToken);
  136. // '+'? n- <signless-integer>
  137. case 2:
  138. if (token.value.charCodeAt(1) !== HYPHENMINUS) {
  139. return 0;
  140. }
  141. offset = skipSC(getNextToken(++offset), offset, getNextToken);
  142. token = getNextToken(offset);
  143. return checkInteger(token, 0, DISALLOW_SIGN, offset);
  144. // '+'? <ndashdigit-ident>
  145. default:
  146. if (token.value.charCodeAt(1) !== HYPHENMINUS) {
  147. return 0;
  148. }
  149. return checkInteger(token, 2, DISALLOW_SIGN, offset);
  150. }
  151. }
  152. // <ndashdigit-dimension>
  153. // <ndash-dimension> <signless-integer>
  154. // <n-dimension>
  155. // <n-dimension> <signed-integer>
  156. // <n-dimension> ['+' | '-'] <signless-integer>
  157. else if (token.type === DIMENSION) {
  158. var code = token.value.charCodeAt(0);
  159. var sign = code === PLUSSIGN || code === HYPHENMINUS ? 1 : 0;
  160. for (var i = sign; i < token.value.length; i++) {
  161. if (!isDigit(token.value.charCodeAt(i))) {
  162. break;
  163. }
  164. }
  165. if (i === sign) {
  166. // Integer is expected
  167. return 0;
  168. }
  169. if (!cmpChar(token.value, i, N)) {
  170. return 0;
  171. }
  172. // <n-dimension>
  173. // <n-dimension> <signed-integer>
  174. // <n-dimension> ['+' | '-'] <signless-integer>
  175. if (i + 1 === token.value.length) {
  176. return consumeB(getNextToken(++offset), offset, getNextToken);
  177. } else {
  178. if (token.value.charCodeAt(i + 1) !== HYPHENMINUS) {
  179. return 0;
  180. }
  181. // <ndash-dimension> <signless-integer>
  182. if (i + 2 === token.value.length) {
  183. offset = skipSC(getNextToken(++offset), offset, getNextToken);
  184. token = getNextToken(offset);
  185. return checkInteger(token, 0, DISALLOW_SIGN, offset);
  186. }
  187. // <ndashdigit-dimension>
  188. else {
  189. return checkInteger(token, i + 2, DISALLOW_SIGN, offset);
  190. }
  191. }
  192. }
  193. return 0;
  194. };