@dan-uni/dan-any 1.3.9 → 1.4.0

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.
@@ -1232,7 +1232,6 @@
1232
1232
  if (y < x) return 1;
1233
1233
  return 0;
1234
1234
  }
1235
- var ONLY_ENUMERABLE = void 0;
1236
1235
  var kStrict = true;
1237
1236
  var kLoose = false;
1238
1237
  var kNoIterator = 0;
@@ -1282,8 +1281,8 @@
1282
1281
  if (val1Tag !== val2Tag) return false;
1283
1282
  if (Array.isArray(val1)) {
1284
1283
  if (val1.length !== val2.length) return false;
1285
- var keys1 = getOwnNonIndexProperties(val1, ONLY_ENUMERABLE);
1286
- var keys2 = getOwnNonIndexProperties(val2, ONLY_ENUMERABLE);
1284
+ var keys1 = getOwnNonIndexProperties(val1);
1285
+ var keys2 = getOwnNonIndexProperties(val2);
1287
1286
  if (keys1.length !== keys2.length) return false;
1288
1287
  return keyCheck(val1, val2, strict, memos, kIsArray, keys1);
1289
1288
  }
@@ -1300,8 +1299,8 @@
1300
1299
  if (!strict && (isFloat32Array(val1) || isFloat64Array(val1))) {
1301
1300
  if (!areSimilarFloatArrays(val1, val2)) return false;
1302
1301
  } else if (!areSimilarTypedArrays(val1, val2)) return false;
1303
- var _keys = getOwnNonIndexProperties(val1, ONLY_ENUMERABLE);
1304
- var _keys2 = getOwnNonIndexProperties(val2, ONLY_ENUMERABLE);
1302
+ var _keys = getOwnNonIndexProperties(val1);
1303
+ var _keys2 = getOwnNonIndexProperties(val2);
1305
1304
  if (_keys.length !== _keys2.length) return false;
1306
1305
  return keyCheck(val1, val2, strict, memos, kNoIterator, _keys);
