@dan-uni/dan-any 1.3.9 → 1.4.1

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)) {
@@ -14049,10 +14048,10 @@
14049
14048
  if ('object' == typeof value && null !== value) return {
14050
14049
  enabled: false !== value.enabled,
14051
14050
  maxEntitySize: Math.max(1, value.maxEntitySize ?? 10000),
14052
- maxExpansionDepth: Math.max(1, value.maxExpansionDepth ?? 10),
14053
- maxTotalExpansions: Math.max(1, value.maxTotalExpansions ?? 1000),
14051
+ maxExpansionDepth: Math.max(1, value.maxExpansionDepth ?? 10000),
14052
+ maxTotalExpansions: Math.max(1, value.maxTotalExpansions ?? 1 / 0),
14054
14053
  maxExpandedLength: Math.max(1, value.maxExpandedLength ?? 100000),
14055
- maxEntityCount: Math.max(1, value.maxEntityCount ?? 100),
14054
+ maxEntityCount: Math.max(1, value.maxEntityCount ?? 1000),
14056
14055
  allowedTags: value.allowedTags ?? null,
14057
14056
  tagFilter: value.tagFilter ?? null
14058
14057
  };
@@ -14085,6 +14084,7 @@
14085
14084
  for (const { value, name } of propertyNameOptions)if (value) validatePropertyName(value, name);
14086
14085
  if (null === built.onDangerousProperty) built.onDangerousProperty = defaultOnDangerousProperty;
14087
14086
  built.processEntities = normalizeProcessEntities(built.processEntities);
14087
+ built.unpairedTagsSet = new Set(built.unpairedTags);
14088
14088
  if (built.stopNodes && Array.isArray(built.stopNodes)) built.stopNodes = built.stopNodes.map((node)=>{
14089
14089
  if ('string' == typeof node && node.startsWith('*.')) return '..' + node.substring(2);
14090
14090
  return node;
@@ -14448,24 +14448,72 @@
14448
14448
  };
14449
14449
  return ()=>false;
14450
14450
  }
14451
- const MUTATING_METHODS = new Set([
14452
- 'push',
14453
- 'pop',
14454
- 'reset',
14455
- 'updateCurrent',
14456
- 'restore'
14457
- ]);
14451
+ class MatcherView {
14452
+ constructor(matcher){
14453
+ this._matcher = matcher;
14454
+ }
14455
+ get separator() {
14456
+ return this._matcher.separator;
14457
+ }
14458
+ getCurrentTag() {
14459
+ const path = this._matcher.path;
14460
+ return path.length > 0 ? path[path.length - 1].tag : void 0;
14461
+ }
14462
+ getCurrentNamespace() {
14463
+ const path = this._matcher.path;
14464
+ return path.length > 0 ? path[path.length - 1].namespace : void 0;
14465
+ }
14466
+ getAttrValue(attrName) {
14467
+ const path = this._matcher.path;
14468
+ if (0 === path.length) return;
14469
+ return path[path.length - 1].values?.[attrName];
14470
+ }
14471
+ hasAttr(attrName) {
14472
+ const path = this._matcher.path;
14473
+ if (0 === path.length) return false;
14474
+ const current = path[path.length - 1];
14475
+ return void 0 !== current.values && attrName in current.values;
14476
+ }
14477
+ getPosition() {
14478
+ const path = this._matcher.path;
14479
+ if (0 === path.length) return -1;
14480
+ return path[path.length - 1].position ?? 0;
14481
+ }
14482
+ getCounter() {
14483
+ const path = this._matcher.path;
14484
+ if (0 === path.length) return -1;
14485
+ return path[path.length - 1].counter ?? 0;
14486
+ }
14487
+ getIndex() {
14488
+ return this.getPosition();
14489
+ }
14490
+ getDepth() {
14491
+ return this._matcher.path.length;
14492
+ }
14493
+ toString(separator, includeNamespace = true) {
14494
+ return this._matcher.toString(separator, includeNamespace);
14495
+ }
14496
+ toArray() {
14497
+ return this._matcher.path.map((n)=>n.tag);
14498
+ }
14499
+ matches(expression) {
14500
+ return this._matcher.matches(expression);
14501
+ }
14502
+ matchesAny(exprSet) {
14503
+ return exprSet.matchesAny(this._matcher);
14504
+ }
14505
+ }
14458
14506
  class Matcher {
14459
14507
  constructor(options = {}){
14460
14508
  this.separator = options.separator || '.';
14461
14509
  this.path = [];
14462
14510
  this.siblingStacks = [];
14511
+ this._pathStringCache = null;
14512
+ this._view = new MatcherView(this);
14463
14513
  }
14464
14514
  push(tagName, attrValues = null, namespace = null) {
14465
- if (this.path.length > 0) {
14466
- const prev = this.path[this.path.length - 1];
14467
- prev.values = void 0;
14468
- }
14515
+ this._pathStringCache = null;
14516
+ if (this.path.length > 0) this.path[this.path.length - 1].values = void 0;
14469
14517
  const currentLevel = this.path.length;
14470
14518
  if (!this.siblingStacks[currentLevel]) this.siblingStacks[currentLevel] = new Map();
14471
14519
  const siblings = this.siblingStacks[currentLevel];
@@ -14485,6 +14533,7 @@
14485
14533
  }
14486
14534
  pop() {
14487
14535
  if (0 === this.path.length) return;
14536
+ this._pathStringCache = null;
14488
14537
  const node = this.path.pop();
14489
14538
  if (this.siblingStacks.length > this.path.length + 1) this.siblingStacks.length = this.path.length + 1;
14490
14539
  return node;
@@ -14503,8 +14552,7 @@
14503
14552
  }
14504
14553
  getAttrValue(attrName) {
14505
14554
  if (0 === this.path.length) return;
14506
- const current = this.path[this.path.length - 1];
14507
- return current.values?.[attrName];
14555
+ return this.path[this.path.length - 1].values?.[attrName];
14508
14556
  }
14509
14557
  hasAttr(attrName) {
14510
14558
  if (0 === this.path.length) return false;
@@ -14527,15 +14575,20 @@
14527
14575
  }
14528
14576
  toString(separator, includeNamespace = true) {
14529
14577
  const sep = separator || this.separator;
14530
- return this.path.map((n)=>{
14531
- if (includeNamespace && n.namespace) return `${n.namespace}:${n.tag}`;
14532
- return n.tag;
14533
- }).join(sep);
14578
+ const isDefault = sep === this.separator && true === includeNamespace;
14579
+ if (isDefault) {
14580
+ if (null !== this._pathStringCache) return this._pathStringCache;
14581
+ const result = this.path.map((n)=>n.namespace ? `${n.namespace}:${n.tag}` : n.tag).join(sep);
14582
+ this._pathStringCache = result;
14583
+ return result;
14584
+ }
14585
+ return this.path.map((n)=>includeNamespace && n.namespace ? `${n.namespace}:${n.tag}` : n.tag).join(sep);
14534
14586
  }
14535
14587
  toArray() {
14536
14588
  return this.path.map((n)=>n.tag);
14537
14589
  }
14538
14590
  reset() {
14591
+ this._pathStringCache = null;
14539
14592
  this.path = [];
14540
14593
  this.siblingStacks = [];
14541
14594
  }
@@ -14547,12 +14600,7 @@
14547
14600
  }
14548
14601
  _matchSimple(segments) {
14549
14602
  if (this.path.length !== segments.length) return false;
14550
- for(let i = 0; i < segments.length; i++){
14551
- const segment = segments[i];
14552
- const node = this.path[i];
14553
- const isCurrentNode = i === this.path.length - 1;
14554
- if (!this._matchSegment(segment, node, isCurrentNode)) return false;
14555
- }
14603
+ for(let i = 0; i < segments.length; i++)if (!this._matchSegment(segments[i], this.path[i], i === this.path.length - 1)) return false;
14556
14604
  return true;
14557
14605
  }
14558
14606
  _matchWithDeepWildcard(segments) {
@@ -14565,19 +14613,15 @@
14565
14613
  if (segIdx < 0) return true;
14566
14614
  const nextSeg = segments[segIdx];
14567
14615
  let found = false;
14568
- for(let i = pathIdx; i >= 0; i--){
14569
- const isCurrentNode = i === this.path.length - 1;
14570
- if (this._matchSegment(nextSeg, this.path[i], isCurrentNode)) {
14571
- pathIdx = i - 1;
14572
- segIdx--;
14573
- found = true;
14574
- break;
14575
- }
14616
+ for(let i = pathIdx; i >= 0; i--)if (this._matchSegment(nextSeg, this.path[i], i === this.path.length - 1)) {
14617
+ pathIdx = i - 1;
14618
+ segIdx--;
14619
+ found = true;
14620
+ break;
14576
14621
  }
14577
14622
  if (!found) return false;
14578
14623
  } else {
14579
- const isCurrentNode = pathIdx === this.path.length - 1;
14580
- if (!this._matchSegment(segment, this.path[pathIdx], isCurrentNode)) return false;
14624
+ if (!this._matchSegment(segment, this.path[pathIdx], pathIdx === this.path.length - 1)) return false;
14581
14625
  pathIdx--;
14582
14626
  segIdx--;
14583
14627
  }
@@ -14593,8 +14637,7 @@
14593
14637
  if (!isCurrentNode) return false;
14594
14638
  if (!node.values || !(segment.attrName in node.values)) return false;
14595
14639
  if (void 0 !== segment.attrValue) {
14596
- const actualValue = node.values[segment.attrName];
14597
- if (String(actualValue) !== String(segment.attrValue)) return false;
14640
+ if (String(node.values[segment.attrName]) !== String(segment.attrValue)) return false;
14598
14641
  }
14599
14642
  }
14600
14643
  if (void 0 !== segment.position) {
@@ -14603,12 +14646,13 @@
14603
14646
  if ('first' === segment.position && 0 !== counter) return false;
14604
14647
  if ('odd' === segment.position && counter % 2 !== 1) return false;
14605
14648
  if ('even' === segment.position && counter % 2 !== 0) return false;
14606
- else if ('nth' === segment.position) {
14607
- if (counter !== segment.positionValue) return false;
14608
- }
14649
+ else if ('nth' === segment.position && counter !== segment.positionValue) return false;
14609
14650
  }
14610
14651
  return true;
14611
14652
  }
