@asamuzakjp/dom-selector 0.12.1 → 0.12.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -42,5 +42,5 @@
42
42
  "test": "c8 --reporter=text mocha --exit test/**/*.test.js",
43
43
  "tsc": "npx tsc"
44
44
  },
45
- "version": "0.12.1"
45
+ "version": "0.12.3"
46
46
  }
package/src/js/matcher.js CHANGED
@@ -25,9 +25,9 @@ const HEX_CAPTURE = /^([\da-f]{1,6}\s?)/i;
25
25
  const HTML_FORM_INPUT = /^(?:(?:inpu|selec)t|textarea)$/;
26
26
  const HTML_FORM_PARTS = /^(?:button|fieldset|opt(?:group|ion))$/;
27
27
  const HTML_INTERACT = /^d(?:etails|ialog)$/;
28
+ const INPUT_TYPE_BARRED = /^(?:(?:butto|hidde)n|reset)$/;
28
29
  const PSEUDO_FUNC = /^(?:(?:ha|i)s|not|where)$/;
29
30
  const PSEUDO_NTH = /^nth-(?:last-)?(?:child|of-type)$/;
30
- const REPLACE_CHAR = /[\0\uD800-\uDFFF]/g;
31
31
  const WHITESPACE = /^[\n\r\f]/;
32
32
 