1307
1306
  } else if (isSet(val1)) {
@@ -14026,10 +14025,10 @@
14026
14025
  if ('object' == typeof value && null !== value) return {
14027
14026
  enabled: false !== value.enabled,
14028
14027
  maxEntitySize: Math.max(1, value.maxEntitySize ?? 10000),
14029
- maxExpansionDepth: Math.max(1, value.maxExpansionDepth ?? 10),
14030
- maxTotalExpansions: Math.max(1, value.maxTotalExpansions ?? 1000),
14028
+ maxExpansionDepth: Math.max(1, value.maxExpansionDepth ?? 10000),
14029
+ maxTotalExpansions: Math.max(1, value.maxTotalExpansions ?? 1 / 0),
14031
14030
  maxExpandedLength: Math.max(1, value.maxExpandedLength ?? 100000),
14032
- maxEntityCount: Math.max(1, value.maxEntityCount ?? 100),
14031
+ maxEntityCount: Math.max(1, value.maxEntityCount ?? 1000),
14033
14032
  allowedTags: value.allowedTags ?? null,
14034
14033
  tagFilter: value.tagFilter ?? null
14035
14034
  };
@@ -14062,6 +14061,7 @@
14062
14061
  for (const { value, name } of propertyNameOptions)if (value) validatePropertyName(value, name);
14063
14062
  if (null === built.onDangerousProperty) built.onDangerousProperty = defaultOnDangerousProperty;
14064
14063
  built.processEntities = normalizeProcessEntities(built.processEntities);
14064
+ built.unpairedTagsSet = new Set(built.unpairedTags);
14065
14065
  if (built.stopNodes && Array.isArray(built.stopNodes)) built.stopNodes = built.stopNodes.map((node)=>{
14066
14066
  if ('string' == typeof node && node.startsWith('*.')) return '..' + node.substring(2);
14067
14067
  return node;
@@ -14425,24 +14425,72 @@
14425
14425
  };
14426
14426
  return ()=>false;
14427
14427
  }
14428
- const MUTATING_METHODS = new Set([
14429
- 'push',
14430
- 'pop',
14431
- 'reset',
14432
- 'updateCurrent',
14433
- 'restore'
14434
- ]);
14428
+ class MatcherView {
14429
+ constructor(matcher){
14430
+ this._matcher = matcher;
14431
+ }
14432
+ get separator() {
14433
+ return this._matcher.separator;
14434
+ }
14435
+ getCurrentTag() {
14436
+ const path = this._matcher.path;
14437
+ return path.length > 0 ? path[path.length - 1].tag : void 0;
14438
+ }
14439
+ getCurrentNamespace() {
14440
+ const path = this._matcher.path;
14441
+ return path.length > 0 ? path[path.length - 1].namespace : void 0;
14442
+ }
14443
+ getAttrValue(attrName) {
14444
+ const path = this._matcher.path;
14445
+ if (0 === path.length) return;
14446
+ return path[path.length - 1].values?.[attrName];
14447
+ }
14448
+ hasAttr(attrName) {
14449
+ const path = this._matcher.path;
14450
+ if (0 === path.length) return false;
14451
+ const current = path[path.length - 1];
14452
+ return void 0 !== current.values && attrName in current.values;
14453
+ }
14454
+ getPosition() {
14455
+ const path = this._matcher.path;
14456
+ if (0 === path.length) return -1;
14457
+ return path[path.length - 1].position ?? 0;
14458
+ }
14459
+ getCounter() {
14460
+ const path = this._matcher.path;
14461
+ if (0 === path.length) return -1;
14462
+ return path[path.length - 1].counter ?? 0;
14463
+ }
14464
+ getIndex() {
14465
+ return this.getPosition();
14466
+ }
14467
+ getDepth() {
14468
+ return this._matcher.path.length;
14469
+ }
14470
+ toString(separator, includeNamespace = true) {
14471
+ return this._matcher.toString(separator, includeNamespace);
14472
+ }
14473
+ toArray() {
14474
+ return this._matcher.path.map((n)=>n.tag);
14475
+ }
14476
+ matches(expression) {
14477
+ return this._matcher.matches(expression);
14478
+ }
14479
+ matchesAny(exprSet) {
14480
+ return exprSet.matchesAny(this._matcher);
14481
+ }
14482
+ }
14435
14483
  class Matcher {
14436
14484
  constructor(options = {}){
14437
14485
  this.separator = options.separator || '.';
14438
14486
  this.path = [];
14439
14487
  this.siblingStacks = [];
14488
+ this._pathStringCache = null;
14489
+ this._view = new MatcherView(this);
14440
14490
  }
14441
14491
  push(tagName, attrValues = null, namespace = null) {
14442
- if (this.path.length > 0) {
14443
- const prev = this.path[this.path.length - 1];
14444
- prev.values = void 0;
14445
- }
14492
+ this._pathStringCache = null;
14493
+ if (this.path.length > 0) this.path[this.path.length - 1].values = void 0;
14446
14494
  const currentLevel = this.path.length;
14447
14495
  if (!this.siblingStacks[currentLevel]) this.siblingStacks[currentLevel] = new Map();
14448
14496
  const siblings = this.siblingStacks[currentLevel];
@@ -14462,6 +14510,7 @@
14462
14510
  }
14463
14511
  pop() {
14464
14512
  if (0 === this.path.length) return;
14513
+ this._pathStringCache = null;
14465
14514
  const node = this.path.pop();
14466
14515
  if (this.siblingStacks.length > this.path.length + 1) this.siblingStacks.length = this.path.length + 1;
14467
14516
  return node;
@@ -14480,8 +14529,7 @@
14480
14529
  }
14481
14530
  getAttrValue(attrName) {
14482
14531
  if (0 === this.path.length) return;
14483
- const current = this.path[this.path.length - 1];
14484
- return current.values?.[attrName];
14532
+ return this.path[this.path.length - 1].values?.[attrName];
14485
14533
  }
14486
14534
  hasAttr(attrName) {
14487
14535
  if (0 === this.path.length) return false;
@@ -14504,15 +14552,20 @@
14504
14552
  }
14505
14553
  toString(separator, includeNamespace = true) {
14506
14554
  const sep = separator || this.separator;
14507
- return this.path.map((n)=>{
14508
- if (includeNamespace && n.namespace) return `${n.namespace}:${n.tag}`;
14509
- return n.tag;
14510
- }).join(sep);
14555
+ const isDefault = sep === this.separator && true === includeNamespace;
14556
+ if (isDefault) {
14557
+ if (null !== this._pathStringCache) return this._pathStringCache;
14558
+ const result = this.path.map((n)=>n.namespace ? `${n.namespace}:${n.tag}` : n.tag).join(sep);
14559
+ this._pathStringCache = result;
14560
+ return result;
14561
+ }
14562
+ return this.path.map((n)=>includeNamespace && n.namespace ? `${n.namespace}:${n.tag}` : n.tag).join(sep);
14511
14563
  }
14512
14564
  toArray() {
14513
14565
  return this.path.map((n)=>n.tag);
14514
14566
  }
14515
14567
  reset() {
14568
+ this._pathStringCache = null;
14516
14569
  this.path = [];
14517
14570
  this.siblingStacks = [];
14518
14571
  }
@@ -14524,12 +14577,7 @@
14524
14577
  }
14525
14578
  _matchSimple(segments) {
14526
14579
  if (this.path.length !== segments.length) return false;
14527
- for(let i = 0; i < segments.length; i++){
14528
- const segment = segments[i];
14529
- const node = this.path[i];
14530
- const isCurrentNode = i === this.path.length - 1;
14531
- if (!this._matchSegment(segment, node, isCurrentNode)) return false;
14532
- }
14580
+ for(let i = 0; i < segments.length; i++)if (!this._matchSegment(segments[i], this.path[i], i === this.path.length - 1)) return false;
14533
14581
  return true;
14534
14582
  }
14535
14583
  _matchWithDeepWildcard(segments) {
@@ -14542,19 +14590,15 @@
14542
14590
  if (segIdx < 0) return true;
14543
14591
  const nextSeg = segments[segIdx];
14544
14592
  let found = false;
14545
- for(let i = pathIdx; i >= 0; i--){
14546
- const isCurrentNode = i === this.path.length - 1;
14547
- if (this._matchSegment(nextSeg, this.path[i], isCurrentNode)) {
14548
- pathIdx = i - 1;
14549
- segIdx--;
14550
- found = true;
14551
- break;
14552
- }
14593
+ for(let i = pathIdx; i >= 0; i--)if (this._matchSegment(nextSeg, this.path[i], i === this.path.length - 1)) {
14594
+ pathIdx = i - 1;
14595
+ segIdx--;
14596
+ found = true;
14597
+ break;
14553
14598
  }
14554
14599
  if (!found) return false;
14555
14600
  } else {
14556
- const isCurrentNode = pathIdx === this.path.length - 1;
14557
- if (!this._matchSegment(segment, this.path[pathIdx], isCurrentNode)) return false;
14601
+ if (!this._matchSegment(segment, this.path[pathIdx], pathIdx === this.path.length - 1)) return false;
14558
14602
  pathIdx--;
14559
14603
  segIdx--;
14560
14604
  }
@@ -14570,8 +14614,7 @@
14570
14614
  if (!isCurrentNode) return false;
14571
14615
  if (!node.values || !(segment.attrName in node.values)) return false;
14572
14616
  if (void 0 !== segment.attrValue) {
14573
- const actualValue = node.values[segment.attrName];
14574
- if (String(actualValue) !== String(segment.attrValue)) return false;
14617
+ if (String(node.values[segment.attrName]) !== String(segment.attrValue)) return false;
14575
14618
  }
14576
14619
  }
14577
14620
  if (void 0 !== segment.position) {
@@ -14580,12 +14623,13 @@
14580
14623
  if ('first' === segment.position && 0 !== counter) return false;
14581
14624
  if ('odd' === segment.position && counter % 2 !== 1) return false;
14582
14625
  if ('even' === segment.position && counter % 2 !== 0) return false;
14583
- else if ('nth' === segment.position) {
14584
- if (counter !== segment.positionValue) return false;
14585
- }
14626
+ else if ('nth' === segment.position && counter !== segment.positionValue) return false;
14586
14627
  }
14587
14628
  return true;
14588
14629
  }