14653
+ matchesAny(exprSet) {
14654
+ return exprSet.matchesAny(this);
14655
+ }
14612
14656
  snapshot() {
14613
14657
  return {
14614
14658
  path: this.path.map((node)=>({
@@ -14618,39 +14662,22 @@
14618
14662
  };
14619
14663
  }
14620
14664
  restore(snapshot) {
14665
+ this._pathStringCache = null;
14621
14666
  this.path = snapshot.path.map((node)=>({
14622
14667
  ...node
14623
14668
  }));
14624
14669
  this.siblingStacks = snapshot.siblingStacks.map((map)=>new Map(map));
14625
14670
  }
14626
14671
  readOnly() {
14627
- const self1 = this;
14628
- return new Proxy(self1, {
14629
- get (target, prop, receiver) {
14630
- if (MUTATING_METHODS.has(prop)) return ()=>{
14631
- throw new TypeError(`Cannot call '${prop}' on a read-only Matcher. Obtain a writable instance to mutate state.`);
14632
- };
14633
- const value = Reflect.get(target, prop, receiver);
14634
- if ('path' === prop || 'siblingStacks' === prop) return Object.freeze(Array.isArray(value) ? value.map((item)=>item instanceof Map ? Object.freeze(new Map(item)) : Object.freeze({
14635
- ...item
14636
- })) : value);
14637
- if ('function' == typeof value) return value.bind(target);
14638
- return value;
14639
- },
14640
- set (_target, prop) {
14641
- throw new TypeError(`Cannot set property '${String(prop)}' on a read-only Matcher.`);
14642
- },
14643
- deleteProperty (_target, prop) {
14644
- throw new TypeError(`Cannot delete property '${String(prop)}' from a read-only Matcher.`);
14645
- }
14646
- });
14672
+ return this._view;
14647
14673
  }
14648
14674
  }
14649
14675
  class Expression {
14650
- constructor(pattern, options = {}){
14676
+ constructor(pattern, options = {}, data){
14651
14677
  this.pattern = pattern;
14652
14678
  this.separator = options.separator || '.';
14653
14679
  this.segments = this._parse(pattern);
14680
+ this.data = data;
14654
14681
  this._hasDeepWildcard = this.segments.some((seg)=>'deep-wildcard' === seg.type);
14655
14682
  this._hasAttributeCondition = this.segments.some((seg)=>void 0 !== seg.attrName);
14656
14683
  this._hasPositionSelector = this.segments.some((seg)=>void 0 !== seg.position);
@@ -14752,6 +14779,71 @@
14752
14779
  return this.pattern;
14753
14780
  }
14754
14781
  }
14782
+ class ExpressionSet {
14783
+ constructor(){
14784
+ this._byDepthAndTag = new Map();
14785
+ this._wildcardByDepth = new Map();
14786
+ this._deepWildcards = [];
14787
+ this._patterns = new Set();
14788
+ this._sealed = false;
14789
+ }
14790
+ add(expression) {
14791
+ if (this._sealed) throw new TypeError('ExpressionSet is sealed. Create a new ExpressionSet to add more expressions.');
14792
+ if (this._patterns.has(expression.pattern)) return this;
14793
+ this._patterns.add(expression.pattern);
14794
+ if (expression.hasDeepWildcard()) {
14795
+ this._deepWildcards.push(expression);
14796
+ return this;
14797
+ }
14798
+ const depth = expression.length;
14799
+ const lastSeg = expression.segments[expression.segments.length - 1];
14800
+ const tag = lastSeg?.tag;
14801
+ if (tag && '*' !== tag) {
14802
+ const key = `${depth}:${tag}`;
14803
+ if (!this._byDepthAndTag.has(key)) this._byDepthAndTag.set(key, []);
14804
+ this._byDepthAndTag.get(key).push(expression);
14805
+ } else {
14806
+ if (!this._wildcardByDepth.has(depth)) this._wildcardByDepth.set(depth, []);
14807
+ this._wildcardByDepth.get(depth).push(expression);
14808
+ }
14809
+ return this;
14810
+ }
14811
+ addAll(expressions) {
14812
+ for (const expr of expressions)this.add(expr);
14813
+ return this;
14814
+ }
14815
+ has(expression) {
14816
+ return this._patterns.has(expression.pattern);
14817
+ }
14818
+ get size() {
14819
+ return this._patterns.size;
14820
+ }
14821
+ seal() {
14822
+ this._sealed = true;
14823
+ return this;
14824
+ }
14825
+ get isSealed() {
14826
+ return this._sealed;
14827
+ }
14828
+ matchesAny(matcher) {
14829
+ return null !== this.findMatch(matcher);
14830
+ }
14831
+ findMatch(matcher) {
14832
+ const depth = matcher.getDepth();
14833
+ const tag = matcher.getCurrentTag();
14834
+ const exactKey = `${depth}:${tag}`;
14835
+ const exactBucket = this._byDepthAndTag.get(exactKey);
14836
+ if (exactBucket) {
14837
+ for(let i = 0; i < exactBucket.length; i++)if (matcher.matches(exactBucket[i])) return exactBucket[i];
14838
+ }
14839
+ const wildcardBucket = this._wildcardByDepth.get(depth);
14840
+ if (wildcardBucket) {
14841
+ for(let i = 0; i < wildcardBucket.length; i++)if (matcher.matches(wildcardBucket[i])) return wildcardBucket[i];
14842
+ }
14843
+ for(let i = 0; i < this._deepWildcards.length; i++)if (matcher.matches(this._deepWildcards[i])) return this._deepWildcards[i];
14844
+ return null;
14845
+ }
14846
+ }
14755
14847
  function extractRawAttributes(prefixedAttrs, options) {
14756
14848
  if (!prefixedAttrs) return {};
14757
14849
  const attrs = options.attributesGroupName ? prefixedAttrs[options.attributesGroupName] : prefixedAttrs;
@@ -14857,13 +14949,15 @@
14857
14949
  this.matcher = new Matcher();
14858
14950
  this.readonlyMatcher = this.matcher.readOnly();
14859
14951
  this.isCurrentNodeStopNode = false;
14860
- if (this.options.stopNodes && this.options.stopNodes.length > 0) {
14861
- this.stopNodeExpressions = [];
14862
- for(let i = 0; i < this.options.stopNodes.length; i++){
14863
- const stopNodeExp = this.options.stopNodes[i];
14864
- if ('string' == typeof stopNodeExp) this.stopNodeExpressions.push(new Expression(stopNodeExp));
14865
- else if (stopNodeExp instanceof Expression) this.stopNodeExpressions.push(stopNodeExp);
14952
+ this.stopNodeExpressionsSet = new ExpressionSet();
14953
+ const stopNodesOpts = this.options.stopNodes;
14954
+ if (stopNodesOpts && stopNodesOpts.length > 0) {
14955
+ for(let i = 0; i < stopNodesOpts.length; i++){
14956
+ const stopNodeExp = stopNodesOpts[i];
14957
+ if ('string' == typeof stopNodeExp) this.stopNodeExpressionsSet.add(new Expression(stopNodeExp));
14958
+ else if (stopNodeExp instanceof Expression) this.stopNodeExpressionsSet.add(stopNodeExp);
14866
14959
  }
14960
+ this.stopNodeExpressionsSet.seal();
14867
14961
  }
14868
14962
  }
14869
14963
  }
@@ -14879,18 +14973,19 @@
14879
14973
  }
14880
14974
  }
14881
14975
  function parseTextData(val, tagName, jPath, dontTrim, hasAttributes, isLeafNode, escapeEntities) {
14976
+ const options = this.options;
14882
14977
  if (void 0 !== val) {
14883
- if (this.options.trimValues && !dontTrim) val = val.trim();
14978
+ if (options.trimValues && !dontTrim) val = val.trim();
14884
14979
  if (val.length > 0) {
14885
14980
  if (!escapeEntities) val = this.replaceEntitiesValue(val, tagName, jPath);
14886
- const jPathOrMatcher = this.options.jPath ? jPath.toString() : jPath;
14887
- const newval = this.options.tagValueProcessor(tagName, val, jPathOrMatcher, hasAttributes, isLeafNode);
14981
+ const jPathOrMatcher = options.jPath ? jPath.toString() : jPath;
14982
+ const newval = options.tagValueProcessor(tagName, val, jPathOrMatcher, hasAttributes, isLeafNode);
14888
14983
  if (null == newval) return val;
14889
14984
  {
14890
14985
  if (typeof newval !== typeof val || newval !== val) return newval;
14891
- if (this.options.trimValues) return parseValue(val, this.options.parseTagValue, this.options.numberParseOptions);
14986
+ if (options.trimValues) return parseValue(val, options.parseTagValue, options.numberParseOptions);
14892
14987
  const trimmedVal = val.trim();
14893
- if (trimmedVal === val) return parseValue(val, this.options.parseTagValue, this.options.numberParseOptions);
14988
+ if (trimmedVal === val) return parseValue(val, options.parseTagValue, options.numberParseOptions);
14894
14989
  return val;
14895
14990
  }
14896
14991
  }
@@ -14907,46 +15002,53 @@
14907
15002
  }
14908
15003
  const attrsRegx = new RegExp('([^\\s=]+)\\s*(=\\s*([\'"])([\\s\\S]*?)\\3)?', 'gm');
14909
15004
  function buildAttributesMap(attrStr, jPath, tagName) {
14910
- if (true !== this.options.ignoreAttributes && 'string' == typeof attrStr) {
15005
+ const options = this.options;
15006
+ if (true !== options.ignoreAttributes && 'string' == typeof attrStr) {
14911
15007
  const matches = getAllMatches(attrStr, attrsRegx);
14912
15008
  const len = matches.length;
14913
15009
  const attrs = {};
15010
+ const processedVals = new Array(len);
15011
+ let hasRawAttrs = false;
14914
15012
  const rawAttrsForMatcher = {};
14915
15013
  for(let i = 0; i < len; i++){
14916
15014
  const attrName = this.resolveNameSpace(matches[i][1]);
14917
15015
  const oldVal = matches[i][4];
14918
15016
  if (attrName.length && void 0 !== oldVal) {
14919
- let parsedVal = oldVal;
14920
- if (this.options.trimValues) parsedVal = parsedVal.trim();
14921
- parsedVal = this.replaceEntitiesValue(parsedVal, tagName, this.readonlyMatcher);
14922
- rawAttrsForMatcher[attrName] = parsedVal;
15017
+ let val = oldVal;
15018
+ if (options.trimValues) val = val.trim();
15019
+ val = this.replaceEntitiesValue(val, tagName, this.readonlyMatcher);
15020
+ processedVals[i] = val;
15021
+ rawAttrsForMatcher[attrName] = val;
15022
+ hasRawAttrs = true;
14923
15023
  }
14924
15024
  }
14925
- if (Object.keys(rawAttrsForMatcher).length > 0 && 'object' == typeof jPath && jPath.updateCurrent) jPath.updateCurrent(rawAttrsForMatcher);
15025
+ if (hasRawAttrs && 'object' == typeof jPath && jPath.updateCurrent) jPath.updateCurrent(rawAttrsForMatcher);
15026
+ const jPathStr = options.jPath ? jPath.toString() : this.readonlyMatcher;
15027
+ let hasAttrs = false;
14926
15028
  for(let i = 0; i < len; i++){
14927
15029
  const attrName = this.resolveNameSpace(matches[i][1]);
14928
- const jPathStr = this.options.jPath ? jPath.toString() : this.readonlyMatcher;
14929
15030
  if (this.ignoreAttributesFn(attrName, jPathStr)) continue;
14930
- let oldVal = matches[i][4];
14931
- let aName = this.options.attributeNamePrefix + attrName;
15031
+ let aName = options.attributeNamePrefix + attrName;
14932
15032
  if (attrName.length) {
14933
- if (this.options.transformAttributeName) aName = this.options.transformAttributeName(aName);
14934
- aName = sanitizeName(aName, this.options);
14935
- if (void 0 !== oldVal) {
14936
- if (this.options.trimValues) oldVal = oldVal.trim();
14937
- oldVal = this.replaceEntitiesValue(oldVal, tagName, this.readonlyMatcher);
14938
- const jPathOrMatcher = this.options.jPath ? jPath.toString() : this.readonlyMatcher;
14939
- const newVal = this.options.attributeValueProcessor(attrName, oldVal, jPathOrMatcher);
15033
+ if (options.transformAttributeName) aName = options.transformAttributeName(aName);
15034
+ aName = sanitizeName(aName, options);
15035
+ if (void 0 !== matches[i][4]) {
15036
+ const oldVal = processedVals[i];
15037
+ const newVal = options.attributeValueProcessor(attrName, oldVal, jPathStr);
14940
15038
  if (null == newVal) attrs[aName] = oldVal;
14941
15039
  else if (typeof newVal !== typeof oldVal || newVal !== oldVal) attrs[aName] = newVal;
14942
- else attrs[aName] = parseValue(oldVal, this.options.parseAttributeValue, this.options.numberParseOptions);
14943
- } else if (this.options.allowBooleanAttributes) attrs[aName] = true;
15040
+ else attrs[aName] = parseValue(oldVal, options.parseAttributeValue, options.numberParseOptions);
15041
+ hasAttrs = true;
15042
+ } else if (options.allowBooleanAttributes) {
15043
+ attrs[aName] = true;
15044
+ hasAttrs = true;
15045
+ }
14944
15046
  }
14945
15047
  }
14946
- if (!Object.keys(attrs).length) return;
14947
- if (this.options.attributesGroupName) {
15048
+ if (!hasAttrs) return;
15049
+ if (options.attributesGroupName) {
14948
15050
  const attrCollection = {};
14949
- attrCollection[this.options.attributesGroupName] = attrs;
15051
+ attrCollection[options.attributesGroupName] = attrs;
14950
15052
  return attrCollection;
14951
15053
  }
14952
15054
  return attrs;
@@ -14960,155 +15062,163 @@
14960
15062
  this.matcher.reset();
14961
15063
  this.entityExpansionCount = 0;
14962
15064
  this.currentExpandedLength = 0;
14963
- const docTypeReader = new DocTypeReader(this.options.processEntities);
14964
- for(let i = 0; i < xmlData.length; i++){
15065
+ this.docTypeEntitiesKeys = [];
15066
+ this.lastEntitiesKeys = Object.keys(this.lastEntities);
15067
+ this.htmlEntitiesKeys = this.options.htmlEntities ? Object.keys(this.htmlEntities) : [];
15068
+ const options = this.options;
15069
+ const docTypeReader = new DocTypeReader(options.processEntities);
15070
+ const xmlLen = xmlData.length;
15071
+ for(let i = 0; i < xmlLen; i++){
14965
15072
  const ch = xmlData[i];
14966
- if ('<' === ch) if ('/' === xmlData[i + 1]) {
14967
- const closeIndex = findClosingIndex(xmlData, ">", i, "Closing Tag is not closed.");
14968
- let tagName = xmlData.substring(i + 2, closeIndex).trim();
14969
- if (this.options.removeNSPrefix) {
14970
- const colonIndex = tagName.indexOf(":");
14971
- if (-1 !== colonIndex) tagName = tagName.substr(colonIndex + 1);
14972
- }
14973
- tagName = transformTagName(this.options.transformTagName, tagName, "", this.options).tagName;
14974
- if (currentNode) textData = this.saveTextToParentTag(textData, currentNode, this.readonlyMatcher);
14975
- const lastTagName = this.matcher.getCurrentTag();
14976
- if (tagName && -1 !== this.options.unpairedTags.indexOf(tagName)) throw new Error(`Unpaired tag can not be used as closing tag: </${tagName}>`);
14977
- if (lastTagName && -1 !== this.options.unpairedTags.indexOf(lastTagName)) {
15073
+ if ('<' === ch) {
15074
+ const c1 = xmlData.charCodeAt(i + 1);
15075
+ if (47 === c1) {
15076
+ const closeIndex = findClosingIndex(xmlData, ">", i, "Closing Tag is not closed.");
15077
+ let tagName = xmlData.substring(i + 2, closeIndex).trim();
15078
+ if (options.removeNSPrefix) {
15079
+ const colonIndex = tagName.indexOf(":");
15080
+ if (-1 !== colonIndex) tagName = tagName.substr(colonIndex + 1);
15081
+ }
15082
+ tagName = transformTagName(options.transformTagName, tagName, "", options).tagName;
15083
+ if (currentNode) textData = this.saveTextToParentTag(textData, currentNode, this.readonlyMatcher);
15084
+ const lastTagName = this.matcher.getCurrentTag();
15085
+ if (tagName && options.unpairedTagsSet.has(tagName)) throw new Error(`Unpaired tag can not be used as closing tag: </${tagName}>`);
15086
+ if (lastTagName && options.unpairedTagsSet.has(lastTagName)) {
15087
+ this.matcher.pop();
15088
+ this.tagsNodeStack.pop();
15089
+ }
14978
15090
  this.matcher.pop();
14979
- this.tagsNodeStack.pop();
14980
- }
14981
- this.matcher.pop();
14982
- this.isCurrentNodeStopNode = false;
14983
- currentNode = this.tagsNodeStack.pop();
14984
- textData = "";
14985
- i = closeIndex;
14986
- } else if ('?' === xmlData[i + 1]) {
14987
- let tagData = readTagExp(xmlData, i, false, "?>");
14988
- if (!tagData) throw new Error("Pi Tag is not closed.");
14989
- textData = this.saveTextToParentTag(textData, currentNode, this.readonlyMatcher);
14990
- if (this.options.ignoreDeclaration && "?xml" === tagData.tagName || this.options.ignorePiTags) ;
14991
- else {
14992
- const childNode = new XmlNode(tagData.tagName);
14993
- childNode.add(this.options.textNodeName, "");
14994
- if (tagData.tagName !== tagData.tagExp && tagData.attrExpPresent) childNode[":@"] = this.buildAttributesMap(tagData.tagExp, this.matcher, tagData.tagName);
14995
- this.addChild(currentNode, childNode, this.readonlyMatcher, i);
14996
- }
14997
- i = tagData.closeIndex + 1;
14998
- } else if ('!--' === xmlData.substr(i + 1, 3)) {
14999
- const endIndex = findClosingIndex(xmlData, "-->", i + 4, "Comment is not closed.");
15000
- if (this.options.commentPropName) {
15001
- const comment = xmlData.substring(i + 4, endIndex - 2);
15091
+ this.isCurrentNodeStopNode = false;
15092
+ currentNode = this.tagsNodeStack.pop();
15093
+ textData = "";
15094
+ i = closeIndex;
15095
+ } else if (63 === c1) {
15096
+ let tagData = readTagExp(xmlData, i, false, "?>");
15097
+ if (!tagData) throw new Error("Pi Tag is not closed.");
15002
15098
  textData = this.saveTextToParentTag(textData, currentNode, this.readonlyMatcher);
15003
- currentNode.add(this.options.commentPropName, [
15099
+ if (options.ignoreDeclaration && "?xml" === tagData.tagName || options.ignorePiTags) ;
15100
+ else {
15101
+ const childNode = new XmlNode(tagData.tagName);
15102
+ childNode.add(options.textNodeName, "");
15103
+ if (tagData.tagName !== tagData.tagExp && tagData.attrExpPresent) childNode[":@"] = this.buildAttributesMap(tagData.tagExp, this.matcher, tagData.tagName);
15104
+ this.addChild(currentNode, childNode, this.readonlyMatcher, i);
15105
+ }
15106
+ i = tagData.closeIndex + 1;
15107
+ } else if (33 === c1 && 45 === xmlData.charCodeAt(i + 2) && 45 === xmlData.charCodeAt(i + 3)) {
15108
+ const endIndex = findClosingIndex(xmlData, "-->", i + 4, "Comment is not closed.");
15109
+ if (options.commentPropName) {
15110
+ const comment = xmlData.substring(i + 4, endIndex - 2);
15111
+ textData = this.saveTextToParentTag(textData, currentNode, this.readonlyMatcher);
15112
+ currentNode.add(options.commentPropName, [
15113
+ {
15114
+ [options.textNodeName]: comment
15115
+ }
15116
+ ]);
15117
+ }
15118
+ i = endIndex;
15119
+ } else if (33 === c1 && 68 === xmlData.charCodeAt(i + 2)) {
15120
+ const result = docTypeReader.readDocType(xmlData, i);
15121
+ this.docTypeEntities = result.entities;
15122
+ this.docTypeEntitiesKeys = Object.keys(this.docTypeEntities) || [];
15123
+ i = result.i;
15124
+ } else if (33 === c1 && 91 === xmlData.charCodeAt(i + 2)) {
15125
+ const closeIndex = findClosingIndex(xmlData, "]]>", i, "CDATA is not closed.") - 2;
15126
+ const tagExp = xmlData.substring(i + 9, closeIndex);
15127
+ textData = this.saveTextToParentTag(textData, currentNode, this.readonlyMatcher);
15128
+ let val = this.parseTextData(tagExp, currentNode.tagname, this.readonlyMatcher, true, false, true, true);
15129
+ if (void 0 == val) val = "";
15130
+ if (options.cdataPropName) currentNode.add(options.cdataPropName, [
15004
15131
  {
15005
- [this.options.textNodeName]: comment
15132
+ [options.textNodeName]: tagExp
15006
15133
  }
15007
15134
  ]);
15008
- }
15009
- i = endIndex;
15010
- } else if ('!D' === xmlData.substr(i + 1, 2)) {
15011
- const result = docTypeReader.readDocType(xmlData, i);
15012
- this.docTypeEntities = result.entities;
15013
- i = result.i;
15014
- } else if ('![' === xmlData.substr(i + 1, 2)) {
15015
- const closeIndex = findClosingIndex(xmlData, "]]>", i, "CDATA is not closed.") - 2;
15016
- const tagExp = xmlData.substring(i + 9, closeIndex);
15017
- textData = this.saveTextToParentTag(textData, currentNode, this.readonlyMatcher);
15018
- let val = this.parseTextData(tagExp, currentNode.tagname, this.readonlyMatcher, true, false, true, true);
15019
- if (void 0 == val) val = "";
15020
- if (this.options.cdataPropName) currentNode.add(this.options.cdataPropName, [
15021
- {
15022
- [this.options.textNodeName]: tagExp
15135
+ else currentNode.add(options.textNodeName, val);
15136
+ i = closeIndex + 2;
15137
+ } else {
15138
+ let result = readTagExp(xmlData, i, options.removeNSPrefix);
15139
+ if (!result) {
15140
+ const context = xmlData.substring(Math.max(0, i - 50), Math.min(xmlLen, i + 50));
15141
+ throw new Error(`readTagExp returned undefined at position ${i}. Context: "${context}"`);
15023
15142
  }
15024
- ]);
15025
- else currentNode.add(this.options.textNodeName, val);
15026
- i = closeIndex + 2;
15027
- } else {
15028
- let result = readTagExp(xmlData, i, this.options.removeNSPrefix);
15029
- if (!result) {
15030
- const context = xmlData.substring(Math.max(0, i - 50), Math.min(xmlData.length, i + 50));
15031
- throw new Error(`readTagExp returned undefined at position ${i}. Context: "${context}"`);
15032
- }
15033
- let tagName = result.tagName;
15034
- const rawTagName = result.rawTagName;
15035
- let tagExp = result.tagExp;
15036
- let attrExpPresent = result.attrExpPresent;
15037
- let closeIndex = result.closeIndex;
15038
- ({ tagName, tagExp } = transformTagName(this.options.transformTagName, tagName, tagExp, this.options));
15039
- 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}`);
15040
- if (currentNode && textData) {
15041
- if ('!xml' !== currentNode.tagname) textData = this.saveTextToParentTag(textData, currentNode, this.readonlyMatcher, false);
15042
- }
15043
- const lastTag = currentNode;
15044
- if (lastTag && -1 !== this.options.unpairedTags.indexOf(lastTag.tagname)) {
15045
- currentNode = this.tagsNodeStack.pop();
15046
- this.matcher.pop();
15047
- }
15048
- let isSelfClosing = false;
15049
- if (tagExp.length > 0 && tagExp.lastIndexOf("/") === tagExp.length - 1) {
15050
- isSelfClosing = true;
15051
- if ("/" === tagName[tagName.length - 1]) {
15052
- tagName = tagName.substr(0, tagName.length - 1);
15053
- tagExp = tagName;
15054
- } else tagExp = tagExp.substr(0, tagExp.length - 1);
15055
- attrExpPresent = tagName !== tagExp;
15056
- }
15057
- let prefixedAttrs = null;
15058
- let namespace;
15059
- namespace = extractNamespace(rawTagName);
15060
- if (tagName !== xmlObj.tagname) this.matcher.push(tagName, {}, namespace);
15061
- if (tagName !== tagExp && attrExpPresent) {
15062
- prefixedAttrs = this.buildAttributesMap(tagExp, this.matcher, tagName);
15063
- if (prefixedAttrs) extractRawAttributes(prefixedAttrs, this.options);
15064
- }
15065
- if (tagName !== xmlObj.tagname) this.isCurrentNodeStopNode = this.isItStopNode(this.stopNodeExpressions, this.matcher);
15066
- const startIndex = i;
15067
- if (this.isCurrentNodeStopNode) {
15068
- let tagContent = "";
15069
- if (isSelfClosing) i = result.closeIndex;
15070
- else if (-1 !== this.options.unpairedTags.indexOf(tagName)) i = result.closeIndex;
15071
- else {
15072
- const result = this.readStopNodeData(xmlData, rawTagName, closeIndex + 1);
15073
- if (!result) throw new Error(`Unexpected end of ${rawTagName}`);
15074
- i = result.i;
15075
- tagContent = result.tagContent;
15143
+ let tagName = result.tagName;
15144
+ const rawTagName = result.rawTagName;
15145
+ let tagExp = result.tagExp;
15146
+ let attrExpPresent = result.attrExpPresent;
15147
+ let closeIndex = result.closeIndex;
15148
+ ({ tagName, tagExp } = transformTagName(options.transformTagName, tagName, tagExp, options));
15149
+ if (options.strictReservedNames && (tagName === options.commentPropName || tagName === options.cdataPropName || tagName === options.textNodeName || tagName === options.attributesGroupName)) throw new Error(`Invalid tag name: ${tagName}`);
15150
+ if (currentNode && textData) {
15151
+ if ('!xml' !== currentNode.tagname) textData = this.saveTextToParentTag(textData, currentNode, this.readonlyMatcher, false);
15076
15152
  }
15077
- const childNode = new XmlNode(tagName);
15078
- if (prefixedAttrs) childNode[":@"] = prefixedAttrs;
15079
- childNode.add(this.options.textNodeName, tagContent);
15080
- this.matcher.pop();
15081
- this.isCurrentNodeStopNode = false;
15082
- this.addChild(currentNode, childNode, this.readonlyMatcher, startIndex);
15083
- } else {
15084
- if (isSelfClosing) {
15085
- ({ tagName, tagExp } = transformTagName(this.options.transformTagName, tagName, tagExp, this.options));
15086
- const childNode = new XmlNode(tagName);
15087
- if (prefixedAttrs) childNode[":@"] = prefixedAttrs;
15088
- this.addChild(currentNode, childNode, this.readonlyMatcher, startIndex);
15153
+ const lastTag = currentNode;
15154
+ if (lastTag && options.unpairedTagsSet.has(lastTag.tagname)) {
15155
+ currentNode = this.tagsNodeStack.pop();
15089
15156
  this.matcher.pop();
15090
- this.isCurrentNodeStopNode = false;
15091
- } else if (-1 !== this.options.unpairedTags.indexOf(tagName)) {
15157
+ }
15158
+ let isSelfClosing = false;
15159
+ if (tagExp.length > 0 && tagExp.lastIndexOf("/") === tagExp.length - 1) {
15160
+ isSelfClosing = true;
15161
+ if ("/" === tagName[tagName.length - 1]) {
15162
+ tagName = tagName.substr(0, tagName.length - 1);
15163
+ tagExp = tagName;
15164
+ } else tagExp = tagExp.substr(0, tagExp.length - 1);
15165
+ attrExpPresent = tagName !== tagExp;
15166
+ }
15167
+ let prefixedAttrs = null;
15168
+ let namespace;
15169
+ namespace = extractNamespace(rawTagName);
15170
+ if (tagName !== xmlObj.tagname) this.matcher.push(tagName, {}, namespace);
15171
+ if (tagName !== tagExp && attrExpPresent) {
15172
+ prefixedAttrs = this.buildAttributesMap(tagExp, this.matcher, tagName);
15173
+ if (prefixedAttrs) extractRawAttributes(prefixedAttrs, options);
15174
+ }
15175
+ if (tagName !== xmlObj.tagname) this.isCurrentNodeStopNode = this.isItStopNode();
15176
+ const startIndex = i;
15177
+ if (this.isCurrentNodeStopNode) {
15178
+ let tagContent = "";
15179
+ if (isSelfClosing) i = result.closeIndex;
15180
+ else if (options.unpairedTagsSet.has(tagName)) i = result.closeIndex;
15181
+ else {
15182
+ const result = this.readStopNodeData(xmlData, rawTagName, closeIndex + 1);
15183
+ if (!result) throw new Error(`Unexpected end of ${rawTagName}`);
15184
+ i = result.i;
15185
+ tagContent = result.tagContent;
15186
+ }
15092
15187
  const childNode = new XmlNode(tagName);
15093
15188
  if (prefixedAttrs) childNode[":@"] = prefixedAttrs;
15094
- this.addChild(currentNode, childNode, this.readonlyMatcher, startIndex);
15189
+ childNode.add(options.textNodeName, tagContent);
15095
15190
  this.matcher.pop();
15096
15191
  this.isCurrentNodeStopNode = false;
15097
- i = result.closeIndex;
15098
- continue;
15099
- } else {
15100
- const childNode = new XmlNode(tagName);
15101
- if (this.tagsNodeStack.length > this.options.maxNestedTags) throw new Error("Maximum nested tags exceeded");
15102
- this.tagsNodeStack.push(currentNode);
15103
- if (prefixedAttrs) childNode[":@"] = prefixedAttrs;
15104
15192
  this.addChild(currentNode, childNode, this.readonlyMatcher, startIndex);
15105
- currentNode = childNode;
15193
+ } else {
15194
+ if (isSelfClosing) {
15195
+ ({ tagName, tagExp } = transformTagName(options.transformTagName, tagName, tagExp, options));
15196
+ const childNode = new XmlNode(tagName);
15197
+ if (prefixedAttrs) childNode[":@"] = prefixedAttrs;
15198
+ this.addChild(currentNode, childNode, this.readonlyMatcher, startIndex);
15199
+ this.matcher.pop();
15200
+ this.isCurrentNodeStopNode = false;
15201
+ } else if (options.unpairedTagsSet.has(tagName)) {
15202
+ const childNode = new XmlNode(tagName);
15203
+ if (prefixedAttrs) childNode[":@"] = prefixedAttrs;
15204
+ this.addChild(currentNode, childNode, this.readonlyMatcher, startIndex);
15205
+ this.matcher.pop();
15206
+ this.isCurrentNodeStopNode = false;
15207
+ i = result.closeIndex;
15208
+ continue;
15209
+ } else {
15210
+ const childNode = new XmlNode(tagName);
15211
+ if (this.tagsNodeStack.length > options.maxNestedTags) throw new Error("Maximum nested tags exceeded");
15212
+ this.tagsNodeStack.push(currentNode);
15213
+ if (prefixedAttrs) childNode[":@"] = prefixedAttrs;
15214
+ this.addChild(currentNode, childNode, this.readonlyMatcher, startIndex);
15215
+ currentNode = childNode;
15216
+ }
15217
+ textData = "";
15218
+ i = closeIndex;
15106
15219
  }
15107
- textData = "";
15108
- i = closeIndex;
15109
15220
  }
15110
- }
15111
- else textData += xmlData[i];
15221
+ } else textData += xmlData[i];
15112
15222
  }
15113
15223
  return xmlObj.child;
15114
15224
  };
@@ -15134,7 +15244,7 @@
15134
15244
  const jPathOrMatcher = this.options.jPath ? jPath.toString() : jPath;
15135
15245
  if (!entityConfig.tagFilter(tagName, jPathOrMatcher)) return val;
15136
15246
  }
15137
- for (const entityName of Object.keys(this.docTypeEntities)){
15247
+ for (const entityName of this.docTypeEntitiesKeys){
15138
15248
  const entity = this.docTypeEntities[entityName];
15139
15249
  const matches = val.match(entity.regx);
15140
15250
  if (matches) {
@@ -15148,7 +15258,8 @@
15148
15258
  }
15149
15259
  }
15150
15260
  }
15151
- for (const entityName of Object.keys(this.lastEntities)){
15261
+ if (-1 === val.indexOf('&')) return val;
15262
+ for (const entityName of this.lastEntitiesKeys){
15152
15263
  const entity = this.lastEntities[entityName];
15153
15264
  const matches = val.match(entity.regex);
15154
15265
  if (matches) {
@@ -15158,7 +15269,7 @@
15158
15269
  val = val.replace(entity.regex, entity.val);
15159
15270
  }
15160
15271
  if (-1 === val.indexOf('&')) return val;
15161
- if (this.options.htmlEntities) for (const entityName of Object.keys(this.htmlEntities)){
15272
+ for (const entityName of this.htmlEntitiesKeys){
15162
15273
  const entity = this.htmlEntities[entityName];
15163
15274
  const matches = val.match(entity.regex);
15164
15275
  if (matches) {
@@ -15179,30 +15290,35 @@
15179
15290
  }
15180
15291
  return textData;
15181
15292
  }
15182
- function isItStopNode(stopNodeExpressions, matcher) {
15183
- if (!stopNodeExpressions || 0 === stopNodeExpressions.length) return false;
15184
- for(let i = 0; i < stopNodeExpressions.length; i++)if (matcher.matches(stopNodeExpressions[i])) return true;
15185
- return false;
15293
+ function isItStopNode() {
15294
+ if (0 === this.stopNodeExpressionsSet.size) return false;
15295
+ return this.matcher.matchesAny(this.stopNodeExpressionsSet);
15186
15296
  }
15187
15297
  function tagExpWithClosingIndex(xmlData, i, closingChar = ">") {
15188
- let attrBoundary;
15189
- let tagExp = "";
15190
- for(let index = i; index < xmlData.length; index++){
15191
- let ch = xmlData[index];
15298
+ let attrBoundary = 0;
15299
+ const chars = [];
15300
+ const len = xmlData.length;
15301
+ const closeCode0 = closingChar.charCodeAt(0);
15302
+ const closeCode1 = closingChar.length > 1 ? closingChar.charCodeAt(1) : -1;
15303
+ for(let index = i; index < len; index++){
15304
+ const code = xmlData.charCodeAt(index);
15192
15305
  if (attrBoundary) {
15193
- if (ch === attrBoundary) attrBoundary = "";
15194
- } else if ('"' === ch || "'" === ch) attrBoundary = ch;
15195
- else if (ch === closingChar[0]) {
15196
- if (!closingChar[1]) return {
15197
- data: tagExp,
15198
- index: index
15306
+ if (code === attrBoundary) attrBoundary = 0;
15307
+ } else if (34 === code || 39 === code) attrBoundary = code;
15308
+ else if (code === closeCode0) {
15309
+ if (-1 === closeCode1) return {
15310
+ data: String.fromCharCode(...chars),
15311
+ index
15199
15312
  };
15200
- else if (xmlData[index + 1] === closingChar[1]) return {
15201
- data: tagExp,
15202
- index: index
15313
+ else if (xmlData.charCodeAt(index + 1) === closeCode1) return {
15314
+ data: String.fromCharCode(...chars),
15315
+ index
15203
15316
  };
15204
- } else if ('\t' === ch) ch = " ";
15205
- tagExp += ch;
15317
+ } else if (9 === code) {
15318
+ chars.push(32);
15319
+ continue;
15320
+ }
15321
+ chars.push(code);
15206
15322
  }
15207
15323
  }
15208
15324
  function findClosingIndex(xmlData, str, i, errMsg) {
@@ -15210,6 +15326,11 @@
15210
15326
  if (-1 !== closingIndex) return closingIndex + str.length - 1;
15211
15327
  throw new Error(errMsg);
15212
15328
  }
15329
+ function findClosingChar(xmlData, char, i, errMsg) {
15330
+ const closingIndex = xmlData.indexOf(char, i);
15331
+ if (-1 === closingIndex) throw new Error(errMsg);
15332
+ return closingIndex;
15333
+ }
15213
15334
  function readTagExp(xmlData, i, removeNSPrefix, closingChar = ">") {
15214
15335
  const result = tagExpWithClosingIndex(xmlData, i + 1, closingChar);
15215
15336
  if (!result) return;
@@ -15241,32 +15362,36 @@
15241
15362
  function readStopNodeData(xmlData, tagName, i) {
15242
15363
  const startIndex = i;
15243
15364
  let openTagCount = 1;
15244
- for(; i < xmlData.length; i++)if ("<" === xmlData[i]) if ("/" === xmlData[i + 1]) {
15245
- const closeIndex = findClosingIndex(xmlData, ">", i, `${tagName} is not closed`);
15246
- let closeTagName = xmlData.substring(i + 2, closeIndex).trim();
15247
- if (closeTagName === tagName) {
15248
- openTagCount--;
15249
- if (0 === openTagCount) return {
15250
- tagContent: xmlData.substring(startIndex, i),
15251
- i: closeIndex
15252
- };
15253
- }
15254
- i = closeIndex;
15255
- } else if ('?' === xmlData[i + 1]) {
15256
- const closeIndex = findClosingIndex(xmlData, "?>", i + 1, "StopNode is not closed.");
15257
- i = closeIndex;
15258
- } else if ('!--' === xmlData.substr(i + 1, 3)) {
15259
- const closeIndex = findClosingIndex(xmlData, "-->", i + 3, "StopNode is not closed.");
15260
- i = closeIndex;
15261
- } else if ('![' === xmlData.substr(i + 1, 2)) {
15262
- const closeIndex = findClosingIndex(xmlData, "]]>", i, "StopNode is not closed.") - 2;
15263
- i = closeIndex;
15264
- } else {
15265
- const tagData = readTagExp(xmlData, i, '>');
15266
- if (tagData) {
15267
- const openTagName = tagData && tagData.tagName;
15268
- if (openTagName === tagName && "/" !== tagData.tagExp[tagData.tagExp.length - 1]) openTagCount++;
15269
- i = tagData.closeIndex;
15365
+ const xmllen = xmlData.length;
15366
+ for(; i < xmllen; i++)if ("<" === xmlData[i]) {
15367
+ const c1 = xmlData.charCodeAt(i + 1);
15368
+ if (47 === c1) {
15369
+ const closeIndex = findClosingChar(xmlData, ">", i, `${tagName} is not closed`);
15370
+ let closeTagName = xmlData.substring(i + 2, closeIndex).trim();
15371
+ if (closeTagName === tagName) {
15372
+ openTagCount--;
15373
+ if (0 === openTagCount) return {
15374
+ tagContent: xmlData.substring(startIndex, i),
15375
+ i: closeIndex
15376
+ };
15377
+ }
15378
+ i = closeIndex;
15379
+ } else if (63 === c1) {
15380
+ const closeIndex = findClosingIndex(xmlData, "?>", i + 1, "StopNode is not closed.");
15381
+ i = closeIndex;
15382
+ } else if (33 === c1 && 45 === xmlData.charCodeAt(i + 2) && 45 === xmlData.charCodeAt(i + 3)) {
15383
+ const closeIndex = findClosingIndex(xmlData, "-->", i + 3, "StopNode is not closed.");
15384
+ i = closeIndex;
15385
+ } else if (33 === c1 && 91 === xmlData.charCodeAt(i + 2)) {
15386
+ const closeIndex = findClosingIndex(xmlData, "]]>", i, "StopNode is not closed.") - 2;
15387
+ i = closeIndex;
15388
+ } else {
15389
+ const tagData = readTagExp(xmlData, i, '>');
15390
+ if (tagData) {
15391
+ const openTagName = tagData && tagData.tagName;
15392
+ if (openTagName === tagName && "/" !== tagData.tagExp[tagData.tagExp.length - 1]) openTagCount++;
15393
+ i = tagData.closeIndex;
15394
+ }
15270
15395
  }
15271
15396
  }
15272
15397
  }
@@ -21084,7 +21209,7 @@
21084
21209
  function timestamp_timestampMs(timestamp) {
21085
21210
  return 1000 * Number(timestamp.seconds) + Math.round(timestamp.nanos / 1000000);
21086
21211
  }
21087
- 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"}');
21212
+ var package_namespaceObject = JSON.parse('{"UU":"@dan-uni/dan-any","rE":"1.4.1","TB":"https://github.com/ani-uni/danuni/tree/master/packages/dan-any#readme"}');
21088
21213
  const color_pad = (s)=>s.length < 2 ? `0${s}` : s;
21089
21214
  const decimalToHex = (n)=>color_pad(n.toString(16));
21090
21215
  const isDarkColor = ({ r, g, b })=>0.299 * r + 0.587 * g + 0.114 * b < 0x30;
@@ -23810,7 +23935,7 @@
23810
23935
  mode: 4,
23811
23936
  senderID: senderID.toString(),
23812
23937
  ctime: new Date(`${args.ctime} GMT+0800`),
23813
- weight: 10,
23938
+ weight: 11,
23814
23939
  pool: 2,
23815
23940
  attr: [
23816
23941
  "Protect"
@@ -23818,6 +23943,9 @@
23818
23943
  platform: platform.PlatformVideoSource.Bilibili,
23819
23944
  extra: {
23820
23945
  bili: {
23946
+ dmid: args.id,
23947
+ attr: args.attr,
23948
+ mid: args.mid,
23821
23949
  command: args
23822
23950
  }
23823
23951
  }
@@ -25057,6 +25185,8 @@
25057
25185
  const ok = this.dans.every((d)=>d.senderID.endsWith(`@${platform.PlatformVideoSource.Bilibili}`));
25058
25186
  if (!ok) throw new Error('存在其他来源的senderID,请关闭该功能再试!');
25059
25187
  }
25188
+ let ds = this.dans.map((dan)=>dan.toBiliXML(options));
25189
+ if (options?.skipBiliCommand) ds = ds.filter((d)=>null !== d);
25060
25190
  const builder = new json2xml({
25061
25191
  ignoreAttributes: false
25062
25192
  });
@@ -25077,7 +25207,7 @@
25077
25207
  ...DanUniConvertTipTemplate,
25078
25208
  data: this.getShared('SOID')
25079
25209
  },
25080
- d: this.dans.map((dan)=>dan.toBiliXML(options))
25210
+ d: ds
25081
25211
  }
25082
25212
  });
25083
25213
  }
@@ -25241,7 +25371,7 @@
25241
25371
  function main(that) {
25242
25372
  that.dans.forEach((d)=>{
25243
25373
  if (d.platform !== platform.PlatformVideoSource.Bilibili) throw new Error('bili-dedupe: 仅支持B站(主站)的弹幕');
25244
- if (!d.extra.bili?.dmid) throw new Error('bili-dedupe: 弹幕缺少bili extra dmid字段');
25374
+ if (!d.extra.bili?.dmid && !d.extra.bili?.command?.id) throw new Error('bili-dedupe: 弹幕缺少bili extra dmid字段');
25245
25375
  });
25246
25376
  const map = new Map();
25247
25377
  that.dans.forEach((d)=>map.set(d.extra.bili.dmid, d));