generic-an-plus-b.cjs 6.7 KB

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