33
33
  /**
@@ -94,7 +94,7 @@ const unescapeSelector = (selector = '') => {
94
94
  selector.indexOf(String.fromCharCode(0x5c), 0) >= 0) {
95
95
  const arr = selector.split('\\');
96
96
  const l = arr.length;
97
- for (let i = 0; i < l; i++) {
97
+ for (let i = 1; i < l; i++) {
98
98
  let item = arr[i];
99
99
  if (i === l - 1 && item === '') {
100
100
  item = '\uFFFD';
@@ -104,12 +104,22 @@ const unescapeSelector = (selector = '') => {
104
104
  const [, hex] = hexExists;
105
105
  let str;
106
106
  try {
107
- str = String.fromCodePoint(`0x${hex.trim()}`)
108
- .replace(REPLACE_CHAR, '\uFFFD');
107
+ const low = parseInt('D800', 16);
108
+ const high = parseInt('DFFF', 16);
109
+ const deci = parseInt(hex, 16);
110
+ if (deci === 0 || (deci >= low && deci <= high)) {
111
+ str = '\uFFFD';
112
+ } else {
113
+ str = String.fromCodePoint(deci);
114
+ }
109
115
  } catch (e) {
110
116
  str = '\uFFFD';
111
117
  }
112
- item = item.replace(`${hex}`, str);
118
+ let postStr = '';
119
+ if (item.length > hex.length) {
120
+ postStr = item.substring(hex.length);
121
+ }
122
+ item = `${str}${postStr}`;
113
123
  } else if (WHITESPACE.test(item)) {
114
124
  item = '\\' + item;
115
125
  }
@@ -150,7 +160,7 @@ const collectNthChild = (anb = {}, node = {}) => {
150
160
  }
151
161
  // :first-child, :last-child, :nth-child(0 of S)
152
162
  if (a === 0) {
153
- if (b >= 0 && b < l) {
163
+ if (b > 0 && b <= l) {
154
164
  if (items.length) {
155
165
  let i = 0;
156
166
  while (i < l) {
@@ -162,7 +172,7 @@ const collectNthChild = (anb = {}, node = {}) => {
162
172
  i++;
163
173
  }
164
174
  } else {
165
- const current = arr[b];
175
+ const current = arr[b - 1];
166
176
  matched.push(current);
167
177
  }
168
178
  }
@@ -225,14 +235,14 @@ const collectNthOfType = (anb = {}, node = {}) => {
225
235
  const l = arr.length;
226
236
  // :first-of-type, :last-of-type
227
237
  if (a === 0) {
228
- if (b >= 0 && b < l) {
238
+ if (b > 0 && b <= l) {
229
239
  let i = 0;
230
240
  let j = 0;
231
241
  while (i < l) {
232
242
  const current = arr[i];
233
243
  const { localName: itemLocalName, prefix: itemPrefix } = current;
234
244
  if (itemLocalName === localName && itemPrefix === prefix) {
235
- if (j === b) {
245
+ if (j === b - 1) {
236
246
  matched.push(current);
237
247
  break;
238
248
  }
@@ -457,6 +467,9 @@ const matchAttributeSelector = (ast = {}, node = {}) => {
457
467
  const l = attributes.length;
458
468
  let { name: astAttrName } = astName;
459
469
  astAttrName = unescapeSelector(astAttrName);
470
+ if (caseInsensitive) {
471
+ astAttrName = astAttrName.toLowerCase();
472
+ }
460
473
  // namespaced
461
474
  if (/\|/.test(astAttrName)) {
462
475
  const [astAttrPrefix, astAttrLocalName] = astAttrName.split('|');
@@ -957,20 +970,21 @@ const matchPseudoClassSelector = (
957
970
  }
958
971
  break;
959
972
  case 'in-range':
960
- if (localName === 'input' &&
961
- node.hasAttribute('min') && node.hasAttribute('max')) {
962
- if (!(node.validity.rangeUnderflow ||
963
- node.validity.rangeOverflow)) {
964
- matched.push(node);
965
- }
973
+ if (localName === 'input' && !node.readonly &&
974
+ !(node.hasAttribute('type') &&
975
+ INPUT_TYPE_BARRED.test(node.getAttribute('type'))) &&
976
+ node.hasAttribute('min') && node.hasAttribute('max') &&
977
+ !(node.validity.rangeUnderflow || node.validity.rangeOverflow)) {
978
+ matched.push(node);
966
979
  }
967
980
  break;
968
981
  case 'out-of-range':
969
- if (localName === 'input' &&
970
- node.hasAttribute('min') && node.hasAttribute('max')) {
971
- if (node.validity.rangeUnderflow || node.validity.rangeOverflow) {
972
- matched.push(node);
973
- }
982
+ if (localName === 'input' && !node.readonly &&
983
+ !(node.hasAttribute('type') &&
984
+ INPUT_TYPE_BARRED.test(node.getAttribute('type'))) &&
985
+ node.hasAttribute('min') && node.hasAttribute('max') &&
986
+ (node.validity.rangeUnderflow || node.validity.rangeOverflow)) {
987
+ matched.push(node);
974
988
  }
975
989
  break;
976
990
  case 'required':
@@ -1018,7 +1032,7 @@ const matchPseudoClassSelector = (
1018
1032
  case 'first-of-type': {
1019
1033
  const [node1] = collectNthOfType({
1020
1034
  a: 0,
1021
- b: 0
1035
+ b: 1
1022
1036
  }, node);
1023
1037
  if (node1) {
1024
1038
  matched.push(node1);
@@ -1028,7 +1042,7 @@ const matchPseudoClassSelector = (
1028
1042
  case 'last-of-type': {
1029
1043
  const [node1] = collectNthOfType({
1030
1044
  a: 0,
1031
- b: 0,
1045
+ b: 1,
1032
1046
  reverse: true
1033
1047
  }, node);
1034
1048
  if (node1) {
@@ -1039,11 +1053,11 @@ const matchPseudoClassSelector = (
1039
1053
  case 'only-of-type': {
1040
1054
  const [node1] = collectNthOfType({
1041
1055
  a: 0,
1042
- b: 0
1056
+ b: 1
1043
1057
  }, node);
1044
1058
  const [node2] = collectNthOfType({
1045
1059
  a: 0,
1046
- b: 0,
1060
+ b: 1,
1047
1061
  reverse: true
1048
1062
  }, node);
1049
1063
  if (node1 === node && node2 === node) {
package/src/js/parser.js CHANGED
@@ -23,16 +23,16 @@ const preprocess = (...args) => {
23
23
  throw new TypeError('1 argument required, but only 0 present');
24
24
  }
25
25
  let [selector] = args;
26
- if (typeof selector !== 'string') {
27
- if (selector === undefined || selector === null) {
28
- selector = Object.prototype.toString.call(selector)
29
- .slice(TYPE_FROM, TYPE_TO).toLowerCase();
30
- } else {
31
- throw new DOMException(`invalid selector ${selector}`, 'SyntaxError');
32
- }
26
+ if (typeof selector === 'string') {
27
+ selector = selector.replace(/\f|\r\n?/g, '\n')
28
+ .replace(/[\0\uD800-\uDFFF]|\\$/g, '\uFFFD').trim();
29
+ } else if (selector === undefined || selector === null) {
30
+ selector = Object.prototype.toString.call(selector)
31
+ .slice(TYPE_FROM, TYPE_TO).toLowerCase();
32
+ } else {
33
+ throw new DOMException(`invalid selector ${selector}`, 'SyntaxError');
33
34
  }
34
- return selector.replace(/\f|\r\n?/g, '\n')
35
- .replace(/[\0\uD800-\uDFFF]/g, '\uFFFD').trim();
35
+ return selector;
36
36
  };
37
37
 
38
38
  /**
@@ -54,7 +54,11 @@ const parseSelector = selector => {
54
54
  });
55
55
  res = toPlainObject(ast);
56
56
  } catch (e) {
57
- throw new DOMException(e.message, 'SyntaxError');
57
+ if (e.message === '"]" is expected' && !selector.endsWith(']')) {
58
+ res = parseSelector(`${selector}]`);
59
+ } else {
60
+ throw new DOMException(e.message, 'SyntaxError');
61
+ }
58
62
  }
59
63
  return res;
60
64
  };