UNPKG

docxml

Version:

TypeScript (component) library for building and parsing a DOCX file

114 lines (113 loc) 4.56 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.Registry = void 0; const fontoxpath_1 = __importDefault(require("fontoxpath")); class Registry { /** * A class that you instantiate to contain "metadata" associated with certain XML nodes. The metadata could be anything, * but in context of being an "xml renderer" you'll probably want to use it for templates or React components. * * See also {@link GenericRenderer} and {@link ReactRenderer} which extend the `Registry` class and add a `.render()` * method to it. * * Render functions (metadata) are associated with XML nodes via an XPath test. For any given node, the renderer will * use the metadata associated the most specific test that matches the node. */ constructor(...sets) { /** * All test/value sets known to this registery. Is kept in descending order of test specificity because {@link * Registry.optimize} is always called when modifying this set through public methods. */ Object.defineProperty(this, "sets", { enumerable: true, configurable: true, writable: true, value: [] }); this.merge(...sets); } /** * Reorders the sets based on XPath test specificity so that an `Array#find` finds the closest matching test * first. * * For example `<b />` could match tests `self::b` as well as `self::b[not(child::*)]`, but the latter is more * specific so it wins. * * This method is private because it is already called at all relevant times. Calling it again will normally not * yield any different results. */ optimize() { this.sets = this.sets // Sort alphabetically by test to get a consistent sorting even if selectors are equally specific .sort((setLeft, setRight) => setLeft.test.localeCompare(setRight.test)) // Sort by descreasing specificity as determined by fontoxpath .sort((setLeft, setRight) => fontoxpath_1.default.compareSpecificity(setRight.test, setLeft.test)); } get length() { return this.sets.length; } /** * Merges other registry instances into this one, and optimizes ({@link Registry.optimize}) when done. */ merge(...sets) { this.sets = sets.reduce((sets, registry) => sets // Remove any duplicates from the pre-existing set .filter((set) => !registry.sets.some((s) => s.test === set.test)) .concat(registry.sets), this.sets); this.optimize(); return this; } /** * Add a test/value set to the registry, and optimizes ({@link Registry.optimize}). */ add(test, value) { if (value === undefined) { throw new TypeError('Required to pass a value when adding to registry.'); } if (this.sets.some((set) => set.test === test)) { throw new TypeError('Refusing to add a selector in duplicate, use #overwrite() instead.'); } this.sets.push({ test, value, }); this.optimize(); return this; } overwrite(test, value) { if (value === undefined) { throw new TypeError('Required to pass a value when overwriting to registry, use #remove() instead.'); } const index = this.sets.findIndex((set) => set.test === test); if (index < 0) { throw new TypeError('Refusing to overwrite a selector because it was never set before.'); } this.sets.splice(index, 1, { test, value, }); return this; } /** * Remove a test/value set from the registry. This is the opposite of the {@link Registry.add} method. */ remove(test) { const index = this.sets.findIndex((set) => set.test === test); if (index >= 0) { this.sets.splice(index, 1).length === 1; } return this; } /** * Retrieve the metadata that was associated with this node before. If there are several rules that match, `.find` * gives you only the value of the best match. */ find(node) { const set = this.sets.find((set) => fontoxpath_1.default.evaluateXPathToBoolean(set.test, node)); return set?.value; } } exports.Registry = Registry;