| 71 | } |
| 72 | |
| 73 | export default class OrderedObjParser { |
| 74 | constructor(options, externalEntities) { |
| 75 | this.options = options; |
| 76 | this.currentNode = null; |
| 77 | this.tagsNodeStack = []; |
| 78 | this.parseXml = parseXml; |
| 79 | this.parseTextData = parseTextData; |
| 80 | this.resolveNameSpace = resolveNameSpace; |
| 81 | this.buildAttributesMap = buildAttributesMap; |
| 82 | this.isItStopNode = isItStopNode; |
| 83 | this.replaceEntitiesValue = replaceEntitiesValue; |
| 84 | this.readStopNodeData = readStopNodeData; |
| 85 | this.saveTextToParentTag = saveTextToParentTag; |
| 86 | this.addChild = addChild; |
| 87 | this.ignoreAttributesFn = getIgnoreAttributesFn(this.options.ignoreAttributes) |
| 88 | this.entityExpansionCount = 0; |
| 89 | this.currentExpandedLength = 0; |
| 90 | this.doctypefound = false; |
| 91 | let namedEntities = { ...XML }; |
| 92 | if (this.options.entityDecoder) { |
| 93 | this.entityDecoder = this.options.entityDecoder |
| 94 | } else { |
| 95 | if (typeof this.options.htmlEntities === "object") namedEntities = this.options.htmlEntities; |
| 96 | else if (this.options.htmlEntities === true) namedEntities = { ...COMMON_HTML, ...CURRENCY }; |
| 97 | this.entityDecoder = new EntityDecoder({ |
| 98 | namedEntities: { ...namedEntities, ...externalEntities }, |
| 99 | numericAllowed: this.options.htmlEntities, |
| 100 | limit: { |
| 101 | maxTotalExpansions: this.options.processEntities.maxTotalExpansions, |
| 102 | maxExpandedLength: this.options.processEntities.maxExpandedLength, |
| 103 | applyLimitsTo: this.options.processEntities.appliesTo, |
| 104 | }, |
| 105 | // onExternalEntity: (name, value) => isUnsafe(value) ? 'block' : 'allow', |
| 106 | onInputEntity: (name, value) => |
| 107 | //TODO: VALID_CONTEXTS.HTML should be set only if this.options.htmlEntities |
| 108 | isUnsafe(value, [HTML_CONTEXT, XML_CONTEXT]) ? ENTITY_ACTION.BLOCK : ENTITY_ACTION.ALLOW, |
| 109 | |
| 110 | //postCheck: resolved => resolved |
| 111 | }); |
| 112 | } |
| 113 | |
| 114 | // Initialize path matcher for path-expression-matcher |
| 115 | this.matcher = new Matcher(); |
| 116 | this.readonlyMatcher = this.matcher.readOnly(); |
| 117 | |
| 118 | // Flag to track if current node is a stop node (optimization) |
| 119 | this.isCurrentNodeStopNode = false; |
| 120 | |
| 121 | // Pre-compile stopNodes expressions |
| 122 | this.stopNodeExpressionsSet = new ExpressionSet(); |
| 123 | const stopNodesOpts = this.options.stopNodes; |
| 124 | if (stopNodesOpts && stopNodesOpts.length > 0) { |
| 125 | for (let i = 0; i < stopNodesOpts.length; i++) { |
| 126 | const stopNodeExp = stopNodesOpts[i]; |
| 127 | if (typeof stopNodeExp === 'string') { |
| 128 | // Convert string to Expression object |
| 129 | this.stopNodeExpressionsSet.add(new Expression(stopNodeExp)); |
| 130 | } else if (stopNodeExp instanceof Expression) { |
nothing calls this directly
no outgoing calls
no test coverage detected