14630
+ matchesAny(exprSet) {
14631
+ return exprSet.matchesAny(this);
14632
+ }
14589
14633
  snapshot() {
14590
14634
  return {
14591
14635
  path: this.path.map((node)=>({
@@ -14595,39 +14639,22 @@
14595
14639
  };
14596
14640
  }
14597
14641
  restore(snapshot) {
14642
+ this._pathStringCache = null;
14598
14643
  this.path = snapshot.path.map((node)=>({
14599
14644
  ...node
14600
14645
  }));
14601
14646
  this.siblingStacks = snapshot.siblingStacks.map((map)=>new Map(map));
14602
14647
  }
14603
14648
  readOnly() {
14604
- const self1 = this;
14605
- return new Proxy(self1, {
14606
- get (target, prop, receiver) {
14607
- if (MUTATING_METHODS.has(prop)) return ()=>{
14608
- throw new TypeError(`Cannot call '${prop}' on a read-only Matcher. Obtain a writable instance to mutate state.`);
14609
- };
14610
- const value = Reflect.get(target, prop, receiver);
14611
- if ('path' === prop || 'siblingStacks' === prop) return Object.freeze(Array.isArray(value) ? value.map((item)=>item instanceof Map ? Object.freeze(new Map(item)) : Object.freeze({
14612
- ...item
14613
- })) : value);
14614
- if ('function' == typeof value) return value.bind(target);
14615
- return value;
14616
- },
14617
- set (_target, prop) {
14618
- throw new TypeError(`Cannot set property '${String(prop)}' on a read-only Matcher.`);
14619
- },
14620
- deleteProperty (_target, prop) {
14621
- throw new TypeError(`Cannot delete property '${String(prop)}' from a read-only Matcher.`);
14622
- }
14623
- });
14649
+ return this._view;
14624
14650
  }
14625
14651
  }
14626
14652
  class Expression {
14627
- constructor(pattern, options = {}){
14653
+ constructor(pattern, options = {}, data){
14628
14654
  this.pattern = pattern;
14629
14655
  this.separator = options.separator || '.';
14630
14656
  this.segments = this._parse(pattern);
14657
+ this.data = data;
14631
14658
  this._hasDeepWildcard = this.segments.some((seg)=>'deep-wildcard' === seg.type);
14632
14659
  this._hasAttributeCondition = this.segments.some((seg)=>void 0 !== seg.attrName);
14633
14660
  this._hasPositionSelector = this.segments.some((seg)=>void 0 !== seg.position);
@@ -14729,6 +14756,71 @@
14729
14756
  return this.pattern;
14730
14757
  }
14731
14758
  }
14759
+ class ExpressionSet {
14760
+ constructor(){
14761
+ this._byDepthAndTag = new Map();
14762
+ this._wildcardByDepth = new Map();
14763
+ this._deepWildcards = [];
14764
+ this._patterns = new Set();
14765
+ this._sealed = false;
14766
+ }
14767
+ add(expression) {
14768
+ if (this._sealed) throw new TypeError('ExpressionSet is sealed. Create a new ExpressionSet to add more expressions.');
14769
+ if (this._patterns.has(expression.pattern)) return this;
14770
+ this._patterns.add(expression.pattern);
14771
+ if (expression.hasDeepWildcard()) {
14772
+ this._deepWildcards.push(expression);
14773
+ return this;
14774
+ }
14775
+ const depth = expression.length;
14776
+ const lastSeg = expression.segments[expression.segments.length - 1];
14777
+ const tag = lastSeg?.tag;
14778
+ if (tag && '*' !== tag) {
14779
+ const key = `${depth}:${tag}`;
14780
+ if (!this._byDepthAndTag.has(key)) this._byDepthAndTag.set(key, []);
14781
+ this._byDepthAndTag.get(key).push(expression);
14782
+ } else {
14783
+ if (!this._wildcardByDepth.has(depth)) this._wildcardByDepth.set(depth, []);
14784
+ this._wildcardByDepth.get(depth).push(expression);
14785
+ }
14786
+ return this;
14787
+ }
14788
+ addAll(expressions) {
14789
+ for (const expr of expressions)this.add(expr);
14790
+ return this;
14791
+ }
14792
+ has(expression) {
14793
+ return this._patterns.has(expression.pattern);
14794
+ }
14795
+ get size() {
14796
+ return this._patterns.size;
14797
+ }
14798
+ seal() {
14799
+ this._sealed = true;
14800
+ return this;
14801
+ }
14802
+ get isSealed() {
14803
+ return this._sealed;
14804
+ }
14805
+ matchesAny(matcher) {
14806
+ return null !== this.findMatch(matcher);
14807
+ }
14808
+ findMatch(matcher) {
14809
+ const depth = matcher.getDepth();
14810
+ const tag = matcher.getCurrentTag();
14811
+ const exactKey = `${depth}:${tag}`;
14812
+ const exactBucket = this._byDepthAndTag.get(exactKey);
14813
+ if (exactBucket) {
14814
+ for(let i = 0; i < exactBucket.length; i++)if (matcher.matches(exactBucket[i])) return exactBucket[i];
14815
+ }
14816
+ const wildcardBucket = this._wildcardByDepth.get(depth);
14817
+ if (wildcardBucket) {
14818
+ for(let i = 0; i < wildcardBucket.length; i++)if (matcher.matches(wildcardBucket[i])) return wildcardBucket[i];
14819
+ }
14820
+ for(let i = 0; i < this._deepWildcards.length; i++)if (matcher.matches(this._deepWildcards[i])) return this._deepWildcards[i];
14821
+ return null;
14822
+ }
14823
+ }
14732
14824
  function extractRawAttributes(prefixedAttrs, options) {
14733
14825
  if (!prefixedAttrs) return {};
14734
14826
  const attrs = options.attributesGroupName ? prefixedAttrs[options.attributesGroupName] : prefixedAttrs;
@@ -14834,13 +14926,15 @@
14834
14926
  this.matcher = new Matcher();
14835
14927
  this.readonlyMatcher = this.matcher.readOnly();
14836
14928
  this.isCurrentNodeStopNode = false;
14837
- if (this.options.stopNodes && this.options.stopNodes.length > 0) {
14838
- this.stopNodeExpressions = [];
14839
- for(let i = 0; i < this.options.stopNodes.length; i++){
14840
- const stopNodeExp = this.options.stopNodes[i];
14841
- if ('string' == typeof stopNodeExp) this.stopNodeExpressions.push(new Expression(stopNodeExp));
14842
- else if (stopNodeExp instanceof Expression) this.stopNodeExpressions.push(stopNodeExp);
14929
+ this.stopNodeExpressionsSet = new ExpressionSet();
14930
+ const stopNodesOpts = this.options.stopNodes;
14931
+ if (stopNodesOpts && stopNodesOpts.length > 0) {
14932
+ for(let i = 0; i < stopNodesOpts.length; i++){
14933
+ const stopNodeExp = stopNodesOpts[i];
14934
+ if ('string' == typeof stopNodeExp) this.stopNodeExpressionsSet.add(new Expression(stopNodeExp));
14935
+ else if (stopNodeExp instanceof Expression) this.stopNodeExpressionsSet.add(stopNodeExp);
14843
14936
  }
14937
+ this.stopNodeExpressionsSet.seal();
14844
14938
  }
14845
14939
  }
14846
14940
  }
@@ -14856,18 +14950,19 @@
14856
14950
  }
14857
14951
  }
14858
14952
  function parseTextData(val, tagName, jPath, dontTrim, hasAttributes, isLeafNode, escapeEntities) {
14953
+ const options = this.options;
14859
14954
  if (void 0 !== val) {
14860
- if (this.options.trimValues && !dontTrim) val = val.trim();
14955
+ if (options.trimValues && !dontTrim) val = val.trim();
14861
14956
  if (val.length > 0) {
14862
14957
  if (!escapeEntities) val = this.replaceEntitiesValue(val, tagName, jPath);
14863
- const jPathOrMatcher = this.options.jPath ? jPath.toString() : jPath;
14864
- const newval = this.options.tagValueProcessor(tagName, val, jPathOrMatcher, hasAttributes, isLeafNode);
14958
+ const jPathOrMatcher = options.jPath ? jPath.toString() : jPath;
14959
+ const newval = options.tagValueProcessor(tagName, val, jPathOrMatcher, hasAttributes, isLeafNode);
14865
14960
  if (null == newval) return val;
14866
14961
  {
14867
14962
  if (typeof newval !== typeof val || newval !== val) return newval;
14868
- if (this.options.trimValues) return parseValue(val, this.options.parseTagValue, this.options.numberParseOptions);
14963
+ if (options.trimValues) return parseValue(val, options.parseTagValue, options.numberParseOptions);
14869
14964
  const trimmedVal = val.trim();
14870
- if (trimmedVal === val) return parseValue(val, this.options.parseTagValue, this.options.numberParseOptions);
14965
+ if (trimmedVal === val) return parseValue(val, options.parseTagValue, options.numberParseOptions);
14871
14966
  return val;
14872
14967
  }
14873
14968
  }
@@ -14884,46 +14979,53 @@
14884
14979
  }
14885
14980
  const attrsRegx = new RegExp('([^\\s=]+)\\s*(=\\s*([\'"])([\\s\\S]*?)\\3)?', 'gm');
14886
14981
  function buildAttributesMap(attrStr, jPath, tagName) {
14887
- if (true !== this.options.ignoreAttributes && 'string' == typeof attrStr) {
14982
+ const options = this.options;
14983
+ if (true !== options.ignoreAttributes && 'string' == typeof attrStr) {
14888
14984
  const matches = getAllMatches(attrStr, attrsRegx);
14889
14985
  const len = matches.length;
14890
14986
  const attrs = {};
14987
+ const processedVals = new Array(len);
14988
+ let hasRawAttrs = false;
14891
14989
  const rawAttrsForMatcher = {};
14892
14990
  for(let i = 0; i < len; i++){
14893
14991
  const attrName = this.resolveNameSpace(matches[i][1]);
14894
14992
  const oldVal = matches[i][4];
14895
14993
  if (attrName.length && void 0 !== oldVal) {
14896
- let parsedVal = oldVal;
14897
- if (this.options.trimValues) parsedVal = parsedVal.trim();
14898
- parsedVal = this.replaceEntitiesValue(parsedVal, tagName, this.readonlyMatcher);
14899
- rawAttrsForMatcher[attrName] = parsedVal;
14994
+ let val = oldVal;
14995
+ if (options.trimValues) val = val.trim();
14996
+ val = this.replaceEntitiesValue(val, tagName, this.readonlyMatcher);
14997
+ processedVals[i] = val;
14998
+ rawAttrsForMatcher[attrName] = val;
14999
+ hasRawAttrs = true;
14900
15000
  }
14901
15001
  }
14902
- if (Object.keys(rawAttrsForMatcher).length > 0 && 'object' == typeof jPath && jPath.updateCurrent) jPath.updateCurrent(rawAttrsForMatcher);
15002
+ if (hasRawAttrs && 'object' == typeof jPath && jPath.updateCurrent) jPath.updateCurrent(rawAttrsForMatcher);
15003
+ const jPathStr = options.jPath ? jPath.toString() : this.readonlyMatcher;
15004
+ let hasAttrs = false;
14903
15005
  for(let i = 0; i < len; i++){
14904
15006
  const attrName = this.resolveNameSpace(matches[i][1]);
14905
- const jPathStr = this.options.jPath ? jPath.toString() : this.readonlyMatcher;
14906
15007
  if (this.ignoreAttributesFn(attrName, jPathStr)) continue;
14907
- let oldVal = matches[i][4];
14908
- let aName = this.options.attributeNamePrefix + attrName;
15008
+ let aName = options.attributeNamePrefix + attrName;
14909
15009
  if (attrName.length) {
14910
- if (this.options.transformAttributeName) aName = this.options.transformAttributeName(aName);
14911
- aName = sanitizeName(aName, this.options);
14912
- if (void 0 !== oldVal) {
14913
- if (this.options.trimValues) oldVal = oldVal.trim();
14914
- oldVal = this.replaceEntitiesValue(oldVal, tagName, this.readonlyMatcher);
14915
- const jPathOrMatcher = this.options.jPath ? jPath.toString() : this.readonlyMatcher;
14916
- const newVal = this.options.attributeValueProcessor(attrName, oldVal, jPathOrMatcher);
15010
+ if (options.transformAttributeName) aName = options.transformAttributeName(aName);
15011
+ aName = sanitizeName(aName, options);
15012
+ if (void 0 !== matches[i][4]) {
15013
+ const oldVal = processedVals[i];
15014
+ const newVal = options.attributeValueProcessor(attrName, oldVal, jPathStr);
14917
15015
  if (null == newVal) attrs[aName] = oldVal;
14918
15016
  else if (typeof newVal !== typeof oldVal || newVal !== oldVal) attrs[aName] = newVal;
14919
- else attrs[aName] = parseValue(oldVal, this.options.parseAttributeValue, this.options.numberParseOptions);
14920
- } else if (this.options.allowBooleanAttributes) attrs[aName] = true;
15017
+ else attrs[aName] = parseValue(oldVal, options.parseAttributeValue, options.numberParseOptions);
15018
+ hasAttrs = true;
15019
+ } else if (options.allowBooleanAttributes) {
15020
+ attrs[aName] = true;
15021
+ hasAttrs = true;
15022
+ }
14921
15023
  }
14922
15024
  }
14923
- if (!Object.keys(attrs).length) return;
14924
- if (this.options.attributesGroupName) {
15025
+ if (!hasAttrs) return;
15026
+ if (options.attributesGroupName) {
14925
15027
  const attrCollection = {};
14926
- attrCollection[this.options.attributesGroupName] = attrs;
15028
+ attrCollection[options.attributesGroupName] = attrs;
14927
15029
  return attrCollection;
14928
15030
  }
14929
15031
  return attrs;
@@ -14937,155 +15039,163 @@
14937
15039
  this.matcher.reset();
14938
15040
  this.entityExpansionCount = 0;
14939
15041
  this.currentExpandedLength = 0;
14940
- const docTypeReader = new DocTypeReader(this.options.processEntities);
14941
- for(let i = 0; i < xmlData.length; i++){
15042
+ this.docTypeEntitiesKeys = [];
15043
+ this.lastEntitiesKeys = Object.keys(this.lastEntities);
15044
+ this.htmlEntitiesKeys = this.options.htmlEntities ? Object.keys(this.htmlEntities) : [];
15045
+ const options = this.options;
15046
+ const docTypeReader = new DocTypeReader(options.processEntities);
15047
+ const xmlLen = xmlData.length;
15048
+ for(let i = 0; i < xmlLen; i++){
14942
15049
  const ch = xmlData[i];
14943
- if ('<' === ch) if ('/' === xmlData[i + 1]) {
14944
- const closeIndex = findClosingIndex(xmlData, ">", i, "Closing Tag is not closed.");
14945
- let tagName = xmlData.substring(i + 2, closeIndex).trim();
14946
- if (this.options.removeNSPrefix) {
14947
- const colonIndex = tagName.indexOf(":");
14948
- if (-1 !== colonIndex) tagName = tagName.substr(colonIndex + 1);
14949
- }
14950
- tagName = transformTagName(this.options.transformTagName, tagName, "", this.options).tagName;
14951
- if (currentNode) textData = this.saveTextToParentTag(textData, currentNode, this.readonlyMatcher);
14952
- const lastTagName = this.matcher.getCurrentTag();
14953
- if (tagName && -1 !== this.options.unpairedTags.indexOf(tagName)) throw new Error(`Unpaired tag can not be used as closing tag: </${tagName}>`);
14954
- if (lastTagName && -1 !== this.options.unpairedTags.indexOf(lastTagName)) {
15050
+ if ('<' === ch) {
15051
+ const c1 = xmlData.charCodeAt(i + 1);
15052
+ if (47 === c1) {
15053
+ const closeIndex = findClosingIndex(xmlData, ">", i, "Closing Tag is not closed.");
15054
+ let tagName = xmlData.substring(i + 2, closeIndex).trim();
15055
+ if (options.removeNSPrefix) {
15056
+ const colonIndex = tagName.indexOf(":");
15057
+ if (-1 !== colonIndex) tagName = tagName.substr(colonIndex + 1);
15058
+ }
15059
+ tagName = transformTagName(options.transformTagName, tagName, "", options).tagName;
15060
+ if (currentNode) textData = this.saveTextToParentTag(textData, currentNode, this.readonlyMatcher);
15061
+ const lastTagName = this.matcher.getCurrentTag();
15062
+ if (tagName && options.unpairedTagsSet.has(tagName)) throw new Error(`Unpaired tag can not be used as closing tag: </${tagName}>`);
15063
+ if (lastTagName && options.unpairedTagsSet.has(lastTagName)) {
15064
+ this.matcher.pop();
15065
+ this.tagsNodeStack.pop();
15066
+ }
14955
15067
  this.matcher.pop();
14956
- this.tagsNodeStack.pop();
14957
- }
14958
- this.matcher.pop();
14959
- this.isCurrentNodeStopNode = false;
14960
- currentNode = this.tagsNodeStack.pop();
14961
- textData = "";
14962
- i = closeIndex;
14963
- } else if ('?' === xmlData[i + 1]) {
14964
- let tagData = readTagExp(xmlData, i, false, "?>");
14965
- if (!tagData) throw new Error("Pi Tag is not closed.");
14966
- textData = this.saveTextToParentTag(textData, currentNode, this.readonlyMatcher);
14967
- if (this.options.ignoreDeclaration && "?xml" === tagData.tagName || this.options.ignorePiTags) ;
14968
- else {
14969
- const childNode = new XmlNode(tagData.tagName);
14970
- childNode.add(this.options.textNodeName, "");
14971
- if (tagData.tagName !== tagData.tagExp && tagData.attrExpPresent) childNode[":@"] = this.buildAttributesMap(tagData.tagExp, this.matcher, tagData.tagName);
14972
- this.addChild(currentNode, childNode, this.readonlyMatcher, i);
14973
- }
14974
- i = tagData.closeIndex + 1;
14975
- } else if ('!--' === xmlData.substr(i + 1, 3)) {
14976
- const endIndex = findClosingIndex(xmlData, "-->", i + 4, "Comment is not closed.");
14977
- if (this.options.commentPropName) {
14978
- const comment = xmlData.substring(i + 4, endIndex - 2);
15068
+ this.isCurrentNodeStopNode = false;
15069
+ currentNode = this.tagsNodeStack.pop();
15070
+ textData = "";
15071
+ i = closeIndex;
15072
+ } else if (63 === c1) {
15073
+ let tagData = readTagExp(xmlData, i, false, "?>");
15074
+ if (!tagData) throw new Error("Pi Tag is not closed.");
14979
15075
  textData = this.saveTextToParentTag(textData, currentNode, this.readonlyMatcher);
14980
- currentNode.add(this.options.commentPropName, [
15076
+ if (options.ignoreDeclaration && "?xml" === tagData.tagName || options.ignorePiTags) ;
15077
+ else {
15078
+ const childNode = new XmlNode(tagData.tagName);
15079
+ childNode.add(options.textNodeName, "");
15080
+ if (tagData.tagName !== tagData.tagExp && tagData.attrExpPresent) childNode[":@"] = this.buildAttributesMap(tagData.tagExp, this.matcher, tagData.tagName);
15081
+ this.addChild(currentNode, childNode, this.readonlyMatcher, i);
15082
+ }
15083
+ i = tagData.closeIndex + 1;
15084
+ } else if (33 === c1 && 45 === xmlData.charCodeAt(i + 2) && 45 === xmlData.charCodeAt(i + 3)) {
15085
+ const endIndex = findClosingIndex(xmlData, "-->", i + 4, "Comment is not closed.");
15086
+ if (options.commentPropName) {
15087
+ const comment = xmlData.substring(i + 4, endIndex - 2);
15088
+ textData = this.saveTextToParentTag(textData, currentNode, this.readonlyMatcher);
15089
+ currentNode.add(options.commentPropName, [
15090
+ {
15091
+ [options.textNodeName]: comment
15092
+ }
15093
+ ]);
15094
+ }
15095
+ i = endIndex;
15096
+ } else if (33 === c1 && 68 === xmlData.charCodeAt(i + 2)) {
15097
+ const result = docTypeReader.readDocType(xmlData, i);
15098
+ this.docTypeEntities = result.entities;
15099
+ this.docTypeEntitiesKeys = Object.keys(this.docTypeEntities) || [];
15100
+ i = result.i;
15101
+ } else if (33 === c1 && 91 === xmlData.charCodeAt(i + 2)) {
15102
+ const closeIndex = findClosingIndex(xmlData, "]]>", i, "CDATA is not closed.") - 2;
15103
+ const tagExp = xmlData.substring(i + 9, closeIndex);
15104
+ textData = this.saveTextToParentTag(textData, currentNode, this.readonlyMatcher);
15105
+ let val = this.parseTextData(tagExp, currentNode.tagname, this.readonlyMatcher, true, false, true, true);
15106
+ if (void 0 == val) val = "";
15107
+ if (options.cdataPropName) currentNode.add(options.cdataPropName, [
14981
15108
  {
14982
- [this.options.textNodeName]: comment
15109
+ [options.textNodeName]: tagExp
14983
15110
  }
14984
15111
  ]);
14985
- }
14986
- i = endIndex;
14987
- } else if ('!D' === xmlData.substr(i + 1, 2)) {
14988
- const result = docTypeReader.readDocType(xmlData, i);
14989
- this.docTypeEntities = result.entities;
14990
- i = result.i;
14991
- } else if ('![' === xmlData.substr(i + 1, 2)) {
14992
- const closeIndex = findClosingIndex(xmlData, "]]>", i, "CDATA is not closed.") - 2;
14993
- const tagExp = xmlData.substring(i + 9, closeIndex);
14994
- textData = this.saveTextToParentTag(textData, currentNode, this.readonlyMatcher);
14995
- let val = this.parseTextData(tagExp, currentNode.tagname, this.readonlyMatcher, true, false, true, true);
14996
- if (void 0 == val) val = "";
14997
- if (this.options.cdataPropName) currentNode.add(this.options.cdataPropName, [
14998
- {
14999
- [this.options.textNodeName]: tagExp
15112
+ else currentNode.add(options.textNodeName, val);
15113
+ i = closeIndex + 2;
15114
+ } else {
15115
+ let result = readTagExp(xmlData, i, options.removeNSPrefix);
15116
+ if (!result) {
15117
+ const context = xmlData.substring(Math.max(0, i - 50), Math.min(xmlLen, i + 50));
15118
+ throw new Error(`readTagExp returned undefined at position ${i}. Context: "${context}"`);
15000
15119
  }
15001
- ]);
15002
- else currentNode.add(this.options.textNodeName, val);
15003
- i = closeIndex + 2;
15004
- } else {
15005
- let result = readTagExp(xmlData, i, this.options.removeNSPrefix);
15006
- if (!result) {
15007
- const context = xmlData.substring(Math.max(0, i - 50), Math.min(xmlData.length, i + 50));
15008
- throw new Error(`readTagExp returned undefined at position ${i}. Context: "${context}"`);
15009
- }
15010
- let tagName = result.tagName;
15011
- const rawTagName = result.rawTagName;
15012
- let tagExp = result.tagExp;
15013
- let attrExpPresent = result.attrExpPresent;
15014
- let closeIndex = result.closeIndex;
15015
- ({ tagName, tagExp } = transformTagName(this.options.transformTagName, tagName, tagExp, this.options));
15016
- if (this.options.strictReservedNames && (tagName === this.options.commentPropName || tagName === this.options.cdataPropName || tagName === this.options.textNodeName || tagName === this.options.attributesGroupName)) throw new Error(`Invalid tag name: ${tagName}`);
15017
- if (currentNode && textData) {
15018
- if ('!xml' !== currentNode.tagname) textData = this.saveTextToParentTag(textData, currentNode, this.readonlyMatcher, false);
15019
- }
15020
- const lastTag = currentNode;
15021
- if (lastTag && -1 !== this.options.unpairedTags.indexOf(lastTag.tagname)) {
15022
- currentNode = this.tagsNodeStack.pop();
15023
- this.matcher.pop();
15024
- }
15025
- let isSelfClosing = false;
15026
- if (tagExp.length > 0 && tagExp.lastIndexOf("/") === tagExp.length - 1) {
15027
- isSelfClosing = true;
15028
- if ("/" === tagName[tagName.length - 1]) {
15029
- tagName = tagName.substr(0, tagName.length - 1);
15030
- tagExp = tagName;
15031
- } else tagExp = tagExp.substr(0, tagExp.length - 1);
15032
- attrExpPresent = tagName !== tagExp;
15033
- }
15034
- let prefixedAttrs = null;
15035
- let namespace;
15036
- namespace = extractNamespace(rawTagName);
15037
- if (tagName !== xmlObj.tagname) this.matcher.push(tagName, {}, namespace);
15038
- if (tagName !== tagExp && attrExpPresent) {
15039
- prefixedAttrs = this.buildAttributesMap(tagExp, this.matcher, tagName);
15040
- if (prefixedAttrs) extractRawAttributes(prefixedAttrs, this.options);
15041
- }
15042
- if (tagName !== xmlObj.tagname) this.isCurrentNodeStopNode = this.isItStopNode(this.stopNodeExpressions, this.matcher);
15043
- const startIndex = i;
15044
- if (this.isCurrentNodeStopNode) {
15045
- let tagContent = "";
15046
- if (isSelfClosing) i = result.closeIndex;
15047
- else if (-1 !== this.options.unpairedTags.indexOf(tagName)) i = result.closeIndex;
15048
- else {
15049
- const result = this.readStopNodeData(xmlData, rawTagName, closeIndex + 1);
15050
- if (!result) throw new Error(`Unexpected end of ${rawTagName}`);
15051
- i = result.i;
15052
- tagContent = result.tagContent;
15120
+ let tagName = result.tagName;
15121
+ const rawTagName = result.rawTagName;
15122
+ let tagExp = result.tagExp;
15123
+ let attrExpPresent = result.attrExpPresent;
15124
+ let closeIndex = result.closeIndex;
15125
+ ({ tagName, tagExp } = transformTagName(options.transformTagName, tagName, tagExp, options));
15126
+ if (options.strictReservedNames && (tagName === options.commentPropName || tagName === options.cdataPropName || tagName === options.textNodeName || tagName === options.attributesGroupName)) throw new Error(`Invalid tag name: ${tagName}`);
15127
+ if (currentNode && textData) {
15128
+ if ('!xml' !== currentNode.tagname) textData = this.saveTextToParentTag(textData, currentNode, this.readonlyMatcher, false);
15053
15129
  }
15054
- const childNode = new XmlNode(tagName);
15055
- if (prefixedAttrs) childNode[":@"] = prefixedAttrs;
15056
- childNode.add(this.options.textNodeName, tagContent);
15057
- this.matcher.pop();
15058
- this.isCurrentNodeStopNode = false;
15059
- this.addChild(currentNode, childNode, this.readonlyMatcher, startIndex);
15060
- } else {
15061
- if (isSelfClosing) {
15062
- ({ tagName, tagExp } = transformTagName(this.options.transformTagName, tagName, tagExp, this.options));
15063
- const childNode = new XmlNode(tagName);
15064
- if (prefixedAttrs) childNode[":@"] = prefixedAttrs;
15065
- this.addChild(currentNode, childNode, this.readonlyMatcher, startIndex);
15130
+ const lastTag = currentNode;
15131
+ if (lastTag && options.unpairedTagsSet.has(lastTag.tagname)) {
15132
+ currentNode = this.tagsNodeStack.pop();
15066
15133
  this.matcher.pop();
15067
- this.isCurrentNodeStopNode = false;
15068
- } else if (-1 !== this.options.unpairedTags.indexOf(tagName)) {
15134
+ }
15135
+ let isSelfClosing = false;
15136
+ if (tagExp.length > 0 && tagExp.lastIndexOf("/") === tagExp.length - 1) {
15137
+ isSelfClosing = true;
15138
+ if ("/" === tagName[tagName.length - 1]) {
15139
+ tagName = tagName.substr(0, tagName.length - 1);
15140
+ tagExp = tagName;
15141
+ } else tagExp = tagExp.substr(0, tagExp.length - 1);
15142
+ attrExpPresent = tagName !== tagExp;
15143
+ }
15144
+ let prefixedAttrs = null;
15145
+ let namespace;
15146
+ namespace = extractNamespace(rawTagName);
15147
+ if (tagName !== xmlObj.tagname) this.matcher.push(tagName, {}, namespace);
15148
+ if (tagName !== tagExp && attrExpPresent) {
15149
+ prefixedAttrs = this.buildAttributesMap(tagExp, this.matcher, tagName);
15150
+ if (prefixedAttrs) extractRawAttributes(prefixedAttrs, options);
15151
+ }
15152
+ if (tagName !== xmlObj.tagname) this.isCurrentNodeStopNode = this.isItStopNode();
15153
+ const startIndex = i;
15154
+ if (this.isCurrentNodeStopNode) {
15155
+ let tagContent = "";
15156
+ if (isSelfClosing) i = result.closeIndex;
15157
+ else if (options.unpairedTagsSet.has(tagName)) i = result.closeIndex;
15158
+ else {
15159
+ const result = this.readStopNodeData(xmlData, rawTagName, closeIndex + 1);
15160
+ if (!result) throw new Error(`Unexpected end of ${rawTagName}`);
15161
+ i = result.i;
15162
+ tagContent = result.tagContent;
15163
+ }
15069
15164
  const childNode = new XmlNode(tagName);
15070
15165
  if (prefixedAttrs) childNode[":@"] = prefixedAttrs;
15071
- this.addChild(currentNode, childNode, this.readonlyMatcher, startIndex);
15166
+ childNode.add(options.textNodeName, tagContent);
15072
15167
  this.matcher.pop();
15073
15168
  this.isCurrentNodeStopNode = false;
15074
- i = result.closeIndex;
15075
- continue;
15076
- } else {
15077
- const childNode = new XmlNode(tagName);
15078
- if (this.tagsNodeStack.length > this.options.maxNestedTags) throw new Error("Maximum nested tags exceeded");
15079
- this.tagsNodeStack.push(currentNode);
15080
- if (prefixedAttrs) childNode[":@"] = prefixedAttrs;
15081
15169
  this.addChild(currentNode, childNode, this.readonlyMatcher, startIndex);
15082
- currentNode = childNode;
15170
+ } else {
15171
+ if (isSelfClosing) {
15172
+ ({ tagName, tagExp } = transformTagName(options.transformTagName, tagName, tagExp, options));
15173
+ const childNode = new XmlNode(tagName);
15174
+ if (prefixedAttrs) childNode[":@"] = prefixedAttrs;
15175
+ this.addChild(currentNode, childNode, this.readonlyMatcher, startIndex);
15176
+ this.matcher.pop();
15177
+ this.isCurrentNodeStopNode = false;
15178
+ } else if (options.unpairedTagsSet.has(tagName)) {
15179
+ const childNode = new XmlNode(tagName);
15180
+ if (prefixedAttrs) childNode[":@"] = prefixedAttrs;
15181
+ this.addChild(currentNode, childNode, this.readonlyMatcher, startIndex);
15182
+ this.matcher.pop();
15183
+ this.isCurrentNodeStopNode = false;
15184
+ i = result.closeIndex;
15185
+ continue;
15186
+ } else {
15187
+ const childNode = new XmlNode(tagName);
15188
+ if (this.tagsNodeStack.length > options.maxNestedTags) throw new Error("Maximum nested tags exceeded");
15189
+ this.tagsNodeStack.push(currentNode);
15190
+ if (prefixedAttrs) childNode[":@"] = prefixedAttrs;
15191
+ this.addChild(currentNode, childNode, this.readonlyMatcher, startIndex);
15192
+ currentNode = childNode;
15193
+ }
15194
+ textData = "";
15195
+ i = closeIndex;
15083
15196
  }
15084
- textData = "";
15085
- i = closeIndex;
15086
15197
  }
15087
- }
15088
- else textData += xmlData[i];
15198
+ } else textData += xmlData[i];
15089
15199
  }
15090
15200
  return xmlObj.child;
15091
15201
  };
@@ -15111,7 +15221,7 @@
15111
15221
  const jPathOrMatcher = this.options.jPath ? jPath.toString() : jPath;
15112
15222
  if (!entityConfig.tagFilter(tagName, jPathOrMatcher)) return val;
15113
15223
  }
15114
- for (const entityName of Object.keys(this.docTypeEntities)){
15224
+ for (const entityName of this.docTypeEntitiesKeys){
15115
15225
  const entity = this.docTypeEntities[entityName];
15116
15226
  const matches = val.match(entity.regx);
15117
15227
  if (matches) {
@@ -15125,7 +15235,8 @@
15125
15235
  }
15126
15236
  }
15127
15237
  }
15128
- for (const entityName of Object.keys(this.lastEntities)){
15238
+ if (-1 === val.indexOf('&')) return val;
15239
+ for (const entityName of this.lastEntitiesKeys){
15129
15240
  const entity = this.lastEntities[entityName];
15130
15241
  const matches = val.match(entity.regex);
15131
15242
  if (matches) {
@@ -15135,7 +15246,7 @@
15135
15246
  val = val.replace(entity.regex, entity.val);
15136
15247
  }
15137
15248
  if (-1 === val.indexOf('&')) return val;
15138
- if (this.options.htmlEntities) for (const entityName of Object.keys(this.htmlEntities)){
15249
+ for (const entityName of this.htmlEntitiesKeys){
15139
15250
  const entity = this.htmlEntities[entityName];
15140
15251
  const matches = val.match(entity.regex);
15141
15252
  if (matches) {
@@ -15156,30 +15267,35 @@
15156
15267
  }
15157
15268
  return textData;
15158
15269
  }
15159
- function isItStopNode(stopNodeExpressions, matcher) {
15160
- if (!stopNodeExpressions || 0 === stopNodeExpressions.length) return false;
15161
- for(let i = 0; i < stopNodeExpressions.length; i++)if (matcher.matches(stopNodeExpressions[i])) return true;
15162
- return false;
15270
+ function isItStopNode() {
15271
+ if (0 === this.stopNodeExpressionsSet.size) return false;
15272
+ return this.matcher.matchesAny(this.stopNodeExpressionsSet);
15163
15273
  }
15164
15274
  function tagExpWithClosingIndex(xmlData, i, closingChar = ">") {
15165
- let attrBoundary;
15166
- let tagExp = "";
15167
- for(let index = i; index < xmlData.length; index++){
15168
- let ch = xmlData[index];
15275
+ let attrBoundary = 0;
15276
+ const chars = [];
15277
+ const len = xmlData.length;
15278
+ const closeCode0 = closingChar.charCodeAt(0);
15279
+ const closeCode1 = closingChar.length > 1 ? closingChar.charCodeAt(1) : -1;
15280
+ for(let index = i; index < len; index++){
15281
+ const code = xmlData.charCodeAt(index);
15169
15282
  if (attrBoundary) {
15170
- if (ch === attrBoundary) attrBoundary = "";
15171
- } else if ('"' === ch || "'" === ch) attrBoundary = ch;
15172
- else if (ch === closingChar[0]) {
15173
- if (!closingChar[1]) return {
15174
- data: tagExp,
15175
- index: index
15283
+ if (code === attrBoundary) attrBoundary = 0;
15284
+ } else if (34 === code || 39 === code) attrBoundary = code;
15285
+ else if (code === closeCode0) {
15286
+ if (-1 === closeCode1) return {
15287
+ data: String.fromCharCode(...chars),
15288
+ index
15176
15289
  };
15177
- else if (xmlData[index + 1] === closingChar[1]) return {
15178
- data: tagExp,
15179
- index: index
15290
+ else if (xmlData.charCodeAt(index + 1) === closeCode1) return {
15291
+ data: String.fromCharCode(...chars),
15292
+ index
15180
15293
  };
15181
- } else if ('\t' === ch) ch = " ";
15182
- tagExp += ch;
15294
+ } else if (9 === code) {
15295
+ chars.push(32);
15296
+ continue;
15297
+ }
15298
+ chars.push(code);
15183
15299
  }
15184
15300
  }
15185
15301
  function findClosingIndex(xmlData, str, i, errMsg) {
@@ -15187,6 +15303,11 @@
15187
15303
  if (-1 !== closingIndex) return closingIndex + str.length - 1;
15188
15304
  throw new Error(errMsg);
15189
15305
  }
15306
+ function findClosingChar(xmlData, char, i, errMsg) {
15307
+ const closingIndex = xmlData.indexOf(char, i);
15308
+ if (-1 === closingIndex) throw new Error(errMsg);
15309
+ return closingIndex;
15310
+ }
15190
15311
  function readTagExp(xmlData, i, removeNSPrefix, closingChar = ">") {
15191
15312
  const result = tagExpWithClosingIndex(xmlData, i + 1, closingChar);
15192
15313
  if (!result) return;
@@ -15218,32 +15339,36 @@
15218
15339
  function readStopNodeData(xmlData, tagName, i) {
15219
15340
  const startIndex = i;
15220
15341
  let openTagCount = 1;
15221
- for(; i < xmlData.length; i++)if ("<" === xmlData[i]) if ("/" === xmlData[i + 1]) {
15222
- const closeIndex = findClosingIndex(xmlData, ">", i, `${tagName} is not closed`);
15223
- let closeTagName = xmlData.substring(i + 2, closeIndex).trim();
15224
- if (closeTagName === tagName) {
15225
- openTagCount--;
15226
- if (0 === openTagCount) return {
15227
- tagContent: xmlData.substring(startIndex, i),
15228
- i: closeIndex
15229
- };
15230
- }
15231
- i = closeIndex;
15232
- } else if ('?' === xmlData[i + 1]) {
15233
- const closeIndex = findClosingIndex(xmlData, "?>", i + 1, "StopNode is not closed.");
15234
- i = closeIndex;
15235
- } else if ('!--' === xmlData.substr(i + 1, 3)) {
15236
- const closeIndex = findClosingIndex(xmlData, "-->", i + 3, "StopNode is not closed.");
15237
- i = closeIndex;
15238
- } else if ('![' === xmlData.substr(i + 1, 2)) {
15239
- const closeIndex = findClosingIndex(xmlData, "]]>", i, "StopNode is not closed.") - 2;
15240
- i = closeIndex;
15241
- } else {
15242
- const tagData = readTagExp(xmlData, i, '>');
15243
- if (tagData) {
15244
- const openTagName = tagData && tagData.tagName;
15245
- if (openTagName === tagName && "/" !== tagData.tagExp[tagData.tagExp.length - 1]) openTagCount++;
15246
- i = tagData.closeIndex;
15342
+ const xmllen = xmlData.length;
15343
+ for(; i < xmllen; i++)if ("<" === xmlData[i]) {
15344
+ const c1 = xmlData.charCodeAt(i + 1);
15345
+ if (47 === c1) {
15346
+ const closeIndex = findClosingChar(xmlData, ">", i, `${tagName} is not closed`);
15347
+ let closeTagName = xmlData.substring(i + 2, closeIndex).trim();
15348
+ if (closeTagName === tagName) {
15349
+ openTagCount--;
15350
+ if (0 === openTagCount) return {
15351
+ tagContent: xmlData.substring(startIndex, i),
15352
+ i: closeIndex
15353
+ };
15354
+ }
15355
+ i = closeIndex;
15356
+ } else if (63 === c1) {
15357
+ const closeIndex = findClosingIndex(xmlData, "?>", i + 1, "StopNode is not closed.");
15358
+ i = closeIndex;
15359
+ } else if (33 === c1 && 45 === xmlData.charCodeAt(i + 2) && 45 === xmlData.charCodeAt(i + 3)) {
15360
+ const closeIndex = findClosingIndex(xmlData, "-->", i + 3, "StopNode is not closed.");
15361
+ i = closeIndex;
15362
+ } else if (33 === c1 && 91 === xmlData.charCodeAt(i + 2)) {
15363
+ const closeIndex = findClosingIndex(xmlData, "]]>", i, "StopNode is not closed.") - 2;
15364
+ i = closeIndex;
15365
+ } else {
15366
+ const tagData = readTagExp(xmlData, i, '>');
15367
+ if (tagData) {
15368
+ const openTagName = tagData && tagData.tagName;
15369
+ if (openTagName === tagName && "/" !== tagData.tagExp[tagData.tagExp.length - 1]) openTagCount++;
15370
+ i = tagData.closeIndex;
15371
+ }
15247
15372
  }
15248
15373
  }
15249
15374
  }
@@ -21061,7 +21186,7 @@
21061
21186
  function timestamp_timestampMs(timestamp) {
21062
21187
  return 1000 * Number(timestamp.seconds) + Math.round(timestamp.nanos / 1000000);
21063
21188
  }
21064
- var package_namespaceObject = JSON.parse('{"UU":"@dan-uni/dan-any","rE":"1.3.9","TB":"https://github.com/ani-uni/danuni/tree/master/packages/dan-any#readme"}');
21189
+ var package_namespaceObject = JSON.parse('{"UU":"@dan-uni/dan-any","rE":"1.4.0","TB":"https://github.com/ani-uni/danuni/tree/master/packages/dan-any#readme"}');
21065
21190
  const color_pad = (s)=>s.length < 2 ? `0${s}` : s;
21066
21191
  const decimalToHex = (n)=>color_pad(n.toString(16));
21067
21192
  const isDarkColor = ({ r, g, b })=>0.299 * r + 0.587 * g + 0.114 * b < 0x30;
@@ -23775,6 +23900,9 @@
23775
23900
  platform: platform.PlatformVideoSource.Bilibili,
23776
23901
  extra: {
23777
23902
  bili: {
23903
+ dmid: args.id,
23904
+ attr: args.attr,
23905
+ mid: args.mid,
23778
23906
  command: args
23779
23907
  }
23780
23908
  }
@@ -25000,6 +25128,8 @@
25000
25128
  const ok = this.dans.every((d)=>d.senderID.endsWith(`@${platform.PlatformVideoSource.Bilibili}`));
25001
25129
  if (!ok) throw new Error('存在其他来源的senderID,请关闭该功能再试!');
25002
25130
  }
25131
+ let ds = this.dans.map((dan)=>dan.toBiliXML(options));
25132
+ if (options?.skipBiliCommand) ds = ds.filter((d)=>null !== d);
25003
25133
  const builder = new json2xml({
25004
25134
  ignoreAttributes: false
25005
25135
  });
@@ -25020,7 +25150,7 @@
25020
25150
  ...DanUniConvertTipTemplate,
25021
25151
  data: this.getShared('SOID')
25022
25152
  },
25023
- d: this.dans.map((dan)=>dan.toBiliXML(options))
25153
+ d: ds
25024
25154
  }
25025
25155
  });
25026
25156
  }
@@ -25184,7 +25314,7 @@
25184
25314
  function main(that) {
25185
25315
  that.dans.forEach((d)=>{
25186
25316
  if (d.platform !== platform.PlatformVideoSource.Bilibili) throw new Error('bili-dedupe: 仅支持B站(主站)的弹幕');
25187
- if (!d.extra.bili?.dmid) throw new Error('bili-dedupe: 弹幕缺少bili extra dmid字段');
25317
+ if (!d.extra.bili?.dmid && !d.extra.bili?.command?.id) throw new Error('bili-dedupe: 弹幕缺少bili extra dmid字段');
25188
25318
  });
25189
25319
  const map = new Map();
25190
25320
  that.dans.forEach((d)=>map.set(d.extra.bili.dmid, d));