You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

229 lines
8.5 KiB

4 years ago
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", { value: true });
  3. exports.default = parse;
  4. var reName = /^(?:\\([\da-f]{1,6}\s?|(\s)|.)|[\w\-\u00b0-\uFFFF])+/, reEscape = /\\([\da-f]{1,6}\s?|(\s)|.)/gi,
  5. //modified version of https://github.com/jquery/sizzle/blob/master/src/sizzle.js#L87
  6. reAttr = /^\s*((?:\\.|[\w\u00b0-\uFFFF-])+)\s*(?:(\S?)=\s*(?:(['"])([^]*?)\3|(#?(?:\\.|[\w\u00b0-\uFFFF-])*)|)|)\s*(i)?\]/;
  7. var actionTypes = {
  8. undefined: "exists",
  9. "": "equals",
  10. "~": "element",
  11. "^": "start",
  12. $: "end",
  13. "*": "any",
  14. "!": "not",
  15. "|": "hyphen"
  16. };
  17. var Traversals = {
  18. ">": "child",
  19. "<": "parent",
  20. "~": "sibling",
  21. "+": "adjacent"
  22. };
  23. var attribSelectors = {
  24. "#": ["id", "equals"],
  25. ".": ["class", "element"]
  26. };
  27. //pseudos, whose data-property is parsed as well
  28. var unpackPseudos = new Set(["has", "not", "matches"]);
  29. var stripQuotesFromPseudos = new Set(["contains", "icontains"]);
  30. var quotes = new Set(['"', "'"]);
  31. //unescape function taken from https://github.com/jquery/sizzle/blob/master/src/sizzle.js#L152
  32. function funescape(_, escaped, escapedWhitespace) {
  33. var high = parseInt(escaped, 16) - 0x10000;
  34. // NaN means non-codepoint
  35. return high !== high || escapedWhitespace
  36. ? escaped
  37. : high < 0
  38. ? // BMP codepoint
  39. String.fromCharCode(high + 0x10000)
  40. : // Supplemental Plane codepoint (surrogate pair)
  41. String.fromCharCode((high >> 10) | 0xd800, (high & 0x3ff) | 0xdc00);
  42. }
  43. function unescapeCSS(str) {
  44. return str.replace(reEscape, funescape);
  45. }
  46. function isWhitespace(c) {
  47. return c === " " || c === "\n" || c === "\t" || c === "\f" || c === "\r";
  48. }
  49. function parse(selector, options) {
  50. var subselects = [];
  51. selector = parseSelector(subselects, selector + "", options);
  52. if (selector !== "") {
  53. throw new Error("Unmatched selector: " + selector);
  54. }
  55. return subselects;
  56. }
  57. function parseSelector(subselects, selector, options) {
  58. var tokens = [], sawWS = false;
  59. function getName() {
  60. var match = selector.match(reName);
  61. if (!match) {
  62. throw new Error("Expected name, found " + selector);
  63. }
  64. var sub = match[0];
  65. selector = selector.substr(sub.length);
  66. return unescapeCSS(sub);
  67. }
  68. function stripWhitespace(start) {
  69. while (isWhitespace(selector.charAt(start)))
  70. start++;
  71. selector = selector.substr(start);
  72. }
  73. function isEscaped(pos) {
  74. var slashCount = 0;
  75. while (selector.charAt(--pos) === "\\")
  76. slashCount++;
  77. return (slashCount & 1) === 1;
  78. }
  79. stripWhitespace(0);
  80. while (selector !== "") {
  81. var firstChar = selector.charAt(0);
  82. if (isWhitespace(firstChar)) {
  83. sawWS = true;
  84. stripWhitespace(1);
  85. }
  86. else if (firstChar in Traversals) {
  87. tokens.push({ type: Traversals[firstChar] });
  88. sawWS = false;
  89. stripWhitespace(1);
  90. }
  91. else if (firstChar === ",") {
  92. if (tokens.length === 0) {
  93. throw new Error("Empty sub-selector");
  94. }
  95. subselects.push(tokens);
  96. tokens = [];
  97. sawWS = false;
  98. stripWhitespace(1);
  99. }
  100. else {
  101. if (sawWS) {
  102. if (tokens.length > 0) {
  103. tokens.push({ type: "descendant" });
  104. }
  105. sawWS = false;
  106. }
  107. if (firstChar === "*") {
  108. selector = selector.substr(1);
  109. tokens.push({ type: "universal" });
  110. }
  111. else if (firstChar in attribSelectors) {
  112. var _a = attribSelectors[firstChar], name_1 = _a[0], action = _a[1];
  113. selector = selector.substr(1);
  114. tokens.push({
  115. type: "attribute",
  116. name: name_1,
  117. action: action,
  118. value: getName(),
  119. ignoreCase: false
  120. });
  121. }
  122. else if (firstChar === "[") {
  123. selector = selector.substr(1);
  124. var data = selector.match(reAttr);
  125. if (!data) {
  126. throw new Error("Malformed attribute selector: " + selector);
  127. }
  128. selector = selector.substr(data[0].length);
  129. var name_2 = unescapeCSS(data[1]);
  130. if (!options ||
  131. ("lowerCaseAttributeNames" in options
  132. ? options.lowerCaseAttributeNames
  133. : !options.xmlMode)) {
  134. name_2 = name_2.toLowerCase();
  135. }
  136. tokens.push({
  137. type: "attribute",
  138. name: name_2,
  139. action: actionTypes[data[2]],
  140. value: unescapeCSS(data[4] || data[5] || ""),
  141. ignoreCase: !!data[6]
  142. });
  143. }
  144. else if (firstChar === ":") {
  145. if (selector.charAt(1) === ":") {
  146. selector = selector.substr(2);
  147. tokens.push({
  148. type: "pseudo-element",
  149. name: getName().toLowerCase()
  150. });
  151. continue;
  152. }
  153. selector = selector.substr(1);
  154. var name_3 = getName().toLowerCase();
  155. var data = null;
  156. if (selector.charAt(0) === "(") {
  157. if (unpackPseudos.has(name_3)) {
  158. var quot = selector.charAt(1);
  159. var quoted = quotes.has(quot);
  160. selector = selector.substr(quoted ? 2 : 1);
  161. data = [];
  162. selector = parseSelector(data, selector, options);
  163. if (quoted) {
  164. if (selector.charAt(0) !== quot) {
  165. throw new Error("Unmatched quotes in :" + name_3);
  166. }
  167. else {
  168. selector = selector.substr(1);
  169. }
  170. }
  171. if (selector.charAt(0) !== ")") {
  172. throw new Error("Missing closing parenthesis in :" + name_3 + " (" + selector + ")");
  173. }
  174. selector = selector.substr(1);
  175. }
  176. else {
  177. var pos = 1, counter = 1;
  178. for (; counter > 0 && pos < selector.length; pos++) {
  179. if (selector.charAt(pos) === "(" && !isEscaped(pos))
  180. counter++;
  181. else if (selector.charAt(pos) === ")" &&
  182. !isEscaped(pos))
  183. counter--;
  184. }
  185. if (counter) {
  186. throw new Error("Parenthesis not matched");
  187. }
  188. data = selector.substr(1, pos - 2);
  189. selector = selector.substr(pos);
  190. if (stripQuotesFromPseudos.has(name_3)) {
  191. var quot = data.charAt(0);
  192. if (quot === data.slice(-1) && quotes.has(quot)) {
  193. data = data.slice(1, -1);
  194. }
  195. data = unescapeCSS(data);
  196. }
  197. }
  198. }
  199. tokens.push({ type: "pseudo", name: name_3, data: data });
  200. }
  201. else if (reName.test(selector)) {
  202. var name_4 = getName();
  203. if (!options ||
  204. ("lowerCaseTags" in options
  205. ? options.lowerCaseTags
  206. : !options.xmlMode)) {
  207. name_4 = name_4.toLowerCase();
  208. }
  209. tokens.push({ type: "tag", name: name_4 });
  210. }
  211. else {
  212. if (tokens.length &&
  213. tokens[tokens.length - 1].type === "descendant") {
  214. tokens.pop();
  215. }
  216. addToken(subselects, tokens);
  217. return selector;
  218. }
  219. }
  220. }
  221. addToken(subselects, tokens);
  222. return selector;
  223. }
  224. function addToken(subselects, tokens) {
  225. if (subselects.length > 0 && tokens.length === 0) {
  226. throw new Error("Empty sub-selector");
  227. }
  228. subselects.push(tokens);
  229. }