import { importShared } from "./__federation_fn_import-hlt2XzeI.js"; import { c as createLucideIcon, s as shimExports, L as List } from "./index-Coq9nAfE.js"; import { j as jsxRuntimeExports } from "./jsx-runtime-CvJTHeKY.js"; import { E as Editor, N as Node3, w as wrappingInputRule, h, m as mergeAttributes, P as Plugin, a as PluginKey, T as TextSelection$1, t as textblockTypeInputRule, S as Selection$1, n as nodeInputRule, c as canInsertNode, i as isNodeSelection, b as NodeSelection$1, r as renderNestedMarkdownContent, d as Extension, e as isNodeActive, f as isAtStartOfNode, g as isAtEndOfNode, j as getNodeAtPosition, k as getNodeType, F as Fragment$1, l as getRenderedAttributes, p as parseIndentedBlocks, o as dropPoint, q as callOrReturn, s as getExtensionField, u as gapCursor, v as history, x as redo, y as undo, z as isNodeEmpty, D as DecorationSet, A as Decoration, B as createStyleTag, C as Bold$3, G as Code$2, I as Italic$3, L as Link$2, H as Strike$2, J as Text$2, U as Underline, K as Schema, M as Mark, O as extensions_exports, Q as DOMParser, R as Mark$1, V as getHTMLFromFragment, W as index_default$2, } from "./index-Bs--Ol2m.js"; import { w as withSelectorExports } from "./with-selector-B2V6shWK.js"; import { g as getDefaultExportFromCjs } from "./_commonjsHelpers-B85MJLTf.js"; /** * @license lucide-react v0.468.0 - ISC * * This source code is licensed under the ISC license. * See the LICENSE file in the root directory of this source tree. */ const Bold$2 = createLucideIcon("Bold", [["path", { d: "M6 12h9a4 4 0 0 1 0 8H7a1 1 0 0 1-1-1V5a1 1 0 0 1 1-1h7a4 4 0 0 1 0 8", key: "mg9rjx" }]]); /** * @license lucide-react v0.468.0 - ISC * * This source code is licensed under the ISC license. * See the LICENSE file in the root directory of this source tree. */ const Italic$2 = createLucideIcon("Italic", [ ["line", { x1: "19", x2: "10", y1: "4", y2: "4", key: "15jd3p" }], ["line", { x1: "14", x2: "5", y1: "20", y2: "20", key: "bu0au3" }], ["line", { x1: "15", x2: "9", y1: "4", y2: "20", key: "uljnxc" }], ]); /** * @license lucide-react v0.468.0 - ISC * * This source code is licensed under the ISC license. * See the LICENSE file in the root directory of this source tree. */ const Link2 = createLucideIcon("Link2", [ ["path", { d: "M9 17H7A5 5 0 0 1 7 7h2", key: "8i5ue5" }], ["path", { d: "M15 7h2a5 5 0 1 1 0 10h-2", key: "1b9ql8" }], ["line", { x1: "8", x2: "16", y1: "12", y2: "12", key: "1jonct" }], ]); /** * @license lucide-react v0.468.0 - ISC * * This source code is licensed under the ISC license. * See the LICENSE file in the root directory of this source tree. */ const ListOrdered = createLucideIcon("ListOrdered", [ ["path", { d: "M10 12h11", key: "6m4ad9" }], ["path", { d: "M10 18h11", key: "11hvi2" }], ["path", { d: "M10 6h11", key: "c7qv1k" }], ["path", { d: "M4 10h2", key: "16xx2s" }], ["path", { d: "M4 6h1v4", key: "cnovpq" }], ["path", { d: "M6 18H4c0-1 2-2 2-3s-1-1.5-2-1", key: "m9a95d" }], ]); /** * @license lucide-react v0.468.0 - ISC * * This source code is licensed under the ISC license. * See the LICENSE file in the root directory of this source tree. */ const Quote = createLucideIcon("Quote", [ [ "path", { d: "M16 3a2 2 0 0 0-2 2v6a2 2 0 0 0 2 2 1 1 0 0 1 1 1v1a2 2 0 0 1-2 2 1 1 0 0 0-1 1v2a1 1 0 0 0 1 1 6 6 0 0 0 6-6V5a2 2 0 0 0-2-2z", key: "rib7q0", }, ], [ "path", { d: "M5 3a2 2 0 0 0-2 2v6a2 2 0 0 0 2 2 1 1 0 0 1 1 1v1a2 2 0 0 1-2 2 1 1 0 0 0-1 1v2a1 1 0 0 0 1 1 6 6 0 0 0 6-6V5a2 2 0 0 0-2-2z", key: "1ymkrd", }, ], ]); const { getOwnPropertyNames, getOwnPropertySymbols } = Object; // eslint-disable-next-line @typescript-eslint/unbound-method const { hasOwnProperty } = Object.prototype; /** * Combine two comparators into a single comparators. */ function combineComparators(comparatorA, comparatorB) { return function isEqual(a, b, state) { return comparatorA(a, b, state) && comparatorB(a, b, state); }; } /** * Wrap the provided `areItemsEqual` method to manage the circular state, allowing * for circular references to be safely included in the comparison without creating * stack overflows. */ function createIsCircular(areItemsEqual) { return function isCircular(a, b, state) { if (!a || !b || typeof a !== "object" || typeof b !== "object") { return areItemsEqual(a, b, state); } const { cache } = state; const cachedA = cache.get(a); const cachedB = cache.get(b); if (cachedA && cachedB) { return cachedA === b && cachedB === a; } cache.set(a, b); cache.set(b, a); const result = areItemsEqual(a, b, state); cache.delete(a); cache.delete(b); return result; }; } /** * Get the `@@toStringTag` of the value, if it exists. */ function getShortTag(value) { return value != null ? value[Symbol.toStringTag] : undefined; } /** * Get the properties to strictly examine, which include both own properties that are * not enumerable and symbol properties. */ function getStrictProperties(object) { return getOwnPropertyNames(object).concat(getOwnPropertySymbols(object)); } /** * Whether the object contains the property passed as an own property. */ const hasOwn = // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition Object.hasOwn || ((object, property) => hasOwnProperty.call(object, property)); /** * Whether the values passed are strictly equal or both NaN. */ function sameValueZeroEqual(a, b) { return a === b || (!a && !b && a !== a && b !== b); } const PREACT_VNODE = "__v"; const PREACT_OWNER = "__o"; const REACT_OWNER = "_owner"; const { getOwnPropertyDescriptor, keys } = Object; /** * Whether the array buffers are equal in value. */ function areArrayBuffersEqual(a, b) { return a.byteLength === b.byteLength && areTypedArraysEqual(new Uint8Array(a), new Uint8Array(b)); } /** * Whether the arrays are equal in value. */ function areArraysEqual(a, b, state) { let index = a.length; if (b.length !== index) { return false; } while (index-- > 0) { if (!state.equals(a[index], b[index], index, index, a, b, state)) { return false; } } return true; } /** * Whether the dataviews are equal in value. */ function areDataViewsEqual(a, b) { return a.byteLength === b.byteLength && areTypedArraysEqual(new Uint8Array(a.buffer, a.byteOffset, a.byteLength), new Uint8Array(b.buffer, b.byteOffset, b.byteLength)); } /** * Whether the dates passed are equal in value. */ function areDatesEqual(a, b) { return sameValueZeroEqual(a.getTime(), b.getTime()); } /** * Whether the errors passed are equal in value. */ function areErrorsEqual(a, b) { return a.name === b.name && a.message === b.message && a.cause === b.cause && a.stack === b.stack; } /** * Whether the functions passed are equal in value. */ function areFunctionsEqual(a, b) { return a === b; } /** * Whether the `Map`s are equal in value. */ function areMapsEqual(a, b, state) { const size = a.size; if (size !== b.size) { return false; } if (!size) { return true; } const matchedIndices = new Array(size); const aIterable = a.entries(); let aResult; let bResult; let index = 0; // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition while ((aResult = aIterable.next())) { if (aResult.done) { break; } const bIterable = b.entries(); let hasMatch = false; let matchIndex = 0; // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition while ((bResult = bIterable.next())) { if (bResult.done) { break; } if (matchedIndices[matchIndex]) { matchIndex++; continue; } const aEntry = aResult.value; const bEntry = bResult.value; if (state.equals(aEntry[0], bEntry[0], index, matchIndex, a, b, state) && state.equals(aEntry[1], bEntry[1], aEntry[0], bEntry[0], a, b, state)) { hasMatch = matchedIndices[matchIndex] = true; break; } matchIndex++; } if (!hasMatch) { return false; } index++; } return true; } /** * Whether the numbers are equal in value. */ const areNumbersEqual = sameValueZeroEqual; /** * Whether the objects are equal in value. */ function areObjectsEqual(a, b, state) { const properties = keys(a); let index = properties.length; if (keys(b).length !== index) { return false; } // Decrementing `while` showed faster results than either incrementing or // decrementing `for` loop and than an incrementing `while` loop. Declarative // methods like `some` / `every` were not used to avoid incurring the garbage // cost of anonymous callbacks. while (index-- > 0) { if (!isPropertyEqual(a, b, state, properties[index])) { return false; } } return true; } /** * Whether the objects are equal in value with strict property checking. */ function areObjectsEqualStrict(a, b, state) { const properties = getStrictProperties(a); let index = properties.length; if (getStrictProperties(b).length !== index) { return false; } let property; let descriptorA; let descriptorB; // Decrementing `while` showed faster results than either incrementing or // decrementing `for` loop and than an incrementing `while` loop. Declarative // methods like `some` / `every` were not used to avoid incurring the garbage // cost of anonymous callbacks. while (index-- > 0) { property = properties[index]; if (!isPropertyEqual(a, b, state, property)) { return false; } descriptorA = getOwnPropertyDescriptor(a, property); descriptorB = getOwnPropertyDescriptor(b, property); if ( (descriptorA || descriptorB) && (!descriptorA || !descriptorB || descriptorA.configurable !== descriptorB.configurable || descriptorA.enumerable !== descriptorB.enumerable || descriptorA.writable !== descriptorB.writable) ) { return false; } } return true; } /** * Whether the primitive wrappers passed are equal in value. */ function arePrimitiveWrappersEqual(a, b) { return sameValueZeroEqual(a.valueOf(), b.valueOf()); } /** * Whether the regexps passed are equal in value. */ function areRegExpsEqual(a, b) { return a.source === b.source && a.flags === b.flags; } /** * Whether the `Set`s are equal in value. */ function areSetsEqual(a, b, state) { const size = a.size; if (size !== b.size) { return false; } if (!size) { return true; } const matchedIndices = new Array(size); const aIterable = a.values(); let aResult; let bResult; // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition while ((aResult = aIterable.next())) { if (aResult.done) { break; } const bIterable = b.values(); let hasMatch = false; let matchIndex = 0; // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition while ((bResult = bIterable.next())) { if (bResult.done) { break; } if (!matchedIndices[matchIndex] && state.equals(aResult.value, bResult.value, aResult.value, bResult.value, a, b, state)) { hasMatch = matchedIndices[matchIndex] = true; break; } matchIndex++; } if (!hasMatch) { return false; } } return true; } /** * Whether the TypedArray instances are equal in value. */ function areTypedArraysEqual(a, b) { let index = a.byteLength; if (b.byteLength !== index || a.byteOffset !== b.byteOffset) { return false; } while (index-- > 0) { if (a[index] !== b[index]) { return false; } } return true; } /** * Whether the URL instances are equal in value. */ function areUrlsEqual(a, b) { return a.hostname === b.hostname && a.pathname === b.pathname && a.protocol === b.protocol && a.port === b.port && a.hash === b.hash && a.username === b.username && a.password === b.password; } function isPropertyEqual(a, b, state, property) { if ((property === REACT_OWNER || property === PREACT_OWNER || property === PREACT_VNODE) && (a.$$typeof || b.$$typeof)) { return true; } return hasOwn(b, property) && state.equals(a[property], b[property], property, property, a, b, state); } const ARRAY_BUFFER_TAG = "[object ArrayBuffer]"; const ARGUMENTS_TAG = "[object Arguments]"; const BOOLEAN_TAG = "[object Boolean]"; const DATA_VIEW_TAG = "[object DataView]"; const DATE_TAG = "[object Date]"; const ERROR_TAG = "[object Error]"; const MAP_TAG = "[object Map]"; const NUMBER_TAG = "[object Number]"; const OBJECT_TAG = "[object Object]"; const REG_EXP_TAG = "[object RegExp]"; const SET_TAG = "[object Set]"; const STRING_TAG = "[object String]"; const TYPED_ARRAY_TAGS = { "[object Int8Array]": true, "[object Uint8Array]": true, "[object Uint8ClampedArray]": true, "[object Int16Array]": true, "[object Uint16Array]": true, "[object Int32Array]": true, "[object Uint32Array]": true, "[object Float16Array]": true, "[object Float32Array]": true, "[object Float64Array]": true, "[object BigInt64Array]": true, "[object BigUint64Array]": true, }; const URL_TAG = "[object URL]"; // eslint-disable-next-line @typescript-eslint/unbound-method const toString = Object.prototype.toString; /** * Create a comparator method based on the type-specific equality comparators passed. */ function createEqualityComparator({ areArrayBuffersEqual, areArraysEqual, areDataViewsEqual, areDatesEqual, areErrorsEqual, areFunctionsEqual, areMapsEqual, areNumbersEqual, areObjectsEqual, arePrimitiveWrappersEqual, areRegExpsEqual, areSetsEqual, areTypedArraysEqual, areUrlsEqual, unknownTagComparators, }) { /** * compare the value of the two objects and return true if they are equivalent in values */ return function comparator(a, b, state) { // If the items are strictly equal, no need to do a value comparison. if (a === b) { return true; } // If either of the items are nullish and fail the strictly equal check // above, then they must be unequal. if (a == null || b == null) { return false; } const type = typeof a; if (type !== typeof b) { return false; } if (type !== "object") { if (type === "number") { return areNumbersEqual(a, b, state); } if (type === "function") { return areFunctionsEqual(a, b, state); } // If a primitive value that is not strictly equal, it must be unequal. return false; } const constructor = a.constructor; // Checks are listed in order of commonality of use-case: // 1. Common complex object types (plain object, array) // 2. Common data values (date, regexp) // 3. Less-common complex object types (map, set) // 4. Less-common data values (promise, primitive wrappers) // Inherently this is both subjective and assumptive, however // when reviewing comparable libraries in the wild this order // appears to be generally consistent. // Constructors should match, otherwise there is potential for false positives // between class and subclass or custom object and POJO. if (constructor !== b.constructor) { return false; } // `isPlainObject` only checks against the object's own realm. Cross-realm // comparisons are rare, and will be handled in the ultimate fallback, so // we can avoid capturing the string tag. if (constructor === Object) { return areObjectsEqual(a, b, state); } // `isArray()` works on subclasses and is cross-realm, so we can avoid capturing // the string tag or doing an `instanceof` check. if (Array.isArray(a)) { return areArraysEqual(a, b, state); } // Try to fast-path equality checks for other complex object types in the // same realm to avoid capturing the string tag. Strict equality is used // instead of `instanceof` because it is more performant for the common // use-case. If someone is subclassing a native class, it will be handled // with the string tag comparison. if (constructor === Date) { return areDatesEqual(a, b, state); } if (constructor === RegExp) { return areRegExpsEqual(a, b, state); } if (constructor === Map) { return areMapsEqual(a, b, state); } if (constructor === Set) { return areSetsEqual(a, b, state); } // Since this is a custom object, capture the string tag to determing its type. // This is reasonably performant in modern environments like v8 and SpiderMonkey. const tag = toString.call(a); if (tag === DATE_TAG) { return areDatesEqual(a, b, state); } // For RegExp, the properties are not enumerable, and therefore will give false positives if // tested like a standard object. if (tag === REG_EXP_TAG) { return areRegExpsEqual(a, b, state); } if (tag === MAP_TAG) { return areMapsEqual(a, b, state); } if (tag === SET_TAG) { return areSetsEqual(a, b, state); } if (tag === OBJECT_TAG) { // The exception for value comparison is custom `Promise`-like class instances. These should // be treated the same as standard `Promise` objects, which means strict equality, and if // it reaches this point then that strict equality comparison has already failed. return typeof a.then !== "function" && typeof b.then !== "function" && areObjectsEqual(a, b, state); } // If a URL tag, it should be tested explicitly. Like RegExp, the properties are not // enumerable, and therefore will give false positives if tested like a standard object. if (tag === URL_TAG) { return areUrlsEqual(a, b, state); } // If an error tag, it should be tested explicitly. Like RegExp, the properties are not // enumerable, and therefore will give false positives if tested like a standard object. if (tag === ERROR_TAG) { return areErrorsEqual(a, b, state); } // If an arguments tag, it should be treated as a standard object. if (tag === ARGUMENTS_TAG) { return areObjectsEqual(a, b, state); } if (TYPED_ARRAY_TAGS[tag]) { return areTypedArraysEqual(a, b, state); } if (tag === ARRAY_BUFFER_TAG) { return areArrayBuffersEqual(a, b, state); } if (tag === DATA_VIEW_TAG) { return areDataViewsEqual(a, b, state); } // As the penultimate fallback, check if the values passed are primitive wrappers. This // is very rare in modern JS, which is why it is deprioritized compared to all other object // types. if (tag === BOOLEAN_TAG || tag === NUMBER_TAG || tag === STRING_TAG) { return arePrimitiveWrappersEqual(a, b, state); } if (unknownTagComparators) { let unknownTagComparator = unknownTagComparators[tag]; if (!unknownTagComparator) { const shortTag = getShortTag(a); if (shortTag) { unknownTagComparator = unknownTagComparators[shortTag]; } } // If the custom config has an unknown tag comparator that matches the captured tag or the // @@toStringTag, it is the source of truth for whether the values are equal. if (unknownTagComparator) { return unknownTagComparator(a, b, state); } } // If not matching any tags that require a specific type of comparison, then we hard-code false because // the only thing remaining is strict equality, which has already been compared. This is for a few reasons: // - Certain types that cannot be introspected (e.g., `WeakMap`). For these types, this is the only // comparison that can be made. // - For types that can be introspected, but rarely have requirements to be compared // (`ArrayBuffer`, `DataView`, etc.), the cost is avoided to prioritize the common // use-cases (may be included in a future release, if requested enough). // - For types that can be introspected but do not have an objective definition of what // equality is (`Error`, etc.), the subjective decision is to be conservative and strictly compare. // In all cases, these decisions should be reevaluated based on changes to the language and // common development practices. return false; }; } /** * Create the configuration object used for building comparators. */ function createEqualityComparatorConfig({ circular, createCustomConfig, strict }) { let config = { areArrayBuffersEqual, areArraysEqual: strict ? areObjectsEqualStrict : areArraysEqual, areDataViewsEqual, areDatesEqual: areDatesEqual, areErrorsEqual: areErrorsEqual, areFunctionsEqual: areFunctionsEqual, areMapsEqual: strict ? combineComparators(areMapsEqual, areObjectsEqualStrict) : areMapsEqual, areNumbersEqual: areNumbersEqual, areObjectsEqual: strict ? areObjectsEqualStrict : areObjectsEqual, arePrimitiveWrappersEqual: arePrimitiveWrappersEqual, areRegExpsEqual: areRegExpsEqual, areSetsEqual: strict ? combineComparators(areSetsEqual, areObjectsEqualStrict) : areSetsEqual, areTypedArraysEqual: strict ? combineComparators(areTypedArraysEqual, areObjectsEqualStrict) : areTypedArraysEqual, areUrlsEqual: areUrlsEqual, unknownTagComparators: undefined, }; if (createCustomConfig) { config = Object.assign({}, config, createCustomConfig(config)); } if (circular) { const areArraysEqual = createIsCircular(config.areArraysEqual); const areMapsEqual = createIsCircular(config.areMapsEqual); const areObjectsEqual = createIsCircular(config.areObjectsEqual); const areSetsEqual = createIsCircular(config.areSetsEqual); config = Object.assign({}, config, { areArraysEqual, areMapsEqual, areObjectsEqual, areSetsEqual, }); } return config; } /** * Default equality comparator pass-through, used as the standard `isEqual` creator for * use inside the built comparator. */ function createInternalEqualityComparator(compare) { return function (a, b, _indexOrKeyA, _indexOrKeyB, _parentA, _parentB, state) { return compare(a, b, state); }; } /** * Create the `isEqual` function used by the consuming application. */ function createIsEqual({ circular, comparator, createState, equals, strict }) { if (createState) { return function isEqual(a, b) { const { cache = circular ? new WeakMap() : undefined, meta } = createState(); return comparator(a, b, { cache, equals, meta, strict, }); }; } if (circular) { return function isEqual(a, b) { return comparator(a, b, { cache: new WeakMap(), equals, meta: undefined, strict, }); }; } const state = { cache: undefined, equals, meta: undefined, strict, }; return function isEqual(a, b) { return comparator(a, b, state); }; } /** * Whether the items passed are deeply-equal in value. */ const deepEqual = createCustomEqual(); /** * Whether the items passed are deeply-equal in value based on strict comparison. */ createCustomEqual({ strict: true }); /** * Whether the items passed are deeply-equal in value, including circular references. */ createCustomEqual({ circular: true }); /** * Whether the items passed are deeply-equal in value, including circular references, * based on strict comparison. */ createCustomEqual({ circular: true, strict: true, }); /** * Whether the items passed are shallowly-equal in value. */ createCustomEqual({ createInternalComparator: () => sameValueZeroEqual, }); /** * Whether the items passed are shallowly-equal in value based on strict comparison */ createCustomEqual({ strict: true, createInternalComparator: () => sameValueZeroEqual, }); /** * Whether the items passed are shallowly-equal in value, including circular references. */ createCustomEqual({ circular: true, createInternalComparator: () => sameValueZeroEqual, }); /** * Whether the items passed are shallowly-equal in value, including circular references, * based on strict comparison. */ createCustomEqual({ circular: true, createInternalComparator: () => sameValueZeroEqual, strict: true, }); /** * Create a custom equality comparison method. * * This can be done to create very targeted comparisons in extreme hot-path scenarios * where the standard methods are not performant enough, but can also be used to provide * support for legacy environments that do not support expected features like * `RegExp.prototype.flags` out of the box. */ function createCustomEqual(options = {}) { const { circular = false, createInternalComparator: createCustomInternalComparator, createState, strict = false } = options; const config = createEqualityComparatorConfig(options); const comparator = createEqualityComparator(config); const equals = createCustomInternalComparator ? createCustomInternalComparator(comparator) : createInternalEqualityComparator(comparator); return createIsEqual({ circular, comparator, createState, equals, strict }); } const { createContext, useContext, useMemo } = await importShared("react"); const React = await importShared("react"); const { forwardRef } = React; const ReactDOM = await importShared("react-dom"); var mergeRefs = (...refs) => { return (node) => { refs.forEach((ref) => { if (typeof ref === "function") { ref(node); } else if (ref) { ref.current = node; } }); }; }; var Portals = ({ contentComponent }) => { const renderers = shimExports.useSyncExternalStore(contentComponent.subscribe, contentComponent.getSnapshot, contentComponent.getServerSnapshot); return /* @__PURE__ */ jsxRuntimeExports.jsx(jsxRuntimeExports.Fragment, { children: Object.values(renderers) }); }; function getInstance() { const subscribers = /* @__PURE__ */ new Set(); let renderers = {}; return { /** * Subscribe to the editor instance's changes. */ subscribe(callback) { subscribers.add(callback); return () => { subscribers.delete(callback); }; }, getSnapshot() { return renderers; }, getServerSnapshot() { return renderers; }, /** * Adds a new NodeView Renderer to the editor. */ setRenderer(id, renderer) { renderers = { ...renderers, [id]: ReactDOM.createPortal(renderer.reactElement, renderer.element, id), }; subscribers.forEach((subscriber) => subscriber()); }, /** * Removes a NodeView Renderer from the editor. */ removeRenderer(id) { const nextRenderers = { ...renderers }; delete nextRenderers[id]; renderers = nextRenderers; subscribers.forEach((subscriber) => subscriber()); }, }; } var PureEditorContent = class extends React.Component { constructor(props) { super(props); this.editorContentRef = React.createRef(); } componentDidMount() { this.init(); } componentDidUpdate() { this.init(); } init() { var _a; const editor = this.props.editor; if (editor && !editor.isDestroyed && ((_a = editor.view.dom) == null ? void 0 : _a.parentNode)) { if (editor.contentComponent) { return; } const element = this.editorContentRef.current; element.append(...editor.view.dom.parentNode.childNodes); editor.setOptions({ element, }); editor.contentComponent = getInstance(); editor.createNodeViews(); editor.isEditorContentInitialized = true; this.forceUpdate(); } } componentWillUnmount() { var _a; const editor = this.props.editor; if (!editor) { return; } editor.isEditorContentInitialized = false; if (!editor.isDestroyed) { editor.view.setProps({ nodeViews: {}, }); } editor.contentComponent = null; try { if (!((_a = editor.view.dom) == null ? void 0 : _a.parentNode)) { return; } const newElement = document.createElement("div"); newElement.append(...editor.view.dom.parentNode.childNodes); editor.setOptions({ element: newElement, }); } catch {} } render() { const { editor, innerRef, ...rest } = this.props; return /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { ref: mergeRefs(innerRef, this.editorContentRef), ...rest }), (editor == null ? void 0 : editor.contentComponent) && /* @__PURE__ */ jsxRuntimeExports.jsx(Portals, { contentComponent: editor.contentComponent }), ], }); } }; var EditorContentWithKey = forwardRef((props, ref) => { const key = React.useMemo(() => { return Math.floor(Math.random() * 4294967295).toString(); }, [props.editor]); return React.createElement(PureEditorContent, { key, innerRef: ref, ...props, }); }); var EditorContent = React.memo(EditorContentWithKey); const { useDebugValue: useDebugValue2, useEffect: useEffect2, useRef, useState: useState2 } = await importShared("react"); const { useDebugValue, useEffect: useEffect$1, useLayoutEffect, useState } = await importShared("react"); var useIsomorphicLayoutEffect = typeof window !== "undefined" ? useLayoutEffect : useEffect$1; var EditorStateManager = class { constructor(initialEditor) { this.transactionNumber = 0; this.lastTransactionNumber = 0; this.subscribers = /* @__PURE__ */ new Set(); this.editor = initialEditor; this.lastSnapshot = { editor: initialEditor, transactionNumber: 0 }; this.getSnapshot = this.getSnapshot.bind(this); this.getServerSnapshot = this.getServerSnapshot.bind(this); this.watch = this.watch.bind(this); this.subscribe = this.subscribe.bind(this); } /** * Get the current editor instance. */ getSnapshot() { if (this.transactionNumber === this.lastTransactionNumber) { return this.lastSnapshot; } this.lastTransactionNumber = this.transactionNumber; this.lastSnapshot = { editor: this.editor, transactionNumber: this.transactionNumber }; return this.lastSnapshot; } /** * Always disable the editor on the server-side. */ getServerSnapshot() { return { editor: null, transactionNumber: 0 }; } /** * Subscribe to the editor instance's changes. */ subscribe(callback) { this.subscribers.add(callback); return () => { this.subscribers.delete(callback); }; } /** * Watch the editor instance for changes. */ watch(nextEditor) { this.editor = nextEditor; if (this.editor) { const fn = () => { this.transactionNumber += 1; this.subscribers.forEach((callback) => callback()); }; const currentEditor = this.editor; currentEditor.on("transaction", fn); return () => { currentEditor.off("transaction", fn); }; } return void 0; } }; function useEditorState(options) { var _a; const [editorStateManager] = useState(() => new EditorStateManager(options.editor)); const selectedState = withSelectorExports.useSyncExternalStoreWithSelector( editorStateManager.subscribe, editorStateManager.getSnapshot, editorStateManager.getServerSnapshot, options.selector, (_a = options.equalityFn) != null ? _a : deepEqual, ); useIsomorphicLayoutEffect(() => { return editorStateManager.watch(options.editor); }, [options.editor, editorStateManager]); useDebugValue(selectedState); return selectedState; } var isDev = false; var isSSR = typeof window === "undefined"; var isNext = isSSR || Boolean(typeof window !== "undefined" && window.next); var EditorInstanceManager = class _EditorInstanceManager { constructor(options) { this.editor = null; this.subscriptions = /* @__PURE__ */ new Set(); this.isComponentMounted = false; this.previousDeps = null; this.instanceId = ""; this.options = options; this.subscriptions = /* @__PURE__ */ new Set(); this.setEditor(this.getInitialEditor()); this.scheduleDestroy(); this.getEditor = this.getEditor.bind(this); this.getServerSnapshot = this.getServerSnapshot.bind(this); this.subscribe = this.subscribe.bind(this); this.refreshEditorInstance = this.refreshEditorInstance.bind(this); this.scheduleDestroy = this.scheduleDestroy.bind(this); this.onRender = this.onRender.bind(this); this.createEditor = this.createEditor.bind(this); } setEditor(editor) { this.editor = editor; this.instanceId = Math.random().toString(36).slice(2, 9); this.subscriptions.forEach((cb) => cb()); } getInitialEditor() { const explicit = this.options.current.immediatelyRender; let immediatelyRender = explicit != null ? explicit : true; if (isSSR) { if (immediatelyRender && isDev) { console.warn("SSR detected. `immediatelyRender` has been set to false to avoid hydration mismatches"); } immediatelyRender = false; } else if (isNext && explicit === void 0) { immediatelyRender = false; } return immediatelyRender ? this.createEditor() : null; } /** * Create a new editor instance. And attach event listeners. */ createEditor() { const optionsToApply = { ...this.options.current, // Always call the most recent version of the callback function by default onBeforeCreate: (...args) => { var _a, _b; return (_b = (_a = this.options.current).onBeforeCreate) == null ? void 0 : _b.call(_a, ...args); }, onBlur: (...args) => { var _a, _b; return (_b = (_a = this.options.current).onBlur) == null ? void 0 : _b.call(_a, ...args); }, onCreate: (...args) => { var _a, _b; return (_b = (_a = this.options.current).onCreate) == null ? void 0 : _b.call(_a, ...args); }, onDestroy: (...args) => { var _a, _b; return (_b = (_a = this.options.current).onDestroy) == null ? void 0 : _b.call(_a, ...args); }, onFocus: (...args) => { var _a, _b; return (_b = (_a = this.options.current).onFocus) == null ? void 0 : _b.call(_a, ...args); }, onSelectionUpdate: (...args) => { var _a, _b; return (_b = (_a = this.options.current).onSelectionUpdate) == null ? void 0 : _b.call(_a, ...args); }, onTransaction: (...args) => { var _a, _b; return (_b = (_a = this.options.current).onTransaction) == null ? void 0 : _b.call(_a, ...args); }, onUpdate: (...args) => { var _a, _b; return (_b = (_a = this.options.current).onUpdate) == null ? void 0 : _b.call(_a, ...args); }, onContentError: (...args) => { var _a, _b; return (_b = (_a = this.options.current).onContentError) == null ? void 0 : _b.call(_a, ...args); }, onDrop: (...args) => { var _a, _b; return (_b = (_a = this.options.current).onDrop) == null ? void 0 : _b.call(_a, ...args); }, onPaste: (...args) => { var _a, _b; return (_b = (_a = this.options.current).onPaste) == null ? void 0 : _b.call(_a, ...args); }, onDelete: (...args) => { var _a, _b; return (_b = (_a = this.options.current).onDelete) == null ? void 0 : _b.call(_a, ...args); }, }; const editor = new Editor(optionsToApply); return editor; } /** * Get the current editor instance. */ getEditor() { return this.editor; } /** * Always disable the editor on the server-side. */ getServerSnapshot() { return null; } /** * Subscribe to the editor instance's changes. */ subscribe(onStoreChange) { this.subscriptions.add(onStoreChange); return () => { this.subscriptions.delete(onStoreChange); }; } static compareOptions(a, b) { return Object.keys(a).every((key) => { if (["onCreate", "onBeforeCreate", "onDestroy", "onUpdate", "onTransaction", "onFocus", "onBlur", "onSelectionUpdate", "onContentError", "onDrop", "onPaste"].includes(key)) { return true; } if (key === "extensions" && a.extensions && b.extensions) { if (a.extensions.length !== b.extensions.length) { return false; } return a.extensions.every((extension, index) => { var _a; if (extension !== ((_a = b.extensions) == null ? void 0 : _a[index])) { return false; } return true; }); } if (a[key] !== b[key]) { return false; } return true; }); } /** * On each render, we will create, update, or destroy the editor instance. * @param deps The dependencies to watch for changes * @returns A cleanup function */ onRender(deps) { return () => { this.isComponentMounted = true; clearTimeout(this.scheduledDestructionTimeout); if (this.editor && !this.editor.isDestroyed && deps.length === 0) { if (!_EditorInstanceManager.compareOptions(this.options.current, this.editor.options)) { this.editor.setOptions({ ...this.options.current, editable: this.editor.isEditable, }); } } else { this.refreshEditorInstance(deps); } return () => { this.isComponentMounted = false; this.scheduleDestroy(); }; }; } /** * Recreate the editor instance if the dependencies have changed. */ refreshEditorInstance(deps) { if (this.editor && !this.editor.isDestroyed) { if (this.previousDeps === null) { this.previousDeps = deps; return; } const depsAreEqual = this.previousDeps.length === deps.length && this.previousDeps.every((dep, index) => dep === deps[index]); if (depsAreEqual) { return; } } if (this.editor && !this.editor.isDestroyed) { this.editor.destroy(); } this.setEditor(this.createEditor()); this.previousDeps = deps; } /** * Schedule the destruction of the editor instance. * This will only destroy the editor if it was not mounted on the next tick. * This is to avoid destroying the editor instance when it's actually still mounted. */ scheduleDestroy() { const currentInstanceId = this.instanceId; const currentEditor = this.editor; this.scheduledDestructionTimeout = setTimeout(() => { if (this.isComponentMounted && this.instanceId === currentInstanceId) { if (currentEditor) { currentEditor.setOptions(this.options.current); } return; } if (currentEditor && !currentEditor.isDestroyed) { currentEditor.destroy(); if (this.instanceId === currentInstanceId) { this.setEditor(null); } } }, 1); } }; function useEditor(options = {}, deps = []) { const mostRecentOptions = useRef(options); mostRecentOptions.current = options; const [instanceManager] = useState2(() => new EditorInstanceManager(mostRecentOptions)); const editor = shimExports.useSyncExternalStore(instanceManager.subscribe, instanceManager.getEditor, instanceManager.getServerSnapshot); useDebugValue2(editor); useEffect2(instanceManager.onRender(deps)); useEditorState({ editor, selector: ({ transactionNumber }) => { if (options.shouldRerenderOnTransaction === false || options.shouldRerenderOnTransaction === void 0) { return null; } if (options.immediatelyRender && transactionNumber === 0) { return 0; } return transactionNumber + 1; }, }); return editor; } var EditorContext = createContext({ editor: null, }); EditorContext.Consumer; const { createContext: createContext2, createElement, useContext: useContext2 } = await importShared("react"); var ReactNodeViewContext = createContext2({ onDragStart: () => {}, nodeViewContentChildren: void 0, nodeViewContentRef: () => {}, }); var useReactNodeView = () => useContext2(ReactNodeViewContext); const React3 = await importShared("react"); React3.forwardRef((props, ref) => { const { onDragStart } = useReactNodeView(); const Tag = props.as || "div"; return ( // @ts-ignore /* @__PURE__ */ jsxRuntimeExports.jsx(Tag, { ...props, ref, "data-node-view-wrapper": "", onDragStart, "style": { whiteSpace: "normal", ...props.style, }, }) ); }); const React4 = await importShared("react"); const { version: reactVersion } = await importShared("react"); const { flushSync } = await importShared("react-dom"); React4.createContext({ markViewContentRef: () => {}, }); const { createElement: createElement2, createRef, memo } = await importShared("react"); const { createContext: createContext3, useContext: useContext3, useMemo: useMemo2 } = await importShared("react"); var TiptapContext = createContext3({ get editor() { throw new Error("useTiptap must be used within a provider"); }, }); TiptapContext.displayName = "TiptapContext"; var useTiptap = () => useContext3(TiptapContext); function TiptapWrapper({ editor, instance, children }) { const resolvedEditor = editor != null ? editor : instance; if (!resolvedEditor) { throw new Error("Tiptap: An editor instance is required. Pass a non-null `editor` prop."); } const tiptapContextValue = useMemo2(() => ({ editor: resolvedEditor }), [resolvedEditor]); const legacyContextValue = useMemo2(() => ({ editor: resolvedEditor }), [resolvedEditor]); return /* @__PURE__ */ jsxRuntimeExports.jsx(EditorContext.Provider, { value: legacyContextValue, children: /* @__PURE__ */ jsxRuntimeExports.jsx(TiptapContext.Provider, { value: tiptapContextValue, children }), }); } TiptapWrapper.displayName = "Tiptap"; function TiptapContent({ ...rest }) { const { editor } = useTiptap(); return /* @__PURE__ */ jsxRuntimeExports.jsx(EditorContent, { editor, ...rest }); } TiptapContent.displayName = "Tiptap.Content"; Object.assign(TiptapWrapper, { /** * The Tiptap Content component that renders the EditorContent with the editor instance from the context. * @see TiptapContent */ Content: TiptapContent, }); // src/blockquote.tsx // ../../node_modules/.pnpm/prosemirror-model@1.25.7/node_modules/prosemirror-model/dist/index.js function findDiffStart(a, b, pos) { for (let i = 0; ; i++) { if (i == a.childCount || i == b.childCount) return a.childCount == b.childCount ? null : pos; let childA = a.child(i), childB = b.child(i); if (childA == childB) { pos += childA.nodeSize; continue; } if (!childA.sameMarkup(childB)) return pos; if (childA.isText && childA.text != childB.text) { for (let j = 0; childA.text[j] == childB.text[j]; j++) pos++; return pos; } if (childA.content.size || childB.content.size) { let inner = findDiffStart(childA.content, childB.content, pos + 1); if (inner != null) return inner; } pos += childA.nodeSize; } } function findDiffEnd(a, b, posA, posB) { for (let iA = a.childCount, iB = b.childCount; ; ) { if (iA == 0 || iB == 0) return iA == iB ? null : { a: posA, b: posB }; let childA = a.child(--iA), childB = b.child(--iB), size = childA.nodeSize; if (childA == childB) { posA -= size; posB -= size; continue; } if (!childA.sameMarkup(childB)) return { a: posA, b: posB }; if (childA.isText && childA.text != childB.text) { let same = 0, minSize = Math.min(childA.text.length, childB.text.length); while (same < minSize && childA.text[childA.text.length - same - 1] == childB.text[childB.text.length - same - 1]) { same++; posA--; posB--; } return { a: posA, b: posB }; } if (childA.content.size || childB.content.size) { let inner = findDiffEnd(childA.content, childB.content, posA - 1, posB - 1); if (inner) return inner; } posA -= size; posB -= size; } } var Fragment = class _Fragment { /** @internal */ constructor(content, size) { this.content = content; this.size = size || 0; if (size == null) for (let i = 0; i < content.length; i++) this.size += content[i].nodeSize; } /** Invoke a callback for all descendant nodes between the given two positions (relative to start of this fragment). Doesn't descend into a node when the callback returns `false`. */ nodesBetween(from, to, f, nodeStart = 0, parent) { for (let i = 0, pos = 0; pos < to; i++) { let child = this.content[i], end = pos + child.nodeSize; if (end > from && f(child, nodeStart + pos, parent || null, i) !== false && child.content.size) { let start = pos + 1; child.nodesBetween(Math.max(0, from - start), Math.min(child.content.size, to - start), f, nodeStart + start); } pos = end; } } /** Call the given callback for every descendant node. `pos` will be relative to the start of the fragment. The callback may return `false` to prevent traversal of a given node's children. */ descendants(f) { this.nodesBetween(0, this.size, f); } /** Extract the text between `from` and `to`. See the same method on [`Node`](https://prosemirror.net/docs/ref/#model.Node.textBetween). */ textBetween(from, to, blockSeparator, leafText) { let text = "", first = true; this.nodesBetween( from, to, (node, pos) => { let nodeText = node.isText ? node.text.slice(Math.max(from, pos) - pos, to - pos) : !node.isLeaf ? "" : leafText ? typeof leafText === "function" ? leafText(node) : leafText : node.type.spec.leafText ? node.type.spec.leafText(node) : ""; if (node.isBlock && ((node.isLeaf && nodeText) || node.isTextblock) && blockSeparator) { if (first) first = false; else text += blockSeparator; } text += nodeText; }, 0, ); return text; } /** Create a new fragment containing the combined content of this fragment and the other. */ append(other) { if (!other.size) return this; if (!this.size) return other; let last = this.lastChild, first = other.firstChild, content = this.content.slice(), i = 0; if (last.isText && last.sameMarkup(first)) { content[content.length - 1] = last.withText(last.text + first.text); i = 1; } for (; i < other.content.length; i++) content.push(other.content[i]); return new _Fragment(content, this.size + other.size); } /** Cut out the sub-fragment between the two given positions. */ cut(from, to = this.size) { if (from == 0 && to == this.size) return this; let result = [], size = 0; if (to > from) for (let i = 0, pos = 0; pos < to; i++) { let child = this.content[i], end = pos + child.nodeSize; if (end > from) { if (pos < from || end > to) { if (child.isText) child = child.cut(Math.max(0, from - pos), Math.min(child.text.length, to - pos)); else child = child.cut(Math.max(0, from - pos - 1), Math.min(child.content.size, to - pos - 1)); } result.push(child); size += child.nodeSize; } pos = end; } return new _Fragment(result, size); } /** @internal */ cutByIndex(from, to) { if (from == to) return _Fragment.empty; if (from == 0 && to == this.content.length) return this; return new _Fragment(this.content.slice(from, to)); } /** Create a new fragment in which the node at the given index is replaced by the given node. */ replaceChild(index, node) { let current = this.content[index]; if (current == node) return this; let copy = this.content.slice(); let size = this.size + node.nodeSize - current.nodeSize; copy[index] = node; return new _Fragment(copy, size); } /** Create a new fragment by prepending the given node to this fragment. */ addToStart(node) { return new _Fragment([node].concat(this.content), this.size + node.nodeSize); } /** Create a new fragment by appending the given node to this fragment. */ addToEnd(node) { return new _Fragment(this.content.concat(node), this.size + node.nodeSize); } /** Compare this fragment to another one. */ eq(other) { if (this.content.length != other.content.length) return false; for (let i = 0; i < this.content.length; i++) if (!this.content[i].eq(other.content[i])) return false; return true; } /** The first child of the fragment, or `null` if it is empty. */ get firstChild() { return this.content.length ? this.content[0] : null; } /** The last child of the fragment, or `null` if it is empty. */ get lastChild() { return this.content.length ? this.content[this.content.length - 1] : null; } /** The number of child nodes in this fragment. */ get childCount() { return this.content.length; } /** Get the child node at the given index. Raise an error when the index is out of range. */ child(index) { let found2 = this.content[index]; if (!found2) throw new RangeError("Index " + index + " out of range for " + this); return found2; } /** Get the child node at the given index, if it exists. */ maybeChild(index) { return this.content[index] || null; } /** Call `f` for every child node, passing the node, its offset into this parent node, and its index. */ forEach(f) { for (let i = 0, p = 0; i < this.content.length; i++) { let child = this.content[i]; f(child, p, i); p += child.nodeSize; } } /** Find the first position at which this fragment and another fragment differ, or `null` if they are the same. */ findDiffStart(other, pos = 0) { return findDiffStart(this, other, pos); } /** Find the first position, searching from the end, at which this fragment and the given fragment differ, or `null` if they are the same. Since this position will not be the same in both nodes, an object with two separate positions is returned. */ findDiffEnd(other, pos = this.size, otherPos = other.size) { return findDiffEnd(this, other, pos, otherPos); } /** Find the index and inner offset corresponding to a given relative position in this fragment. The result object will be reused (overwritten) the next time the function is called. @internal */ findIndex(pos) { if (pos == 0) return retIndex(0, pos); if (pos == this.size) return retIndex(this.content.length, pos); if (pos > this.size || pos < 0) throw new RangeError(`Position ${pos} outside of fragment (${this})`); for (let i = 0, curPos = 0; ; i++) { let cur = this.child(i), end = curPos + cur.nodeSize; if (end >= pos) { if (end == pos) return retIndex(i + 1, end); return retIndex(i, curPos); } curPos = end; } } /** Return a debugging string that describes this fragment. */ toString() { return "<" + this.toStringInner() + ">"; } /** @internal */ toStringInner() { return this.content.join(", "); } /** Create a JSON-serializeable representation of this fragment. */ toJSON() { return this.content.length ? this.content.map((n) => n.toJSON()) : null; } /** Deserialize a fragment from its JSON representation. */ static fromJSON(schema, value) { if (!value) return _Fragment.empty; if (!Array.isArray(value)) throw new RangeError("Invalid input for Fragment.fromJSON"); return _Fragment.fromArray(value.map(schema.nodeFromJSON)); } /** Build a fragment from an array of nodes. Ensures that adjacent text nodes with the same marks are joined together. */ static fromArray(array) { if (!array.length) return _Fragment.empty; let joined, size = 0; for (let i = 0; i < array.length; i++) { let node = array[i]; size += node.nodeSize; if (i && node.isText && array[i - 1].sameMarkup(node)) { if (!joined) joined = array.slice(0, i); joined[joined.length - 1] = node.withText(joined[joined.length - 1].text + node.text); } else if (joined) { joined.push(node); } } return new _Fragment(joined || array, size); } /** Create a fragment from something that can be interpreted as a set of nodes. For `null`, it returns the empty fragment. For a fragment, the fragment itself. For a node or array of nodes, a fragment containing those nodes. */ static from(nodes) { if (!nodes) return _Fragment.empty; if (nodes instanceof _Fragment) return nodes; if (Array.isArray(nodes)) return this.fromArray(nodes); if (nodes.attrs) return new _Fragment([nodes], nodes.nodeSize); throw new RangeError("Can not convert " + nodes + " to a Fragment" + (nodes.nodesBetween ? " (looks like multiple versions of prosemirror-model were loaded)" : "")); } }; Fragment.empty = new Fragment([], 0); var found = { index: 0, offset: 0 }; function retIndex(index, offset) { found.index = index; found.offset = offset; return found; } var ReplaceError = class extends Error {}; var Slice = class _Slice { /** Create a slice. When specifying a non-zero open depth, you must make sure that there are nodes of at least that depth at the appropriate side of the fragment—i.e. if the fragment is an empty paragraph node, `openStart` and `openEnd` can't be greater than 1. It is not necessary for the content of open nodes to conform to the schema's content constraints, though it should be a valid start/end/middle for such a node, depending on which sides are open. */ constructor(content, openStart, openEnd) { this.content = content; this.openStart = openStart; this.openEnd = openEnd; } /** The size this slice would add when inserted into a document. */ get size() { return this.content.size - this.openStart - this.openEnd; } /** @internal */ insertAt(pos, fragment) { let content = insertInto(this.content, pos + this.openStart, fragment, this.openStart + 1, this.openEnd + 1); return content && new _Slice(content, this.openStart, this.openEnd); } /** @internal */ removeBetween(from, to) { return new _Slice(removeRange(this.content, from + this.openStart, to + this.openStart), this.openStart, this.openEnd); } /** Tests whether this slice is equal to another slice. */ eq(other) { return this.content.eq(other.content) && this.openStart == other.openStart && this.openEnd == other.openEnd; } /** @internal */ toString() { return this.content + "(" + this.openStart + "," + this.openEnd + ")"; } /** Convert a slice to a JSON-serializable representation. */ toJSON() { if (!this.content.size) return null; let json = { content: this.content.toJSON() }; if (this.openStart > 0) json.openStart = this.openStart; if (this.openEnd > 0) json.openEnd = this.openEnd; return json; } /** Deserialize a slice from its JSON representation. */ static fromJSON(schema, json) { if (!json) return _Slice.empty; let openStart = json.openStart || 0, openEnd = json.openEnd || 0; if (typeof openStart != "number" || typeof openEnd != "number") throw new RangeError("Invalid input for Slice.fromJSON"); return new _Slice(Fragment.fromJSON(schema, json.content), openStart, openEnd); } /** Create a slice from a fragment by taking the maximum possible open value on both side of the fragment. */ static maxOpen(fragment, openIsolating = true) { let openStart = 0, openEnd = 0; for (let n = fragment.firstChild; n && !n.isLeaf && (openIsolating || !n.type.spec.isolating); n = n.firstChild) openStart++; for (let n = fragment.lastChild; n && !n.isLeaf && (openIsolating || !n.type.spec.isolating); n = n.lastChild) openEnd++; return new _Slice(fragment, openStart, openEnd); } }; Slice.empty = new Slice(Fragment.empty, 0, 0); function removeRange(content, from, to) { let { index, offset } = content.findIndex(from), child = content.maybeChild(index); let { index: indexTo, offset: offsetTo } = content.findIndex(to); if (offset == from || child.isText) { if (offsetTo != to && !content.child(indexTo).isText) throw new RangeError("Removing non-flat range"); return content.cut(0, from).append(content.cut(to)); } if (index != indexTo) throw new RangeError("Removing non-flat range"); return content.replaceChild(index, child.copy(removeRange(child.content, from - offset - 1, to - offset - 1))); } function insertInto(content, dist, insert, openStart, openEnd, parent) { let { index, offset } = content.findIndex(dist), child = content.maybeChild(index); if (offset == dist || child.isText) { if (parent && openStart <= 0 && openEnd <= 0 && !parent.canReplace(index, index, insert)) return null; return content.cut(0, dist).append(insert).append(content.cut(dist)); } let inner = insertInto(child.content, dist - offset - 1, insert, index == 0 ? openStart - 1 : 0, index == content.childCount - 1 ? openEnd - 1 : 0, child); return inner && content.replaceChild(index, child.copy(inner)); } // ../../node_modules/.pnpm/prosemirror-transform@1.12.0/node_modules/prosemirror-transform/dist/index.js var lower16 = 65535; var factor16 = Math.pow(2, 16); function makeRecover(index, offset) { return index + offset * factor16; } function recoverIndex(value) { return value & lower16; } function recoverOffset(value) { return (value - (value & lower16)) / factor16; } var DEL_BEFORE = 1; var DEL_AFTER = 2; var DEL_ACROSS = 4; var DEL_SIDE = 8; var MapResult = class { /** @internal */ constructor(pos, delInfo, recover) { this.pos = pos; this.delInfo = delInfo; this.recover = recover; } /** Tells you whether the position was deleted, that is, whether the step removed the token on the side queried (via the `assoc`) argument from the document. */ get deleted() { return (this.delInfo & DEL_SIDE) > 0; } /** Tells you whether the token before the mapped position was deleted. */ get deletedBefore() { return (this.delInfo & (DEL_BEFORE | DEL_ACROSS)) > 0; } /** True when the token after the mapped position was deleted. */ get deletedAfter() { return (this.delInfo & (DEL_AFTER | DEL_ACROSS)) > 0; } /** Tells whether any of the steps mapped through deletes across the position (including both the token before and after the position). */ get deletedAcross() { return (this.delInfo & DEL_ACROSS) > 0; } }; var StepMap = class _StepMap { /** Create a position map. The modifications to the document are represented as an array of numbers, in which each group of three represents a modified chunk as `[start, oldSize, newSize]`. */ constructor(ranges, inverted = false) { this.ranges = ranges; this.inverted = inverted; if (!ranges.length && _StepMap.empty) return _StepMap.empty; } /** @internal */ recover(value) { let diff = 0, index = recoverIndex(value); if (!this.inverted) for (let i = 0; i < index; i++) diff += this.ranges[i * 3 + 2] - this.ranges[i * 3 + 1]; return this.ranges[index * 3] + diff + recoverOffset(value); } mapResult(pos, assoc = 1) { return this._map(pos, assoc, false); } map(pos, assoc = 1) { return this._map(pos, assoc, true); } /** @internal */ _map(pos, assoc, simple) { let diff = 0, oldIndex = this.inverted ? 2 : 1, newIndex = this.inverted ? 1 : 2; for (let i = 0; i < this.ranges.length; i += 3) { let start = this.ranges[i] - (this.inverted ? diff : 0); if (start > pos) break; let oldSize = this.ranges[i + oldIndex], newSize = this.ranges[i + newIndex], end = start + oldSize; if (pos <= end) { let side = !oldSize ? assoc : pos == start ? -1 : pos == end ? 1 : assoc; let result = start + diff + (side < 0 ? 0 : newSize); if (simple) return result; let recover = pos == (assoc < 0 ? start : end) ? null : makeRecover(i / 3, pos - start); let del = pos == start ? DEL_AFTER : pos == end ? DEL_BEFORE : DEL_ACROSS; if (assoc < 0 ? pos != start : pos != end) del |= DEL_SIDE; return new MapResult(result, del, recover); } diff += newSize - oldSize; } return simple ? pos + diff : new MapResult(pos + diff, 0, null); } /** @internal */ touches(pos, recover) { let diff = 0, index = recoverIndex(recover); let oldIndex = this.inverted ? 2 : 1, newIndex = this.inverted ? 1 : 2; for (let i = 0; i < this.ranges.length; i += 3) { let start = this.ranges[i] - (this.inverted ? diff : 0); if (start > pos) break; let oldSize = this.ranges[i + oldIndex], end = start + oldSize; if (pos <= end && i == index * 3) return true; diff += this.ranges[i + newIndex] - oldSize; } return false; } /** Calls the given function on each of the changed ranges included in this map. */ forEach(f) { let oldIndex = this.inverted ? 2 : 1, newIndex = this.inverted ? 1 : 2; for (let i = 0, diff = 0; i < this.ranges.length; i += 3) { let start = this.ranges[i], oldStart = start - (this.inverted ? diff : 0), newStart = start + (this.inverted ? 0 : diff); let oldSize = this.ranges[i + oldIndex], newSize = this.ranges[i + newIndex]; f(oldStart, oldStart + oldSize, newStart, newStart + newSize); diff += newSize - oldSize; } } /** Create an inverted version of this map. The result can be used to map positions in the post-step document to the pre-step document. */ invert() { return new _StepMap(this.ranges, !this.inverted); } /** @internal */ toString() { return (this.inverted ? "-" : "") + JSON.stringify(this.ranges); } /** Create a map that moves all positions by offset `n` (which may be negative). This can be useful when applying steps meant for a sub-document to a larger document, or vice-versa. */ static offset(n) { return n == 0 ? _StepMap.empty : new _StepMap(n < 0 ? [0, -n, 0] : [0, 0, n]); } }; StepMap.empty = new StepMap([]); var stepsByID = /* @__PURE__ */ Object.create(null); var Step = class { /** Get the step map that represents the changes made by this step, and which can be used to transform between positions in the old and the new document. */ getMap() { return StepMap.empty; } /** Try to merge this step with another one, to be applied directly after it. Returns the merged step when possible, null if the steps can't be merged. */ merge(other) { return null; } /** Deserialize a step from its JSON representation. Will call through to the step class' own implementation of this method. */ static fromJSON(schema, json) { if (!json || !json.stepType) throw new RangeError("Invalid input for Step.fromJSON"); let type = stepsByID[json.stepType]; if (!type) throw new RangeError(`No step type ${json.stepType} defined`); return type.fromJSON(schema, json); } /** To be able to serialize steps to JSON, each step needs a string ID to attach to its JSON representation. Use this method to register an ID for your step classes. Try to pick something that's unlikely to clash with steps from other modules. */ static jsonID(id, stepClass) { if (id in stepsByID) throw new RangeError("Duplicate use of step JSON ID " + id); stepsByID[id] = stepClass; stepClass.prototype.jsonID = id; return stepClass; } }; var StepResult = class _StepResult { /** @internal */ constructor(doc, failed) { this.doc = doc; this.failed = failed; } /** Create a successful step result. */ static ok(doc) { return new _StepResult(doc, null); } /** Create a failed step result. */ static fail(message) { return new _StepResult(null, message); } /** Call [`Node.replace`](https://prosemirror.net/docs/ref/#model.Node.replace) with the given arguments. Create a successful result if it succeeds, and a failed one if it throws a `ReplaceError`. */ static fromReplace(doc, from, to, slice) { try { return _StepResult.ok(doc.replace(from, to, slice)); } catch (e) { if (e instanceof ReplaceError) return _StepResult.fail(e.message); throw e; } } }; function mapFragment(fragment, f, parent) { let mapped = []; for (let i = 0; i < fragment.childCount; i++) { let child = fragment.child(i); if (child.content.size) child = child.copy(mapFragment(child.content, f, child)); if (child.isInline) child = f(child, parent, i); mapped.push(child); } return Fragment.fromArray(mapped); } var AddMarkStep = class _AddMarkStep extends Step { /** Create a mark step. */ constructor(from, to, mark) { super(); this.from = from; this.to = to; this.mark = mark; } apply(doc) { let oldSlice = doc.slice(this.from, this.to), $from = doc.resolve(this.from); let parent = $from.node($from.sharedDepth(this.to)); let slice = new Slice( mapFragment( oldSlice.content, (node, parent2) => { if (!node.isAtom || !parent2.type.allowsMarkType(this.mark.type)) return node; return node.mark(this.mark.addToSet(node.marks)); }, parent, ), oldSlice.openStart, oldSlice.openEnd, ); return StepResult.fromReplace(doc, this.from, this.to, slice); } invert() { return new RemoveMarkStep(this.from, this.to, this.mark); } map(mapping) { let from = mapping.mapResult(this.from, 1), to = mapping.mapResult(this.to, -1); if ((from.deleted && to.deleted) || from.pos >= to.pos) return null; return new _AddMarkStep(from.pos, to.pos, this.mark); } merge(other) { if (other instanceof _AddMarkStep && other.mark.eq(this.mark) && this.from <= other.to && this.to >= other.from) return new _AddMarkStep(Math.min(this.from, other.from), Math.max(this.to, other.to), this.mark); return null; } toJSON() { return { stepType: "addMark", mark: this.mark.toJSON(), from: this.from, to: this.to, }; } /** @internal */ static fromJSON(schema, json) { if (typeof json.from != "number" || typeof json.to != "number") throw new RangeError("Invalid input for AddMarkStep.fromJSON"); return new _AddMarkStep(json.from, json.to, schema.markFromJSON(json.mark)); } }; Step.jsonID("addMark", AddMarkStep); var RemoveMarkStep = class _RemoveMarkStep extends Step { /** Create a mark-removing step. */ constructor(from, to, mark) { super(); this.from = from; this.to = to; this.mark = mark; } apply(doc) { let oldSlice = doc.slice(this.from, this.to); let slice = new Slice( mapFragment( oldSlice.content, (node) => { return node.mark(this.mark.removeFromSet(node.marks)); }, doc, ), oldSlice.openStart, oldSlice.openEnd, ); return StepResult.fromReplace(doc, this.from, this.to, slice); } invert() { return new AddMarkStep(this.from, this.to, this.mark); } map(mapping) { let from = mapping.mapResult(this.from, 1), to = mapping.mapResult(this.to, -1); if ((from.deleted && to.deleted) || from.pos >= to.pos) return null; return new _RemoveMarkStep(from.pos, to.pos, this.mark); } merge(other) { if (other instanceof _RemoveMarkStep && other.mark.eq(this.mark) && this.from <= other.to && this.to >= other.from) return new _RemoveMarkStep(Math.min(this.from, other.from), Math.max(this.to, other.to), this.mark); return null; } toJSON() { return { stepType: "removeMark", mark: this.mark.toJSON(), from: this.from, to: this.to, }; } /** @internal */ static fromJSON(schema, json) { if (typeof json.from != "number" || typeof json.to != "number") throw new RangeError("Invalid input for RemoveMarkStep.fromJSON"); return new _RemoveMarkStep(json.from, json.to, schema.markFromJSON(json.mark)); } }; Step.jsonID("removeMark", RemoveMarkStep); var AddNodeMarkStep = class _AddNodeMarkStep extends Step { /** Create a node mark step. */ constructor(pos, mark) { super(); this.pos = pos; this.mark = mark; } apply(doc) { let node = doc.nodeAt(this.pos); if (!node) return StepResult.fail("No node at mark step's position"); let updated = node.type.create(node.attrs, null, this.mark.addToSet(node.marks)); return StepResult.fromReplace(doc, this.pos, this.pos + 1, new Slice(Fragment.from(updated), 0, node.isLeaf ? 0 : 1)); } invert(doc) { let node = doc.nodeAt(this.pos); if (node) { let newSet = this.mark.addToSet(node.marks); if (newSet.length == node.marks.length) { for (let i = 0; i < node.marks.length; i++) if (!node.marks[i].isInSet(newSet)) return new _AddNodeMarkStep(this.pos, node.marks[i]); return new _AddNodeMarkStep(this.pos, this.mark); } } return new RemoveNodeMarkStep(this.pos, this.mark); } map(mapping) { let pos = mapping.mapResult(this.pos, 1); return pos.deletedAfter ? null : new _AddNodeMarkStep(pos.pos, this.mark); } toJSON() { return { stepType: "addNodeMark", pos: this.pos, mark: this.mark.toJSON() }; } /** @internal */ static fromJSON(schema, json) { if (typeof json.pos != "number") throw new RangeError("Invalid input for AddNodeMarkStep.fromJSON"); return new _AddNodeMarkStep(json.pos, schema.markFromJSON(json.mark)); } }; Step.jsonID("addNodeMark", AddNodeMarkStep); var RemoveNodeMarkStep = class _RemoveNodeMarkStep extends Step { /** Create a mark-removing step. */ constructor(pos, mark) { super(); this.pos = pos; this.mark = mark; } apply(doc) { let node = doc.nodeAt(this.pos); if (!node) return StepResult.fail("No node at mark step's position"); let updated = node.type.create(node.attrs, null, this.mark.removeFromSet(node.marks)); return StepResult.fromReplace(doc, this.pos, this.pos + 1, new Slice(Fragment.from(updated), 0, node.isLeaf ? 0 : 1)); } invert(doc) { let node = doc.nodeAt(this.pos); if (!node || !this.mark.isInSet(node.marks)) return this; return new AddNodeMarkStep(this.pos, this.mark); } map(mapping) { let pos = mapping.mapResult(this.pos, 1); return pos.deletedAfter ? null : new _RemoveNodeMarkStep(pos.pos, this.mark); } toJSON() { return { stepType: "removeNodeMark", pos: this.pos, mark: this.mark.toJSON() }; } /** @internal */ static fromJSON(schema, json) { if (typeof json.pos != "number") throw new RangeError("Invalid input for RemoveNodeMarkStep.fromJSON"); return new _RemoveNodeMarkStep(json.pos, schema.markFromJSON(json.mark)); } }; Step.jsonID("removeNodeMark", RemoveNodeMarkStep); var ReplaceStep = class _ReplaceStep extends Step { /** The given `slice` should fit the 'gap' between `from` and `to`—the depths must line up, and the surrounding nodes must be able to be joined with the open sides of the slice. When `structure` is true, the step will fail if the content between from and to is not just a sequence of closing and then opening tokens (this is to guard against rebased replace steps overwriting something they weren't supposed to). */ constructor(from, to, slice, structure = false) { super(); this.from = from; this.to = to; this.slice = slice; this.structure = structure; } apply(doc) { if (this.structure && contentBetween(doc, this.from, this.to)) return StepResult.fail("Structure replace would overwrite content"); return StepResult.fromReplace(doc, this.from, this.to, this.slice); } getMap() { return new StepMap([this.from, this.to - this.from, this.slice.size]); } invert(doc) { return new _ReplaceStep(this.from, this.from + this.slice.size, doc.slice(this.from, this.to)); } map(mapping) { let to = mapping.mapResult(this.to, -1); let from = this.from == this.to && _ReplaceStep.MAP_BIAS < 0 ? to : mapping.mapResult(this.from, 1); if (from.deletedAcross && to.deletedAcross) return null; return new _ReplaceStep(from.pos, Math.max(from.pos, to.pos), this.slice, this.structure); } merge(other) { if (!(other instanceof _ReplaceStep) || other.structure || this.structure) return null; if (this.from + this.slice.size == other.from && !this.slice.openEnd && !other.slice.openStart) { let slice = this.slice.size + other.slice.size == 0 ? Slice.empty : new Slice(this.slice.content.append(other.slice.content), this.slice.openStart, other.slice.openEnd); return new _ReplaceStep(this.from, this.to + (other.to - other.from), slice, this.structure); } else if (other.to == this.from && !this.slice.openStart && !other.slice.openEnd) { let slice = this.slice.size + other.slice.size == 0 ? Slice.empty : new Slice(other.slice.content.append(this.slice.content), other.slice.openStart, this.slice.openEnd); return new _ReplaceStep(other.from, this.to, slice, this.structure); } else { return null; } } toJSON() { let json = { stepType: "replace", from: this.from, to: this.to }; if (this.slice.size) json.slice = this.slice.toJSON(); if (this.structure) json.structure = true; return json; } /** @internal */ static fromJSON(schema, json) { if (typeof json.from != "number" || typeof json.to != "number") throw new RangeError("Invalid input for ReplaceStep.fromJSON"); return new _ReplaceStep(json.from, json.to, Slice.fromJSON(schema, json.slice), !!json.structure); } }; ReplaceStep.MAP_BIAS = 1; Step.jsonID("replace", ReplaceStep); var ReplaceAroundStep = class _ReplaceAroundStep extends Step { /** Create a replace-around step with the given range and gap. `insert` should be the point in the slice into which the content of the gap should be moved. `structure` has the same meaning as it has in the [`ReplaceStep`](https://prosemirror.net/docs/ref/#transform.ReplaceStep) class. */ constructor(from, to, gapFrom, gapTo, slice, insert, structure = false) { super(); this.from = from; this.to = to; this.gapFrom = gapFrom; this.gapTo = gapTo; this.slice = slice; this.insert = insert; this.structure = structure; } apply(doc) { if (this.structure && (contentBetween(doc, this.from, this.gapFrom) || contentBetween(doc, this.gapTo, this.to))) return StepResult.fail("Structure gap-replace would overwrite content"); let gap = doc.slice(this.gapFrom, this.gapTo); if (gap.openStart || gap.openEnd) return StepResult.fail("Gap is not a flat range"); let inserted = this.slice.insertAt(this.insert, gap.content); if (!inserted) return StepResult.fail("Content does not fit in gap"); return StepResult.fromReplace(doc, this.from, this.to, inserted); } getMap() { return new StepMap([this.from, this.gapFrom - this.from, this.insert, this.gapTo, this.to - this.gapTo, this.slice.size - this.insert]); } invert(doc) { let gap = this.gapTo - this.gapFrom; return new _ReplaceAroundStep( this.from, this.from + this.slice.size + gap, this.from + this.insert, this.from + this.insert + gap, doc.slice(this.from, this.to).removeBetween(this.gapFrom - this.from, this.gapTo - this.from), this.gapFrom - this.from, this.structure, ); } map(mapping) { let from = mapping.mapResult(this.from, 1), to = mapping.mapResult(this.to, -1); let gapFrom = this.from == this.gapFrom ? from.pos : mapping.map(this.gapFrom, -1); let gapTo = this.to == this.gapTo ? to.pos : mapping.map(this.gapTo, 1); if ((from.deletedAcross && to.deletedAcross) || gapFrom < from.pos || gapTo > to.pos) return null; return new _ReplaceAroundStep(from.pos, to.pos, gapFrom, gapTo, this.slice, this.insert, this.structure); } toJSON() { let json = { stepType: "replaceAround", from: this.from, to: this.to, gapFrom: this.gapFrom, gapTo: this.gapTo, insert: this.insert, }; if (this.slice.size) json.slice = this.slice.toJSON(); if (this.structure) json.structure = true; return json; } /** @internal */ static fromJSON(schema, json) { if (typeof json.from != "number" || typeof json.to != "number" || typeof json.gapFrom != "number" || typeof json.gapTo != "number" || typeof json.insert != "number") throw new RangeError("Invalid input for ReplaceAroundStep.fromJSON"); return new _ReplaceAroundStep(json.from, json.to, json.gapFrom, json.gapTo, Slice.fromJSON(schema, json.slice), json.insert, !!json.structure); } }; Step.jsonID("replaceAround", ReplaceAroundStep); function contentBetween(doc, from, to) { let $from = doc.resolve(from), dist = to - from, depth = $from.depth; while (dist > 0 && depth > 0 && $from.indexAfter(depth) == $from.node(depth).childCount) { depth--; dist--; } if (dist > 0) { let next = $from.node(depth).maybeChild($from.indexAfter(depth)); while (dist > 0) { if (!next || next.isLeaf) return true; next = next.firstChild; dist--; } } return false; } var AttrStep = class _AttrStep extends Step { /** Construct an attribute step. */ constructor(pos, attr, value) { super(); this.pos = pos; this.attr = attr; this.value = value; } apply(doc) { let node = doc.nodeAt(this.pos); if (!node) return StepResult.fail("No node at attribute step's position"); let attrs = /* @__PURE__ */ Object.create(null); for (let name in node.attrs) attrs[name] = node.attrs[name]; attrs[this.attr] = this.value; let updated = node.type.create(attrs, null, node.marks); return StepResult.fromReplace(doc, this.pos, this.pos + 1, new Slice(Fragment.from(updated), 0, node.isLeaf ? 0 : 1)); } getMap() { return StepMap.empty; } invert(doc) { return new _AttrStep(this.pos, this.attr, doc.nodeAt(this.pos).attrs[this.attr]); } map(mapping) { let pos = mapping.mapResult(this.pos, 1); return pos.deletedAfter ? null : new _AttrStep(pos.pos, this.attr, this.value); } toJSON() { return { stepType: "attr", pos: this.pos, attr: this.attr, value: this.value }; } static fromJSON(schema, json) { if (typeof json.pos != "number" || typeof json.attr != "string") throw new RangeError("Invalid input for AttrStep.fromJSON"); return new _AttrStep(json.pos, json.attr, json.value); } }; Step.jsonID("attr", AttrStep); var DocAttrStep = class _DocAttrStep extends Step { /** Construct an attribute step. */ constructor(attr, value) { super(); this.attr = attr; this.value = value; } apply(doc) { let attrs = /* @__PURE__ */ Object.create(null); for (let name in doc.attrs) attrs[name] = doc.attrs[name]; attrs[this.attr] = this.value; let updated = doc.type.create(attrs, doc.content, doc.marks); return StepResult.ok(updated); } getMap() { return StepMap.empty; } invert(doc) { return new _DocAttrStep(this.attr, doc.attrs[this.attr]); } map(mapping) { return this; } toJSON() { return { stepType: "docAttr", attr: this.attr, value: this.value }; } static fromJSON(schema, json) { if (typeof json.attr != "string") throw new RangeError("Invalid input for DocAttrStep.fromJSON"); return new _DocAttrStep(json.attr, json.value); } }; Step.jsonID("docAttr", DocAttrStep); var TransformError = class extends Error {}; TransformError = function TransformError2(message) { let err = Error.call(this, message); err.__proto__ = TransformError2.prototype; return err; }; TransformError.prototype = Object.create(Error.prototype); TransformError.prototype.constructor = TransformError; TransformError.prototype.name = "TransformError"; // ../../node_modules/.pnpm/prosemirror-state@1.4.4/node_modules/prosemirror-state/dist/index.js var classesById = /* @__PURE__ */ Object.create(null); var Selection = class { /** Initialize a selection with the head and anchor and ranges. If no ranges are given, constructs a single range across `$anchor` and `$head`. */ constructor($anchor, $head, ranges) { this.$anchor = $anchor; this.$head = $head; this.ranges = ranges || [new SelectionRange($anchor.min($head), $anchor.max($head))]; } /** The selection's anchor, as an unresolved position. */ get anchor() { return this.$anchor.pos; } /** The selection's head. */ get head() { return this.$head.pos; } /** The lower bound of the selection's main range. */ get from() { return this.$from.pos; } /** The upper bound of the selection's main range. */ get to() { return this.$to.pos; } /** The resolved lower bound of the selection's main range. */ get $from() { return this.ranges[0].$from; } /** The resolved upper bound of the selection's main range. */ get $to() { return this.ranges[0].$to; } /** Indicates whether the selection contains any content. */ get empty() { let ranges = this.ranges; for (let i = 0; i < ranges.length; i++) if (ranges[i].$from.pos != ranges[i].$to.pos) return false; return true; } /** Get the content of this selection as a slice. */ content() { return this.$from.doc.slice(this.from, this.to, true); } /** Replace the selection with a slice or, if no slice is given, delete the selection. Will append to the given transaction. */ replace(tr, content = Slice.empty) { let lastNode = content.content.lastChild, lastParent = null; for (let i = 0; i < content.openEnd; i++) { lastParent = lastNode; lastNode = lastNode.lastChild; } let mapFrom = tr.steps.length, ranges = this.ranges; for (let i = 0; i < ranges.length; i++) { let { $from, $to } = ranges[i], mapping = tr.mapping.slice(mapFrom); tr.replaceRange(mapping.map($from.pos), mapping.map($to.pos), i ? Slice.empty : content); if (i == 0) selectionToInsertionEnd( tr, mapFrom, ( lastNode ? lastNode.isInline : lastParent && lastParent.isTextblock ) ? -1 : 1, ); } } /** Replace the selection with the given node, appending the changes to the given transaction. */ replaceWith(tr, node) { let mapFrom = tr.steps.length, ranges = this.ranges; for (let i = 0; i < ranges.length; i++) { let { $from, $to } = ranges[i], mapping = tr.mapping.slice(mapFrom); let from = mapping.map($from.pos), to = mapping.map($to.pos); if (i) { tr.deleteRange(from, to); } else { tr.replaceRangeWith(from, to, node); selectionToInsertionEnd(tr, mapFrom, node.isInline ? -1 : 1); } } } /** Find a valid cursor or leaf node selection starting at the given position and searching back if `dir` is negative, and forward if positive. When `textOnly` is true, only consider cursor selections. Will return null when no valid selection position is found. */ static findFrom($pos, dir, textOnly = false) { let inner = $pos.parent.inlineContent ? new TextSelection($pos) : findSelectionIn($pos.node(0), $pos.parent, $pos.pos, $pos.index(), dir, textOnly); if (inner) return inner; for (let depth = $pos.depth - 1; depth >= 0; depth--) { let found2 = dir < 0 ? findSelectionIn($pos.node(0), $pos.node(depth), $pos.before(depth + 1), $pos.index(depth), dir, textOnly) : findSelectionIn($pos.node(0), $pos.node(depth), $pos.after(depth + 1), $pos.index(depth) + 1, dir, textOnly); if (found2) return found2; } return null; } /** Find a valid cursor or leaf node selection near the given position. Searches forward first by default, but if `bias` is negative, it will search backwards first. */ static near($pos, bias = 1) { return this.findFrom($pos, bias) || this.findFrom($pos, -bias) || new AllSelection($pos.node(0)); } /** Find the cursor or leaf node selection closest to the start of the given document. Will return an [`AllSelection`](https://prosemirror.net/docs/ref/#state.AllSelection) if no valid position exists. */ static atStart(doc) { return findSelectionIn(doc, doc, 0, 0, 1) || new AllSelection(doc); } /** Find the cursor or leaf node selection closest to the end of the given document. */ static atEnd(doc) { return findSelectionIn(doc, doc, doc.content.size, doc.childCount, -1) || new AllSelection(doc); } /** Deserialize the JSON representation of a selection. Must be implemented for custom classes (as a static class method). */ static fromJSON(doc, json) { if (!json || !json.type) throw new RangeError("Invalid input for Selection.fromJSON"); let cls = classesById[json.type]; if (!cls) throw new RangeError(`No selection type ${json.type} defined`); return cls.fromJSON(doc, json); } /** To be able to deserialize selections from JSON, custom selection classes must register themselves with an ID string, so that they can be disambiguated. Try to pick something that's unlikely to clash with classes from other modules. */ static jsonID(id, selectionClass) { if (id in classesById) throw new RangeError("Duplicate use of selection JSON ID " + id); classesById[id] = selectionClass; selectionClass.prototype.jsonID = id; return selectionClass; } /** Get a [bookmark](https://prosemirror.net/docs/ref/#state.SelectionBookmark) for this selection, which is a value that can be mapped without having access to a current document, and later resolved to a real selection for a given document again. (This is used mostly by the history to track and restore old selections.) The default implementation of this method just converts the selection to a text selection and returns the bookmark for that. */ getBookmark() { return TextSelection.between(this.$anchor, this.$head).getBookmark(); } }; Selection.prototype.visible = true; var SelectionRange = class { /** Create a range. */ constructor($from, $to) { this.$from = $from; this.$to = $to; } }; var warnedAboutTextSelection = false; function checkTextSelection($pos) { if (!warnedAboutTextSelection && !$pos.parent.inlineContent) { warnedAboutTextSelection = true; console["warn"]("TextSelection endpoint not pointing into a node with inline content (" + $pos.parent.type.name + ")"); } } var TextSelection = class _TextSelection extends Selection { /** Construct a text selection between the given points. */ constructor($anchor, $head = $anchor) { checkTextSelection($anchor); checkTextSelection($head); super($anchor, $head); } /** Returns a resolved position if this is a cursor selection (an empty text selection), and null otherwise. */ get $cursor() { return this.$anchor.pos == this.$head.pos ? this.$head : null; } map(doc, mapping) { let $head = doc.resolve(mapping.map(this.head)); if (!$head.parent.inlineContent) return Selection.near($head); let $anchor = doc.resolve(mapping.map(this.anchor)); return new _TextSelection($anchor.parent.inlineContent ? $anchor : $head, $head); } replace(tr, content = Slice.empty) { super.replace(tr, content); if (content == Slice.empty) { let marks = this.$from.marksAcross(this.$to); if (marks) tr.ensureMarks(marks); } } eq(other) { return other instanceof _TextSelection && other.anchor == this.anchor && other.head == this.head; } getBookmark() { return new TextBookmark(this.anchor, this.head); } toJSON() { return { type: "text", anchor: this.anchor, head: this.head }; } /** @internal */ static fromJSON(doc, json) { if (typeof json.anchor != "number" || typeof json.head != "number") throw new RangeError("Invalid input for TextSelection.fromJSON"); return new _TextSelection(doc.resolve(json.anchor), doc.resolve(json.head)); } /** Create a text selection from non-resolved positions. */ static create(doc, anchor, head = anchor) { let $anchor = doc.resolve(anchor); return new this($anchor, head == anchor ? $anchor : doc.resolve(head)); } /** Return a text selection that spans the given positions or, if they aren't text positions, find a text selection near them. `bias` determines whether the method searches forward (default) or backwards (negative number) first. Will fall back to calling [`Selection.near`](https://prosemirror.net/docs/ref/#state.Selection^near) when the document doesn't contain a valid text position. */ static between($anchor, $head, bias) { let dPos = $anchor.pos - $head.pos; if (!bias || dPos) bias = dPos >= 0 ? 1 : -1; if (!$head.parent.inlineContent) { let found2 = Selection.findFrom($head, bias, true) || Selection.findFrom($head, -bias, true); if (found2) $head = found2.$head; else return Selection.near($head, bias); } if (!$anchor.parent.inlineContent) { if (dPos == 0) { $anchor = $head; } else { $anchor = (Selection.findFrom($anchor, -bias, true) || Selection.findFrom($anchor, bias, true)).$anchor; if ($anchor.pos < $head.pos != dPos < 0) $anchor = $head; } } return new _TextSelection($anchor, $head); } }; Selection.jsonID("text", TextSelection); var TextBookmark = class _TextBookmark { constructor(anchor, head) { this.anchor = anchor; this.head = head; } map(mapping) { return new _TextBookmark(mapping.map(this.anchor), mapping.map(this.head)); } resolve(doc) { return TextSelection.between(doc.resolve(this.anchor), doc.resolve(this.head)); } }; var NodeSelection = class _NodeSelection extends Selection { /** Create a node selection. Does not verify the validity of its argument. */ constructor($pos) { let node = $pos.nodeAfter; let $end = $pos.node(0).resolve($pos.pos + node.nodeSize); super($pos, $end); this.node = node; } map(doc, mapping) { let { deleted, pos } = mapping.mapResult(this.anchor); let $pos = doc.resolve(pos); if (deleted) return Selection.near($pos); return new _NodeSelection($pos); } content() { return new Slice(Fragment.from(this.node), 0, 0); } eq(other) { return other instanceof _NodeSelection && other.anchor == this.anchor; } toJSON() { return { type: "node", anchor: this.anchor }; } getBookmark() { return new NodeBookmark(this.anchor); } /** @internal */ static fromJSON(doc, json) { if (typeof json.anchor != "number") throw new RangeError("Invalid input for NodeSelection.fromJSON"); return new _NodeSelection(doc.resolve(json.anchor)); } /** Create a node selection from non-resolved positions. */ static create(doc, from) { return new _NodeSelection(doc.resolve(from)); } /** Determines whether the given node may be selected as a node selection. */ static isSelectable(node) { return !node.isText && node.type.spec.selectable !== false; } }; NodeSelection.prototype.visible = false; Selection.jsonID("node", NodeSelection); var NodeBookmark = class _NodeBookmark { constructor(anchor) { this.anchor = anchor; } map(mapping) { let { deleted, pos } = mapping.mapResult(this.anchor); return deleted ? new TextBookmark(pos, pos) : new _NodeBookmark(pos); } resolve(doc) { let $pos = doc.resolve(this.anchor), node = $pos.nodeAfter; if (node && NodeSelection.isSelectable(node)) return new NodeSelection($pos); return Selection.near($pos); } }; var AllSelection = class _AllSelection extends Selection { /** Create an all-selection over the given document. */ constructor(doc) { super(doc.resolve(0), doc.resolve(doc.content.size)); } replace(tr, content = Slice.empty) { if (content == Slice.empty) { tr.delete(0, tr.doc.content.size); let sel = Selection.atStart(tr.doc); if (!sel.eq(tr.selection)) tr.setSelection(sel); } else { super.replace(tr, content); } } toJSON() { return { type: "all" }; } /** @internal */ static fromJSON(doc) { return new _AllSelection(doc); } map(doc) { return new _AllSelection(doc); } eq(other) { return other instanceof _AllSelection; } getBookmark() { return AllBookmark; } }; Selection.jsonID("all", AllSelection); var AllBookmark = { map() { return this; }, resolve(doc) { return new AllSelection(doc); }, }; function findSelectionIn(doc, node, pos, index, dir, text = false) { if (node.inlineContent) return TextSelection.create(doc, pos); for (let i = index - (dir > 0 ? 0 : 1); dir > 0 ? i < node.childCount : i >= 0; i += dir) { let child = node.child(i); if (!child.isAtom) { let inner = findSelectionIn(doc, child, pos + dir, dir < 0 ? child.childCount : 0, dir, text); if (inner) return inner; } else if (!text && NodeSelection.isSelectable(child)) { return NodeSelection.create(doc, pos - (dir < 0 ? child.nodeSize : 0)); } pos += child.nodeSize * dir; } return null; } function selectionToInsertionEnd(tr, startLen, bias) { let last = tr.steps.length - 1; if (last < startLen) return; let step = tr.steps[last]; if (!(step instanceof ReplaceStep || step instanceof ReplaceAroundStep)) return; let map = tr.mapping.maps[last], end; map.forEach((_from, _to, _newFrom, newTo) => { if (end == null) end = newTo; }); tr.setSelection(Selection.near(tr.doc.resolve(end), bias)); } function bind(f, self) { return !self || !f ? f : f.bind(self); } var FieldDesc = class { constructor(name, desc, self) { this.name = name; this.init = bind(desc.init, self); this.apply = bind(desc.apply, self); } }; [ new FieldDesc("doc", { init(config) { return config.doc || config.schema.topNodeType.createAndFill(); }, apply(tr) { return tr.doc; }, }), new FieldDesc("selection", { init(config, instance) { return config.selection || Selection.atStart(instance.doc); }, apply(tr) { return tr.selection; }, }), new FieldDesc("storedMarks", { init(config) { return config.storedMarks || null; }, apply(tr, _marks, _old, state) { return state.selection.$cursor ? tr.storedMarks : null; }, }), new FieldDesc("scrollToSelection", { init() { return 0; }, apply(tr, prev) { return tr.scrolledIntoView ? prev + 1 : prev; }, }), ]; // src/handleBackspace.ts var handleBackspace$1 = (editor, type) => { var _a; const { state, view } = editor; const { selection } = state; if (!selection.empty) return false; const { $from } = selection; if ($from.parentOffset !== 0) return false; const parentDepth = $from.depth - 1; const parent = $from.node(parentDepth); const index = $from.index(parentDepth); if (index === 0) return false; if (parent.type === type) { return editor.commands.lift(type.name); } const previous = parent.child(index - 1); if (previous.type !== type || !((_a = previous.lastChild) == null ? void 0 : _a.isTextblock)) { return false; } const blockStart = $from.before(); const insideBlockquoteEnd = blockStart - 1; const targetPos = insideBlockquoteEnd - 1; const { tr } = state; tr.delete(blockStart, $from.after()).insert(targetPos, $from.parent.content); tr.setSelection(TextSelection.create(tr.doc, targetPos)); view.dispatch(tr.scrollIntoView()); return true; }; var inputRegex$1 = /^\s*>\s$/; var Blockquote$2 = Node3.create({ name: "blockquote", addOptions() { return { HTMLAttributes: {}, }; }, content: "block+", group: "block", defining: true, parseHTML() { return [{ tag: "blockquote" }]; }, renderHTML({ HTMLAttributes }) { return /* @__PURE__ */ h("blockquote", { ...mergeAttributes(this.options.HTMLAttributes, HTMLAttributes), children: /* @__PURE__ */ h("slot", {}) }); }, parseMarkdown: (token, helpers) => { var _a; const parseBlockChildren = (_a = helpers.parseBlockChildren) != null ? _a : helpers.parseChildren; return helpers.createNode("blockquote", void 0, parseBlockChildren(token.tokens || [])); }, renderMarkdown: (node, h) => { if (!node.content) { return ""; } const prefix = ">"; const result = []; node.content.forEach((child, index) => { var _a, _b; const childContent = (_b = (_a = h.renderChild) == null ? void 0 : _a.call(h, child, index)) != null ? _b : h.renderChildren([child]); const lines = childContent.split("\n"); const linesWithPrefix = lines.map((line) => { if (line.trim() === "") { return prefix; } return `${prefix} ${line}`; }); result.push(linesWithPrefix.join("\n")); }); return result.join(` ${prefix} `); }, addCommands() { return { setBlockquote: () => ({ commands }) => { return commands.wrapIn(this.name); }, toggleBlockquote: () => ({ commands }) => { return commands.toggleWrap(this.name); }, unsetBlockquote: () => ({ commands }) => { return commands.lift(this.name); }, }; }, addKeyboardShortcuts() { return { "Mod-Shift-b": () => this.editor.commands.toggleBlockquote(), "Backspace": () => handleBackspace$1(this.editor, this.type), }; }, addInputRules() { return [ wrappingInputRule({ find: inputRegex$1, type: this.type, }), ]; }, }); // src/code-block.ts var DEFAULT_TAB_SIZE = 4; var backtickInputRegex = /^```([a-z]+)?[\s\n]$/; var tildeInputRegex = /^~~~([a-z]+)?[\s\n]$/; var CodeBlock$2 = Node3.create({ name: "codeBlock", addOptions() { return { languageClassPrefix: "language-", exitOnTripleEnter: true, exitOnArrowDown: true, defaultLanguage: null, enableTabIndentation: false, tabSize: DEFAULT_TAB_SIZE, HTMLAttributes: {}, }; }, content: "text*", marks: "", group: "block", code: true, defining: true, addAttributes() { return { language: { default: this.options.defaultLanguage, parseHTML: (element) => { var _a; const { languageClassPrefix } = this.options; if (!languageClassPrefix) { return null; } const classNames = [...(((_a = element.firstElementChild) == null ? void 0 : _a.classList) || [])]; const languages = classNames.filter((className) => className.startsWith(languageClassPrefix)).map((className) => className.replace(languageClassPrefix, "")); const language = languages[0]; if (!language) { return null; } return language; }, rendered: false, }, }; }, parseHTML() { return [ { tag: "pre", preserveWhitespace: "full", }, ]; }, renderHTML({ node, HTMLAttributes }) { return [ "pre", mergeAttributes(this.options.HTMLAttributes, HTMLAttributes), [ "code", { class: node.attrs.language ? this.options.languageClassPrefix + node.attrs.language : null, }, 0, ], ]; }, markdownTokenName: "code", parseMarkdown: (token, helpers) => { var _a, _b; if (((_a = token.raw) == null ? void 0 : _a.startsWith("```")) === false && ((_b = token.raw) == null ? void 0 : _b.startsWith("~~~")) === false && token.codeBlockStyle !== "indented") { return []; } return helpers.createNode("codeBlock", { language: token.lang || null }, token.text ? [helpers.createTextNode(token.text)] : []); }, renderMarkdown: (node, h) => { var _a; let output = ""; const language = ((_a = node.attrs) == null ? void 0 : _a.language) || ""; if (!node.content) { output = `\`\`\`${language} \`\`\``; } else { const lines = [`\`\`\`${language}`, h.renderChildren(node.content), "```"]; output = lines.join("\n"); } return output; }, addCommands() { return { setCodeBlock: (attributes) => ({ commands }) => { return commands.setNode(this.name, attributes); }, toggleCodeBlock: (attributes) => ({ commands }) => { return commands.toggleNode(this.name, "paragraph", attributes); }, }; }, addKeyboardShortcuts() { return { "Mod-Alt-c": () => this.editor.commands.toggleCodeBlock(), // remove code block when at start of document or code block is empty "Backspace": () => { const { empty, $anchor } = this.editor.state.selection; const isAtStart = $anchor.pos === 1; if (!empty || $anchor.parent.type.name !== this.name) { return false; } if (isAtStart || !$anchor.parent.textContent.length) { return this.editor.commands.clearNodes(); } return false; }, // handle tab indentation "Tab": ({ editor }) => { var _a; if (!this.options.enableTabIndentation) { return false; } const tabSize = (_a = this.options.tabSize) != null ? _a : DEFAULT_TAB_SIZE; const { state } = editor; const { selection } = state; const { $from, empty } = selection; if ($from.parent.type !== this.type) { return false; } const indent = " ".repeat(tabSize); if (empty) { return editor.commands.insertContent(indent); } return editor.commands.command(({ tr }) => { const { from, to } = selection; const text = state.doc.textBetween(from, to, "\n", "\n"); const lines = text.split("\n"); const indentedText = lines.map((line) => indent + line).join("\n"); tr.replaceWith(from, to, state.schema.text(indentedText)); return true; }); }, // handle shift+tab reverse indentation "Shift-Tab": ({ editor }) => { var _a; if (!this.options.enableTabIndentation) { return false; } const tabSize = (_a = this.options.tabSize) != null ? _a : DEFAULT_TAB_SIZE; const { state } = editor; const { selection } = state; const { $from, empty } = selection; if ($from.parent.type !== this.type) { return false; } if (empty) { return editor.commands.command(({ tr }) => { var _a2; const { pos } = $from; const codeBlockStart = $from.start(); const codeBlockEnd = $from.end(); const allText = state.doc.textBetween(codeBlockStart, codeBlockEnd, "\n", "\n"); const lines = allText.split("\n"); let currentLineIndex = 0; let charCount = 0; const relativeCursorPos = pos - codeBlockStart; for (let i = 0; i < lines.length; i += 1) { if (charCount + lines[i].length >= relativeCursorPos) { currentLineIndex = i; break; } charCount += lines[i].length + 1; } const currentLine = lines[currentLineIndex]; const leadingSpaces = ((_a2 = currentLine.match(/^ */)) == null ? void 0 : _a2[0]) || ""; const spacesToRemove = Math.min(leadingSpaces.length, tabSize); if (spacesToRemove === 0) { return true; } let lineStartPos = codeBlockStart; for (let i = 0; i < currentLineIndex; i += 1) { lineStartPos += lines[i].length + 1; } tr.delete(lineStartPos, lineStartPos + spacesToRemove); const cursorPosInLine = pos - lineStartPos; if (cursorPosInLine <= spacesToRemove) { tr.setSelection(TextSelection$1.create(tr.doc, lineStartPos)); } return true; }); } return editor.commands.command(({ tr }) => { const { from, to } = selection; const text = state.doc.textBetween(from, to, "\n", "\n"); const lines = text.split("\n"); const reverseIndentText = lines .map((line) => { var _a2; const leadingSpaces = ((_a2 = line.match(/^ */)) == null ? void 0 : _a2[0]) || ""; const spacesToRemove = Math.min(leadingSpaces.length, tabSize); return line.slice(spacesToRemove); }) .join("\n"); tr.replaceWith(from, to, state.schema.text(reverseIndentText)); return true; }); }, // exit node on triple enter "Enter": ({ editor }) => { if (!this.options.exitOnTripleEnter) { return false; } const { state } = editor; const { selection } = state; const { $from, empty } = selection; if (!empty || $from.parent.type !== this.type) { return false; } const isAtEnd = $from.parentOffset === $from.parent.nodeSize - 2; const endsWithDoubleNewline = $from.parent.textContent.endsWith("\n\n"); if (!isAtEnd || !endsWithDoubleNewline) { return false; } return editor .chain() .command(({ tr }) => { tr.delete($from.pos - 2, $from.pos); return true; }) .exitCode() .run(); }, // exit node on arrow down "ArrowDown": ({ editor }) => { if (!this.options.exitOnArrowDown) { return false; } const { state } = editor; const { selection, doc } = state; const { $from, empty } = selection; if (!empty || $from.parent.type !== this.type) { return false; } const isAtEnd = $from.parentOffset === $from.parent.nodeSize - 2; if (!isAtEnd) { return false; } const after = $from.after(); if (after === void 0) { return false; } const nodeAfter = doc.nodeAt(after); if (nodeAfter) { return editor.commands.command(({ tr }) => { tr.setSelection(Selection$1.near(doc.resolve(after))); return true; }); } return editor.commands.exitCode(); }, }; }, addInputRules() { return [ textblockTypeInputRule({ find: backtickInputRegex, type: this.type, getAttributes: (match) => ({ language: match[1], }), }), textblockTypeInputRule({ find: tildeInputRegex, type: this.type, getAttributes: (match) => ({ language: match[1], }), }), ]; }, addProseMirrorPlugins() { return [ // this plugin creates a code block for pasted content from VS Code // we can also detect the copied code language new Plugin({ key: new PluginKey("codeBlockVSCodeHandler"), props: { handlePaste: (view, event) => { if (!event.clipboardData) { return false; } if (this.editor.isActive(this.type.name)) { return false; } const text = event.clipboardData.getData("text/plain"); const vscode = event.clipboardData.getData("vscode-editor-data"); const vscodeData = vscode ? JSON.parse(vscode) : void 0; const language = vscodeData == null ? void 0 : vscodeData.mode; if (!text || !language) { return false; } const { tr, schema } = view.state; const textNode = schema.text(text.replace(/\r\n?/g, "\n")); tr.replaceSelectionWith(this.type.create({ language }, textNode)); if (tr.selection.$from.parent.type !== this.type) { tr.setSelection(TextSelection$1.near(tr.doc.resolve(Math.max(0, tr.selection.from - 2)))); } tr.setMeta("paste", true); view.dispatch(tr); return true; }, }, }), ]; }, }); // src/document.ts var Document = Node3.create({ name: "doc", topNode: true, content: "block+", renderMarkdown: (node, h) => { if (!node.content) { return ""; } return h.renderChildren(node.content, "\n\n"); }, }); // src/hard-break.ts var HardBreak$2 = Node3.create({ name: "hardBreak", markdownTokenName: "br", addOptions() { return { keepMarks: true, HTMLAttributes: {}, }; }, inline: true, group: "inline", selectable: false, linebreakReplacement: true, parseHTML() { return [{ tag: "br" }]; }, renderHTML({ HTMLAttributes }) { return ["br", mergeAttributes(this.options.HTMLAttributes, HTMLAttributes)]; }, renderText() { return "\n"; }, renderMarkdown: () => ` `, parseMarkdown: () => { return { type: "hardBreak", }; }, addCommands() { return { setHardBreak: () => ({ commands, chain, state, editor }) => { return commands.first([ () => commands.exitCode(), () => commands.command(() => { const { selection, storedMarks } = state; if (selection.$from.parent.type.spec.isolating) { return false; } const { keepMarks } = this.options; const { splittableMarks } = editor.extensionManager; const marks = storedMarks || (selection.$to.parentOffset && selection.$from.marks()); return chain() .insertContent({ type: this.name }) .command(({ tr, dispatch }) => { if (dispatch && marks && keepMarks) { const filteredMarks = marks.filter((mark) => splittableMarks.includes(mark.type.name)); tr.ensureMarks(filteredMarks); } return true; }) .run(); }), ]); }, }; }, addKeyboardShortcuts() { return { "Mod-Enter": () => this.editor.commands.setHardBreak(), "Shift-Enter": () => this.editor.commands.setHardBreak(), }; }, }); // src/heading.ts var Heading$2 = Node3.create({ name: "heading", addOptions() { return { levels: [1, 2, 3, 4, 5, 6], HTMLAttributes: {}, }; }, content: "inline*", group: "block", defining: true, addAttributes() { return { level: { default: 1, rendered: false, }, }; }, parseHTML() { return this.options.levels.map((level) => ({ tag: `h${level}`, attrs: { level }, })); }, renderHTML({ node, HTMLAttributes }) { const hasLevel = this.options.levels.includes(node.attrs.level); const level = hasLevel ? node.attrs.level : this.options.levels[0]; return [`h${level}`, mergeAttributes(this.options.HTMLAttributes, HTMLAttributes), 0]; }, parseMarkdown: (token, helpers) => { return helpers.createNode("heading", { level: token.depth || 1 }, helpers.parseInline(token.tokens || [])); }, renderMarkdown: (node, h) => { var _a; const level = ( (_a = node.attrs) == null ? void 0 : _a.level ) ? parseInt(node.attrs.level, 10) : 1; const headingChars = "#".repeat(level); if (!node.content) { return ""; } return `${headingChars} ${h.renderChildren(node.content)}`; }, addCommands() { return { setHeading: (attributes) => ({ commands }) => { if (!this.options.levels.includes(attributes.level)) { return false; } return commands.setNode(this.name, attributes); }, toggleHeading: (attributes) => ({ commands }) => { if (!this.options.levels.includes(attributes.level)) { return false; } return commands.toggleNode(this.name, "paragraph", attributes); }, }; }, addKeyboardShortcuts() { return this.options.levels.reduce( (items, level) => ({ ...items, [`Mod-Alt-${level}`]: () => this.editor.commands.toggleHeading({ level }), }), {}, ); }, addInputRules() { return this.options.levels.map((level) => { return textblockTypeInputRule({ find: new RegExp(`^(#{${Math.min(...this.options.levels)},${level}})\\s$`), type: this.type, getAttributes: { level, }, }); }); }, }); // src/horizontal-rule.ts var HorizontalRule$2 = Node3.create({ name: "horizontalRule", addOptions() { return { HTMLAttributes: {}, nextNodeType: "paragraph", }; }, group: "block", parseHTML() { return [{ tag: "hr" }]; }, renderHTML({ HTMLAttributes }) { return ["hr", mergeAttributes(this.options.HTMLAttributes, HTMLAttributes)]; }, markdownTokenName: "hr", parseMarkdown: (token, helpers) => { return helpers.createNode("horizontalRule"); }, renderMarkdown: () => { return "---"; }, addCommands() { return { setHorizontalRule: () => ({ chain, state }) => { if (!canInsertNode(state, state.schema.nodes[this.name])) { return false; } const { selection } = state; const { $to: $originTo } = selection; const currentChain = chain(); if (isNodeSelection(selection)) { currentChain.insertContentAt($originTo.pos, { type: this.name, }); } else { currentChain.insertContent({ type: this.name }); } return currentChain .command(({ state: chainState, tr, dispatch }) => { if (dispatch) { const { $to } = tr.selection; const posAfter = $to.end(); if ($to.nodeAfter) { if ($to.nodeAfter.isTextblock) { tr.setSelection(TextSelection$1.create(tr.doc, $to.pos + 1)); } else if ($to.nodeAfter.isBlock) { tr.setSelection(NodeSelection$1.create(tr.doc, $to.pos)); } else { tr.setSelection(TextSelection$1.create(tr.doc, $to.pos)); } } else { const nodeType = chainState.schema.nodes[this.options.nextNodeType] || $to.parent.type.contentMatch.defaultType; const node = nodeType == null ? void 0 : nodeType.create(); if (node) { tr.insert(posAfter, node); tr.setSelection(TextSelection$1.create(tr.doc, posAfter + 1)); } } tr.scrollIntoView(); } return true; }) .run(); }, }; }, addInputRules() { return [ nodeInputRule({ find: /^(?:---|—-|___\s|\*\*\*\s)$/, type: this.type, }), ]; }, }); var __defProp$1 = Object.defineProperty; var __export = (target, all) => { for (var name in all) __defProp$1(target, name, { get: all[name], enumerable: true }); }; var ListItemName = "listItem"; var TextStyleName = "textStyle"; var bulletListInputRegex = /^\s*([-+*])\s$/; var BulletList$2 = Node3.create({ name: "bulletList", addOptions() { return { itemTypeName: "listItem", HTMLAttributes: {}, keepMarks: false, keepAttributes: false, }; }, group: "block list", content() { return `${this.options.itemTypeName}+`; }, parseHTML() { return [{ tag: "ul" }]; }, renderHTML({ HTMLAttributes }) { return ["ul", mergeAttributes(this.options.HTMLAttributes, HTMLAttributes), 0]; }, markdownTokenName: "list", parseMarkdown: (token, helpers) => { if (token.type !== "list" || token.ordered) { return []; } return { type: "bulletList", content: token.items ? helpers.parseChildren(token.items) : [], }; }, renderMarkdown: (node, h) => { if (!node.content) { return ""; } return h.renderChildren(node.content, "\n"); }, markdownOptions: { indentsContent: true, }, addCommands() { return { toggleBulletList: () => ({ commands, chain }) => { if (this.options.keepAttributes) { return chain().toggleList(this.name, this.options.itemTypeName, this.options.keepMarks).updateAttributes(ListItemName, this.editor.getAttributes(TextStyleName)).run(); } return commands.toggleList(this.name, this.options.itemTypeName, this.options.keepMarks); }, }; }, addKeyboardShortcuts() { return { "Mod-Shift-8": () => this.editor.commands.toggleBulletList(), }; }, addInputRules() { let inputRule = wrappingInputRule({ find: bulletListInputRegex, type: this.type, }); if (this.options.keepMarks || this.options.keepAttributes) { inputRule = wrappingInputRule({ find: bulletListInputRegex, type: this.type, keepMarks: this.options.keepMarks, keepAttributes: this.options.keepAttributes, getAttributes: () => { return this.editor.getAttributes(TextStyleName); }, editor: this.editor, }); } return [inputRule]; }, }); // src/helpers/getBranchingNestedListAtCursor.ts var getBranchingNestedListAtCursor = (state, itemName, wrapperNames) => { const { selection } = state; if (!selection.empty) { return null; } const { $from } = selection; if (!$from.parent.isTextblock) { return null; } if ($from.parentOffset !== $from.parent.content.size) { return null; } let listItemDepth = -1; for (let depth = $from.depth; depth > 0; depth -= 1) { if ($from.node(depth).type.name === itemName) { listItemDepth = depth; break; } } if (listItemDepth < 0) { return null; } const listItem = $from.node(listItemDepth); const indexInListItem = $from.index(listItemDepth); if (indexInListItem + 1 >= listItem.childCount) { return null; } const nextChild = listItem.child(indexInListItem + 1); if (!wrapperNames.includes(nextChild.type.name)) { return null; } const itemType = state.schema.nodes[itemName]; let hasBranching = false; nextChild.forEach((child) => { if (child.type === itemType && child.childCount > 1) { hasBranching = true; } }); if (!hasBranching) { return null; } const nodeAfter = state.doc.resolve($from.after()).nodeAfter; if (!nodeAfter || !wrapperNames.includes(nodeAfter.type.name)) { return null; } const items = []; nodeAfter.forEach((child) => { items.push(child); }); if (items.length === 0) { return null; } return { listItemDepth, nestedList: nodeAfter, nestedListPos: $from.after(), insertPos: $from.after(listItemDepth), items, }; }; // src/helpers/hoistBranchingNestedList.ts var hoistBranchingNestedList = (state, dispatch, itemName, wrapperNames) => { const context = getBranchingNestedListAtCursor(state, itemName, wrapperNames); if (!context) { return false; } const { selection } = state; const { nestedList, nestedListPos, insertPos, items } = context; const tr = state.tr; tr.delete(nestedListPos, nestedListPos + nestedList.nodeSize); const mappedInsertPos = tr.mapping.map(insertPos); tr.insert(mappedInsertPos, Fragment$1.from(items)); tr.setSelection(selection.map(tr.doc, tr.mapping)); if (dispatch) { dispatch(tr); } return true; }; // src/helpers/handleDeleteBranchingNestedList.ts var handleDeleteBranchingNestedList = (editor, itemName, wrapperNames) => { return hoistBranchingNestedList(editor.state, editor.view.dispatch, itemName, wrapperNames); }; // src/helpers/createBranchingListDeleteKeymap.ts var createBranchingListDeleteKeymap = (itemName, wrapperNames) => { return Extension.create({ name: `${itemName}BranchingDeleteKeymap`, priority: 101, addKeyboardShortcuts() { const handleDelete2 = () => handleDeleteBranchingNestedList(this.editor, itemName, wrapperNames); return { "Delete": handleDelete2, "Mod-Delete": handleDelete2, }; }, }); }; // src/ordered-list/roman.ts var ROMAN_NUMERALS = [ [1e3, "m"], [900, "cm"], [500, "d"], [400, "cd"], [100, "c"], [90, "xc"], [50, "l"], [40, "xl"], [10, "x"], [9, "ix"], [5, "v"], [4, "iv"], [1, "i"], ]; var ALPHA_NUMERALS = "abcdefghijklmnopqrstuvwxyz"; var ORDERED_LIST_ALPHA_MARKER_PATTERN = "[a-zA-Z]{1,2}"; var ORDERED_LIST_MARKER_PATTERN = String.raw`\d+|[ivxlcdmIVXLCDM]+|${ORDERED_LIST_ALPHA_MARKER_PATTERN}`; function toRoman(num) { let remaining = num; let result = ""; for (const [value, numeral] of ROMAN_NUMERALS) { while (remaining >= value) { result += numeral; remaining -= value; } } return result; } function toRomanUpper(num) { return toRoman(num).toUpperCase(); } function fromRoman(roman) { const lower = roman.toLowerCase(); let index = 0; let result = 0; while (index < lower.length) { let matched = false; for (const [value, numeral] of ROMAN_NUMERALS) { if (lower.startsWith(numeral, index)) { result += value; index += numeral.length; matched = true; break; } } if (!matched) { return 0; } } return result; } function isValidRoman(marker) { if (!/^[ivxlcdmIVXLCDM]+$/.test(marker)) { return false; } const value = fromRoman(marker); if (value <= 0) { return false; } const expected = marker === marker.toLowerCase() ? toRoman(value) : toRomanUpper(value); return expected === marker; } function fromAlpha(marker) { const lower = marker.toLowerCase(); if (lower.length === 1) { return lower.charCodeAt(0) - "a".charCodeAt(0) + 1; } if (lower.length === 2) { const first = lower.charCodeAt(0) - "a".charCodeAt(0); const second = lower.charCodeAt(1) - "a".charCodeAt(0); return (first + 1) * 26 + second + 1; } return 0; } function toRomanAlpha(num) { if (num <= 26) { return ALPHA_NUMERALS[num - 1]; } const first = Math.floor((num - 1) / 26) - 1; const second = (num - 1) % 26; if (first < 0) { return ALPHA_NUMERALS[second]; } return ALPHA_NUMERALS[first] + ALPHA_NUMERALS[second]; } function detectMarkerType(marker) { if (!marker || /^\d+$/.test(marker)) { return void 0; } if (isValidRoman(marker)) { return marker === marker.toLowerCase() ? "i" : "I"; } if (/^[a-z]{1,2}$/.test(marker)) { return "a"; } if (/^[A-Z]{1,2}$/.test(marker)) { return "A"; } return void 0; } function markerToStart(marker) { if (/^\d+$/.test(marker)) { return parseInt(marker, 10); } const type = detectMarkerType(marker); if (type === "i" || type === "I") { return fromRoman(marker); } if (type === "a" || type === "A") { const start = fromAlpha(marker); return start > 0 ? start : 1; } const parsed = parseInt(marker, 10); return Number.isNaN(parsed) ? 1 : parsed; } function startToMarker(type, start) { if (type === "numeric") { return String(start); } switch (type) { case "a": return toRomanAlpha(start); case "A": return toRomanAlpha(start).toUpperCase(); case "i": return toRoman(start); case "I": return toRomanUpper(start); default: return String(start); } } function areOrderedListMarkersSequential(markers) { var _a; if (markers.length === 0) { return false; } const firstType = (_a = detectMarkerType(markers[0])) != null ? _a : "numeric"; const firstStart = markerToStart(markers[0]); if (firstStart < 1) { return false; } for (let i = 0; i < markers.length; i++) { const expected = startToMarker(firstType, firstStart + i); if (markers[i] !== expected) { return false; } } return true; } function parseListMarker(marker) { return { type: detectMarkerType(marker), start: markerToStart(marker), }; } function buildOrderedListAttrsFromMarker(marker) { const { type, start } = parseListMarker(marker); const attrs = {}; if (type) { attrs.type = type; } if (start !== 1) { attrs.start = start; } return attrs; } function getListMarker(type, index, separator = ". ") { const position = index + 1; if (!type || type === "1") { return `${position}${separator}`; } switch (type) { case "a": return `${toRomanAlpha(position)}${separator}`; case "A": return `${toRomanAlpha(position).toUpperCase()}${separator}`; case "i": return `${toRoman(position)}${separator}`; case "I": return `${toRomanUpper(position)}${separator}`; default: return `${position}${separator}`; } } // src/item/list-item.ts function isSameLineOrderedListToken(token) { var _a, _b; const nestedToken = (_a = token.tokens) == null ? void 0 : _a[0]; return Boolean( token.text && ((_b = token.tokens) == null ? void 0 : _b.length) === 1 && (nestedToken == null ? void 0 : nestedToken.type) === "list" && nestedToken.ordered && nestedToken.raw === token.text, ); } function parseSameLineOrderedListText(text, helpers) { if (helpers.tokenizeInline) { return helpers.parseInline(helpers.tokenizeInline(text)); } return helpers.parseInline([ { type: "text", raw: text, text, }, ]); } var ListItem$2 = Node3.create({ name: "listItem", addOptions() { return { HTMLAttributes: {}, bulletListTypeName: "bulletList", orderedListTypeName: "orderedList", }; }, content: "paragraph block*", defining: true, parseHTML() { return [ { tag: "li", }, ]; }, renderHTML({ HTMLAttributes }) { return ["li", mergeAttributes(this.options.HTMLAttributes, HTMLAttributes), 0]; }, markdownTokenName: "list_item", parseMarkdown: (token, helpers) => { var _a; if (token.type !== "list_item") { return []; } const parseBlockChildren = (_a = helpers.parseBlockChildren) != null ? _a : helpers.parseChildren; let content = []; if (token.tokens && token.tokens.length > 0) { if (isSameLineOrderedListToken(token)) { return { type: "listItem", content: [ { type: "paragraph", content: parseSameLineOrderedListText(token.text || "", helpers), }, ], }; } const hasParagraphTokens = token.tokens.some((t) => t.type === "paragraph"); if (hasParagraphTokens) { content = parseBlockChildren(token.tokens); } else { const firstToken = token.tokens[0]; if (firstToken && firstToken.type === "text" && firstToken.tokens && firstToken.tokens.length > 0) { const inlineContent = helpers.parseInline(firstToken.tokens); content = [ { type: "paragraph", content: inlineContent, }, ]; if (token.tokens.length > 1) { const remainingTokens = token.tokens.slice(1); const additionalContent = parseBlockChildren(remainingTokens); content.push(...additionalContent); } } else { content = parseBlockChildren(token.tokens); } } } if (content.length === 0) { content = [ { type: "paragraph", content: [], }, ]; } return { type: "listItem", content, }; }, renderMarkdown: (node, h, ctx) => { return renderNestedMarkdownContent( node, h, (context) => { var _a, _b, _c, _d; if (context.parentType === "bulletList") { return "- "; } if (context.parentType === "orderedList") { const start = ((_b = (_a = context.meta) == null ? void 0 : _a.parentAttrs) == null ? void 0 : _b.start) || 1; const type = (_d = (_c = context.meta) == null ? void 0 : _c.parentAttrs) == null ? void 0 : _d.type; const index = start - 1 + (context.index || 0); return getListMarker(type, index, ". "); } return "- "; }, ctx, ); }, addExtensions() { return [createBranchingListDeleteKeymap(this.name, [this.options.bulletListTypeName, this.options.orderedListTypeName])]; }, addKeyboardShortcuts() { return { "Enter": () => this.editor.commands.splitListItem(this.name), "Tab": () => this.editor.commands.sinkListItem(this.name), "Shift-Tab": () => this.editor.commands.liftListItem(this.name), }; }, }); // src/keymap/listHelpers/index.ts var listHelpers_exports = {}; __export(listHelpers_exports, { findListItemPos: () => findListItemPos, getNextListDepth: () => getNextListDepth, handleBackspace: () => handleBackspace, handleDelete: () => handleDelete, hasListBefore: () => hasListBefore, hasListItemAfter: () => hasListItemAfter, hasListItemBefore: () => hasListItemBefore, listItemHasSubList: () => listItemHasSubList, nextListIsDeeper: () => nextListIsDeeper, nextListIsHigher: () => nextListIsHigher, }); var findListItemPos = (typeOrName, state) => { const { $from } = state.selection; const nodeType = getNodeType(typeOrName, state.schema); let currentNode = null; let currentDepth = $from.depth; let currentPos = $from.pos; let targetDepth = null; while (currentDepth > 0 && targetDepth === null) { currentNode = $from.node(currentDepth); if (currentNode.type === nodeType) { targetDepth = currentDepth; } else { currentDepth -= 1; currentPos -= 1; } } if (targetDepth === null) { return null; } return { $pos: state.doc.resolve(currentPos), depth: targetDepth }; }; var getNextListDepth = (typeOrName, state) => { const listItemPos = findListItemPos(typeOrName, state); if (!listItemPos) { return false; } const [, depth] = getNodeAtPosition(state, typeOrName, listItemPos.$pos.pos + 4); return depth; }; // src/keymap/listHelpers/hasListBefore.ts var hasListBefore = (editorState, name, parentListTypes) => { const { $anchor } = editorState.selection; const previousNodePos = Math.max(0, $anchor.pos - 2); const previousNode = editorState.doc.resolve(previousNodePos).node(); if (!previousNode || !parentListTypes.includes(previousNode.type.name)) { return false; } return true; }; // src/keymap/listHelpers/handleBackspace.ts var handleBackspace = (editor, name, parentListTypes) => { if (editor.commands.undoInputRule()) { return true; } if (editor.state.selection.from !== editor.state.selection.to) { return false; } if (!isNodeActive(editor.state, name) && hasListBefore(editor.state, name, parentListTypes)) { const { $anchor } = editor.state.selection; const $listPos = editor.state.doc.resolve($anchor.before() - 1); const listDescendants = []; $listPos.node().descendants((node, pos) => { if (node.type.name === name) { listDescendants.push({ node, pos }); } }); const lastItem = listDescendants.at(-1); if (!lastItem) { return false; } const $lastItemPos = editor.state.doc.resolve($listPos.start() + lastItem.pos + 1); return editor .chain() .cut({ from: $anchor.start() - 1, to: $anchor.end() + 1 }, $lastItemPos.end()) .joinForward() .run(); } if (!isNodeActive(editor.state, name)) { return false; } if (!isAtStartOfNode(editor.state)) { return false; } return editor.chain().liftListItem(name).run(); }; // src/keymap/listHelpers/nextListIsDeeper.ts var nextListIsDeeper = (typeOrName, state) => { const listDepth = getNextListDepth(typeOrName, state); const listItemPos = findListItemPos(typeOrName, state); if (!listItemPos || !listDepth) { return false; } if (listDepth > listItemPos.depth) { return true; } return false; }; // src/keymap/listHelpers/nextListIsHigher.ts var nextListIsHigher = (typeOrName, state) => { const listDepth = getNextListDepth(typeOrName, state); const listItemPos = findListItemPos(typeOrName, state); if (!listItemPos || !listDepth) { return false; } if (listDepth < listItemPos.depth) { return true; } return false; }; // src/keymap/listHelpers/handleDelete.ts var handleDelete = (editor, name) => { if (!isNodeActive(editor.state, name)) { return false; } if (!isAtEndOfNode(editor.state, name)) { return false; } const { selection } = editor.state; const { $from, $to } = selection; if (!selection.empty && $from.sameParent($to)) { return false; } if (nextListIsDeeper(name, editor.state)) { return editor .chain() .focus(editor.state.selection.from + 4) .lift(name) .joinBackward() .run(); } if (nextListIsHigher(name, editor.state)) { return editor.chain().joinForward().joinBackward().run(); } return editor.commands.joinItemForward(); }; // src/keymap/listHelpers/hasListItemAfter.ts var hasListItemAfter = (typeOrName, state) => { var _a; const { $anchor } = state.selection; const $targetPos = state.doc.resolve($anchor.pos - $anchor.parentOffset - 2); if ($targetPos.index() === $targetPos.parent.childCount - 1) { return false; } if (((_a = $targetPos.nodeAfter) == null ? void 0 : _a.type.name) !== typeOrName) { return false; } return true; }; // src/keymap/listHelpers/hasListItemBefore.ts var hasListItemBefore = (typeOrName, state) => { var _a; const { $anchor } = state.selection; const $targetPos = state.doc.resolve($anchor.pos - 2); if ($targetPos.index() === 0) { return false; } if (((_a = $targetPos.nodeBefore) == null ? void 0 : _a.type.name) !== typeOrName) { return false; } return true; }; var listItemHasSubList = (typeOrName, state, node) => { if (!node) { return false; } const nodeType = getNodeType(typeOrName, state.schema); let hasSubList = false; node.descendants((child) => { if (child.type === nodeType) { hasSubList = true; } }); return hasSubList; }; // src/keymap/list-keymap.ts var ListKeymap = Extension.create({ name: "listKeymap", addOptions() { return { listTypes: [ { itemName: "listItem", wrapperNames: ["bulletList", "orderedList"], }, { itemName: "taskItem", wrapperNames: ["taskList"], }, ], }; }, addKeyboardShortcuts() { return { "Delete": ({ editor }) => { let handled = false; this.options.listTypes.forEach(({ itemName }) => { if (editor.state.schema.nodes[itemName] === void 0) { return; } if (handleDelete(editor, itemName)) { handled = true; } }); return handled; }, "Mod-Delete": ({ editor }) => { let handled = false; this.options.listTypes.forEach(({ itemName }) => { if (editor.state.schema.nodes[itemName] === void 0) { return; } if (handleDelete(editor, itemName)) { handled = true; } }); return handled; }, "Backspace": ({ editor }) => { let handled = false; this.options.listTypes.forEach(({ itemName, wrapperNames }) => { if (editor.state.schema.nodes[itemName] === void 0) { return; } if (handleBackspace(editor, itemName, wrapperNames)) { handled = true; } }); return handled; }, "Mod-Backspace": ({ editor }) => { let handled = false; this.options.listTypes.forEach(({ itemName, wrapperNames }) => { if (editor.state.schema.nodes[itemName] === void 0) { return; } if (handleBackspace(editor, itemName, wrapperNames)) { handled = true; } }); return handled; }, }; }, }); // src/ordered-list/utils.ts var ORDERED_LIST_ITEM_REGEX = new RegExp(`^(\\s*)(${ORDERED_LIST_MARKER_PATTERN})([.)])\\s+(.*)$`); var ORDERED_LIST_LINE_START_REGEX = new RegExp(`^(\\s*)(${ORDERED_LIST_MARKER_PATTERN})([.)])\\s+`); var INDENTED_LINE_REGEX = /^\s/; function isOrderedListMarkerLine(line) { return ORDERED_LIST_ITEM_REGEX.test(line.trimStart()); } function isBlockContentLine(line) { const trimmedLine = line.trimStart(); return ( // oxlint-disable-next-line prefer-string-starts-ends-with /^[-+*]\s+/.test(trimmedLine) || isOrderedListMarkerLine(trimmedLine) || // oxlint-disable-next-line prefer-string-starts-ends-with /^>\s?/.test(trimmedLine) || // oxlint-disable-next-line prefer-string-starts-ends-with /^```/.test(trimmedLine) || // oxlint-disable-next-line prefer-string-starts-ends-with /^~~~/.test(trimmedLine) ); } function splitItemContent(contentLines) { const paragraphLines = []; const blockLines = []; let reachedBlockBoundary = false; contentLines.forEach((line) => { if (reachedBlockBoundary) { blockLines.push(line); return; } if (line.trim() === "") { reachedBlockBoundary = true; blockLines.push(line); return; } if (paragraphLines.length > 0 && isBlockContentLine(line)) { reachedBlockBoundary = true; blockLines.push(line); return; } paragraphLines.push(line); }); return { paragraphLines, blockLines, }; } function collectOrderedListItems(lines) { const listItems = []; let currentLineIndex = 0; let consumed = 0; while (currentLineIndex < lines.length) { const line = lines[currentLineIndex]; const match = line.match(ORDERED_LIST_ITEM_REGEX); if (!match) { break; } const [, indent, marker, _separator, content] = match; const indentLevel = indent.length; const number = parseInt(marker, 10); const markerType = isNaN(number) ? detectMarkerType(marker) : void 0; const itemNumber = isNaN(number) ? markerToStart(marker) : number; const itemContentLines = [content]; let nextLineIndex = currentLineIndex + 1; const itemLines = [line]; let sawBlankLine = false; while (nextLineIndex < lines.length) { const nextLine = lines[nextLineIndex]; const nextMatch = nextLine.match(ORDERED_LIST_ITEM_REGEX); if (nextMatch) { break; } if (nextLine.trim() === "") { itemLines.push(nextLine); itemContentLines.push(""); sawBlankLine = true; nextLineIndex += 1; } else if (nextLine.match(INDENTED_LINE_REGEX)) { const leadingWhitespace = nextLine.length - nextLine.trimStart().length; const contentIndent = indentLevel + marker.length + 1; itemLines.push(nextLine); itemContentLines.push(nextLine.slice(Math.min(leadingWhitespace, contentIndent))); nextLineIndex += 1; } else { if (sawBlankLine) { break; } itemLines.push(nextLine); itemContentLines.push(nextLine); nextLineIndex += 1; } } listItems.push({ indent: indentLevel, number: itemNumber, type: markerType, content: itemContentLines.join("\n").trim(), contentLines: itemContentLines, raw: itemLines.join("\n"), }); consumed = nextLineIndex; currentLineIndex = nextLineIndex; } return [listItems, consumed]; } var PLAIN_TEXT_ORDERED_LIST_LINE_REGEX = new RegExp(`^(${ORDERED_LIST_MARKER_PATTERN})([.)])\\s+(.+)$`); function parsePlainTextOrderedListPaste(text) { const lines = text.split("\n").filter((l) => l.trim().length > 0); if (lines.length === 0) { return null; } const parsedItems = []; for (const line of lines) { const match = line.trim().match(PLAIN_TEXT_ORDERED_LIST_LINE_REGEX); if (!match) { return null; } parsedItems.push({ marker: match[1], content: match[3], }); } const markers = parsedItems.map((item) => item.marker); if (!areOrderedListMarkersSequential(markers)) { return null; } const attrs = buildOrderedListAttrsFromMarker(parsedItems[0].marker); return { type: "orderedList", attrs, content: parsedItems.map((item) => ({ type: "listItem", content: [ { type: "paragraph", content: [{ type: "text", text: item.content }], }, ], })), }; } function buildNestedStructure(items, baseIndent, lexer) { const result = []; let currentIndex = 0; while (currentIndex < items.length) { const item = items[currentIndex]; if (item.indent === baseIndent) { const { paragraphLines, blockLines } = splitItemContent(item.contentLines); const mainText = paragraphLines.join("\n").trim(); const tokens = []; if (mainText) { tokens.push({ type: "paragraph", raw: mainText, tokens: lexer.inlineTokens(mainText), }); } const additionalContent = blockLines.join("\n").trim(); if (additionalContent) { const blockTokens = lexer.blockTokens(additionalContent); tokens.push(...blockTokens); } let lookAheadIndex = currentIndex + 1; const nestedItems = []; while (lookAheadIndex < items.length && items[lookAheadIndex].indent > baseIndent) { nestedItems.push(items[lookAheadIndex]); lookAheadIndex += 1; } if (nestedItems.length > 0) { const nextIndent = Math.min(...nestedItems.map((nestedItem) => nestedItem.indent)); const nestedListItems = buildNestedStructure(nestedItems, nextIndent, lexer); tokens.push({ type: "list", ordered: true, start: nestedItems[0].number, typeMarker: nestedItems[0].type, items: nestedListItems, raw: nestedItems.map((nestedItem) => nestedItem.raw).join("\n"), }); } result.push({ type: "list_item", raw: item.raw, tokens, }); currentIndex = lookAheadIndex; } else { currentIndex += 1; } } return result; } function parseListItems(items, helpers) { return items.map((item) => { if (item.type !== "list_item") { return helpers.parseChildren([item])[0]; } const content = []; if (item.tokens && item.tokens.length > 0) { item.tokens.forEach((itemToken) => { if (itemToken.type === "paragraph" || itemToken.type === "list" || itemToken.type === "blockquote" || itemToken.type === "code") { content.push(...helpers.parseChildren([itemToken])); } else if (itemToken.type === "text" && itemToken.tokens) { const inlineContent = helpers.parseChildren([itemToken]); content.push({ type: "paragraph", content: inlineContent, }); } else { const parsed = helpers.parseChildren([itemToken]); if (parsed.length > 0) { content.push(...parsed); } } }); } return { type: "listItem", content, }; }); } // src/ordered-list/ordered-list.ts var ListItemName2 = "listItem"; var TextStyleName2 = "textStyle"; var orderedListInputRegex = /^(\d+)\.\s$/; function cssListStyleTypeToHtmlType(style) { const match = style.match(/list-style-type\s*:\s*([^;]+)/i); if (!match) { return null; } const cssValue = match[1].trim().toLowerCase(); switch (cssValue) { case "upper-roman": return "I"; case "lower-roman": return "i"; case "upper-alpha": case "upper-latin": return "A"; case "lower-alpha": case "lower-latin": return "a"; default: return null; } } var OrderedList$2 = Node3.create({ name: "orderedList", addOptions() { return { itemTypeName: "listItem", HTMLAttributes: {}, keepMarks: false, keepAttributes: false, }; }, group: "block list", content() { return `${this.options.itemTypeName}+`; }, addAttributes() { return { start: { default: 1, parseHTML: (element) => { return element.hasAttribute("start") ? parseInt(element.getAttribute("start") || "", 10) : 1; }, }, type: { default: null, parseHTML: (element) => { const htmlType = element.getAttribute("type"); if (htmlType) { return htmlType; } const style = element.getAttribute("style"); if (style) { const mappedFromOl = cssListStyleTypeToHtmlType(style); if (mappedFromOl) { return mappedFromOl; } } const firstLi = element.querySelector("li"); if (firstLi) { const liStyle = firstLi.getAttribute("style"); if (liStyle) { const mappedFromLi = cssListStyleTypeToHtmlType(liStyle); if (mappedFromLi) { return mappedFromLi; } } } return null; }, }, }; }, parseHTML() { return [ { tag: "ol", }, ]; }, renderHTML({ HTMLAttributes }) { const { start, type, ...attributesWithoutType } = HTMLAttributes; const attrs = mergeAttributes(this.options.HTMLAttributes, attributesWithoutType); if (start !== 1) { attrs.start = start; } if (type && type !== "1") { attrs.type = type; } return ["ol", attrs, 0]; }, markdownTokenName: "list", parseMarkdown: (token, helpers) => { if (token.type !== "list" || !token.ordered) { return []; } const startValue = token.start || 1; const typeValue = token.typeMarker; const content = token.items ? parseListItems(token.items, helpers) : []; const attrs = {}; if (startValue !== 1) { attrs.start = startValue; } if (typeValue) { attrs.type = typeValue; } if (Object.keys(attrs).length > 0) { return { type: "orderedList", attrs, content, }; } return { type: "orderedList", content, }; }, renderMarkdown: (node, h) => { if (!node.content) { return ""; } return h.renderChildren(node.content, "\n"); }, markdownTokenizer: { name: "orderedList", level: "block", start: (src) => { const match = src.match(ORDERED_LIST_LINE_START_REGEX); const index = match == null ? void 0 : match.index; return index !== void 0 ? index : -1; }, tokenize: (src, _tokens, lexer) => { var _a, _b; const lines = src.split("\n"); const [listItems, consumed] = collectOrderedListItems(lines); if (listItems.length === 0) { return void 0; } const items = buildNestedStructure(listItems, 0, lexer); if (items.length === 0) { return void 0; } const startValue = ((_a = listItems[0]) == null ? void 0 : _a.number) || 1; const typeMarker = (_b = listItems[0]) == null ? void 0 : _b.type; return { type: "list", ordered: true, start: startValue, typeMarker, items, raw: lines.slice(0, consumed).join("\n"), }; }, }, markdownOptions: { indentsContent: true, }, addCommands() { return { toggleOrderedList: () => ({ commands, chain }) => { if (this.options.keepAttributes) { return chain().toggleList(this.name, this.options.itemTypeName, this.options.keepMarks).updateAttributes(ListItemName2, this.editor.getAttributes(TextStyleName2)).run(); } return commands.toggleList(this.name, this.options.itemTypeName, this.options.keepMarks); }, }; }, addKeyboardShortcuts() { return { "Mod-Shift-7": () => this.editor.commands.toggleOrderedList(), }; }, addProseMirrorPlugins() { return [ new Plugin({ props: { handlePaste: (view, event) => { var _a, _b; const html = (_a = event.clipboardData) == null ? void 0 : _a.getData("text/html"); if (html == null ? void 0 : html.trim()) { return false; } const text = (_b = event.clipboardData) == null ? void 0 : _b.getData("text/plain"); if (!text) { return false; } const orderedListContent = parsePlainTextOrderedListPaste(text); if (!orderedListContent) { return false; } try { const orderedListNode = view.state.schema.nodeFromJSON(orderedListContent); const tr = view.state.tr.replaceSelectionWith(orderedListNode); view.dispatch(tr); return true; } catch { return false; } }, }, }), ]; }, addInputRules() { const joinPredicate = (match, node) => { const hasDefaultType = !node.attrs.type || node.attrs.type === "1"; return hasDefaultType && node.childCount + node.attrs.start === +match[1]; }; let inputRule = wrappingInputRule({ find: orderedListInputRegex, type: this.type, getAttributes: (match) => ({ start: +match[1] }), joinPredicate, }); if (this.options.keepMarks || this.options.keepAttributes) { inputRule = wrappingInputRule({ find: orderedListInputRegex, type: this.type, keepMarks: this.options.keepMarks, keepAttributes: this.options.keepAttributes, getAttributes: (match) => ({ start: +match[1], ...this.editor.getAttributes(TextStyleName2) }), joinPredicate, editor: this.editor, }); } return [inputRule]; }, }); var inputRegex = /^\s*(\[([( |x])?\])\s$/; var TaskItem$2 = Node3.create({ name: "taskItem", addOptions() { return { nested: false, HTMLAttributes: {}, taskListTypeName: "taskList", a11y: void 0, }; }, content() { return this.options.nested ? "paragraph block*" : "paragraph+"; }, defining: true, addAttributes() { return { checked: { default: false, keepOnSplit: false, parseHTML: (element) => { const dataChecked = element.getAttribute("data-checked"); return dataChecked === "" || dataChecked === "true"; }, renderHTML: (attributes) => ({ "data-checked": attributes.checked, }), }, }; }, parseHTML() { return [ { tag: `li[data-type="${this.name}"]`, priority: 51, }, ]; }, renderHTML({ node, HTMLAttributes }) { return [ "li", mergeAttributes(this.options.HTMLAttributes, HTMLAttributes, { "data-type": this.name, }), [ "label", [ "input", { type: "checkbox", checked: node.attrs.checked ? "checked" : null, }, ], ["span"], ], ["div", 0], ]; }, parseMarkdown: (token, h) => { const content = []; if (token.tokens && token.tokens.length > 0) { content.push(h.createNode("paragraph", {}, h.parseInline(token.tokens))); } else if (token.text) { content.push(h.createNode("paragraph", {}, [h.createNode("text", { text: token.text })])); } else { content.push(h.createNode("paragraph", {}, [])); } if (token.nestedTokens && token.nestedTokens.length > 0) { const nestedContent = h.parseChildren(token.nestedTokens); content.push(...nestedContent); } return h.createNode("taskItem", { checked: token.checked || false }, content); }, renderMarkdown: (node, h) => { var _a; const checkedChar = ( (_a = node.attrs) == null ? void 0 : _a.checked ) ? "x" : " "; const prefix = `- [${checkedChar}] `; return renderNestedMarkdownContent(node, h, prefix); }, addExtensions() { if (!this.options.nested) { return []; } return [createBranchingListDeleteKeymap(this.name, [this.options.taskListTypeName])]; }, addKeyboardShortcuts() { const shortcuts = { "Enter": () => this.editor.commands.splitListItem(this.name), "Shift-Tab": () => this.editor.commands.liftListItem(this.name), }; if (!this.options.nested) { return shortcuts; } return { ...shortcuts, Tab: () => this.editor.commands.sinkListItem(this.name), }; }, addNodeView() { return ({ node, HTMLAttributes, getPos, editor }) => { const listItem = document.createElement("li"); const checkboxWrapper = document.createElement("label"); const checkboxStyler = document.createElement("span"); const checkbox = document.createElement("input"); const content = document.createElement("div"); const updateA11Y = (currentNode) => { var _a, _b; checkbox.ariaLabel = ((_b = (_a = this.options.a11y) == null ? void 0 : _a.checkboxLabel) == null ? void 0 : _b.call(_a, currentNode, checkbox.checked)) || `Task item checkbox for ${currentNode.textContent || "empty task item"}`; }; updateA11Y(node); checkboxWrapper.contentEditable = "false"; checkbox.type = "checkbox"; checkbox.addEventListener("mousedown", (event) => event.preventDefault()); checkbox.addEventListener("change", (event) => { if (!editor.isEditable && !this.options.onReadOnlyChecked) { checkbox.checked = !checkbox.checked; return; } const { checked } = event.target; if (editor.isEditable && typeof getPos === "function") { editor .chain() .focus(void 0, { scrollIntoView: false }) .command(({ tr }) => { const position = getPos(); if (typeof position !== "number") { return false; } const currentNode = tr.doc.nodeAt(position); tr.setNodeMarkup(position, void 0, { ...(currentNode == null ? void 0 : currentNode.attrs), checked, }); return true; }) .run(); } if (!editor.isEditable && this.options.onReadOnlyChecked) { if (!this.options.onReadOnlyChecked(node, checked)) { checkbox.checked = !checkbox.checked; } } }); Object.entries(this.options.HTMLAttributes).forEach(([key, value]) => { listItem.setAttribute(key, value); }); listItem.dataset.checked = node.attrs.checked; checkbox.checked = node.attrs.checked; checkboxWrapper.append(checkbox, checkboxStyler); listItem.append(checkboxWrapper, content); Object.entries(HTMLAttributes).forEach(([key, value]) => { listItem.setAttribute(key, value); }); let prevRenderedAttributeKeys = new Set(Object.keys(HTMLAttributes)); return { dom: listItem, contentDOM: content, update: (updatedNode) => { if (updatedNode.type !== this.type) { return false; } listItem.dataset.checked = updatedNode.attrs.checked; checkbox.checked = updatedNode.attrs.checked; updateA11Y(updatedNode); const extensionAttributes = editor.extensionManager.attributes; const newHTMLAttributes = getRenderedAttributes(updatedNode, extensionAttributes); const newKeys = new Set(Object.keys(newHTMLAttributes)); const staticAttrs = this.options.HTMLAttributes; prevRenderedAttributeKeys.forEach((key) => { if (!newKeys.has(key)) { if (key in staticAttrs) { listItem.setAttribute(key, staticAttrs[key]); } else { listItem.removeAttribute(key); } } }); Object.entries(newHTMLAttributes).forEach(([key, value]) => { if (value === null || value === void 0) { if (key in staticAttrs) { listItem.setAttribute(key, staticAttrs[key]); } else { listItem.removeAttribute(key); } } else { listItem.setAttribute(key, value); } }); prevRenderedAttributeKeys = newKeys; return true; }, }; }; }, addInputRules() { return [ wrappingInputRule({ find: inputRegex, type: this.type, getAttributes: (match) => ({ checked: match[match.length - 1] === "x", }), }), ]; }, }); var TaskList$2 = Node3.create({ name: "taskList", addOptions() { return { itemTypeName: "taskItem", HTMLAttributes: {}, }; }, group: "block list", content() { return `${this.options.itemTypeName}+`; }, parseHTML() { return [ { tag: `ul[data-type="${this.name}"]`, priority: 51, }, ]; }, renderHTML({ HTMLAttributes }) { return ["ul", mergeAttributes(this.options.HTMLAttributes, HTMLAttributes, { "data-type": this.name }), 0]; }, parseMarkdown: (token, h) => { return h.createNode("taskList", {}, h.parseChildren(token.items || [])); }, renderMarkdown: (node, h) => { if (!node.content) { return ""; } return h.renderChildren(node.content, "\n"); }, markdownTokenizer: { name: "taskList", level: "block", start(src) { var _a; const index = (_a = src.match(/^\s*[-+*]\s+\[([ xX])\]\s+/)) == null ? void 0 : _a.index; return index !== void 0 ? index : -1; }, tokenize(src, tokens, lexer) { const parseTaskListContent = (content) => { const nestedResult = parseIndentedBlocks( content, { itemPattern: /^(\s*)([-+*])\s+\[([ xX])\]\s+(.*)$/, extractItemData: (match) => ({ indentLevel: match[1].length, mainContent: match[4], checked: match[3].toLowerCase() === "x", }), createToken: (data, nestedTokens) => ({ type: "taskItem", raw: "", mainContent: data.mainContent, indentLevel: data.indentLevel, checked: data.checked, text: data.mainContent, tokens: lexer.inlineTokens(data.mainContent), nestedTokens, }), // Allow recursive nesting customNestedParser: parseTaskListContent, }, lexer, ); if (nestedResult) { return [ { type: "taskList", raw: nestedResult.raw, items: nestedResult.items, }, ]; } return lexer.blockTokens(content); }; const result = parseIndentedBlocks( src, { itemPattern: /^(\s*)([-+*])\s+\[([ xX])\]\s+(.*)$/, extractItemData: (match) => ({ indentLevel: match[1].length, mainContent: match[4], checked: match[3].toLowerCase() === "x", }), createToken: (data, nestedTokens) => ({ type: "taskItem", raw: "", mainContent: data.mainContent, indentLevel: data.indentLevel, checked: data.checked, text: data.mainContent, tokens: lexer.inlineTokens(data.mainContent), nestedTokens, }), // Use the recursive parser for nested content customNestedParser: parseTaskListContent, }, lexer, ); if (!result) { return void 0; } return { type: "taskList", raw: result.raw, items: result.items, }; }, }, markdownOptions: { indentsContent: true, }, addCommands() { return { toggleTaskList: () => ({ commands }) => { return commands.toggleList(this.name, this.options.itemTypeName); }, }; }, addKeyboardShortcuts() { return { "Mod-Shift-9": () => this.editor.commands.toggleTaskList(), }; }, }); // src/kit/index.ts Extension.create({ name: "listKit", addExtensions() { const extensions = []; if (this.options.bulletList !== false) { extensions.push(BulletList$2.configure(this.options.bulletList)); } if (this.options.listItem !== false) { extensions.push(ListItem$2.configure(this.options.listItem)); } if (this.options.listKeymap !== false) { extensions.push(ListKeymap.configure(this.options.listKeymap)); } if (this.options.orderedList !== false) { extensions.push(OrderedList$2.configure(this.options.orderedList)); } if (this.options.taskItem !== false) { extensions.push(TaskItem$2.configure(this.options.taskItem)); } if (this.options.taskList !== false) { extensions.push(TaskList$2.configure(this.options.taskList)); } return extensions; }, }); // src/paragraph.ts var EMPTY_PARAGRAPH_MARKDOWN = " "; var NBSP_CHAR = "\xA0"; var Paragraph$2 = Node3.create({ name: "paragraph", priority: 1e3, addOptions() { return { HTMLAttributes: {}, }; }, group: "block", content: "inline*", parseHTML() { return [{ tag: "p" }]; }, renderHTML({ HTMLAttributes }) { return ["p", mergeAttributes(this.options.HTMLAttributes, HTMLAttributes), 0]; }, parseMarkdown: (token, helpers) => { const tokens = token.tokens || []; if (tokens.length === 1 && tokens[0].type === "image") { return helpers.parseChildren([tokens[0]]); } const content = helpers.parseInline(tokens); const hasExplicitEmptyParagraphMarker = tokens.length === 1 && tokens[0].type === "text" && (tokens[0].raw === EMPTY_PARAGRAPH_MARKDOWN || tokens[0].text === EMPTY_PARAGRAPH_MARKDOWN || tokens[0].raw === NBSP_CHAR || tokens[0].text === NBSP_CHAR); if (hasExplicitEmptyParagraphMarker && content.length === 1 && content[0].type === "text" && (content[0].text === EMPTY_PARAGRAPH_MARKDOWN || content[0].text === NBSP_CHAR)) { return helpers.createNode("paragraph", void 0, []); } return helpers.createNode("paragraph", void 0, content); }, renderMarkdown: (node, h, ctx) => { var _a, _b; if (!node) { return ""; } const content = Array.isArray(node.content) ? node.content : []; if (content.length === 0) { const previousContent = Array.isArray((_a = ctx == null ? void 0 : ctx.previousNode) == null ? void 0 : _a.content) ? ctx.previousNode.content : []; const previousNodeIsEmptyParagraph = ((_b = ctx == null ? void 0 : ctx.previousNode) == null ? void 0 : _b.type) === "paragraph" && previousContent.length === 0; return previousNodeIsEmptyParagraph ? EMPTY_PARAGRAPH_MARKDOWN : ""; } return h.renderChildren(content); }, addCommands() { return { setParagraph: () => ({ commands }) => { return commands.setNode(this.name); }, }; }, addKeyboardShortcuts() { return { "Mod-Alt-0": () => this.editor.commands.setParagraph(), }; }, }); /** Create a plugin that, when added to a ProseMirror instance, causes a decoration to show up at the drop position when something is dragged over the editor. Nodes may add a `disableDropCursor` property to their spec to control the showing of a drop cursor inside them. This may be a boolean or a function, which will be called with a view and a position, and should return a boolean. */ function dropCursor(options = {}) { return new Plugin({ view(editorView) { return new DropCursorView(editorView, options); }, }); } class DropCursorView { constructor(editorView, options) { var _a; this.editorView = editorView; this.cursorPos = null; this.element = null; this.timeout = -1; this.width = (_a = options.width) !== null && _a !== void 0 ? _a : 1; this.color = options.color === false ? undefined : options.color || "black"; this.class = options.class; this.handlers = ["dragover", "dragend", "drop", "dragleave"].map((name) => { let handler = (e) => { this[name](e); }; editorView.dom.addEventListener(name, handler); return { name, handler }; }); } destroy() { this.handlers.forEach(({ name, handler }) => this.editorView.dom.removeEventListener(name, handler)); } update(editorView, prevState) { if (this.cursorPos != null && prevState.doc != editorView.state.doc) { if (this.cursorPos > editorView.state.doc.content.size) this.setCursor(null); else this.updateOverlay(); } } setCursor(pos) { if (pos == this.cursorPos) return; this.cursorPos = pos; if (pos == null) { this.element.parentNode.removeChild(this.element); this.element = null; } else { this.updateOverlay(); } } updateOverlay() { let $pos = this.editorView.state.doc.resolve(this.cursorPos); let isBlock = !$pos.parent.inlineContent, rect; let editorDOM = this.editorView.dom, editorRect = editorDOM.getBoundingClientRect(); let scaleX = editorRect.width / editorDOM.offsetWidth, scaleY = editorRect.height / editorDOM.offsetHeight; if (isBlock) { let before = $pos.nodeBefore, after = $pos.nodeAfter; if (before || after) { let node = this.editorView.nodeDOM(this.cursorPos - (before ? before.nodeSize : 0)); if (node) { let nodeRect = node.getBoundingClientRect(); let top = before ? nodeRect.bottom : nodeRect.top; if (before && after) top = (top + this.editorView.nodeDOM(this.cursorPos).getBoundingClientRect().top) / 2; let halfWidth = (this.width / 2) * scaleY; rect = { left: nodeRect.left, right: nodeRect.right, top: top - halfWidth, bottom: top + halfWidth }; } } } if (!rect) { let coords = this.editorView.coordsAtPos(this.cursorPos); let halfWidth = (this.width / 2) * scaleX; rect = { left: coords.left - halfWidth, right: coords.left + halfWidth, top: coords.top, bottom: coords.bottom }; } let parent = this.editorView.dom.offsetParent; if (!this.element) { this.element = parent.appendChild(document.createElement("div")); if (this.class) this.element.className = this.class; this.element.style.cssText = "position: absolute; z-index: 50; pointer-events: none;"; if (this.color) { this.element.style.backgroundColor = this.color; } } this.element.classList.toggle("prosemirror-dropcursor-block", isBlock); this.element.classList.toggle("prosemirror-dropcursor-inline", !isBlock); let parentLeft, parentTop; if (!parent || (parent == document.body && getComputedStyle(parent).position == "static")) { parentLeft = -pageXOffset; parentTop = -pageYOffset; } else { let rect = parent.getBoundingClientRect(); let parentScaleX = rect.width / parent.offsetWidth, parentScaleY = rect.height / parent.offsetHeight; parentLeft = rect.left - parent.scrollLeft * parentScaleX; parentTop = rect.top - parent.scrollTop * parentScaleY; } this.element.style.left = (rect.left - parentLeft) / scaleX + "px"; this.element.style.top = (rect.top - parentTop) / scaleY + "px"; this.element.style.width = (rect.right - rect.left) / scaleX + "px"; this.element.style.height = (rect.bottom - rect.top) / scaleY + "px"; } scheduleRemoval(timeout) { clearTimeout(this.timeout); this.timeout = setTimeout(() => this.setCursor(null), timeout); } dragover(event) { if (!this.editorView.editable) return; let pos = this.editorView.posAtCoords({ left: event.clientX, top: event.clientY }); let node = pos && pos.inside >= 0 && this.editorView.state.doc.nodeAt(pos.inside); let disableDropCursor = node && node.type.spec.disableDropCursor; let disabled = typeof disableDropCursor == "function" ? disableDropCursor(this.editorView, pos, event) : disableDropCursor; if (pos && !disabled) { let target = pos.pos; if (this.editorView.dragging && this.editorView.dragging.slice) { let point = dropPoint(this.editorView.state.doc, target, this.editorView.dragging.slice); if (point != null) target = point; } this.setCursor(target); this.scheduleRemoval(5000); } } dragend() { this.scheduleRemoval(20); } drop() { this.scheduleRemoval(20); } dragleave(event) { if (!this.editorView.dom.contains(event.relatedTarget)) this.setCursor(null); } } // src/character-count/character-count.ts Extension.create({ name: "characterCount", addOptions() { return { limit: null, autoTrim: true, mode: "textSize", textCounter: (text) => text.length, wordCounter: (text) => text.split(" ").filter((word) => word !== "").length, }; }, addStorage() { return { characters: () => 0, words: () => 0, }; }, onBeforeCreate() { this.storage.characters = (options) => { const node = (options == null ? void 0 : options.node) || this.editor.state.doc; const mode = (options == null ? void 0 : options.mode) || this.options.mode; if (mode === "textSize") { const text = node.textBetween(0, node.content.size, void 0, " "); return this.options.textCounter(text); } return node.nodeSize; }; this.storage.words = (options) => { const node = (options == null ? void 0 : options.node) || this.editor.state.doc; const text = node.textBetween(0, node.content.size, " ", " "); return this.options.wordCounter(text); }; }, addProseMirrorPlugins() { let initialEvaluationDone = false; return [ new Plugin({ key: new PluginKey("characterCount"), appendTransaction: (transactions, oldState, newState) => { if (initialEvaluationDone) { return; } const limit = this.options.limit; const autoTrim = this.options.autoTrim; if (limit === null || limit === void 0 || limit === 0 || autoTrim === false) { initialEvaluationDone = true; return; } const initialContentSize = this.storage.characters({ node: newState.doc }); if (initialContentSize > limit) { const over = initialContentSize - limit; const from = 0; const to = over; console.warn(`[CharacterCount] Initial content exceeded limit of ${limit} characters. Content was automatically trimmed.`); const tr = newState.tr.deleteRange(from, to); initialEvaluationDone = true; return tr; } initialEvaluationDone = true; }, filterTransaction: (transaction, state) => { const limit = this.options.limit; if (!transaction.docChanged || limit === 0 || limit === null || limit === void 0) { return true; } const oldSize = this.storage.characters({ node: state.doc }); const newSize = this.storage.characters({ node: transaction.doc }); if (newSize <= limit) { return true; } if (oldSize > limit && newSize > limit && newSize <= oldSize) { return true; } if (oldSize > limit && newSize > limit && newSize > oldSize) { return false; } const isPaste = transaction.getMeta("paste"); if (!isPaste) { return false; } const pos = transaction.selection.$head.pos; const over = newSize - limit; const from = pos - over; const to = pos; transaction.deleteRange(from, to); const updatedSize = this.storage.characters({ node: transaction.doc }); if (updatedSize > limit) { return false; } return true; }, }), ]; }, }); var Dropcursor = Extension.create({ name: "dropCursor", addOptions() { return { color: "currentColor", width: 1, class: void 0, }; }, addProseMirrorPlugins() { return [dropCursor(this.options)]; }, }); Extension.create({ name: "focus", addOptions() { return { className: "has-focus", mode: "all", }; }, addProseMirrorPlugins() { return [ new Plugin({ key: new PluginKey("focus"), props: { decorations: ({ doc, selection }) => { const { isEditable, isFocused } = this.editor; const { anchor } = selection; const decorations = []; if (!isEditable || !isFocused) { return DecorationSet.create(doc, []); } let maxLevels = 0; if (this.options.mode === "deepest") { doc.descendants((node, pos) => { if (node.isText) { return; } const isCurrent = anchor >= pos && anchor <= pos + node.nodeSize - 1; if (!isCurrent) { return false; } maxLevels += 1; }); } let currentLevel = 0; doc.descendants((node, pos) => { if (node.isText) { return false; } const isCurrent = anchor >= pos && anchor <= pos + node.nodeSize - 1; if (!isCurrent) { return false; } currentLevel += 1; const outOfScope = (this.options.mode === "deepest" && maxLevels - currentLevel > 0) || (this.options.mode === "shallowest" && currentLevel > 1); if (outOfScope) { return this.options.mode === "deepest"; } decorations.push( Decoration.node(pos, pos + node.nodeSize, { class: this.options.className, }), ); }); return DecorationSet.create(doc, decorations); }, }, }), ]; }, }); var Gapcursor = Extension.create({ name: "gapCursor", addProseMirrorPlugins() { return [gapCursor()]; }, extendNodeSchema(extension) { var _a; const context = { name: extension.name, options: extension.options, storage: extension.storage, }; return { allowGapCursor: (_a = callOrReturn(getExtensionField(extension, "allowGapCursor", context))) != null ? _a : null, }; }, }); var DEFAULT_DATA_ATTRIBUTE = "placeholder"; var PLUGIN_KEY = new PluginKey("tiptap__placeholder"); var VIEWPORT_OVERSCAN_PX = 200; function createPlaceholderDecoration(options) { const { editor, placeholder, dataAttribute, pos, node, isEmptyDoc, hasAnchor, classes: { emptyNode, emptyEditor }, } = options; const classes = [emptyNode]; if (isEmptyDoc) { classes.push(emptyEditor); } return Decoration.node(pos, pos + node.nodeSize, { class: classes.join(" "), [dataAttribute]: typeof placeholder === "function" ? placeholder({ editor, node, pos, hasAnchor, }) : placeholder, }); } // src/placeholder/utils/buildPlaceholderDecorations.ts function resolveEmptyNodeClass(emptyNodeClass, props) { return typeof emptyNodeClass === "function" ? emptyNodeClass(props) : emptyNodeClass; } function buildPlaceholderDecorations({ editor, options, dataAttribute, doc, selection }) { var _a, _b; const active = editor.isEditable || !options.showOnlyWhenEditable; if (!active) { return null; } const { anchor } = selection; const decorations = []; const isEmptyDoc = editor.isEmpty; const useResolvedPath = options.showOnlyCurrent && !options.includeChildren; if (useResolvedPath) { const resolved = doc.resolve(anchor); const node = resolved.depth > 0 ? resolved.node(1) : resolved.nodeAfter; const nodeStart = resolved.depth > 0 ? resolved.before(1) : anchor; if (node && node.type.isTextblock && isNodeEmpty(node)) { const hasAnchor = anchor >= nodeStart && anchor <= nodeStart + node.nodeSize; decorations.push( createPlaceholderDecoration({ editor, isEmptyDoc, dataAttribute, hasAnchor, placeholder: options.placeholder, classes: { emptyEditor: options.emptyEditorClass, emptyNode: resolveEmptyNodeClass(options.emptyNodeClass, { editor, node, pos: nodeStart, hasAnchor, }), }, node, pos: nodeStart, }), ); } } else { const pluginState = PLUGIN_KEY.getState(editor.state); const from = (_a = pluginState == null ? void 0 : pluginState.topPos) != null ? _a : 0; const to = (_b = pluginState == null ? void 0 : pluginState.bottomPos) != null ? _b : doc.content.size; doc.nodesBetween(from, to, (node, pos) => { const hasAnchor = anchor >= pos && anchor <= pos + node.nodeSize; const isEmpty = !node.isLeaf && isNodeEmpty(node); if (!node.type.isTextblock) { return options.includeChildren; } if ((hasAnchor || !options.showOnlyCurrent) && isEmpty) { decorations.push( createPlaceholderDecoration({ editor, isEmptyDoc, dataAttribute, hasAnchor, placeholder: options.placeholder, classes: { emptyEditor: options.emptyEditorClass, emptyNode: resolveEmptyNodeClass(options.emptyNodeClass, { editor, node, pos, hasAnchor, }), }, node, pos, }), ); } return options.includeChildren; }); } return DecorationSet.create(doc, decorations); } // src/placeholder/utils/preparePlaceholderAttribute.ts function preparePlaceholderAttribute(attr) { return attr .replace(/\s+/g, "-") .replace(/[^a-zA-Z0-9-]/g, "") .replace(/^[0-9-]+/, "") .replace(/^-+/, "") .toLowerCase(); } // src/placeholder/utils/findScrollParent.ts function isScrollable(el) { const style = getComputedStyle(el); const overflow = `${style.overflow} ${style.overflowY} ${style.overflowX}`; return /auto|scroll|overlay/.test(overflow); } function findScrollParent(element) { let el = element; while (el) { if (isScrollable(el)) { return el; } const parent = el.parentElement; if (!parent) { const root = el.getRootNode(); if (root instanceof ShadowRoot) { el = root.host; continue; } return window; } el = parent; } return window; } // src/placeholder/utils/getViewportBoundaryPositions.ts function getContainerRect(container) { if (container === window) { return { top: 0, bottom: window.innerHeight }; } return container.getBoundingClientRect(); } function getViewportBoundaryPositions({ view, scrollContainer }) { const editorRect = view.dom.getBoundingClientRect(); if (editorRect.width <= 0 || editorRect.height <= 0) { return null; } const containerRect = scrollContainer ? getContainerRect(scrollContainer) : { top: 0, bottom: window.innerHeight }; const visibleTop = Math.max(editorRect.top, containerRect.top) - VIEWPORT_OVERSCAN_PX; const visibleBottom = Math.min(editorRect.bottom, containerRect.bottom) + VIEWPORT_OVERSCAN_PX; if (visibleTop >= visibleBottom) { return null; } const minX = editorRect.left + 1; const maxX = editorRect.right - 1; if (minX > maxX) { return null; } const isRTL = getComputedStyle(view.dom).direction === "rtl"; const targetX = isRTL ? editorRect.right - 2 : editorRect.left + 2; const x = Math.min(Math.max(targetX, minX), maxX); const probeTop = Math.max(visibleTop + 2, editorRect.top + 1); const probeBottom = Math.min(visibleBottom - 2, editorRect.bottom - 1); if (probeTop > probeBottom) { return null; } const topPos = view.posAtCoords({ left: x, top: probeTop }); const bottomPos = view.posAtCoords({ left: x, top: probeBottom }); if (!topPos || !bottomPos) { return null; } return { top: topPos.pos, bottom: bottomPos.pos }; } // src/placeholder/utils/viewportTracking.ts var viewportPluginState = { /** * Initialises the viewport state with no known positions. * @returns The initial viewport state. */ init() { return { topPos: null, bottomPos: null }; }, /** * Updates the viewport state from incoming transactions. * @param tr - The transaction being applied. * @param prev - The previous viewport state. * @returns The next viewport state. */ apply(tr, prev) { const meta = tr.getMeta(PLUGIN_KEY); if (meta == null ? void 0 : meta.positions) { return { topPos: meta.positions.top, bottomPos: meta.positions.bottom }; } if (!tr.docChanged) { return prev; } return { topPos: prev.topPos !== null ? tr.mapping.map(prev.topPos) : null, bottomPos: prev.bottomPos !== null ? tr.mapping.map(prev.bottomPos) : null, }; }, }; function createViewportPluginView(view) { const scrollContainer = findScrollParent(view.dom); const computeAndDispatch = () => { const positions = getViewportBoundaryPositions({ view, scrollContainer, }); if (positions === null) { return; } const prev = PLUGIN_KEY.getState(view.state); if ((prev == null ? void 0 : prev.topPos) === positions.top && (prev == null ? void 0 : prev.bottomPos) === positions.bottom) { return; } const tr = view.state.tr.setMeta(PLUGIN_KEY, { positions }); view.dispatch(tr); }; let frame = null; let lastCompute = 0; const MIN_SCROLL_INTERVAL = 150; const scheduleFrame = () => { if (frame !== null) return; frame = requestAnimationFrame(() => { frame = null; const now = performance.now(); if (now - lastCompute >= MIN_SCROLL_INTERVAL) { lastCompute = now; computeAndDispatch(); } else { scheduleFrame(); } }); }; scrollContainer.addEventListener("scroll", scheduleFrame, { passive: true }); const resizeObserver = typeof ResizeObserver !== "undefined" ? new ResizeObserver(scheduleFrame) : null; resizeObserver == null ? void 0 : resizeObserver.observe(view.dom); const intersectionObserver = typeof IntersectionObserver !== "undefined" ? new IntersectionObserver(scheduleFrame) : null; intersectionObserver == null ? void 0 : intersectionObserver.observe(view.dom); view.dom.addEventListener("focus", scheduleFrame); computeAndDispatch(); return { update(_view, prevState) { if (view.state.doc.content.size !== prevState.doc.content.size) { scheduleFrame(); } }, destroy: () => { if (frame !== null) { cancelAnimationFrame(frame); } scrollContainer.removeEventListener("scroll", scheduleFrame); resizeObserver == null ? void 0 : resizeObserver.disconnect(); intersectionObserver == null ? void 0 : intersectionObserver.disconnect(); view.dom.removeEventListener("focus", scheduleFrame); }, }; } // src/placeholder/plugins/PlaceholderPlugin.ts function createPlaceholderPlugin({ editor, options }) { const dataAttribute = options.dataAttribute ? `data-${preparePlaceholderAttribute(options.dataAttribute)}` : `data-${DEFAULT_DATA_ATTRIBUTE}`; return new Plugin({ key: PLUGIN_KEY, state: viewportPluginState, view: createViewportPluginView, props: { decorations: ({ doc, selection }) => buildPlaceholderDecorations({ editor, options, dataAttribute, doc, selection }), }, }); } // src/placeholder/placeholder.ts var Placeholder = Extension.create({ name: "placeholder", addOptions() { return { emptyEditorClass: "is-editor-empty", emptyNodeClass: "is-empty", dataAttribute: DEFAULT_DATA_ATTRIBUTE, placeholder: "Write something \u2026", showOnlyWhenEditable: true, showOnlyCurrent: true, includeChildren: false, }; }, addProseMirrorPlugins() { return [createPlaceholderPlugin({ editor: this.editor, options: this.options })]; }, }); var selectionStyle = `.ProseMirror:not(.ProseMirror-focused) *::selection { background: transparent; } .ProseMirror:not(.ProseMirror-focused) *::-moz-selection { background: transparent; }`; Extension.create({ name: "selection", addOptions() { return { className: "selection", }; }, addProseMirrorPlugins() { const { editor, options } = this; if (editor.options.injectCSS && typeof document !== "undefined") { createStyleTag(selectionStyle, editor.options.injectNonce, "selection"); } return [ new Plugin({ key: new PluginKey("selection"), props: { decorations(state) { if (state.selection.empty || editor.isFocused || !editor.isEditable || isNodeSelection(state.selection) || editor.view.dragging) { return null; } return DecorationSet.create(state.doc, [ Decoration.inline(state.selection.from, state.selection.to, { class: options.className, }), ]); }, }, }), ]; }, }); var skipTrailingNodeMeta = "skipTrailingNode"; function nodeEqualsType({ types, node }) { return (node && Array.isArray(types) && types.includes(node.type)) || (node == null ? void 0 : node.type) === types; } var TrailingNode = Extension.create({ name: "trailingNode", addOptions() { return { node: void 0, notAfter: [], }; }, addProseMirrorPlugins() { var _a; const plugin = new PluginKey(this.name); const defaultNode = this.options.node || ((_a = this.editor.schema.topNodeType.contentMatch.defaultType) == null ? void 0 : _a.name) || "paragraph"; const disabledNodes = Object.entries(this.editor.schema.nodes) .map(([, value]) => value) .filter((node) => (this.options.notAfter || []).concat(defaultNode).includes(node.name)); return [ new Plugin({ key: plugin, appendTransaction: (transactions, __, state) => { const { doc, tr, schema } = state; const shouldInsertNodeAtEnd = plugin.getState(state); const endPosition = doc.content.size; const type = schema.nodes[defaultNode]; if (transactions.some((transaction) => transaction.getMeta(skipTrailingNodeMeta))) { return; } if (!shouldInsertNodeAtEnd) { return; } return tr.insert(endPosition, type.create()); }, state: { init: (_, state) => { const lastNode = state.tr.doc.lastChild; return !nodeEqualsType({ node: lastNode, types: disabledNodes }); }, apply: (tr, value) => { if (!tr.docChanged) { return value; } if (tr.getMeta("__uniqueIDTransaction")) { return value; } const lastNode = tr.doc.lastChild; return !nodeEqualsType({ node: lastNode, types: disabledNodes }); }, }, }), ]; }, }); var UndoRedo = Extension.create({ name: "undoRedo", addOptions() { return { depth: 100, newGroupDelay: 500, }; }, addCommands() { return { undo: () => ({ state, dispatch }) => { return undo(state, dispatch); }, redo: () => ({ state, dispatch }) => { return redo(state, dispatch); }, }; }, addProseMirrorPlugins() { return [history(this.options)]; }, addKeyboardShortcuts() { return { "Mod-z": () => this.editor.commands.undo(), "Shift-Mod-z": () => this.editor.commands.redo(), "Mod-y": () => this.editor.commands.redo(), // Russian keyboard layouts "Mod-\u044F": () => this.editor.commands.undo(), "Shift-Mod-\u044F": () => this.editor.commands.redo(), }; }, }); // src/starter-kit.ts var StarterKit = Extension.create({ name: "starterKit", addExtensions() { var _a, _b, _c, _d; const extensions = []; if (this.options.bold !== false) { extensions.push(Bold$3.configure(this.options.bold)); } if (this.options.blockquote !== false) { extensions.push(Blockquote$2.configure(this.options.blockquote)); } if (this.options.bulletList !== false) { extensions.push(BulletList$2.configure(this.options.bulletList)); } if (this.options.code !== false) { extensions.push(Code$2.configure(this.options.code)); } if (this.options.codeBlock !== false) { extensions.push(CodeBlock$2.configure(this.options.codeBlock)); } if (this.options.document !== false) { extensions.push(Document.configure(this.options.document)); } if (this.options.dropcursor !== false) { extensions.push(Dropcursor.configure(this.options.dropcursor)); } if (this.options.gapcursor !== false) { extensions.push(Gapcursor.configure(this.options.gapcursor)); } if (this.options.hardBreak !== false) { extensions.push(HardBreak$2.configure(this.options.hardBreak)); } if (this.options.heading !== false) { extensions.push(Heading$2.configure(this.options.heading)); } if (this.options.undoRedo !== false) { extensions.push(UndoRedo.configure(this.options.undoRedo)); } if (this.options.horizontalRule !== false) { extensions.push(HorizontalRule$2.configure(this.options.horizontalRule)); } if (this.options.italic !== false) { extensions.push(Italic$3.configure(this.options.italic)); } if (this.options.listItem !== false) { extensions.push(ListItem$2.configure(this.options.listItem)); } if (this.options.listKeymap !== false) { extensions.push(ListKeymap.configure((_a = this.options) == null ? void 0 : _a.listKeymap)); } if (this.options.link !== false) { extensions.push(Link$2.configure((_b = this.options) == null ? void 0 : _b.link)); } if (this.options.orderedList !== false) { extensions.push(OrderedList$2.configure(this.options.orderedList)); } if (this.options.paragraph !== false) { extensions.push(Paragraph$2.configure(this.options.paragraph)); } if (this.options.strike !== false) { extensions.push(Strike$2.configure(this.options.strike)); } if (this.options.text !== false) { extensions.push(Text$2.configure(this.options.text)); } if (this.options.underline !== false) { extensions.push(Underline.configure((_c = this.options) == null ? void 0 : _c.underline)); } if (this.options.trailingNode !== false) { extensions.push(TrailingNode.configure((_d = this.options) == null ? void 0 : _d.trailingNode)); } return extensions; }, }); // src/index.ts var index_default$1 = StarterKit; // src/index.ts var index_default = Placeholder; /* eslint-disable no-bitwise */ const decodeCache = {}; function getDecodeCache(exclude) { let cache = decodeCache[exclude]; if (cache) { return cache; } cache = decodeCache[exclude] = []; for (let i = 0; i < 128; i++) { const ch = String.fromCharCode(i); cache.push(ch); } for (let i = 0; i < exclude.length; i++) { const ch = exclude.charCodeAt(i); cache[ch] = "%" + ("0" + ch.toString(16).toUpperCase()).slice(-2); } return cache; } // Decode percent-encoded string. // function decode$1(string, exclude) { if (typeof exclude !== "string") { exclude = decode$1.defaultChars; } const cache = getDecodeCache(exclude); return string.replace(/(%[a-f0-9]{2})+/gi, function (seq) { let result = ""; for (let i = 0, l = seq.length; i < l; i += 3) { const b1 = parseInt(seq.slice(i + 1, i + 3), 16); if (b1 < 0x80) { result += cache[b1]; continue; } if ((b1 & 0xe0) === 0xc0 && i + 3 < l) { // 110xxxxx 10xxxxxx const b2 = parseInt(seq.slice(i + 4, i + 6), 16); if ((b2 & 0xc0) === 0x80) { const chr = ((b1 << 6) & 0x7c0) | (b2 & 0x3f); if (chr < 0x80) { result += "\ufffd\ufffd"; } else { result += String.fromCharCode(chr); } i += 3; continue; } } if ((b1 & 0xf0) === 0xe0 && i + 6 < l) { // 1110xxxx 10xxxxxx 10xxxxxx const b2 = parseInt(seq.slice(i + 4, i + 6), 16); const b3 = parseInt(seq.slice(i + 7, i + 9), 16); if ((b2 & 0xc0) === 0x80 && (b3 & 0xc0) === 0x80) { const chr = ((b1 << 12) & 0xf000) | ((b2 << 6) & 0xfc0) | (b3 & 0x3f); if (chr < 0x800 || (chr >= 0xd800 && chr <= 0xdfff)) { result += "\ufffd\ufffd\ufffd"; } else { result += String.fromCharCode(chr); } i += 6; continue; } } if ((b1 & 0xf8) === 0xf0 && i + 9 < l) { // 111110xx 10xxxxxx 10xxxxxx 10xxxxxx const b2 = parseInt(seq.slice(i + 4, i + 6), 16); const b3 = parseInt(seq.slice(i + 7, i + 9), 16); const b4 = parseInt(seq.slice(i + 10, i + 12), 16); if ((b2 & 0xc0) === 0x80 && (b3 & 0xc0) === 0x80 && (b4 & 0xc0) === 0x80) { let chr = ((b1 << 18) & 0x1c0000) | ((b2 << 12) & 0x3f000) | ((b3 << 6) & 0xfc0) | (b4 & 0x3f); if (chr < 0x10000 || chr > 0x10ffff) { result += "\ufffd\ufffd\ufffd\ufffd"; } else { chr -= 0x10000; result += String.fromCharCode(0xd800 + (chr >> 10), 0xdc00 + (chr & 0x3ff)); } i += 9; continue; } } result += "\ufffd"; } return result; }); } decode$1.defaultChars = ";/?:@&=+$,#"; decode$1.componentChars = ""; const encodeCache = {}; // Create a lookup array where anything but characters in `chars` string // and alphanumeric chars is percent-encoded. // function getEncodeCache(exclude) { let cache = encodeCache[exclude]; if (cache) { return cache; } cache = encodeCache[exclude] = []; for (let i = 0; i < 128; i++) { const ch = String.fromCharCode(i); if (/^[0-9a-z]$/i.test(ch)) { // always allow unencoded alphanumeric characters cache.push(ch); } else { cache.push("%" + ("0" + i.toString(16).toUpperCase()).slice(-2)); } } for (let i = 0; i < exclude.length; i++) { cache[exclude.charCodeAt(i)] = exclude[i]; } return cache; } // Encode unsafe characters with percent-encoding, skipping already // encoded sequences. // // - string - string to encode // - exclude - list of characters to ignore (in addition to a-zA-Z0-9) // - keepEscaped - don't encode '%' in a correct escape sequence (default: true) // function encode$1(string, exclude, keepEscaped) { if (typeof exclude !== "string") { // encode(string, keepEscaped) keepEscaped = exclude; exclude = encode$1.defaultChars; } if (typeof keepEscaped === "undefined") { keepEscaped = true; } const cache = getEncodeCache(exclude); let result = ""; for (let i = 0, l = string.length; i < l; i++) { const code = string.charCodeAt(i); if (keepEscaped && code === 0x25 /* % */ && i + 2 < l) { if (/^[0-9a-f]{2}$/i.test(string.slice(i + 1, i + 3))) { result += string.slice(i, i + 3); i += 2; continue; } } if (code < 128) { result += cache[code]; continue; } if (code >= 0xd800 && code <= 0xdfff) { if (code >= 0xd800 && code <= 0xdbff && i + 1 < l) { const nextCode = string.charCodeAt(i + 1); if (nextCode >= 0xdc00 && nextCode <= 0xdfff) { result += encodeURIComponent(string[i] + string[i + 1]); i++; continue; } } result += "%EF%BF%BD"; continue; } result += encodeURIComponent(string[i]); } return result; } encode$1.defaultChars = ";/?:@&=+$,-_.!~*'()#"; encode$1.componentChars = "-_.!~*'()"; function format(url) { let result = ""; result += url.protocol || ""; result += url.slashes ? "//" : ""; result += url.auth ? url.auth + "@" : ""; if (url.hostname && url.hostname.indexOf(":") !== -1) { // ipv6 address result += "[" + url.hostname + "]"; } else { result += url.hostname || ""; } result += url.port ? ":" + url.port : ""; result += url.pathname || ""; result += url.search || ""; result += url.hash || ""; return result; } // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a // copy of this software and associated documentation files (the // "Software"), to deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, publish, // distribute, sublicense, and/or sell copies of the Software, and to permit // persons to whom the Software is furnished to do so, subject to the // following conditions: // // The above copyright notice and this permission notice shall be included // in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. // // Changes from joyent/node: // // 1. No leading slash in paths, // e.g. in `url.parse('http://foo?bar')` pathname is ``, not `/` // // 2. Backslashes are not replaced with slashes, // so `http:\\example.org\` is treated like a relative path // // 3. Trailing colon is treated like a part of the path, // i.e. in `http://example.org:foo` pathname is `:foo` // // 4. Nothing is URL-encoded in the resulting object, // (in joyent/node some chars in auth and paths are encoded) // // 5. `url.parse()` does not have `parseQueryString` argument // // 6. Removed extraneous result properties: `host`, `path`, `query`, etc., // which can be constructed using other parts of the url. // function Url() { this.protocol = null; this.slashes = null; this.auth = null; this.port = null; this.hostname = null; this.hash = null; this.search = null; this.pathname = null; } // Reference: RFC 3986, RFC 1808, RFC 2396 // define these here so at least they only have to be // compiled once on the first module load. const protocolPattern = /^([a-z0-9.+-]+:)/i; const portPattern = /:[0-9]*$/; // Special case for a simple path URL /* eslint-disable-next-line no-useless-escape */ const simplePathPattern = /^(\/\/?(?!\/)[^\?\s]*)(\?[^\s]*)?$/; // RFC 2396: characters reserved for delimiting URLs. // We actually just auto-escape these. const delims = ["<", ">", '"', "`", " ", "\r", "\n", "\t"]; // RFC 2396: characters not allowed for various reasons. const unwise = ["{", "}", "|", "\\", "^", "`"].concat(delims); // Allowed by RFCs, but cause of XSS attacks. Always escape these. const autoEscape = ["'"].concat(unwise); // Characters that are never ever allowed in a hostname. // Note that any invalid chars are also handled, but these // are the ones that are *expected* to be seen, so we fast-path // them. const nonHostChars = ["%", "/", "?", ";", "#"].concat(autoEscape); const hostEndingChars = ["/", "?", "#"]; const hostnameMaxLen = 255; const hostnamePartPattern = /^[+a-z0-9A-Z_-]{0,63}$/; const hostnamePartStart = /^([+a-z0-9A-Z_-]{0,63})(.*)$/; // protocols that can allow "unsafe" and "unwise" chars. // protocols that never have a hostname. const hostlessProtocol = { "javascript": true, "javascript:": true, }; // protocols that always contain a // bit. const slashedProtocol = { "http": true, "https": true, "ftp": true, "gopher": true, "file": true, "http:": true, "https:": true, "ftp:": true, "gopher:": true, "file:": true, }; function urlParse(url, slashesDenoteHost) { if (url && url instanceof Url) return url; const u = new Url(); u.parse(url, slashesDenoteHost); return u; } Url.prototype.parse = function (url, slashesDenoteHost) { let lowerProto, hec, slashes; let rest = url; // trim before proceeding. // This is to support parse stuff like " http://foo.com \n" rest = rest.trim(); if (!slashesDenoteHost && url.split("#").length === 1) { // Try fast path regexp const simplePath = simplePathPattern.exec(rest); if (simplePath) { this.pathname = simplePath[1]; if (simplePath[2]) { this.search = simplePath[2]; } return this; } } let proto = protocolPattern.exec(rest); if (proto) { proto = proto[0]; lowerProto = proto.toLowerCase(); this.protocol = proto; rest = rest.substr(proto.length); } // figure out if it's got a host // user@server is *always* interpreted as a hostname, and url // resolution will treat //foo/bar as host=foo,path=bar because that's // how the browser resolves relative URLs. /* eslint-disable-next-line no-useless-escape */ if (slashesDenoteHost || proto || rest.match(/^\/\/[^@\/]+@[^@\/]+/)) { slashes = rest.substr(0, 2) === "//"; if (slashes && !(proto && hostlessProtocol[proto])) { rest = rest.substr(2); this.slashes = true; } } if (!hostlessProtocol[proto] && (slashes || (proto && !slashedProtocol[proto]))) { // there's a hostname. // the first instance of /, ?, ;, or # ends the host. // // If there is an @ in the hostname, then non-host chars *are* allowed // to the left of the last @ sign, unless some host-ending character // comes *before* the @-sign. // URLs are obnoxious. // // ex: // http://a@b@c/ => user:a@b host:c // http://a@b?@c => user:a host:c path:/?@c // v0.12 TODO(isaacs): This is not quite how Chrome does things. // Review our test case against browsers more comprehensively. // find the first instance of any hostEndingChars let hostEnd = -1; for (let i = 0; i < hostEndingChars.length; i++) { hec = rest.indexOf(hostEndingChars[i]); if (hec !== -1 && (hostEnd === -1 || hec < hostEnd)) { hostEnd = hec; } } // at this point, either we have an explicit point where the // auth portion cannot go past, or the last @ char is the decider. let auth, atSign; if (hostEnd === -1) { // atSign can be anywhere. atSign = rest.lastIndexOf("@"); } else { // atSign must be in auth portion. // http://a@b/c@d => host:b auth:a path:/c@d atSign = rest.lastIndexOf("@", hostEnd); } // Now we have a portion which is definitely the auth. // Pull that off. if (atSign !== -1) { auth = rest.slice(0, atSign); rest = rest.slice(atSign + 1); this.auth = auth; } // the host is the remaining to the left of the first non-host char hostEnd = -1; for (let i = 0; i < nonHostChars.length; i++) { hec = rest.indexOf(nonHostChars[i]); if (hec !== -1 && (hostEnd === -1 || hec < hostEnd)) { hostEnd = hec; } } // if we still have not hit it, then the entire thing is a host. if (hostEnd === -1) { hostEnd = rest.length; } if (rest[hostEnd - 1] === ":") { hostEnd--; } const host = rest.slice(0, hostEnd); rest = rest.slice(hostEnd); // pull out port. this.parseHost(host); // we've indicated that there is a hostname, // so even if it's empty, it has to be present. this.hostname = this.hostname || ""; // if hostname begins with [ and ends with ] // assume that it's an IPv6 address. const ipv6Hostname = this.hostname[0] === "[" && this.hostname[this.hostname.length - 1] === "]"; // validate a little. if (!ipv6Hostname) { const hostparts = this.hostname.split(/\./); for (let i = 0, l = hostparts.length; i < l; i++) { const part = hostparts[i]; if (!part) { continue; } if (!part.match(hostnamePartPattern)) { let newpart = ""; for (let j = 0, k = part.length; j < k; j++) { if (part.charCodeAt(j) > 127) { // we replace non-ASCII char with a temporary placeholder // we need this to make sure size of hostname is not // broken by replacing non-ASCII by nothing newpart += "x"; } else { newpart += part[j]; } } // we test again with ASCII char only if (!newpart.match(hostnamePartPattern)) { const validParts = hostparts.slice(0, i); const notHost = hostparts.slice(i + 1); const bit = part.match(hostnamePartStart); if (bit) { validParts.push(bit[1]); notHost.unshift(bit[2]); } if (notHost.length) { rest = notHost.join(".") + rest; } this.hostname = validParts.join("."); break; } } } } if (this.hostname.length > hostnameMaxLen) { this.hostname = ""; } // strip [ and ] from the hostname // the host field still retains them, though if (ipv6Hostname) { this.hostname = this.hostname.substr(1, this.hostname.length - 2); } } // chop off from the tail first. const hash = rest.indexOf("#"); if (hash !== -1) { // got a fragment string. this.hash = rest.substr(hash); rest = rest.slice(0, hash); } const qm = rest.indexOf("?"); if (qm !== -1) { this.search = rest.substr(qm); rest = rest.slice(0, qm); } if (rest) { this.pathname = rest; } if (slashedProtocol[lowerProto] && this.hostname && !this.pathname) { this.pathname = ""; } return this; }; Url.prototype.parseHost = function (host) { let port = portPattern.exec(host); if (port) { port = port[0]; if (port !== ":") { this.port = port.substr(1); } host = host.substr(0, host.length - port.length); } if (host) { this.hostname = host; } }; const mdurl = /*#__PURE__*/ Object.freeze( /*#__PURE__*/ Object.defineProperty( { __proto__: null, decode: decode$1, encode: encode$1, format, parse: urlParse, }, Symbol.toStringTag, { value: "Module" }, ), ); const Any = /[\0-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF]/; const Cc = /[\0-\x1F\x7F-\x9F]/; const regex$1 = /[\xAD\u0600-\u0605\u061C\u06DD\u070F\u0890\u0891\u08E2\u180E\u200B-\u200F\u202A-\u202E\u2060-\u2064\u2066-\u206F\uFEFF\uFFF9-\uFFFB]|\uD804[\uDCBD\uDCCD]|\uD80D[\uDC30-\uDC3F]|\uD82F[\uDCA0-\uDCA3]|\uD834[\uDD73-\uDD7A]|\uDB40[\uDC01\uDC20-\uDC7F]/; const P = /[!-#%-\*,-\/:;\?@\[-\]_\{\}\xA1\xA7\xAB\xB6\xB7\xBB\xBF\u037E\u0387\u055A-\u055F\u0589\u058A\u05BE\u05C0\u05C3\u05C6\u05F3\u05F4\u0609\u060A\u060C\u060D\u061B\u061D-\u061F\u066A-\u066D\u06D4\u0700-\u070D\u07F7-\u07F9\u0830-\u083E\u085E\u0964\u0965\u0970\u09FD\u0A76\u0AF0\u0C77\u0C84\u0DF4\u0E4F\u0E5A\u0E5B\u0F04-\u0F12\u0F14\u0F3A-\u0F3D\u0F85\u0FD0-\u0FD4\u0FD9\u0FDA\u104A-\u104F\u10FB\u1360-\u1368\u1400\u166E\u169B\u169C\u16EB-\u16ED\u1735\u1736\u17D4-\u17D6\u17D8-\u17DA\u1800-\u180A\u1944\u1945\u1A1E\u1A1F\u1AA0-\u1AA6\u1AA8-\u1AAD\u1B5A-\u1B60\u1B7D\u1B7E\u1BFC-\u1BFF\u1C3B-\u1C3F\u1C7E\u1C7F\u1CC0-\u1CC7\u1CD3\u2010-\u2027\u2030-\u2043\u2045-\u2051\u2053-\u205E\u207D\u207E\u208D\u208E\u2308-\u230B\u2329\u232A\u2768-\u2775\u27C5\u27C6\u27E6-\u27EF\u2983-\u2998\u29D8-\u29DB\u29FC\u29FD\u2CF9-\u2CFC\u2CFE\u2CFF\u2D70\u2E00-\u2E2E\u2E30-\u2E4F\u2E52-\u2E5D\u3001-\u3003\u3008-\u3011\u3014-\u301F\u3030\u303D\u30A0\u30FB\uA4FE\uA4FF\uA60D-\uA60F\uA673\uA67E\uA6F2-\uA6F7\uA874-\uA877\uA8CE\uA8CF\uA8F8-\uA8FA\uA8FC\uA92E\uA92F\uA95F\uA9C1-\uA9CD\uA9DE\uA9DF\uAA5C-\uAA5F\uAADE\uAADF\uAAF0\uAAF1\uABEB\uFD3E\uFD3F\uFE10-\uFE19\uFE30-\uFE52\uFE54-\uFE61\uFE63\uFE68\uFE6A\uFE6B\uFF01-\uFF03\uFF05-\uFF0A\uFF0C-\uFF0F\uFF1A\uFF1B\uFF1F\uFF20\uFF3B-\uFF3D\uFF3F\uFF5B\uFF5D\uFF5F-\uFF65]|\uD800[\uDD00-\uDD02\uDF9F\uDFD0]|\uD801\uDD6F|\uD802[\uDC57\uDD1F\uDD3F\uDE50-\uDE58\uDE7F\uDEF0-\uDEF6\uDF39-\uDF3F\uDF99-\uDF9C]|\uD803[\uDEAD\uDF55-\uDF59\uDF86-\uDF89]|\uD804[\uDC47-\uDC4D\uDCBB\uDCBC\uDCBE-\uDCC1\uDD40-\uDD43\uDD74\uDD75\uDDC5-\uDDC8\uDDCD\uDDDB\uDDDD-\uDDDF\uDE38-\uDE3D\uDEA9]|\uD805[\uDC4B-\uDC4F\uDC5A\uDC5B\uDC5D\uDCC6\uDDC1-\uDDD7\uDE41-\uDE43\uDE60-\uDE6C\uDEB9\uDF3C-\uDF3E]|\uD806[\uDC3B\uDD44-\uDD46\uDDE2\uDE3F-\uDE46\uDE9A-\uDE9C\uDE9E-\uDEA2\uDF00-\uDF09]|\uD807[\uDC41-\uDC45\uDC70\uDC71\uDEF7\uDEF8\uDF43-\uDF4F\uDFFF]|\uD809[\uDC70-\uDC74]|\uD80B[\uDFF1\uDFF2]|\uD81A[\uDE6E\uDE6F\uDEF5\uDF37-\uDF3B\uDF44]|\uD81B[\uDE97-\uDE9A\uDFE2]|\uD82F\uDC9F|\uD836[\uDE87-\uDE8B]|\uD83A[\uDD5E\uDD5F]/; const regex = /[\$\+<->\^`\|~\xA2-\xA6\xA8\xA9\xAC\xAE-\xB1\xB4\xB8\xD7\xF7\u02C2-\u02C5\u02D2-\u02DF\u02E5-\u02EB\u02ED\u02EF-\u02FF\u0375\u0384\u0385\u03F6\u0482\u058D-\u058F\u0606-\u0608\u060B\u060E\u060F\u06DE\u06E9\u06FD\u06FE\u07F6\u07FE\u07FF\u0888\u09F2\u09F3\u09FA\u09FB\u0AF1\u0B70\u0BF3-\u0BFA\u0C7F\u0D4F\u0D79\u0E3F\u0F01-\u0F03\u0F13\u0F15-\u0F17\u0F1A-\u0F1F\u0F34\u0F36\u0F38\u0FBE-\u0FC5\u0FC7-\u0FCC\u0FCE\u0FCF\u0FD5-\u0FD8\u109E\u109F\u1390-\u1399\u166D\u17DB\u1940\u19DE-\u19FF\u1B61-\u1B6A\u1B74-\u1B7C\u1FBD\u1FBF-\u1FC1\u1FCD-\u1FCF\u1FDD-\u1FDF\u1FED-\u1FEF\u1FFD\u1FFE\u2044\u2052\u207A-\u207C\u208A-\u208C\u20A0-\u20C0\u2100\u2101\u2103-\u2106\u2108\u2109\u2114\u2116-\u2118\u211E-\u2123\u2125\u2127\u2129\u212E\u213A\u213B\u2140-\u2144\u214A-\u214D\u214F\u218A\u218B\u2190-\u2307\u230C-\u2328\u232B-\u2426\u2440-\u244A\u249C-\u24E9\u2500-\u2767\u2794-\u27C4\u27C7-\u27E5\u27F0-\u2982\u2999-\u29D7\u29DC-\u29FB\u29FE-\u2B73\u2B76-\u2B95\u2B97-\u2BFF\u2CE5-\u2CEA\u2E50\u2E51\u2E80-\u2E99\u2E9B-\u2EF3\u2F00-\u2FD5\u2FF0-\u2FFF\u3004\u3012\u3013\u3020\u3036\u3037\u303E\u303F\u309B\u309C\u3190\u3191\u3196-\u319F\u31C0-\u31E3\u31EF\u3200-\u321E\u322A-\u3247\u3250\u3260-\u327F\u328A-\u32B0\u32C0-\u33FF\u4DC0-\u4DFF\uA490-\uA4C6\uA700-\uA716\uA720\uA721\uA789\uA78A\uA828-\uA82B\uA836-\uA839\uAA77-\uAA79\uAB5B\uAB6A\uAB6B\uFB29\uFBB2-\uFBC2\uFD40-\uFD4F\uFDCF\uFDFC-\uFDFF\uFE62\uFE64-\uFE66\uFE69\uFF04\uFF0B\uFF1C-\uFF1E\uFF3E\uFF40\uFF5C\uFF5E\uFFE0-\uFFE6\uFFE8-\uFFEE\uFFFC\uFFFD]|\uD800[\uDD37-\uDD3F\uDD79-\uDD89\uDD8C-\uDD8E\uDD90-\uDD9C\uDDA0\uDDD0-\uDDFC]|\uD802[\uDC77\uDC78\uDEC8]|\uD805\uDF3F|\uD807[\uDFD5-\uDFF1]|\uD81A[\uDF3C-\uDF3F\uDF45]|\uD82F\uDC9C|\uD833[\uDF50-\uDFC3]|\uD834[\uDC00-\uDCF5\uDD00-\uDD26\uDD29-\uDD64\uDD6A-\uDD6C\uDD83\uDD84\uDD8C-\uDDA9\uDDAE-\uDDEA\uDE00-\uDE41\uDE45\uDF00-\uDF56]|\uD835[\uDEC1\uDEDB\uDEFB\uDF15\uDF35\uDF4F\uDF6F\uDF89\uDFA9\uDFC3]|\uD836[\uDC00-\uDDFF\uDE37-\uDE3A\uDE6D-\uDE74\uDE76-\uDE83\uDE85\uDE86]|\uD838[\uDD4F\uDEFF]|\uD83B[\uDCAC\uDCB0\uDD2E\uDEF0\uDEF1]|\uD83C[\uDC00-\uDC2B\uDC30-\uDC93\uDCA0-\uDCAE\uDCB1-\uDCBF\uDCC1-\uDCCF\uDCD1-\uDCF5\uDD0D-\uDDAD\uDDE6-\uDE02\uDE10-\uDE3B\uDE40-\uDE48\uDE50\uDE51\uDE60-\uDE65\uDF00-\uDFFF]|\uD83D[\uDC00-\uDED7\uDEDC-\uDEEC\uDEF0-\uDEFC\uDF00-\uDF76\uDF7B-\uDFD9\uDFE0-\uDFEB\uDFF0]|\uD83E[\uDC00-\uDC0B\uDC10-\uDC47\uDC50-\uDC59\uDC60-\uDC87\uDC90-\uDCAD\uDCB0\uDCB1\uDD00-\uDE53\uDE60-\uDE6D\uDE70-\uDE7C\uDE80-\uDE88\uDE90-\uDEBD\uDEBF-\uDEC5\uDECE-\uDEDB\uDEE0-\uDEE8\uDEF0-\uDEF8\uDF00-\uDF92\uDF94-\uDFCA]/; const Z = /[ \xA0\u1680\u2000-\u200A\u2028\u2029\u202F\u205F\u3000]/; const ucmicro = /*#__PURE__*/ Object.freeze( /*#__PURE__*/ Object.defineProperty( { __proto__: null, Any, Cc, Cf: regex$1, P, S: regex, Z, }, Symbol.toStringTag, { value: "Module" }, ), ); // Generated using scripts/write-decode-map.ts const htmlDecodeTree = new Uint16Array( // prettier-ignore "\u1d41<\xd5\u0131\u028a\u049d\u057b\u05d0\u0675\u06de\u07a2\u07d6\u080f\u0a4a\u0a91\u0da1\u0e6d\u0f09\u0f26\u10ca\u1228\u12e1\u1415\u149d\u14c3\u14df\u1525\0\0\0\0\0\0\u156b\u16cd\u198d\u1c12\u1ddd\u1f7e\u2060\u21b0\u228d\u23c0\u23fb\u2442\u2824\u2912\u2d08\u2e48\u2fce\u3016\u32ba\u3639\u37ac\u38fe\u3a28\u3a71\u3ae0\u3b2e\u0800EMabcfglmnoprstu\\bfms\x7f\x84\x8b\x90\x95\x98\xa6\xb3\xb9\xc8\xcflig\u803b\xc6\u40c6P\u803b&\u4026cute\u803b\xc1\u40c1reve;\u4102\u0100iyx}rc\u803b\xc2\u40c2;\u4410r;\uc000\ud835\udd04rave\u803b\xc0\u40c0pha;\u4391acr;\u4100d;\u6a53\u0100gp\x9d\xa1on;\u4104f;\uc000\ud835\udd38plyFunction;\u6061ing\u803b\xc5\u40c5\u0100cs\xbe\xc3r;\uc000\ud835\udc9cign;\u6254ilde\u803b\xc3\u40c3ml\u803b\xc4\u40c4\u0400aceforsu\xe5\xfb\xfe\u0117\u011c\u0122\u0127\u012a\u0100cr\xea\xf2kslash;\u6216\u0176\xf6\xf8;\u6ae7ed;\u6306y;\u4411\u0180crt\u0105\u010b\u0114ause;\u6235noullis;\u612ca;\u4392r;\uc000\ud835\udd05pf;\uc000\ud835\udd39eve;\u42d8c\xf2\u0113mpeq;\u624e\u0700HOacdefhilorsu\u014d\u0151\u0156\u0180\u019e\u01a2\u01b5\u01b7\u01ba\u01dc\u0215\u0273\u0278\u027ecy;\u4427PY\u803b\xa9\u40a9\u0180cpy\u015d\u0162\u017aute;\u4106\u0100;i\u0167\u0168\u62d2talDifferentialD;\u6145leys;\u612d\u0200aeio\u0189\u018e\u0194\u0198ron;\u410cdil\u803b\xc7\u40c7rc;\u4108nint;\u6230ot;\u410a\u0100dn\u01a7\u01adilla;\u40b8terDot;\u40b7\xf2\u017fi;\u43a7rcle\u0200DMPT\u01c7\u01cb\u01d1\u01d6ot;\u6299inus;\u6296lus;\u6295imes;\u6297o\u0100cs\u01e2\u01f8kwiseContourIntegral;\u6232eCurly\u0100DQ\u0203\u020foubleQuote;\u601duote;\u6019\u0200lnpu\u021e\u0228\u0247\u0255on\u0100;e\u0225\u0226\u6237;\u6a74\u0180git\u022f\u0236\u023aruent;\u6261nt;\u622fourIntegral;\u622e\u0100fr\u024c\u024e;\u6102oduct;\u6210nterClockwiseContourIntegral;\u6233oss;\u6a2fcr;\uc000\ud835\udc9ep\u0100;C\u0284\u0285\u62d3ap;\u624d\u0580DJSZacefios\u02a0\u02ac\u02b0\u02b4\u02b8\u02cb\u02d7\u02e1\u02e6\u0333\u048d\u0100;o\u0179\u02a5trahd;\u6911cy;\u4402cy;\u4405cy;\u440f\u0180grs\u02bf\u02c4\u02c7ger;\u6021r;\u61a1hv;\u6ae4\u0100ay\u02d0\u02d5ron;\u410e;\u4414l\u0100;t\u02dd\u02de\u6207a;\u4394r;\uc000\ud835\udd07\u0100af\u02eb\u0327\u0100cm\u02f0\u0322ritical\u0200ADGT\u0300\u0306\u0316\u031ccute;\u40b4o\u0174\u030b\u030d;\u42d9bleAcute;\u42ddrave;\u4060ilde;\u42dcond;\u62c4ferentialD;\u6146\u0470\u033d\0\0\0\u0342\u0354\0\u0405f;\uc000\ud835\udd3b\u0180;DE\u0348\u0349\u034d\u40a8ot;\u60dcqual;\u6250ble\u0300CDLRUV\u0363\u0372\u0382\u03cf\u03e2\u03f8ontourIntegra\xec\u0239o\u0274\u0379\0\0\u037b\xbb\u0349nArrow;\u61d3\u0100eo\u0387\u03a4ft\u0180ART\u0390\u0396\u03a1rrow;\u61d0ightArrow;\u61d4e\xe5\u02cang\u0100LR\u03ab\u03c4eft\u0100AR\u03b3\u03b9rrow;\u67f8ightArrow;\u67faightArrow;\u67f9ight\u0100AT\u03d8\u03derrow;\u61d2ee;\u62a8p\u0241\u03e9\0\0\u03efrrow;\u61d1ownArrow;\u61d5erticalBar;\u6225n\u0300ABLRTa\u0412\u042a\u0430\u045e\u047f\u037crrow\u0180;BU\u041d\u041e\u0422\u6193ar;\u6913pArrow;\u61f5reve;\u4311eft\u02d2\u043a\0\u0446\0\u0450ightVector;\u6950eeVector;\u695eector\u0100;B\u0459\u045a\u61bdar;\u6956ight\u01d4\u0467\0\u0471eeVector;\u695fector\u0100;B\u047a\u047b\u61c1ar;\u6957ee\u0100;A\u0486\u0487\u62a4rrow;\u61a7\u0100ct\u0492\u0497r;\uc000\ud835\udc9frok;\u4110\u0800NTacdfglmopqstux\u04bd\u04c0\u04c4\u04cb\u04de\u04e2\u04e7\u04ee\u04f5\u0521\u052f\u0536\u0552\u055d\u0560\u0565G;\u414aH\u803b\xd0\u40d0cute\u803b\xc9\u40c9\u0180aiy\u04d2\u04d7\u04dcron;\u411arc\u803b\xca\u40ca;\u442dot;\u4116r;\uc000\ud835\udd08rave\u803b\xc8\u40c8ement;\u6208\u0100ap\u04fa\u04fecr;\u4112ty\u0253\u0506\0\0\u0512mallSquare;\u65fberySmallSquare;\u65ab\u0100gp\u0526\u052aon;\u4118f;\uc000\ud835\udd3csilon;\u4395u\u0100ai\u053c\u0549l\u0100;T\u0542\u0543\u6a75ilde;\u6242librium;\u61cc\u0100ci\u0557\u055ar;\u6130m;\u6a73a;\u4397ml\u803b\xcb\u40cb\u0100ip\u056a\u056fsts;\u6203onentialE;\u6147\u0280cfios\u0585\u0588\u058d\u05b2\u05ccy;\u4424r;\uc000\ud835\udd09lled\u0253\u0597\0\0\u05a3mallSquare;\u65fcerySmallSquare;\u65aa\u0370\u05ba\0\u05bf\0\0\u05c4f;\uc000\ud835\udd3dAll;\u6200riertrf;\u6131c\xf2\u05cb\u0600JTabcdfgorst\u05e8\u05ec\u05ef\u05fa\u0600\u0612\u0616\u061b\u061d\u0623\u066c\u0672cy;\u4403\u803b>\u403emma\u0100;d\u05f7\u05f8\u4393;\u43dcreve;\u411e\u0180eiy\u0607\u060c\u0610dil;\u4122rc;\u411c;\u4413ot;\u4120r;\uc000\ud835\udd0a;\u62d9pf;\uc000\ud835\udd3eeater\u0300EFGLST\u0635\u0644\u064e\u0656\u065b\u0666qual\u0100;L\u063e\u063f\u6265ess;\u62dbullEqual;\u6267reater;\u6aa2ess;\u6277lantEqual;\u6a7eilde;\u6273cr;\uc000\ud835\udca2;\u626b\u0400Aacfiosu\u0685\u068b\u0696\u069b\u069e\u06aa\u06be\u06caRDcy;\u442a\u0100ct\u0690\u0694ek;\u42c7;\u405eirc;\u4124r;\u610clbertSpace;\u610b\u01f0\u06af\0\u06b2f;\u610dizontalLine;\u6500\u0100ct\u06c3\u06c5\xf2\u06a9rok;\u4126mp\u0144\u06d0\u06d8ownHum\xf0\u012fqual;\u624f\u0700EJOacdfgmnostu\u06fa\u06fe\u0703\u0707\u070e\u071a\u071e\u0721\u0728\u0744\u0778\u078b\u078f\u0795cy;\u4415lig;\u4132cy;\u4401cute\u803b\xcd\u40cd\u0100iy\u0713\u0718rc\u803b\xce\u40ce;\u4418ot;\u4130r;\u6111rave\u803b\xcc\u40cc\u0180;ap\u0720\u072f\u073f\u0100cg\u0734\u0737r;\u412ainaryI;\u6148lie\xf3\u03dd\u01f4\u0749\0\u0762\u0100;e\u074d\u074e\u622c\u0100gr\u0753\u0758ral;\u622bsection;\u62c2isible\u0100CT\u076c\u0772omma;\u6063imes;\u6062\u0180gpt\u077f\u0783\u0788on;\u412ef;\uc000\ud835\udd40a;\u4399cr;\u6110ilde;\u4128\u01eb\u079a\0\u079ecy;\u4406l\u803b\xcf\u40cf\u0280cfosu\u07ac\u07b7\u07bc\u07c2\u07d0\u0100iy\u07b1\u07b5rc;\u4134;\u4419r;\uc000\ud835\udd0dpf;\uc000\ud835\udd41\u01e3\u07c7\0\u07ccr;\uc000\ud835\udca5rcy;\u4408kcy;\u4404\u0380HJacfos\u07e4\u07e8\u07ec\u07f1\u07fd\u0802\u0808cy;\u4425cy;\u440cppa;\u439a\u0100ey\u07f6\u07fbdil;\u4136;\u441ar;\uc000\ud835\udd0epf;\uc000\ud835\udd42cr;\uc000\ud835\udca6\u0580JTaceflmost\u0825\u0829\u082c\u0850\u0863\u09b3\u09b8\u09c7\u09cd\u0a37\u0a47cy;\u4409\u803b<\u403c\u0280cmnpr\u0837\u083c\u0841\u0844\u084dute;\u4139bda;\u439bg;\u67ealacetrf;\u6112r;\u619e\u0180aey\u0857\u085c\u0861ron;\u413ddil;\u413b;\u441b\u0100fs\u0868\u0970t\u0500ACDFRTUVar\u087e\u08a9\u08b1\u08e0\u08e6\u08fc\u092f\u095b\u0390\u096a\u0100nr\u0883\u088fgleBracket;\u67e8row\u0180;BR\u0899\u089a\u089e\u6190ar;\u61e4ightArrow;\u61c6eiling;\u6308o\u01f5\u08b7\0\u08c3bleBracket;\u67e6n\u01d4\u08c8\0\u08d2eeVector;\u6961ector\u0100;B\u08db\u08dc\u61c3ar;\u6959loor;\u630aight\u0100AV\u08ef\u08f5rrow;\u6194ector;\u694e\u0100er\u0901\u0917e\u0180;AV\u0909\u090a\u0910\u62a3rrow;\u61a4ector;\u695aiangle\u0180;BE\u0924\u0925\u0929\u62b2ar;\u69cfqual;\u62b4p\u0180DTV\u0937\u0942\u094cownVector;\u6951eeVector;\u6960ector\u0100;B\u0956\u0957\u61bfar;\u6958ector\u0100;B\u0965\u0966\u61bcar;\u6952ight\xe1\u039cs\u0300EFGLST\u097e\u098b\u0995\u099d\u09a2\u09adqualGreater;\u62daullEqual;\u6266reater;\u6276ess;\u6aa1lantEqual;\u6a7dilde;\u6272r;\uc000\ud835\udd0f\u0100;e\u09bd\u09be\u62d8ftarrow;\u61daidot;\u413f\u0180npw\u09d4\u0a16\u0a1bg\u0200LRlr\u09de\u09f7\u0a02\u0a10eft\u0100AR\u09e6\u09ecrrow;\u67f5ightArrow;\u67f7ightArrow;\u67f6eft\u0100ar\u03b3\u0a0aight\xe1\u03bfight\xe1\u03caf;\uc000\ud835\udd43er\u0100LR\u0a22\u0a2ceftArrow;\u6199ightArrow;\u6198\u0180cht\u0a3e\u0a40\u0a42\xf2\u084c;\u61b0rok;\u4141;\u626a\u0400acefiosu\u0a5a\u0a5d\u0a60\u0a77\u0a7c\u0a85\u0a8b\u0a8ep;\u6905y;\u441c\u0100dl\u0a65\u0a6fiumSpace;\u605flintrf;\u6133r;\uc000\ud835\udd10nusPlus;\u6213pf;\uc000\ud835\udd44c\xf2\u0a76;\u439c\u0480Jacefostu\u0aa3\u0aa7\u0aad\u0ac0\u0b14\u0b19\u0d91\u0d97\u0d9ecy;\u440acute;\u4143\u0180aey\u0ab4\u0ab9\u0aberon;\u4147dil;\u4145;\u441d\u0180gsw\u0ac7\u0af0\u0b0eative\u0180MTV\u0ad3\u0adf\u0ae8ediumSpace;\u600bhi\u0100cn\u0ae6\u0ad8\xeb\u0ad9eryThi\xee\u0ad9ted\u0100GL\u0af8\u0b06reaterGreate\xf2\u0673essLes\xf3\u0a48Line;\u400ar;\uc000\ud835\udd11\u0200Bnpt\u0b22\u0b28\u0b37\u0b3areak;\u6060BreakingSpace;\u40a0f;\u6115\u0680;CDEGHLNPRSTV\u0b55\u0b56\u0b6a\u0b7c\u0ba1\u0beb\u0c04\u0c5e\u0c84\u0ca6\u0cd8\u0d61\u0d85\u6aec\u0100ou\u0b5b\u0b64ngruent;\u6262pCap;\u626doubleVerticalBar;\u6226\u0180lqx\u0b83\u0b8a\u0b9bement;\u6209ual\u0100;T\u0b92\u0b93\u6260ilde;\uc000\u2242\u0338ists;\u6204reater\u0380;EFGLST\u0bb6\u0bb7\u0bbd\u0bc9\u0bd3\u0bd8\u0be5\u626fqual;\u6271ullEqual;\uc000\u2267\u0338reater;\uc000\u226b\u0338ess;\u6279lantEqual;\uc000\u2a7e\u0338ilde;\u6275ump\u0144\u0bf2\u0bfdownHump;\uc000\u224e\u0338qual;\uc000\u224f\u0338e\u0100fs\u0c0a\u0c27tTriangle\u0180;BE\u0c1a\u0c1b\u0c21\u62eaar;\uc000\u29cf\u0338qual;\u62ecs\u0300;EGLST\u0c35\u0c36\u0c3c\u0c44\u0c4b\u0c58\u626equal;\u6270reater;\u6278ess;\uc000\u226a\u0338lantEqual;\uc000\u2a7d\u0338ilde;\u6274ested\u0100GL\u0c68\u0c79reaterGreater;\uc000\u2aa2\u0338essLess;\uc000\u2aa1\u0338recedes\u0180;ES\u0c92\u0c93\u0c9b\u6280qual;\uc000\u2aaf\u0338lantEqual;\u62e0\u0100ei\u0cab\u0cb9verseElement;\u620cghtTriangle\u0180;BE\u0ccb\u0ccc\u0cd2\u62ebar;\uc000\u29d0\u0338qual;\u62ed\u0100qu\u0cdd\u0d0cuareSu\u0100bp\u0ce8\u0cf9set\u0100;E\u0cf0\u0cf3\uc000\u228f\u0338qual;\u62e2erset\u0100;E\u0d03\u0d06\uc000\u2290\u0338qual;\u62e3\u0180bcp\u0d13\u0d24\u0d4eset\u0100;E\u0d1b\u0d1e\uc000\u2282\u20d2qual;\u6288ceeds\u0200;EST\u0d32\u0d33\u0d3b\u0d46\u6281qual;\uc000\u2ab0\u0338lantEqual;\u62e1ilde;\uc000\u227f\u0338erset\u0100;E\u0d58\u0d5b\uc000\u2283\u20d2qual;\u6289ilde\u0200;EFT\u0d6e\u0d6f\u0d75\u0d7f\u6241qual;\u6244ullEqual;\u6247ilde;\u6249erticalBar;\u6224cr;\uc000\ud835\udca9ilde\u803b\xd1\u40d1;\u439d\u0700Eacdfgmoprstuv\u0dbd\u0dc2\u0dc9\u0dd5\u0ddb\u0de0\u0de7\u0dfc\u0e02\u0e20\u0e22\u0e32\u0e3f\u0e44lig;\u4152cute\u803b\xd3\u40d3\u0100iy\u0dce\u0dd3rc\u803b\xd4\u40d4;\u441eblac;\u4150r;\uc000\ud835\udd12rave\u803b\xd2\u40d2\u0180aei\u0dee\u0df2\u0df6cr;\u414cga;\u43a9cron;\u439fpf;\uc000\ud835\udd46enCurly\u0100DQ\u0e0e\u0e1aoubleQuote;\u601cuote;\u6018;\u6a54\u0100cl\u0e27\u0e2cr;\uc000\ud835\udcaaash\u803b\xd8\u40d8i\u016c\u0e37\u0e3cde\u803b\xd5\u40d5es;\u6a37ml\u803b\xd6\u40d6er\u0100BP\u0e4b\u0e60\u0100ar\u0e50\u0e53r;\u603eac\u0100ek\u0e5a\u0e5c;\u63deet;\u63b4arenthesis;\u63dc\u0480acfhilors\u0e7f\u0e87\u0e8a\u0e8f\u0e92\u0e94\u0e9d\u0eb0\u0efcrtialD;\u6202y;\u441fr;\uc000\ud835\udd13i;\u43a6;\u43a0usMinus;\u40b1\u0100ip\u0ea2\u0eadncareplan\xe5\u069df;\u6119\u0200;eio\u0eb9\u0eba\u0ee0\u0ee4\u6abbcedes\u0200;EST\u0ec8\u0ec9\u0ecf\u0eda\u627aqual;\u6aaflantEqual;\u627cilde;\u627eme;\u6033\u0100dp\u0ee9\u0eeeuct;\u620fortion\u0100;a\u0225\u0ef9l;\u621d\u0100ci\u0f01\u0f06r;\uc000\ud835\udcab;\u43a8\u0200Ufos\u0f11\u0f16\u0f1b\u0f1fOT\u803b\"\u4022r;\uc000\ud835\udd14pf;\u611acr;\uc000\ud835\udcac\u0600BEacefhiorsu\u0f3e\u0f43\u0f47\u0f60\u0f73\u0fa7\u0faa\u0fad\u1096\u10a9\u10b4\u10bearr;\u6910G\u803b\xae\u40ae\u0180cnr\u0f4e\u0f53\u0f56ute;\u4154g;\u67ebr\u0100;t\u0f5c\u0f5d\u61a0l;\u6916\u0180aey\u0f67\u0f6c\u0f71ron;\u4158dil;\u4156;\u4420\u0100;v\u0f78\u0f79\u611cerse\u0100EU\u0f82\u0f99\u0100lq\u0f87\u0f8eement;\u620builibrium;\u61cbpEquilibrium;\u696fr\xbb\u0f79o;\u43a1ght\u0400ACDFTUVa\u0fc1\u0feb\u0ff3\u1022\u1028\u105b\u1087\u03d8\u0100nr\u0fc6\u0fd2gleBracket;\u67e9row\u0180;BL\u0fdc\u0fdd\u0fe1\u6192ar;\u61e5eftArrow;\u61c4eiling;\u6309o\u01f5\u0ff9\0\u1005bleBracket;\u67e7n\u01d4\u100a\0\u1014eeVector;\u695dector\u0100;B\u101d\u101e\u61c2ar;\u6955loor;\u630b\u0100er\u102d\u1043e\u0180;AV\u1035\u1036\u103c\u62a2rrow;\u61a6ector;\u695biangle\u0180;BE\u1050\u1051\u1055\u62b3ar;\u69d0qual;\u62b5p\u0180DTV\u1063\u106e\u1078ownVector;\u694feeVector;\u695cector\u0100;B\u1082\u1083\u61bear;\u6954ector\u0100;B\u1091\u1092\u61c0ar;\u6953\u0100pu\u109b\u109ef;\u611dndImplies;\u6970ightarrow;\u61db\u0100ch\u10b9\u10bcr;\u611b;\u61b1leDelayed;\u69f4\u0680HOacfhimoqstu\u10e4\u10f1\u10f7\u10fd\u1119\u111e\u1151\u1156\u1161\u1167\u11b5\u11bb\u11bf\u0100Cc\u10e9\u10eeHcy;\u4429y;\u4428FTcy;\u442ccute;\u415a\u0280;aeiy\u1108\u1109\u110e\u1113\u1117\u6abcron;\u4160dil;\u415erc;\u415c;\u4421r;\uc000\ud835\udd16ort\u0200DLRU\u112a\u1134\u113e\u1149ownArrow\xbb\u041eeftArrow\xbb\u089aightArrow\xbb\u0fddpArrow;\u6191gma;\u43a3allCircle;\u6218pf;\uc000\ud835\udd4a\u0272\u116d\0\0\u1170t;\u621aare\u0200;ISU\u117b\u117c\u1189\u11af\u65a1ntersection;\u6293u\u0100bp\u118f\u119eset\u0100;E\u1197\u1198\u628fqual;\u6291erset\u0100;E\u11a8\u11a9\u6290qual;\u6292nion;\u6294cr;\uc000\ud835\udcaear;\u62c6\u0200bcmp\u11c8\u11db\u1209\u120b\u0100;s\u11cd\u11ce\u62d0et\u0100;E\u11cd\u11d5qual;\u6286\u0100ch\u11e0\u1205eeds\u0200;EST\u11ed\u11ee\u11f4\u11ff\u627bqual;\u6ab0lantEqual;\u627dilde;\u627fTh\xe1\u0f8c;\u6211\u0180;es\u1212\u1213\u1223\u62d1rset\u0100;E\u121c\u121d\u6283qual;\u6287et\xbb\u1213\u0580HRSacfhiors\u123e\u1244\u1249\u1255\u125e\u1271\u1276\u129f\u12c2\u12c8\u12d1ORN\u803b\xde\u40deADE;\u6122\u0100Hc\u124e\u1252cy;\u440by;\u4426\u0100bu\u125a\u125c;\u4009;\u43a4\u0180aey\u1265\u126a\u126fron;\u4164dil;\u4162;\u4422r;\uc000\ud835\udd17\u0100ei\u127b\u1289\u01f2\u1280\0\u1287efore;\u6234a;\u4398\u0100cn\u128e\u1298kSpace;\uc000\u205f\u200aSpace;\u6009lde\u0200;EFT\u12ab\u12ac\u12b2\u12bc\u623cqual;\u6243ullEqual;\u6245ilde;\u6248pf;\uc000\ud835\udd4bipleDot;\u60db\u0100ct\u12d6\u12dbr;\uc000\ud835\udcafrok;\u4166\u0ae1\u12f7\u130e\u131a\u1326\0\u132c\u1331\0\0\0\0\0\u1338\u133d\u1377\u1385\0\u13ff\u1404\u140a\u1410\u0100cr\u12fb\u1301ute\u803b\xda\u40dar\u0100;o\u1307\u1308\u619fcir;\u6949r\u01e3\u1313\0\u1316y;\u440eve;\u416c\u0100iy\u131e\u1323rc\u803b\xdb\u40db;\u4423blac;\u4170r;\uc000\ud835\udd18rave\u803b\xd9\u40d9acr;\u416a\u0100di\u1341\u1369er\u0100BP\u1348\u135d\u0100ar\u134d\u1350r;\u405fac\u0100ek\u1357\u1359;\u63dfet;\u63b5arenthesis;\u63ddon\u0100;P\u1370\u1371\u62c3lus;\u628e\u0100gp\u137b\u137fon;\u4172f;\uc000\ud835\udd4c\u0400ADETadps\u1395\u13ae\u13b8\u13c4\u03e8\u13d2\u13d7\u13f3rrow\u0180;BD\u1150\u13a0\u13a4ar;\u6912ownArrow;\u61c5ownArrow;\u6195quilibrium;\u696eee\u0100;A\u13cb\u13cc\u62a5rrow;\u61a5own\xe1\u03f3er\u0100LR\u13de\u13e8eftArrow;\u6196ightArrow;\u6197i\u0100;l\u13f9\u13fa\u43d2on;\u43a5ing;\u416ecr;\uc000\ud835\udcb0ilde;\u4168ml\u803b\xdc\u40dc\u0480Dbcdefosv\u1427\u142c\u1430\u1433\u143e\u1485\u148a\u1490\u1496ash;\u62abar;\u6aeby;\u4412ash\u0100;l\u143b\u143c\u62a9;\u6ae6\u0100er\u1443\u1445;\u62c1\u0180bty\u144c\u1450\u147aar;\u6016\u0100;i\u144f\u1455cal\u0200BLST\u1461\u1465\u146a\u1474ar;\u6223ine;\u407ceparator;\u6758ilde;\u6240ThinSpace;\u600ar;\uc000\ud835\udd19pf;\uc000\ud835\udd4dcr;\uc000\ud835\udcb1dash;\u62aa\u0280cefos\u14a7\u14ac\u14b1\u14b6\u14bcirc;\u4174dge;\u62c0r;\uc000\ud835\udd1apf;\uc000\ud835\udd4ecr;\uc000\ud835\udcb2\u0200fios\u14cb\u14d0\u14d2\u14d8r;\uc000\ud835\udd1b;\u439epf;\uc000\ud835\udd4fcr;\uc000\ud835\udcb3\u0480AIUacfosu\u14f1\u14f5\u14f9\u14fd\u1504\u150f\u1514\u151a\u1520cy;\u442fcy;\u4407cy;\u442ecute\u803b\xdd\u40dd\u0100iy\u1509\u150drc;\u4176;\u442br;\uc000\ud835\udd1cpf;\uc000\ud835\udd50cr;\uc000\ud835\udcb4ml;\u4178\u0400Hacdefos\u1535\u1539\u153f\u154b\u154f\u155d\u1560\u1564cy;\u4416cute;\u4179\u0100ay\u1544\u1549ron;\u417d;\u4417ot;\u417b\u01f2\u1554\0\u155boWidt\xe8\u0ad9a;\u4396r;\u6128pf;\u6124cr;\uc000\ud835\udcb5\u0be1\u1583\u158a\u1590\0\u15b0\u15b6\u15bf\0\0\0\0\u15c6\u15db\u15eb\u165f\u166d\0\u1695\u169b\u16b2\u16b9\0\u16becute\u803b\xe1\u40e1reve;\u4103\u0300;Ediuy\u159c\u159d\u15a1\u15a3\u15a8\u15ad\u623e;\uc000\u223e\u0333;\u623frc\u803b\xe2\u40e2te\u80bb\xb4\u0306;\u4430lig\u803b\xe6\u40e6\u0100;r\xb2\u15ba;\uc000\ud835\udd1erave\u803b\xe0\u40e0\u0100ep\u15ca\u15d6\u0100fp\u15cf\u15d4sym;\u6135\xe8\u15d3ha;\u43b1\u0100ap\u15dfc\u0100cl\u15e4\u15e7r;\u4101g;\u6a3f\u0264\u15f0\0\0\u160a\u0280;adsv\u15fa\u15fb\u15ff\u1601\u1607\u6227nd;\u6a55;\u6a5clope;\u6a58;\u6a5a\u0380;elmrsz\u1618\u1619\u161b\u161e\u163f\u164f\u1659\u6220;\u69a4e\xbb\u1619sd\u0100;a\u1625\u1626\u6221\u0461\u1630\u1632\u1634\u1636\u1638\u163a\u163c\u163e;\u69a8;\u69a9;\u69aa;\u69ab;\u69ac;\u69ad;\u69ae;\u69aft\u0100;v\u1645\u1646\u621fb\u0100;d\u164c\u164d\u62be;\u699d\u0100pt\u1654\u1657h;\u6222\xbb\xb9arr;\u637c\u0100gp\u1663\u1667on;\u4105f;\uc000\ud835\udd52\u0380;Eaeiop\u12c1\u167b\u167d\u1682\u1684\u1687\u168a;\u6a70cir;\u6a6f;\u624ad;\u624bs;\u4027rox\u0100;e\u12c1\u1692\xf1\u1683ing\u803b\xe5\u40e5\u0180cty\u16a1\u16a6\u16a8r;\uc000\ud835\udcb6;\u402amp\u0100;e\u12c1\u16af\xf1\u0288ilde\u803b\xe3\u40e3ml\u803b\xe4\u40e4\u0100ci\u16c2\u16c8onin\xf4\u0272nt;\u6a11\u0800Nabcdefiklnoprsu\u16ed\u16f1\u1730\u173c\u1743\u1748\u1778\u177d\u17e0\u17e6\u1839\u1850\u170d\u193d\u1948\u1970ot;\u6aed\u0100cr\u16f6\u171ek\u0200ceps\u1700\u1705\u170d\u1713ong;\u624cpsilon;\u43f6rime;\u6035im\u0100;e\u171a\u171b\u623dq;\u62cd\u0176\u1722\u1726ee;\u62bded\u0100;g\u172c\u172d\u6305e\xbb\u172drk\u0100;t\u135c\u1737brk;\u63b6\u0100oy\u1701\u1741;\u4431quo;\u601e\u0280cmprt\u1753\u175b\u1761\u1764\u1768aus\u0100;e\u010a\u0109ptyv;\u69b0s\xe9\u170cno\xf5\u0113\u0180ahw\u176f\u1771\u1773;\u43b2;\u6136een;\u626cr;\uc000\ud835\udd1fg\u0380costuvw\u178d\u179d\u17b3\u17c1\u17d5\u17db\u17de\u0180aiu\u1794\u1796\u179a\xf0\u0760rc;\u65efp\xbb\u1371\u0180dpt\u17a4\u17a8\u17adot;\u6a00lus;\u6a01imes;\u6a02\u0271\u17b9\0\0\u17becup;\u6a06ar;\u6605riangle\u0100du\u17cd\u17d2own;\u65bdp;\u65b3plus;\u6a04e\xe5\u1444\xe5\u14adarow;\u690d\u0180ako\u17ed\u1826\u1835\u0100cn\u17f2\u1823k\u0180lst\u17fa\u05ab\u1802ozenge;\u69ebriangle\u0200;dlr\u1812\u1813\u1818\u181d\u65b4own;\u65beeft;\u65c2ight;\u65b8k;\u6423\u01b1\u182b\0\u1833\u01b2\u182f\0\u1831;\u6592;\u65914;\u6593ck;\u6588\u0100eo\u183e\u184d\u0100;q\u1843\u1846\uc000=\u20e5uiv;\uc000\u2261\u20e5t;\u6310\u0200ptwx\u1859\u185e\u1867\u186cf;\uc000\ud835\udd53\u0100;t\u13cb\u1863om\xbb\u13cctie;\u62c8\u0600DHUVbdhmptuv\u1885\u1896\u18aa\u18bb\u18d7\u18db\u18ec\u18ff\u1905\u190a\u1910\u1921\u0200LRlr\u188e\u1890\u1892\u1894;\u6557;\u6554;\u6556;\u6553\u0280;DUdu\u18a1\u18a2\u18a4\u18a6\u18a8\u6550;\u6566;\u6569;\u6564;\u6567\u0200LRlr\u18b3\u18b5\u18b7\u18b9;\u655d;\u655a;\u655c;\u6559\u0380;HLRhlr\u18ca\u18cb\u18cd\u18cf\u18d1\u18d3\u18d5\u6551;\u656c;\u6563;\u6560;\u656b;\u6562;\u655fox;\u69c9\u0200LRlr\u18e4\u18e6\u18e8\u18ea;\u6555;\u6552;\u6510;\u650c\u0280;DUdu\u06bd\u18f7\u18f9\u18fb\u18fd;\u6565;\u6568;\u652c;\u6534inus;\u629flus;\u629eimes;\u62a0\u0200LRlr\u1919\u191b\u191d\u191f;\u655b;\u6558;\u6518;\u6514\u0380;HLRhlr\u1930\u1931\u1933\u1935\u1937\u1939\u193b\u6502;\u656a;\u6561;\u655e;\u653c;\u6524;\u651c\u0100ev\u0123\u1942bar\u803b\xa6\u40a6\u0200ceio\u1951\u1956\u195a\u1960r;\uc000\ud835\udcb7mi;\u604fm\u0100;e\u171a\u171cl\u0180;bh\u1968\u1969\u196b\u405c;\u69c5sub;\u67c8\u016c\u1974\u197el\u0100;e\u1979\u197a\u6022t\xbb\u197ap\u0180;Ee\u012f\u1985\u1987;\u6aae\u0100;q\u06dc\u06db\u0ce1\u19a7\0\u19e8\u1a11\u1a15\u1a32\0\u1a37\u1a50\0\0\u1ab4\0\0\u1ac1\0\0\u1b21\u1b2e\u1b4d\u1b52\0\u1bfd\0\u1c0c\u0180cpr\u19ad\u19b2\u19ddute;\u4107\u0300;abcds\u19bf\u19c0\u19c4\u19ca\u19d5\u19d9\u6229nd;\u6a44rcup;\u6a49\u0100au\u19cf\u19d2p;\u6a4bp;\u6a47ot;\u6a40;\uc000\u2229\ufe00\u0100eo\u19e2\u19e5t;\u6041\xee\u0693\u0200aeiu\u19f0\u19fb\u1a01\u1a05\u01f0\u19f5\0\u19f8s;\u6a4don;\u410ddil\u803b\xe7\u40e7rc;\u4109ps\u0100;s\u1a0c\u1a0d\u6a4cm;\u6a50ot;\u410b\u0180dmn\u1a1b\u1a20\u1a26il\u80bb\xb8\u01adptyv;\u69b2t\u8100\xa2;e\u1a2d\u1a2e\u40a2r\xe4\u01b2r;\uc000\ud835\udd20\u0180cei\u1a3d\u1a40\u1a4dy;\u4447ck\u0100;m\u1a47\u1a48\u6713ark\xbb\u1a48;\u43c7r\u0380;Ecefms\u1a5f\u1a60\u1a62\u1a6b\u1aa4\u1aaa\u1aae\u65cb;\u69c3\u0180;el\u1a69\u1a6a\u1a6d\u42c6q;\u6257e\u0261\u1a74\0\0\u1a88rrow\u0100lr\u1a7c\u1a81eft;\u61baight;\u61bb\u0280RSacd\u1a92\u1a94\u1a96\u1a9a\u1a9f\xbb\u0f47;\u64c8st;\u629birc;\u629aash;\u629dnint;\u6a10id;\u6aefcir;\u69c2ubs\u0100;u\u1abb\u1abc\u6663it\xbb\u1abc\u02ec\u1ac7\u1ad4\u1afa\0\u1b0aon\u0100;e\u1acd\u1ace\u403a\u0100;q\xc7\xc6\u026d\u1ad9\0\0\u1ae2a\u0100;t\u1ade\u1adf\u402c;\u4040\u0180;fl\u1ae8\u1ae9\u1aeb\u6201\xee\u1160e\u0100mx\u1af1\u1af6ent\xbb\u1ae9e\xf3\u024d\u01e7\u1afe\0\u1b07\u0100;d\u12bb\u1b02ot;\u6a6dn\xf4\u0246\u0180fry\u1b10\u1b14\u1b17;\uc000\ud835\udd54o\xe4\u0254\u8100\xa9;s\u0155\u1b1dr;\u6117\u0100ao\u1b25\u1b29rr;\u61b5ss;\u6717\u0100cu\u1b32\u1b37r;\uc000\ud835\udcb8\u0100bp\u1b3c\u1b44\u0100;e\u1b41\u1b42\u6acf;\u6ad1\u0100;e\u1b49\u1b4a\u6ad0;\u6ad2dot;\u62ef\u0380delprvw\u1b60\u1b6c\u1b77\u1b82\u1bac\u1bd4\u1bf9arr\u0100lr\u1b68\u1b6a;\u6938;\u6935\u0270\u1b72\0\0\u1b75r;\u62dec;\u62dfarr\u0100;p\u1b7f\u1b80\u61b6;\u693d\u0300;bcdos\u1b8f\u1b90\u1b96\u1ba1\u1ba5\u1ba8\u622arcap;\u6a48\u0100au\u1b9b\u1b9ep;\u6a46p;\u6a4aot;\u628dr;\u6a45;\uc000\u222a\ufe00\u0200alrv\u1bb5\u1bbf\u1bde\u1be3rr\u0100;m\u1bbc\u1bbd\u61b7;\u693cy\u0180evw\u1bc7\u1bd4\u1bd8q\u0270\u1bce\0\0\u1bd2re\xe3\u1b73u\xe3\u1b75ee;\u62ceedge;\u62cfen\u803b\xa4\u40a4earrow\u0100lr\u1bee\u1bf3eft\xbb\u1b80ight\xbb\u1bbde\xe4\u1bdd\u0100ci\u1c01\u1c07onin\xf4\u01f7nt;\u6231lcty;\u632d\u0980AHabcdefhijlorstuwz\u1c38\u1c3b\u1c3f\u1c5d\u1c69\u1c75\u1c8a\u1c9e\u1cac\u1cb7\u1cfb\u1cff\u1d0d\u1d7b\u1d91\u1dab\u1dbb\u1dc6\u1dcdr\xf2\u0381ar;\u6965\u0200glrs\u1c48\u1c4d\u1c52\u1c54ger;\u6020eth;\u6138\xf2\u1133h\u0100;v\u1c5a\u1c5b\u6010\xbb\u090a\u016b\u1c61\u1c67arow;\u690fa\xe3\u0315\u0100ay\u1c6e\u1c73ron;\u410f;\u4434\u0180;ao\u0332\u1c7c\u1c84\u0100gr\u02bf\u1c81r;\u61catseq;\u6a77\u0180glm\u1c91\u1c94\u1c98\u803b\xb0\u40b0ta;\u43b4ptyv;\u69b1\u0100ir\u1ca3\u1ca8sht;\u697f;\uc000\ud835\udd21ar\u0100lr\u1cb3\u1cb5\xbb\u08dc\xbb\u101e\u0280aegsv\u1cc2\u0378\u1cd6\u1cdc\u1ce0m\u0180;os\u0326\u1cca\u1cd4nd\u0100;s\u0326\u1cd1uit;\u6666amma;\u43ddin;\u62f2\u0180;io\u1ce7\u1ce8\u1cf8\u40f7de\u8100\xf7;o\u1ce7\u1cf0ntimes;\u62c7n\xf8\u1cf7cy;\u4452c\u026f\u1d06\0\0\u1d0arn;\u631eop;\u630d\u0280lptuw\u1d18\u1d1d\u1d22\u1d49\u1d55lar;\u4024f;\uc000\ud835\udd55\u0280;emps\u030b\u1d2d\u1d37\u1d3d\u1d42q\u0100;d\u0352\u1d33ot;\u6251inus;\u6238lus;\u6214quare;\u62a1blebarwedg\xe5\xfan\u0180adh\u112e\u1d5d\u1d67ownarrow\xf3\u1c83arpoon\u0100lr\u1d72\u1d76ef\xf4\u1cb4igh\xf4\u1cb6\u0162\u1d7f\u1d85karo\xf7\u0f42\u026f\u1d8a\0\0\u1d8ern;\u631fop;\u630c\u0180cot\u1d98\u1da3\u1da6\u0100ry\u1d9d\u1da1;\uc000\ud835\udcb9;\u4455l;\u69f6rok;\u4111\u0100dr\u1db0\u1db4ot;\u62f1i\u0100;f\u1dba\u1816\u65bf\u0100ah\u1dc0\u1dc3r\xf2\u0429a\xf2\u0fa6angle;\u69a6\u0100ci\u1dd2\u1dd5y;\u445fgrarr;\u67ff\u0900Dacdefglmnopqrstux\u1e01\u1e09\u1e19\u1e38\u0578\u1e3c\u1e49\u1e61\u1e7e\u1ea5\u1eaf\u1ebd\u1ee1\u1f2a\u1f37\u1f44\u1f4e\u1f5a\u0100Do\u1e06\u1d34o\xf4\u1c89\u0100cs\u1e0e\u1e14ute\u803b\xe9\u40e9ter;\u6a6e\u0200aioy\u1e22\u1e27\u1e31\u1e36ron;\u411br\u0100;c\u1e2d\u1e2e\u6256\u803b\xea\u40ealon;\u6255;\u444dot;\u4117\u0100Dr\u1e41\u1e45ot;\u6252;\uc000\ud835\udd22\u0180;rs\u1e50\u1e51\u1e57\u6a9aave\u803b\xe8\u40e8\u0100;d\u1e5c\u1e5d\u6a96ot;\u6a98\u0200;ils\u1e6a\u1e6b\u1e72\u1e74\u6a99nters;\u63e7;\u6113\u0100;d\u1e79\u1e7a\u6a95ot;\u6a97\u0180aps\u1e85\u1e89\u1e97cr;\u4113ty\u0180;sv\u1e92\u1e93\u1e95\u6205et\xbb\u1e93p\u01001;\u1e9d\u1ea4\u0133\u1ea1\u1ea3;\u6004;\u6005\u6003\u0100gs\u1eaa\u1eac;\u414bp;\u6002\u0100gp\u1eb4\u1eb8on;\u4119f;\uc000\ud835\udd56\u0180als\u1ec4\u1ece\u1ed2r\u0100;s\u1eca\u1ecb\u62d5l;\u69e3us;\u6a71i\u0180;lv\u1eda\u1edb\u1edf\u43b5on\xbb\u1edb;\u43f5\u0200csuv\u1eea\u1ef3\u1f0b\u1f23\u0100io\u1eef\u1e31rc\xbb\u1e2e\u0269\u1ef9\0\0\u1efb\xed\u0548ant\u0100gl\u1f02\u1f06tr\xbb\u1e5dess\xbb\u1e7a\u0180aei\u1f12\u1f16\u1f1als;\u403dst;\u625fv\u0100;D\u0235\u1f20D;\u6a78parsl;\u69e5\u0100Da\u1f2f\u1f33ot;\u6253rr;\u6971\u0180cdi\u1f3e\u1f41\u1ef8r;\u612fo\xf4\u0352\u0100ah\u1f49\u1f4b;\u43b7\u803b\xf0\u40f0\u0100mr\u1f53\u1f57l\u803b\xeb\u40ebo;\u60ac\u0180cip\u1f61\u1f64\u1f67l;\u4021s\xf4\u056e\u0100eo\u1f6c\u1f74ctatio\xee\u0559nential\xe5\u0579\u09e1\u1f92\0\u1f9e\0\u1fa1\u1fa7\0\0\u1fc6\u1fcc\0\u1fd3\0\u1fe6\u1fea\u2000\0\u2008\u205allingdotse\xf1\u1e44y;\u4444male;\u6640\u0180ilr\u1fad\u1fb3\u1fc1lig;\u8000\ufb03\u0269\u1fb9\0\0\u1fbdg;\u8000\ufb00ig;\u8000\ufb04;\uc000\ud835\udd23lig;\u8000\ufb01lig;\uc000fj\u0180alt\u1fd9\u1fdc\u1fe1t;\u666dig;\u8000\ufb02ns;\u65b1of;\u4192\u01f0\u1fee\0\u1ff3f;\uc000\ud835\udd57\u0100ak\u05bf\u1ff7\u0100;v\u1ffc\u1ffd\u62d4;\u6ad9artint;\u6a0d\u0100ao\u200c\u2055\u0100cs\u2011\u2052\u03b1\u201a\u2030\u2038\u2045\u2048\0\u2050\u03b2\u2022\u2025\u2027\u202a\u202c\0\u202e\u803b\xbd\u40bd;\u6153\u803b\xbc\u40bc;\u6155;\u6159;\u615b\u01b3\u2034\0\u2036;\u6154;\u6156\u02b4\u203e\u2041\0\0\u2043\u803b\xbe\u40be;\u6157;\u615c5;\u6158\u01b6\u204c\0\u204e;\u615a;\u615d8;\u615el;\u6044wn;\u6322cr;\uc000\ud835\udcbb\u0880Eabcdefgijlnorstv\u2082\u2089\u209f\u20a5\u20b0\u20b4\u20f0\u20f5\u20fa\u20ff\u2103\u2112\u2138\u0317\u213e\u2152\u219e\u0100;l\u064d\u2087;\u6a8c\u0180cmp\u2090\u2095\u209dute;\u41f5ma\u0100;d\u209c\u1cda\u43b3;\u6a86reve;\u411f\u0100iy\u20aa\u20aerc;\u411d;\u4433ot;\u4121\u0200;lqs\u063e\u0642\u20bd\u20c9\u0180;qs\u063e\u064c\u20c4lan\xf4\u0665\u0200;cdl\u0665\u20d2\u20d5\u20e5c;\u6aa9ot\u0100;o\u20dc\u20dd\u6a80\u0100;l\u20e2\u20e3\u6a82;\u6a84\u0100;e\u20ea\u20ed\uc000\u22db\ufe00s;\u6a94r;\uc000\ud835\udd24\u0100;g\u0673\u061bmel;\u6137cy;\u4453\u0200;Eaj\u065a\u210c\u210e\u2110;\u6a92;\u6aa5;\u6aa4\u0200Eaes\u211b\u211d\u2129\u2134;\u6269p\u0100;p\u2123\u2124\u6a8arox\xbb\u2124\u0100;q\u212e\u212f\u6a88\u0100;q\u212e\u211bim;\u62e7pf;\uc000\ud835\udd58\u0100ci\u2143\u2146r;\u610am\u0180;el\u066b\u214e\u2150;\u6a8e;\u6a90\u8300>;cdlqr\u05ee\u2160\u216a\u216e\u2173\u2179\u0100ci\u2165\u2167;\u6aa7r;\u6a7aot;\u62d7Par;\u6995uest;\u6a7c\u0280adels\u2184\u216a\u2190\u0656\u219b\u01f0\u2189\0\u218epro\xf8\u209er;\u6978q\u0100lq\u063f\u2196les\xf3\u2088i\xed\u066b\u0100en\u21a3\u21adrtneqq;\uc000\u2269\ufe00\xc5\u21aa\u0500Aabcefkosy\u21c4\u21c7\u21f1\u21f5\u21fa\u2218\u221d\u222f\u2268\u227dr\xf2\u03a0\u0200ilmr\u21d0\u21d4\u21d7\u21dbrs\xf0\u1484f\xbb\u2024il\xf4\u06a9\u0100dr\u21e0\u21e4cy;\u444a\u0180;cw\u08f4\u21eb\u21efir;\u6948;\u61adar;\u610firc;\u4125\u0180alr\u2201\u220e\u2213rts\u0100;u\u2209\u220a\u6665it\xbb\u220alip;\u6026con;\u62b9r;\uc000\ud835\udd25s\u0100ew\u2223\u2229arow;\u6925arow;\u6926\u0280amopr\u223a\u223e\u2243\u225e\u2263rr;\u61fftht;\u623bk\u0100lr\u2249\u2253eftarrow;\u61a9ightarrow;\u61aaf;\uc000\ud835\udd59bar;\u6015\u0180clt\u226f\u2274\u2278r;\uc000\ud835\udcbdas\xe8\u21f4rok;\u4127\u0100bp\u2282\u2287ull;\u6043hen\xbb\u1c5b\u0ae1\u22a3\0\u22aa\0\u22b8\u22c5\u22ce\0\u22d5\u22f3\0\0\u22f8\u2322\u2367\u2362\u237f\0\u2386\u23aa\u23b4cute\u803b\xed\u40ed\u0180;iy\u0771\u22b0\u22b5rc\u803b\xee\u40ee;\u4438\u0100cx\u22bc\u22bfy;\u4435cl\u803b\xa1\u40a1\u0100fr\u039f\u22c9;\uc000\ud835\udd26rave\u803b\xec\u40ec\u0200;ino\u073e\u22dd\u22e9\u22ee\u0100in\u22e2\u22e6nt;\u6a0ct;\u622dfin;\u69dcta;\u6129lig;\u4133\u0180aop\u22fe\u231a\u231d\u0180cgt\u2305\u2308\u2317r;\u412b\u0180elp\u071f\u230f\u2313in\xe5\u078ear\xf4\u0720h;\u4131f;\u62b7ed;\u41b5\u0280;cfot\u04f4\u232c\u2331\u233d\u2341are;\u6105in\u0100;t\u2338\u2339\u621eie;\u69dddo\xf4\u2319\u0280;celp\u0757\u234c\u2350\u235b\u2361al;\u62ba\u0100gr\u2355\u2359er\xf3\u1563\xe3\u234darhk;\u6a17rod;\u6a3c\u0200cgpt\u236f\u2372\u2376\u237by;\u4451on;\u412ff;\uc000\ud835\udd5aa;\u43b9uest\u803b\xbf\u40bf\u0100ci\u238a\u238fr;\uc000\ud835\udcben\u0280;Edsv\u04f4\u239b\u239d\u23a1\u04f3;\u62f9ot;\u62f5\u0100;v\u23a6\u23a7\u62f4;\u62f3\u0100;i\u0777\u23aelde;\u4129\u01eb\u23b8\0\u23bccy;\u4456l\u803b\xef\u40ef\u0300cfmosu\u23cc\u23d7\u23dc\u23e1\u23e7\u23f5\u0100iy\u23d1\u23d5rc;\u4135;\u4439r;\uc000\ud835\udd27ath;\u4237pf;\uc000\ud835\udd5b\u01e3\u23ec\0\u23f1r;\uc000\ud835\udcbfrcy;\u4458kcy;\u4454\u0400acfghjos\u240b\u2416\u2422\u2427\u242d\u2431\u2435\u243bppa\u0100;v\u2413\u2414\u43ba;\u43f0\u0100ey\u241b\u2420dil;\u4137;\u443ar;\uc000\ud835\udd28reen;\u4138cy;\u4445cy;\u445cpf;\uc000\ud835\udd5ccr;\uc000\ud835\udcc0\u0b80ABEHabcdefghjlmnoprstuv\u2470\u2481\u2486\u248d\u2491\u250e\u253d\u255a\u2580\u264e\u265e\u2665\u2679\u267d\u269a\u26b2\u26d8\u275d\u2768\u278b\u27c0\u2801\u2812\u0180art\u2477\u247a\u247cr\xf2\u09c6\xf2\u0395ail;\u691barr;\u690e\u0100;g\u0994\u248b;\u6a8bar;\u6962\u0963\u24a5\0\u24aa\0\u24b1\0\0\0\0\0\u24b5\u24ba\0\u24c6\u24c8\u24cd\0\u24f9ute;\u413amptyv;\u69b4ra\xee\u084cbda;\u43bbg\u0180;dl\u088e\u24c1\u24c3;\u6991\xe5\u088e;\u6a85uo\u803b\xab\u40abr\u0400;bfhlpst\u0899\u24de\u24e6\u24e9\u24eb\u24ee\u24f1\u24f5\u0100;f\u089d\u24e3s;\u691fs;\u691d\xeb\u2252p;\u61abl;\u6939im;\u6973l;\u61a2\u0180;ae\u24ff\u2500\u2504\u6aabil;\u6919\u0100;s\u2509\u250a\u6aad;\uc000\u2aad\ufe00\u0180abr\u2515\u2519\u251drr;\u690crk;\u6772\u0100ak\u2522\u252cc\u0100ek\u2528\u252a;\u407b;\u405b\u0100es\u2531\u2533;\u698bl\u0100du\u2539\u253b;\u698f;\u698d\u0200aeuy\u2546\u254b\u2556\u2558ron;\u413e\u0100di\u2550\u2554il;\u413c\xec\u08b0\xe2\u2529;\u443b\u0200cqrs\u2563\u2566\u256d\u257da;\u6936uo\u0100;r\u0e19\u1746\u0100du\u2572\u2577har;\u6967shar;\u694bh;\u61b2\u0280;fgqs\u258b\u258c\u0989\u25f3\u25ff\u6264t\u0280ahlrt\u2598\u25a4\u25b7\u25c2\u25e8rrow\u0100;t\u0899\u25a1a\xe9\u24f6arpoon\u0100du\u25af\u25b4own\xbb\u045ap\xbb\u0966eftarrows;\u61c7ight\u0180ahs\u25cd\u25d6\u25derrow\u0100;s\u08f4\u08a7arpoon\xf3\u0f98quigarro\xf7\u21f0hreetimes;\u62cb\u0180;qs\u258b\u0993\u25falan\xf4\u09ac\u0280;cdgs\u09ac\u260a\u260d\u261d\u2628c;\u6aa8ot\u0100;o\u2614\u2615\u6a7f\u0100;r\u261a\u261b\u6a81;\u6a83\u0100;e\u2622\u2625\uc000\u22da\ufe00s;\u6a93\u0280adegs\u2633\u2639\u263d\u2649\u264bppro\xf8\u24c6ot;\u62d6q\u0100gq\u2643\u2645\xf4\u0989gt\xf2\u248c\xf4\u099bi\xed\u09b2\u0180ilr\u2655\u08e1\u265asht;\u697c;\uc000\ud835\udd29\u0100;E\u099c\u2663;\u6a91\u0161\u2669\u2676r\u0100du\u25b2\u266e\u0100;l\u0965\u2673;\u696alk;\u6584cy;\u4459\u0280;acht\u0a48\u2688\u268b\u2691\u2696r\xf2\u25c1orne\xf2\u1d08ard;\u696bri;\u65fa\u0100io\u269f\u26a4dot;\u4140ust\u0100;a\u26ac\u26ad\u63b0che\xbb\u26ad\u0200Eaes\u26bb\u26bd\u26c9\u26d4;\u6268p\u0100;p\u26c3\u26c4\u6a89rox\xbb\u26c4\u0100;q\u26ce\u26cf\u6a87\u0100;q\u26ce\u26bbim;\u62e6\u0400abnoptwz\u26e9\u26f4\u26f7\u271a\u272f\u2741\u2747\u2750\u0100nr\u26ee\u26f1g;\u67ecr;\u61fdr\xeb\u08c1g\u0180lmr\u26ff\u270d\u2714eft\u0100ar\u09e6\u2707ight\xe1\u09f2apsto;\u67fcight\xe1\u09fdparrow\u0100lr\u2725\u2729ef\xf4\u24edight;\u61ac\u0180afl\u2736\u2739\u273dr;\u6985;\uc000\ud835\udd5dus;\u6a2dimes;\u6a34\u0161\u274b\u274fst;\u6217\xe1\u134e\u0180;ef\u2757\u2758\u1800\u65cange\xbb\u2758ar\u0100;l\u2764\u2765\u4028t;\u6993\u0280achmt\u2773\u2776\u277c\u2785\u2787r\xf2\u08a8orne\xf2\u1d8car\u0100;d\u0f98\u2783;\u696d;\u600eri;\u62bf\u0300achiqt\u2798\u279d\u0a40\u27a2\u27ae\u27bbquo;\u6039r;\uc000\ud835\udcc1m\u0180;eg\u09b2\u27aa\u27ac;\u6a8d;\u6a8f\u0100bu\u252a\u27b3o\u0100;r\u0e1f\u27b9;\u601arok;\u4142\u8400<;cdhilqr\u082b\u27d2\u2639\u27dc\u27e0\u27e5\u27ea\u27f0\u0100ci\u27d7\u27d9;\u6aa6r;\u6a79re\xe5\u25f2mes;\u62c9arr;\u6976uest;\u6a7b\u0100Pi\u27f5\u27f9ar;\u6996\u0180;ef\u2800\u092d\u181b\u65c3r\u0100du\u2807\u280dshar;\u694ahar;\u6966\u0100en\u2817\u2821rtneqq;\uc000\u2268\ufe00\xc5\u281e\u0700Dacdefhilnopsu\u2840\u2845\u2882\u288e\u2893\u28a0\u28a5\u28a8\u28da\u28e2\u28e4\u0a83\u28f3\u2902Dot;\u623a\u0200clpr\u284e\u2852\u2863\u287dr\u803b\xaf\u40af\u0100et\u2857\u2859;\u6642\u0100;e\u285e\u285f\u6720se\xbb\u285f\u0100;s\u103b\u2868to\u0200;dlu\u103b\u2873\u2877\u287bow\xee\u048cef\xf4\u090f\xf0\u13d1ker;\u65ae\u0100oy\u2887\u288cmma;\u6a29;\u443cash;\u6014asuredangle\xbb\u1626r;\uc000\ud835\udd2ao;\u6127\u0180cdn\u28af\u28b4\u28c9ro\u803b\xb5\u40b5\u0200;acd\u1464\u28bd\u28c0\u28c4s\xf4\u16a7ir;\u6af0ot\u80bb\xb7\u01b5us\u0180;bd\u28d2\u1903\u28d3\u6212\u0100;u\u1d3c\u28d8;\u6a2a\u0163\u28de\u28e1p;\u6adb\xf2\u2212\xf0\u0a81\u0100dp\u28e9\u28eeels;\u62a7f;\uc000\ud835\udd5e\u0100ct\u28f8\u28fdr;\uc000\ud835\udcc2pos\xbb\u159d\u0180;lm\u2909\u290a\u290d\u43bctimap;\u62b8\u0c00GLRVabcdefghijlmoprstuvw\u2942\u2953\u297e\u2989\u2998\u29da\u29e9\u2a15\u2a1a\u2a58\u2a5d\u2a83\u2a95\u2aa4\u2aa8\u2b04\u2b07\u2b44\u2b7f\u2bae\u2c34\u2c67\u2c7c\u2ce9\u0100gt\u2947\u294b;\uc000\u22d9\u0338\u0100;v\u2950\u0bcf\uc000\u226b\u20d2\u0180elt\u295a\u2972\u2976ft\u0100ar\u2961\u2967rrow;\u61cdightarrow;\u61ce;\uc000\u22d8\u0338\u0100;v\u297b\u0c47\uc000\u226a\u20d2ightarrow;\u61cf\u0100Dd\u298e\u2993ash;\u62afash;\u62ae\u0280bcnpt\u29a3\u29a7\u29ac\u29b1\u29ccla\xbb\u02deute;\u4144g;\uc000\u2220\u20d2\u0280;Eiop\u0d84\u29bc\u29c0\u29c5\u29c8;\uc000\u2a70\u0338d;\uc000\u224b\u0338s;\u4149ro\xf8\u0d84ur\u0100;a\u29d3\u29d4\u666el\u0100;s\u29d3\u0b38\u01f3\u29df\0\u29e3p\u80bb\xa0\u0b37mp\u0100;e\u0bf9\u0c00\u0280aeouy\u29f4\u29fe\u2a03\u2a10\u2a13\u01f0\u29f9\0\u29fb;\u6a43on;\u4148dil;\u4146ng\u0100;d\u0d7e\u2a0aot;\uc000\u2a6d\u0338p;\u6a42;\u443dash;\u6013\u0380;Aadqsx\u0b92\u2a29\u2a2d\u2a3b\u2a41\u2a45\u2a50rr;\u61d7r\u0100hr\u2a33\u2a36k;\u6924\u0100;o\u13f2\u13f0ot;\uc000\u2250\u0338ui\xf6\u0b63\u0100ei\u2a4a\u2a4ear;\u6928\xed\u0b98ist\u0100;s\u0ba0\u0b9fr;\uc000\ud835\udd2b\u0200Eest\u0bc5\u2a66\u2a79\u2a7c\u0180;qs\u0bbc\u2a6d\u0be1\u0180;qs\u0bbc\u0bc5\u2a74lan\xf4\u0be2i\xed\u0bea\u0100;r\u0bb6\u2a81\xbb\u0bb7\u0180Aap\u2a8a\u2a8d\u2a91r\xf2\u2971rr;\u61aear;\u6af2\u0180;sv\u0f8d\u2a9c\u0f8c\u0100;d\u2aa1\u2aa2\u62fc;\u62facy;\u445a\u0380AEadest\u2ab7\u2aba\u2abe\u2ac2\u2ac5\u2af6\u2af9r\xf2\u2966;\uc000\u2266\u0338rr;\u619ar;\u6025\u0200;fqs\u0c3b\u2ace\u2ae3\u2aeft\u0100ar\u2ad4\u2ad9rro\xf7\u2ac1ightarro\xf7\u2a90\u0180;qs\u0c3b\u2aba\u2aealan\xf4\u0c55\u0100;s\u0c55\u2af4\xbb\u0c36i\xed\u0c5d\u0100;r\u0c35\u2afei\u0100;e\u0c1a\u0c25i\xe4\u0d90\u0100pt\u2b0c\u2b11f;\uc000\ud835\udd5f\u8180\xac;in\u2b19\u2b1a\u2b36\u40acn\u0200;Edv\u0b89\u2b24\u2b28\u2b2e;\uc000\u22f9\u0338ot;\uc000\u22f5\u0338\u01e1\u0b89\u2b33\u2b35;\u62f7;\u62f6i\u0100;v\u0cb8\u2b3c\u01e1\u0cb8\u2b41\u2b43;\u62fe;\u62fd\u0180aor\u2b4b\u2b63\u2b69r\u0200;ast\u0b7b\u2b55\u2b5a\u2b5flle\xec\u0b7bl;\uc000\u2afd\u20e5;\uc000\u2202\u0338lint;\u6a14\u0180;ce\u0c92\u2b70\u2b73u\xe5\u0ca5\u0100;c\u0c98\u2b78\u0100;e\u0c92\u2b7d\xf1\u0c98\u0200Aait\u2b88\u2b8b\u2b9d\u2ba7r\xf2\u2988rr\u0180;cw\u2b94\u2b95\u2b99\u619b;\uc000\u2933\u0338;\uc000\u219d\u0338ghtarrow\xbb\u2b95ri\u0100;e\u0ccb\u0cd6\u0380chimpqu\u2bbd\u2bcd\u2bd9\u2b04\u0b78\u2be4\u2bef\u0200;cer\u0d32\u2bc6\u0d37\u2bc9u\xe5\u0d45;\uc000\ud835\udcc3ort\u026d\u2b05\0\0\u2bd6ar\xe1\u2b56m\u0100;e\u0d6e\u2bdf\u0100;q\u0d74\u0d73su\u0100bp\u2beb\u2bed\xe5\u0cf8\xe5\u0d0b\u0180bcp\u2bf6\u2c11\u2c19\u0200;Ees\u2bff\u2c00\u0d22\u2c04\u6284;\uc000\u2ac5\u0338et\u0100;e\u0d1b\u2c0bq\u0100;q\u0d23\u2c00c\u0100;e\u0d32\u2c17\xf1\u0d38\u0200;Ees\u2c22\u2c23\u0d5f\u2c27\u6285;\uc000\u2ac6\u0338et\u0100;e\u0d58\u2c2eq\u0100;q\u0d60\u2c23\u0200gilr\u2c3d\u2c3f\u2c45\u2c47\xec\u0bd7lde\u803b\xf1\u40f1\xe7\u0c43iangle\u0100lr\u2c52\u2c5ceft\u0100;e\u0c1a\u2c5a\xf1\u0c26ight\u0100;e\u0ccb\u2c65\xf1\u0cd7\u0100;m\u2c6c\u2c6d\u43bd\u0180;es\u2c74\u2c75\u2c79\u4023ro;\u6116p;\u6007\u0480DHadgilrs\u2c8f\u2c94\u2c99\u2c9e\u2ca3\u2cb0\u2cb6\u2cd3\u2ce3ash;\u62adarr;\u6904p;\uc000\u224d\u20d2ash;\u62ac\u0100et\u2ca8\u2cac;\uc000\u2265\u20d2;\uc000>\u20d2nfin;\u69de\u0180Aet\u2cbd\u2cc1\u2cc5rr;\u6902;\uc000\u2264\u20d2\u0100;r\u2cca\u2ccd\uc000<\u20d2ie;\uc000\u22b4\u20d2\u0100At\u2cd8\u2cdcrr;\u6903rie;\uc000\u22b5\u20d2im;\uc000\u223c\u20d2\u0180Aan\u2cf0\u2cf4\u2d02rr;\u61d6r\u0100hr\u2cfa\u2cfdk;\u6923\u0100;o\u13e7\u13e5ear;\u6927\u1253\u1a95\0\0\0\0\0\0\0\0\0\0\0\0\0\u2d2d\0\u2d38\u2d48\u2d60\u2d65\u2d72\u2d84\u1b07\0\0\u2d8d\u2dab\0\u2dc8\u2dce\0\u2ddc\u2e19\u2e2b\u2e3e\u2e43\u0100cs\u2d31\u1a97ute\u803b\xf3\u40f3\u0100iy\u2d3c\u2d45r\u0100;c\u1a9e\u2d42\u803b\xf4\u40f4;\u443e\u0280abios\u1aa0\u2d52\u2d57\u01c8\u2d5alac;\u4151v;\u6a38old;\u69bclig;\u4153\u0100cr\u2d69\u2d6dir;\u69bf;\uc000\ud835\udd2c\u036f\u2d79\0\0\u2d7c\0\u2d82n;\u42dbave\u803b\xf2\u40f2;\u69c1\u0100bm\u2d88\u0df4ar;\u69b5\u0200acit\u2d95\u2d98\u2da5\u2da8r\xf2\u1a80\u0100ir\u2d9d\u2da0r;\u69beoss;\u69bbn\xe5\u0e52;\u69c0\u0180aei\u2db1\u2db5\u2db9cr;\u414dga;\u43c9\u0180cdn\u2dc0\u2dc5\u01cdron;\u43bf;\u69b6pf;\uc000\ud835\udd60\u0180ael\u2dd4\u2dd7\u01d2r;\u69b7rp;\u69b9\u0380;adiosv\u2dea\u2deb\u2dee\u2e08\u2e0d\u2e10\u2e16\u6228r\xf2\u1a86\u0200;efm\u2df7\u2df8\u2e02\u2e05\u6a5dr\u0100;o\u2dfe\u2dff\u6134f\xbb\u2dff\u803b\xaa\u40aa\u803b\xba\u40bagof;\u62b6r;\u6a56lope;\u6a57;\u6a5b\u0180clo\u2e1f\u2e21\u2e27\xf2\u2e01ash\u803b\xf8\u40f8l;\u6298i\u016c\u2e2f\u2e34de\u803b\xf5\u40f5es\u0100;a\u01db\u2e3as;\u6a36ml\u803b\xf6\u40f6bar;\u633d\u0ae1\u2e5e\0\u2e7d\0\u2e80\u2e9d\0\u2ea2\u2eb9\0\0\u2ecb\u0e9c\0\u2f13\0\0\u2f2b\u2fbc\0\u2fc8r\u0200;ast\u0403\u2e67\u2e72\u0e85\u8100\xb6;l\u2e6d\u2e6e\u40b6le\xec\u0403\u0269\u2e78\0\0\u2e7bm;\u6af3;\u6afdy;\u443fr\u0280cimpt\u2e8b\u2e8f\u2e93\u1865\u2e97nt;\u4025od;\u402eil;\u6030enk;\u6031r;\uc000\ud835\udd2d\u0180imo\u2ea8\u2eb0\u2eb4\u0100;v\u2ead\u2eae\u43c6;\u43d5ma\xf4\u0a76ne;\u660e\u0180;tv\u2ebf\u2ec0\u2ec8\u43c0chfork\xbb\u1ffd;\u43d6\u0100au\u2ecf\u2edfn\u0100ck\u2ed5\u2eddk\u0100;h\u21f4\u2edb;\u610e\xf6\u21f4s\u0480;abcdemst\u2ef3\u2ef4\u1908\u2ef9\u2efd\u2f04\u2f06\u2f0a\u2f0e\u402bcir;\u6a23ir;\u6a22\u0100ou\u1d40\u2f02;\u6a25;\u6a72n\u80bb\xb1\u0e9dim;\u6a26wo;\u6a27\u0180ipu\u2f19\u2f20\u2f25ntint;\u6a15f;\uc000\ud835\udd61nd\u803b\xa3\u40a3\u0500;Eaceinosu\u0ec8\u2f3f\u2f41\u2f44\u2f47\u2f81\u2f89\u2f92\u2f7e\u2fb6;\u6ab3p;\u6ab7u\xe5\u0ed9\u0100;c\u0ece\u2f4c\u0300;acens\u0ec8\u2f59\u2f5f\u2f66\u2f68\u2f7eppro\xf8\u2f43urlye\xf1\u0ed9\xf1\u0ece\u0180aes\u2f6f\u2f76\u2f7approx;\u6ab9qq;\u6ab5im;\u62e8i\xed\u0edfme\u0100;s\u2f88\u0eae\u6032\u0180Eas\u2f78\u2f90\u2f7a\xf0\u2f75\u0180dfp\u0eec\u2f99\u2faf\u0180als\u2fa0\u2fa5\u2faalar;\u632eine;\u6312urf;\u6313\u0100;t\u0efb\u2fb4\xef\u0efbrel;\u62b0\u0100ci\u2fc0\u2fc5r;\uc000\ud835\udcc5;\u43c8ncsp;\u6008\u0300fiopsu\u2fda\u22e2\u2fdf\u2fe5\u2feb\u2ff1r;\uc000\ud835\udd2epf;\uc000\ud835\udd62rime;\u6057cr;\uc000\ud835\udcc6\u0180aeo\u2ff8\u3009\u3013t\u0100ei\u2ffe\u3005rnion\xf3\u06b0nt;\u6a16st\u0100;e\u3010\u3011\u403f\xf1\u1f19\xf4\u0f14\u0a80ABHabcdefhilmnoprstux\u3040\u3051\u3055\u3059\u30e0\u310e\u312b\u3147\u3162\u3172\u318e\u3206\u3215\u3224\u3229\u3258\u326e\u3272\u3290\u32b0\u32b7\u0180art\u3047\u304a\u304cr\xf2\u10b3\xf2\u03ddail;\u691car\xf2\u1c65ar;\u6964\u0380cdenqrt\u3068\u3075\u3078\u307f\u308f\u3094\u30cc\u0100eu\u306d\u3071;\uc000\u223d\u0331te;\u4155i\xe3\u116emptyv;\u69b3g\u0200;del\u0fd1\u3089\u308b\u308d;\u6992;\u69a5\xe5\u0fd1uo\u803b\xbb\u40bbr\u0580;abcfhlpstw\u0fdc\u30ac\u30af\u30b7\u30b9\u30bc\u30be\u30c0\u30c3\u30c7\u30cap;\u6975\u0100;f\u0fe0\u30b4s;\u6920;\u6933s;\u691e\xeb\u225d\xf0\u272el;\u6945im;\u6974l;\u61a3;\u619d\u0100ai\u30d1\u30d5il;\u691ao\u0100;n\u30db\u30dc\u6236al\xf3\u0f1e\u0180abr\u30e7\u30ea\u30eer\xf2\u17e5rk;\u6773\u0100ak\u30f3\u30fdc\u0100ek\u30f9\u30fb;\u407d;\u405d\u0100es\u3102\u3104;\u698cl\u0100du\u310a\u310c;\u698e;\u6990\u0200aeuy\u3117\u311c\u3127\u3129ron;\u4159\u0100di\u3121\u3125il;\u4157\xec\u0ff2\xe2\u30fa;\u4440\u0200clqs\u3134\u3137\u313d\u3144a;\u6937dhar;\u6969uo\u0100;r\u020e\u020dh;\u61b3\u0180acg\u314e\u315f\u0f44l\u0200;ips\u0f78\u3158\u315b\u109cn\xe5\u10bbar\xf4\u0fa9t;\u65ad\u0180ilr\u3169\u1023\u316esht;\u697d;\uc000\ud835\udd2f\u0100ao\u3177\u3186r\u0100du\u317d\u317f\xbb\u047b\u0100;l\u1091\u3184;\u696c\u0100;v\u318b\u318c\u43c1;\u43f1\u0180gns\u3195\u31f9\u31fcht\u0300ahlrst\u31a4\u31b0\u31c2\u31d8\u31e4\u31eerrow\u0100;t\u0fdc\u31ada\xe9\u30c8arpoon\u0100du\u31bb\u31bfow\xee\u317ep\xbb\u1092eft\u0100ah\u31ca\u31d0rrow\xf3\u0feaarpoon\xf3\u0551ightarrows;\u61c9quigarro\xf7\u30cbhreetimes;\u62ccg;\u42daingdotse\xf1\u1f32\u0180ahm\u320d\u3210\u3213r\xf2\u0feaa\xf2\u0551;\u600foust\u0100;a\u321e\u321f\u63b1che\xbb\u321fmid;\u6aee\u0200abpt\u3232\u323d\u3240\u3252\u0100nr\u3237\u323ag;\u67edr;\u61fer\xeb\u1003\u0180afl\u3247\u324a\u324er;\u6986;\uc000\ud835\udd63us;\u6a2eimes;\u6a35\u0100ap\u325d\u3267r\u0100;g\u3263\u3264\u4029t;\u6994olint;\u6a12ar\xf2\u31e3\u0200achq\u327b\u3280\u10bc\u3285quo;\u603ar;\uc000\ud835\udcc7\u0100bu\u30fb\u328ao\u0100;r\u0214\u0213\u0180hir\u3297\u329b\u32a0re\xe5\u31f8mes;\u62cai\u0200;efl\u32aa\u1059\u1821\u32ab\u65b9tri;\u69celuhar;\u6968;\u611e\u0d61\u32d5\u32db\u32df\u332c\u3338\u3371\0\u337a\u33a4\0\0\u33ec\u33f0\0\u3428\u3448\u345a\u34ad\u34b1\u34ca\u34f1\0\u3616\0\0\u3633cute;\u415bqu\xef\u27ba\u0500;Eaceinpsy\u11ed\u32f3\u32f5\u32ff\u3302\u330b\u330f\u331f\u3326\u3329;\u6ab4\u01f0\u32fa\0\u32fc;\u6ab8on;\u4161u\xe5\u11fe\u0100;d\u11f3\u3307il;\u415frc;\u415d\u0180Eas\u3316\u3318\u331b;\u6ab6p;\u6abaim;\u62e9olint;\u6a13i\xed\u1204;\u4441ot\u0180;be\u3334\u1d47\u3335\u62c5;\u6a66\u0380Aacmstx\u3346\u334a\u3357\u335b\u335e\u3363\u336drr;\u61d8r\u0100hr\u3350\u3352\xeb\u2228\u0100;o\u0a36\u0a34t\u803b\xa7\u40a7i;\u403bwar;\u6929m\u0100in\u3369\xf0nu\xf3\xf1t;\u6736r\u0100;o\u3376\u2055\uc000\ud835\udd30\u0200acoy\u3382\u3386\u3391\u33a0rp;\u666f\u0100hy\u338b\u338fcy;\u4449;\u4448rt\u026d\u3399\0\0\u339ci\xe4\u1464ara\xec\u2e6f\u803b\xad\u40ad\u0100gm\u33a8\u33b4ma\u0180;fv\u33b1\u33b2\u33b2\u43c3;\u43c2\u0400;deglnpr\u12ab\u33c5\u33c9\u33ce\u33d6\u33de\u33e1\u33e6ot;\u6a6a\u0100;q\u12b1\u12b0\u0100;E\u33d3\u33d4\u6a9e;\u6aa0\u0100;E\u33db\u33dc\u6a9d;\u6a9fe;\u6246lus;\u6a24arr;\u6972ar\xf2\u113d\u0200aeit\u33f8\u3408\u340f\u3417\u0100ls\u33fd\u3404lsetm\xe9\u336ahp;\u6a33parsl;\u69e4\u0100dl\u1463\u3414e;\u6323\u0100;e\u341c\u341d\u6aaa\u0100;s\u3422\u3423\u6aac;\uc000\u2aac\ufe00\u0180flp\u342e\u3433\u3442tcy;\u444c\u0100;b\u3438\u3439\u402f\u0100;a\u343e\u343f\u69c4r;\u633ff;\uc000\ud835\udd64a\u0100dr\u344d\u0402es\u0100;u\u3454\u3455\u6660it\xbb\u3455\u0180csu\u3460\u3479\u349f\u0100au\u3465\u346fp\u0100;s\u1188\u346b;\uc000\u2293\ufe00p\u0100;s\u11b4\u3475;\uc000\u2294\ufe00u\u0100bp\u347f\u348f\u0180;es\u1197\u119c\u3486et\u0100;e\u1197\u348d\xf1\u119d\u0180;es\u11a8\u11ad\u3496et\u0100;e\u11a8\u349d\xf1\u11ae\u0180;af\u117b\u34a6\u05b0r\u0165\u34ab\u05b1\xbb\u117car\xf2\u1148\u0200cemt\u34b9\u34be\u34c2\u34c5r;\uc000\ud835\udcc8tm\xee\xf1i\xec\u3415ar\xe6\u11be\u0100ar\u34ce\u34d5r\u0100;f\u34d4\u17bf\u6606\u0100an\u34da\u34edight\u0100ep\u34e3\u34eapsilo\xee\u1ee0h\xe9\u2eafs\xbb\u2852\u0280bcmnp\u34fb\u355e\u1209\u358b\u358e\u0480;Edemnprs\u350e\u350f\u3511\u3515\u351e\u3523\u352c\u3531\u3536\u6282;\u6ac5ot;\u6abd\u0100;d\u11da\u351aot;\u6ac3ult;\u6ac1\u0100Ee\u3528\u352a;\u6acb;\u628alus;\u6abfarr;\u6979\u0180eiu\u353d\u3552\u3555t\u0180;en\u350e\u3545\u354bq\u0100;q\u11da\u350feq\u0100;q\u352b\u3528m;\u6ac7\u0100bp\u355a\u355c;\u6ad5;\u6ad3c\u0300;acens\u11ed\u356c\u3572\u3579\u357b\u3326ppro\xf8\u32faurlye\xf1\u11fe\xf1\u11f3\u0180aes\u3582\u3588\u331bppro\xf8\u331aq\xf1\u3317g;\u666a\u0680123;Edehlmnps\u35a9\u35ac\u35af\u121c\u35b2\u35b4\u35c0\u35c9\u35d5\u35da\u35df\u35e8\u35ed\u803b\xb9\u40b9\u803b\xb2\u40b2\u803b\xb3\u40b3;\u6ac6\u0100os\u35b9\u35bct;\u6abeub;\u6ad8\u0100;d\u1222\u35c5ot;\u6ac4s\u0100ou\u35cf\u35d2l;\u67c9b;\u6ad7arr;\u697bult;\u6ac2\u0100Ee\u35e4\u35e6;\u6acc;\u628blus;\u6ac0\u0180eiu\u35f4\u3609\u360ct\u0180;en\u121c\u35fc\u3602q\u0100;q\u1222\u35b2eq\u0100;q\u35e7\u35e4m;\u6ac8\u0100bp\u3611\u3613;\u6ad4;\u6ad6\u0180Aan\u361c\u3620\u362drr;\u61d9r\u0100hr\u3626\u3628\xeb\u222e\u0100;o\u0a2b\u0a29war;\u692alig\u803b\xdf\u40df\u0be1\u3651\u365d\u3660\u12ce\u3673\u3679\0\u367e\u36c2\0\0\0\0\0\u36db\u3703\0\u3709\u376c\0\0\0\u3787\u0272\u3656\0\0\u365bget;\u6316;\u43c4r\xeb\u0e5f\u0180aey\u3666\u366b\u3670ron;\u4165dil;\u4163;\u4442lrec;\u6315r;\uc000\ud835\udd31\u0200eiko\u3686\u369d\u36b5\u36bc\u01f2\u368b\0\u3691e\u01004f\u1284\u1281a\u0180;sv\u3698\u3699\u369b\u43b8ym;\u43d1\u0100cn\u36a2\u36b2k\u0100as\u36a8\u36aeppro\xf8\u12c1im\xbb\u12acs\xf0\u129e\u0100as\u36ba\u36ae\xf0\u12c1rn\u803b\xfe\u40fe\u01ec\u031f\u36c6\u22e7es\u8180\xd7;bd\u36cf\u36d0\u36d8\u40d7\u0100;a\u190f\u36d5r;\u6a31;\u6a30\u0180eps\u36e1\u36e3\u3700\xe1\u2a4d\u0200;bcf\u0486\u36ec\u36f0\u36f4ot;\u6336ir;\u6af1\u0100;o\u36f9\u36fc\uc000\ud835\udd65rk;\u6ada\xe1\u3362rime;\u6034\u0180aip\u370f\u3712\u3764d\xe5\u1248\u0380adempst\u3721\u374d\u3740\u3751\u3757\u375c\u375fngle\u0280;dlqr\u3730\u3731\u3736\u3740\u3742\u65b5own\xbb\u1dbbeft\u0100;e\u2800\u373e\xf1\u092e;\u625cight\u0100;e\u32aa\u374b\xf1\u105aot;\u65ecinus;\u6a3alus;\u6a39b;\u69cdime;\u6a3bezium;\u63e2\u0180cht\u3772\u377d\u3781\u0100ry\u3777\u377b;\uc000\ud835\udcc9;\u4446cy;\u445brok;\u4167\u0100io\u378b\u378ex\xf4\u1777head\u0100lr\u3797\u37a0eftarro\xf7\u084fightarrow\xbb\u0f5d\u0900AHabcdfghlmoprstuw\u37d0\u37d3\u37d7\u37e4\u37f0\u37fc\u380e\u381c\u3823\u3834\u3851\u385d\u386b\u38a9\u38cc\u38d2\u38ea\u38f6r\xf2\u03edar;\u6963\u0100cr\u37dc\u37e2ute\u803b\xfa\u40fa\xf2\u1150r\u01e3\u37ea\0\u37edy;\u445eve;\u416d\u0100iy\u37f5\u37farc\u803b\xfb\u40fb;\u4443\u0180abh\u3803\u3806\u380br\xf2\u13adlac;\u4171a\xf2\u13c3\u0100ir\u3813\u3818sht;\u697e;\uc000\ud835\udd32rave\u803b\xf9\u40f9\u0161\u3827\u3831r\u0100lr\u382c\u382e\xbb\u0957\xbb\u1083lk;\u6580\u0100ct\u3839\u384d\u026f\u383f\0\0\u384arn\u0100;e\u3845\u3846\u631cr\xbb\u3846op;\u630fri;\u65f8\u0100al\u3856\u385acr;\u416b\u80bb\xa8\u0349\u0100gp\u3862\u3866on;\u4173f;\uc000\ud835\udd66\u0300adhlsu\u114b\u3878\u387d\u1372\u3891\u38a0own\xe1\u13b3arpoon\u0100lr\u3888\u388cef\xf4\u382digh\xf4\u382fi\u0180;hl\u3899\u389a\u389c\u43c5\xbb\u13faon\xbb\u389aparrows;\u61c8\u0180cit\u38b0\u38c4\u38c8\u026f\u38b6\0\0\u38c1rn\u0100;e\u38bc\u38bd\u631dr\xbb\u38bdop;\u630eng;\u416fri;\u65f9cr;\uc000\ud835\udcca\u0180dir\u38d9\u38dd\u38e2ot;\u62f0lde;\u4169i\u0100;f\u3730\u38e8\xbb\u1813\u0100am\u38ef\u38f2r\xf2\u38a8l\u803b\xfc\u40fcangle;\u69a7\u0780ABDacdeflnoprsz\u391c\u391f\u3929\u392d\u39b5\u39b8\u39bd\u39df\u39e4\u39e8\u39f3\u39f9\u39fd\u3a01\u3a20r\xf2\u03f7ar\u0100;v\u3926\u3927\u6ae8;\u6ae9as\xe8\u03e1\u0100nr\u3932\u3937grt;\u699c\u0380eknprst\u34e3\u3946\u394b\u3952\u395d\u3964\u3996app\xe1\u2415othin\xe7\u1e96\u0180hir\u34eb\u2ec8\u3959op\xf4\u2fb5\u0100;h\u13b7\u3962\xef\u318d\u0100iu\u3969\u396dgm\xe1\u33b3\u0100bp\u3972\u3984setneq\u0100;q\u397d\u3980\uc000\u228a\ufe00;\uc000\u2acb\ufe00setneq\u0100;q\u398f\u3992\uc000\u228b\ufe00;\uc000\u2acc\ufe00\u0100hr\u399b\u399fet\xe1\u369ciangle\u0100lr\u39aa\u39afeft\xbb\u0925ight\xbb\u1051y;\u4432ash\xbb\u1036\u0180elr\u39c4\u39d2\u39d7\u0180;be\u2dea\u39cb\u39cfar;\u62bbq;\u625alip;\u62ee\u0100bt\u39dc\u1468a\xf2\u1469r;\uc000\ud835\udd33tr\xe9\u39aesu\u0100bp\u39ef\u39f1\xbb\u0d1c\xbb\u0d59pf;\uc000\ud835\udd67ro\xf0\u0efbtr\xe9\u39b4\u0100cu\u3a06\u3a0br;\uc000\ud835\udccb\u0100bp\u3a10\u3a18n\u0100Ee\u3980\u3a16\xbb\u397en\u0100Ee\u3992\u3a1e\xbb\u3990igzag;\u699a\u0380cefoprs\u3a36\u3a3b\u3a56\u3a5b\u3a54\u3a61\u3a6airc;\u4175\u0100di\u3a40\u3a51\u0100bg\u3a45\u3a49ar;\u6a5fe\u0100;q\u15fa\u3a4f;\u6259erp;\u6118r;\uc000\ud835\udd34pf;\uc000\ud835\udd68\u0100;e\u1479\u3a66at\xe8\u1479cr;\uc000\ud835\udccc\u0ae3\u178e\u3a87\0\u3a8b\0\u3a90\u3a9b\0\0\u3a9d\u3aa8\u3aab\u3aaf\0\0\u3ac3\u3ace\0\u3ad8\u17dc\u17dftr\xe9\u17d1r;\uc000\ud835\udd35\u0100Aa\u3a94\u3a97r\xf2\u03c3r\xf2\u09f6;\u43be\u0100Aa\u3aa1\u3aa4r\xf2\u03b8r\xf2\u09eba\xf0\u2713is;\u62fb\u0180dpt\u17a4\u3ab5\u3abe\u0100fl\u3aba\u17a9;\uc000\ud835\udd69im\xe5\u17b2\u0100Aa\u3ac7\u3acar\xf2\u03cer\xf2\u0a01\u0100cq\u3ad2\u17b8r;\uc000\ud835\udccd\u0100pt\u17d6\u3adcr\xe9\u17d4\u0400acefiosu\u3af0\u3afd\u3b08\u3b0c\u3b11\u3b15\u3b1b\u3b21c\u0100uy\u3af6\u3afbte\u803b\xfd\u40fd;\u444f\u0100iy\u3b02\u3b06rc;\u4177;\u444bn\u803b\xa5\u40a5r;\uc000\ud835\udd36cy;\u4457pf;\uc000\ud835\udd6acr;\uc000\ud835\udcce\u0100cm\u3b26\u3b29y;\u444el\u803b\xff\u40ff\u0500acdefhiosw\u3b42\u3b48\u3b54\u3b58\u3b64\u3b69\u3b6d\u3b74\u3b7a\u3b80cute;\u417a\u0100ay\u3b4d\u3b52ron;\u417e;\u4437ot;\u417c\u0100et\u3b5d\u3b61tr\xe6\u155fa;\u43b6r;\uc000\ud835\udd37cy;\u4436grarr;\u61ddpf;\uc000\ud835\udd6bcr;\uc000\ud835\udccf\u0100jn\u3b85\u3b87;\u600dj;\u600c" .split("") .map((c) => c.charCodeAt(0)), ); // Generated using scripts/write-decode-map.ts const xmlDecodeTree = new Uint16Array( // prettier-ignore "\u0200aglq\t\x15\x18\x1b\u026d\x0f\0\0\x12p;\u4026os;\u4027t;\u403et;\u403cuot;\u4022" .split("") .map((c) => c.charCodeAt(0)), ); // Adapted from https://github.com/mathiasbynens/he/blob/36afe179392226cf1b6ccdb16ebbb7a5a844d93a/src/he.js#L106-L134 var _a; const decodeMap = new Map([ [0, 65533], // C1 Unicode control character reference replacements [128, 8364], [130, 8218], [131, 402], [132, 8222], [133, 8230], [134, 8224], [135, 8225], [136, 710], [137, 8240], [138, 352], [139, 8249], [140, 338], [142, 381], [145, 8216], [146, 8217], [147, 8220], [148, 8221], [149, 8226], [150, 8211], [151, 8212], [152, 732], [153, 8482], [154, 353], [155, 8250], [156, 339], [158, 382], [159, 376], ]); /** * Polyfill for `String.fromCodePoint`. It is used to create a string from a Unicode code point. */ const fromCodePoint$1 = // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition, node/no-unsupported-features/es-builtins (_a = String.fromCodePoint) !== null && _a !== void 0 ? _a : function (codePoint) { let output = ""; if (codePoint > 0xffff) { codePoint -= 0x10000; output += String.fromCharCode(((codePoint >>> 10) & 0x3ff) | 0xd800); codePoint = 0xdc00 | (codePoint & 0x3ff); } output += String.fromCharCode(codePoint); return output; }; /** * Replace the given code point with a replacement character if it is a * surrogate or is outside the valid range. Otherwise return the code * point unchanged. */ function replaceCodePoint(codePoint) { var _a; if ((codePoint >= 0xd800 && codePoint <= 0xdfff) || codePoint > 0x10ffff) { return 0xfffd; } return (_a = decodeMap.get(codePoint)) !== null && _a !== void 0 ? _a : codePoint; } var CharCodes; (function (CharCodes) { CharCodes[(CharCodes["NUM"] = 35)] = "NUM"; CharCodes[(CharCodes["SEMI"] = 59)] = "SEMI"; CharCodes[(CharCodes["EQUALS"] = 61)] = "EQUALS"; CharCodes[(CharCodes["ZERO"] = 48)] = "ZERO"; CharCodes[(CharCodes["NINE"] = 57)] = "NINE"; CharCodes[(CharCodes["LOWER_A"] = 97)] = "LOWER_A"; CharCodes[(CharCodes["LOWER_F"] = 102)] = "LOWER_F"; CharCodes[(CharCodes["LOWER_X"] = 120)] = "LOWER_X"; CharCodes[(CharCodes["LOWER_Z"] = 122)] = "LOWER_Z"; CharCodes[(CharCodes["UPPER_A"] = 65)] = "UPPER_A"; CharCodes[(CharCodes["UPPER_F"] = 70)] = "UPPER_F"; CharCodes[(CharCodes["UPPER_Z"] = 90)] = "UPPER_Z"; })(CharCodes || (CharCodes = {})); /** Bit that needs to be set to convert an upper case ASCII character to lower case */ const TO_LOWER_BIT = 0b100000; var BinTrieFlags; (function (BinTrieFlags) { BinTrieFlags[(BinTrieFlags["VALUE_LENGTH"] = 49152)] = "VALUE_LENGTH"; BinTrieFlags[(BinTrieFlags["BRANCH_LENGTH"] = 16256)] = "BRANCH_LENGTH"; BinTrieFlags[(BinTrieFlags["JUMP_TABLE"] = 127)] = "JUMP_TABLE"; })(BinTrieFlags || (BinTrieFlags = {})); function isNumber(code) { return code >= CharCodes.ZERO && code <= CharCodes.NINE; } function isHexadecimalCharacter(code) { return (code >= CharCodes.UPPER_A && code <= CharCodes.UPPER_F) || (code >= CharCodes.LOWER_A && code <= CharCodes.LOWER_F); } function isAsciiAlphaNumeric(code) { return (code >= CharCodes.UPPER_A && code <= CharCodes.UPPER_Z) || (code >= CharCodes.LOWER_A && code <= CharCodes.LOWER_Z) || isNumber(code); } /** * Checks if the given character is a valid end character for an entity in an attribute. * * Attribute values that aren't terminated properly aren't parsed, and shouldn't lead to a parser error. * See the example in https://html.spec.whatwg.org/multipage/parsing.html#named-character-reference-state */ function isEntityInAttributeInvalidEnd(code) { return code === CharCodes.EQUALS || isAsciiAlphaNumeric(code); } var EntityDecoderState; (function (EntityDecoderState) { EntityDecoderState[(EntityDecoderState["EntityStart"] = 0)] = "EntityStart"; EntityDecoderState[(EntityDecoderState["NumericStart"] = 1)] = "NumericStart"; EntityDecoderState[(EntityDecoderState["NumericDecimal"] = 2)] = "NumericDecimal"; EntityDecoderState[(EntityDecoderState["NumericHex"] = 3)] = "NumericHex"; EntityDecoderState[(EntityDecoderState["NamedEntity"] = 4)] = "NamedEntity"; })(EntityDecoderState || (EntityDecoderState = {})); var DecodingMode; (function (DecodingMode) { /** Entities in text nodes that can end with any character. */ DecodingMode[(DecodingMode["Legacy"] = 0)] = "Legacy"; /** Only allow entities terminated with a semicolon. */ DecodingMode[(DecodingMode["Strict"] = 1)] = "Strict"; /** Entities in attributes have limitations on ending characters. */ DecodingMode[(DecodingMode["Attribute"] = 2)] = "Attribute"; })(DecodingMode || (DecodingMode = {})); /** * Token decoder with support of writing partial entities. */ class EntityDecoder { constructor( /** The tree used to decode entities. */ decodeTree, /** * The function that is called when a codepoint is decoded. * * For multi-byte named entities, this will be called multiple times, * with the second codepoint, and the same `consumed` value. * * @param codepoint The decoded codepoint. * @param consumed The number of bytes consumed by the decoder. */ emitCodePoint, /** An object that is used to produce errors. */ errors, ) { this.decodeTree = decodeTree; this.emitCodePoint = emitCodePoint; this.errors = errors; /** The current state of the decoder. */ this.state = EntityDecoderState.EntityStart; /** Characters that were consumed while parsing an entity. */ this.consumed = 1; /** * The result of the entity. * * Either the result index of a numeric entity, or the codepoint of a * numeric entity. */ this.result = 0; /** The current index in the decode tree. */ this.treeIndex = 0; /** The number of characters that were consumed in excess. */ this.excess = 1; /** The mode in which the decoder is operating. */ this.decodeMode = DecodingMode.Strict; } /** Resets the instance to make it reusable. */ startEntity(decodeMode) { this.decodeMode = decodeMode; this.state = EntityDecoderState.EntityStart; this.result = 0; this.treeIndex = 0; this.excess = 1; this.consumed = 1; } /** * Write an entity to the decoder. This can be called multiple times with partial entities. * If the entity is incomplete, the decoder will return -1. * * Mirrors the implementation of `getDecoder`, but with the ability to stop decoding if the * entity is incomplete, and resume when the next string is written. * * @param string The string containing the entity (or a continuation of the entity). * @param offset The offset at which the entity begins. Should be 0 if this is not the first call. * @returns The number of characters that were consumed, or -1 if the entity is incomplete. */ write(str, offset) { switch (this.state) { case EntityDecoderState.EntityStart: { if (str.charCodeAt(offset) === CharCodes.NUM) { this.state = EntityDecoderState.NumericStart; this.consumed += 1; return this.stateNumericStart(str, offset + 1); } this.state = EntityDecoderState.NamedEntity; return this.stateNamedEntity(str, offset); } case EntityDecoderState.NumericStart: { return this.stateNumericStart(str, offset); } case EntityDecoderState.NumericDecimal: { return this.stateNumericDecimal(str, offset); } case EntityDecoderState.NumericHex: { return this.stateNumericHex(str, offset); } case EntityDecoderState.NamedEntity: { return this.stateNamedEntity(str, offset); } } } /** * Switches between the numeric decimal and hexadecimal states. * * Equivalent to the `Numeric character reference state` in the HTML spec. * * @param str The string containing the entity (or a continuation of the entity). * @param offset The current offset. * @returns The number of characters that were consumed, or -1 if the entity is incomplete. */ stateNumericStart(str, offset) { if (offset >= str.length) { return -1; } if ((str.charCodeAt(offset) | TO_LOWER_BIT) === CharCodes.LOWER_X) { this.state = EntityDecoderState.NumericHex; this.consumed += 1; return this.stateNumericHex(str, offset + 1); } this.state = EntityDecoderState.NumericDecimal; return this.stateNumericDecimal(str, offset); } addToNumericResult(str, start, end, base) { if (start !== end) { const digitCount = end - start; this.result = this.result * Math.pow(base, digitCount) + parseInt(str.substr(start, digitCount), base); this.consumed += digitCount; } } /** * Parses a hexadecimal numeric entity. * * Equivalent to the `Hexademical character reference state` in the HTML spec. * * @param str The string containing the entity (or a continuation of the entity). * @param offset The current offset. * @returns The number of characters that were consumed, or -1 if the entity is incomplete. */ stateNumericHex(str, offset) { const startIdx = offset; while (offset < str.length) { const char = str.charCodeAt(offset); if (isNumber(char) || isHexadecimalCharacter(char)) { offset += 1; } else { this.addToNumericResult(str, startIdx, offset, 16); return this.emitNumericEntity(char, 3); } } this.addToNumericResult(str, startIdx, offset, 16); return -1; } /** * Parses a decimal numeric entity. * * Equivalent to the `Decimal character reference state` in the HTML spec. * * @param str The string containing the entity (or a continuation of the entity). * @param offset The current offset. * @returns The number of characters that were consumed, or -1 if the entity is incomplete. */ stateNumericDecimal(str, offset) { const startIdx = offset; while (offset < str.length) { const char = str.charCodeAt(offset); if (isNumber(char)) { offset += 1; } else { this.addToNumericResult(str, startIdx, offset, 10); return this.emitNumericEntity(char, 2); } } this.addToNumericResult(str, startIdx, offset, 10); return -1; } /** * Validate and emit a numeric entity. * * Implements the logic from the `Hexademical character reference start * state` and `Numeric character reference end state` in the HTML spec. * * @param lastCp The last code point of the entity. Used to see if the * entity was terminated with a semicolon. * @param expectedLength The minimum number of characters that should be * consumed. Used to validate that at least one digit * was consumed. * @returns The number of characters that were consumed. */ emitNumericEntity(lastCp, expectedLength) { var _a; // Ensure we consumed at least one digit. if (this.consumed <= expectedLength) { (_a = this.errors) === null || _a === void 0 ? void 0 : _a.absenceOfDigitsInNumericCharacterReference(this.consumed); return 0; } // Figure out if this is a legit end of the entity if (lastCp === CharCodes.SEMI) { this.consumed += 1; } else if (this.decodeMode === DecodingMode.Strict) { return 0; } this.emitCodePoint(replaceCodePoint(this.result), this.consumed); if (this.errors) { if (lastCp !== CharCodes.SEMI) { this.errors.missingSemicolonAfterCharacterReference(); } this.errors.validateNumericCharacterReference(this.result); } return this.consumed; } /** * Parses a named entity. * * Equivalent to the `Named character reference state` in the HTML spec. * * @param str The string containing the entity (or a continuation of the entity). * @param offset The current offset. * @returns The number of characters that were consumed, or -1 if the entity is incomplete. */ stateNamedEntity(str, offset) { const { decodeTree } = this; let current = decodeTree[this.treeIndex]; // The mask is the number of bytes of the value, including the current byte. let valueLength = (current & BinTrieFlags.VALUE_LENGTH) >> 14; for (; offset < str.length; offset++, this.excess++) { const char = str.charCodeAt(offset); this.treeIndex = determineBranch(decodeTree, current, this.treeIndex + Math.max(1, valueLength), char); if (this.treeIndex < 0) { return ( this.result === 0 || // If we are parsing an attribute (this.decodeMode === DecodingMode.Attribute && // We shouldn't have consumed any characters after the entity, (valueLength === 0 || // And there should be no invalid characters. isEntityInAttributeInvalidEnd(char))) ) ? 0 : this.emitNotTerminatedNamedEntity(); } current = decodeTree[this.treeIndex]; valueLength = (current & BinTrieFlags.VALUE_LENGTH) >> 14; // If the branch is a value, store it and continue if (valueLength !== 0) { // If the entity is terminated by a semicolon, we are done. if (char === CharCodes.SEMI) { return this.emitNamedEntityData(this.treeIndex, valueLength, this.consumed + this.excess); } // If we encounter a non-terminated (legacy) entity while parsing strictly, then ignore it. if (this.decodeMode !== DecodingMode.Strict) { this.result = this.treeIndex; this.consumed += this.excess; this.excess = 0; } } } return -1; } /** * Emit a named entity that was not terminated with a semicolon. * * @returns The number of characters consumed. */ emitNotTerminatedNamedEntity() { var _a; const { result, decodeTree } = this; const valueLength = (decodeTree[result] & BinTrieFlags.VALUE_LENGTH) >> 14; this.emitNamedEntityData(result, valueLength, this.consumed); (_a = this.errors) === null || _a === void 0 ? void 0 : _a.missingSemicolonAfterCharacterReference(); return this.consumed; } /** * Emit a named entity. * * @param result The index of the entity in the decode tree. * @param valueLength The number of bytes in the entity. * @param consumed The number of characters consumed. * * @returns The number of characters consumed. */ emitNamedEntityData(result, valueLength, consumed) { const { decodeTree } = this; this.emitCodePoint(valueLength === 1 ? decodeTree[result] & ~BinTrieFlags.VALUE_LENGTH : decodeTree[result + 1], consumed); if (valueLength === 3) { // For multi-byte values, we need to emit the second byte. this.emitCodePoint(decodeTree[result + 2], consumed); } return consumed; } /** * Signal to the parser that the end of the input was reached. * * Remaining data will be emitted and relevant errors will be produced. * * @returns The number of characters consumed. */ end() { var _a; switch (this.state) { case EntityDecoderState.NamedEntity: { // Emit a named entity if we have one. return this.result !== 0 && (this.decodeMode !== DecodingMode.Attribute || this.result === this.treeIndex) ? this.emitNotTerminatedNamedEntity() : 0; } // Otherwise, emit a numeric entity if we have one. case EntityDecoderState.NumericDecimal: { return this.emitNumericEntity(0, 2); } case EntityDecoderState.NumericHex: { return this.emitNumericEntity(0, 3); } case EntityDecoderState.NumericStart: { (_a = this.errors) === null || _a === void 0 ? void 0 : _a.absenceOfDigitsInNumericCharacterReference(this.consumed); return 0; } case EntityDecoderState.EntityStart: { // Return 0 if we have no entity. return 0; } } } } /** * Creates a function that decodes entities in a string. * * @param decodeTree The decode tree. * @returns A function that decodes entities in a string. */ function getDecoder(decodeTree) { let ret = ""; const decoder = new EntityDecoder(decodeTree, (str) => (ret += fromCodePoint$1(str))); return function decodeWithTrie(str, decodeMode) { let lastIndex = 0; let offset = 0; while ((offset = str.indexOf("&", offset)) >= 0) { ret += str.slice(lastIndex, offset); decoder.startEntity(decodeMode); const len = decoder.write( str, // Skip the "&" offset + 1, ); if (len < 0) { lastIndex = offset + decoder.end(); break; } lastIndex = offset + len; // If `len` is 0, skip the current `&` and continue. offset = len === 0 ? lastIndex + 1 : lastIndex; } const result = ret + str.slice(lastIndex); // Make sure we don't keep a reference to the final string. ret = ""; return result; }; } /** * Determines the branch of the current node that is taken given the current * character. This function is used to traverse the trie. * * @param decodeTree The trie. * @param current The current node. * @param nodeIdx The index right after the current node and its value. * @param char The current character. * @returns The index of the next node, or -1 if no branch is taken. */ function determineBranch(decodeTree, current, nodeIdx, char) { const branchCount = (current & BinTrieFlags.BRANCH_LENGTH) >> 7; const jumpOffset = current & BinTrieFlags.JUMP_TABLE; // Case 1: Single branch encoded in jump offset if (branchCount === 0) { return jumpOffset !== 0 && char === jumpOffset ? nodeIdx : -1; } // Case 2: Multiple branches encoded in jump table if (jumpOffset) { const value = char - jumpOffset; return value < 0 || value >= branchCount ? -1 : decodeTree[nodeIdx + value] - 1; } // Case 3: Multiple branches encoded in dictionary // Binary search for the character. let lo = nodeIdx; let hi = lo + branchCount - 1; while (lo <= hi) { const mid = (lo + hi) >>> 1; const midVal = decodeTree[mid]; if (midVal < char) { lo = mid + 1; } else if (midVal > char) { hi = mid - 1; } else { return decodeTree[mid + branchCount]; } } return -1; } const htmlDecoder = getDecoder(htmlDecodeTree); getDecoder(xmlDecodeTree); /** * Decodes an HTML string. * * @param str The string to decode. * @param mode The decoding mode. * @returns The decoded string. */ function decodeHTML(str, mode = DecodingMode.Legacy) { return htmlDecoder(str, mode); } /** * Decodes an HTML string, requiring all entities to be terminated by a semicolon. * * @param str The string to decode. * @returns The decoded string. */ function decodeHTMLStrict(str) { return htmlDecoder(str, DecodingMode.Strict); } // Utilities // function _class$1(obj) { return Object.prototype.toString.call(obj); } function isString$1(obj) { return _class$1(obj) === "[object String]"; } const _hasOwnProperty = Object.prototype.hasOwnProperty; function has(object, key) { return _hasOwnProperty.call(object, key); } // Merge objects // function assign$1(obj /* from1, from2, from3, ... */) { const sources = Array.prototype.slice.call(arguments, 1); sources.forEach(function (source) { if (!source) { return; } if (typeof source !== "object") { throw new TypeError(source + "must be object"); } Object.keys(source).forEach(function (key) { obj[key] = source[key]; }); }); return obj; } // Remove element from array and put another array at those position. // Useful for some operations with tokens function arrayReplaceAt(src, pos, newElements) { return [].concat(src.slice(0, pos), newElements, src.slice(pos + 1)); } function isValidEntityCode(c) { // broken sequence if (c >= 0xd800 && c <= 0xdfff) { return false; } // never used if (c >= 0xfdd0 && c <= 0xfdef) { return false; } if ((c & 0xffff) === 0xffff || (c & 0xffff) === 0xfffe) { return false; } // control codes if (c >= 0x00 && c <= 0x08) { return false; } if (c === 0x0b) { return false; } if (c >= 0x0e && c <= 0x1f) { return false; } if (c >= 0x7f && c <= 0x9f) { return false; } // out of range if (c > 0x10ffff) { return false; } return true; } function fromCodePoint(c) { /* eslint no-bitwise:0 */ if (c > 0xffff) { c -= 0x10000; const surrogate1 = 0xd800 + (c >> 10); const surrogate2 = 0xdc00 + (c & 0x3ff); return String.fromCharCode(surrogate1, surrogate2); } return String.fromCharCode(c); } const UNESCAPE_MD_RE = /\\([!"#$%&'()*+,\-./:;<=>?@[\\\]^_`{|}~])/g; const ENTITY_RE = /&([a-z#][a-z0-9]{1,31});/gi; const UNESCAPE_ALL_RE = new RegExp(UNESCAPE_MD_RE.source + "|" + ENTITY_RE.source, "gi"); const DIGITAL_ENTITY_TEST_RE = /^#((?:x[a-f0-9]{1,8}|[0-9]{1,8}))$/i; function replaceEntityPattern(match, name) { if (name.charCodeAt(0) === 0x23 /* # */ && DIGITAL_ENTITY_TEST_RE.test(name)) { const code = name[1].toLowerCase() === "x" ? parseInt(name.slice(2), 16) : parseInt(name.slice(1), 10); if (isValidEntityCode(code)) { return fromCodePoint(code); } return match; } const decoded = decodeHTML(match); if (decoded !== match) { return decoded; } return match; } function unescapeMd(str) { if (str.indexOf("\\") < 0) { return str; } return str.replace(UNESCAPE_MD_RE, "$1"); } function unescapeAll(str) { if (str.indexOf("\\") < 0 && str.indexOf("&") < 0) { return str; } return str.replace(UNESCAPE_ALL_RE, function (match, escaped, entity) { if (escaped) { return escaped; } return replaceEntityPattern(match, entity); }); } const HTML_ESCAPE_TEST_RE = /[&<>"]/; const HTML_ESCAPE_REPLACE_RE = /[&<>"]/g; const HTML_REPLACEMENTS = { "&": "&", "<": "<", ">": ">", '"': """, }; function replaceUnsafeChar(ch) { return HTML_REPLACEMENTS[ch]; } function escapeHtml(str) { if (HTML_ESCAPE_TEST_RE.test(str)) { return str.replace(HTML_ESCAPE_REPLACE_RE, replaceUnsafeChar); } return str; } const REGEXP_ESCAPE_RE = /[.?*+^$[\]\\(){}|-]/g; function escapeRE$1(str) { return str.replace(REGEXP_ESCAPE_RE, "\\$&"); } function isSpace(code) { switch (code) { case 0x09: case 0x20: return true; } return false; } // Zs (unicode class) || [\t\f\v\r\n] function isWhiteSpace(code) { if (code >= 0x2000 && code <= 0x200a) { return true; } switch (code) { case 0x09: // \t case 0x0a: // \n case 0x0b: // \v case 0x0c: // \f case 0x0d: // \r case 0x20: case 0xa0: case 0x1680: case 0x202f: case 0x205f: case 0x3000: return true; } return false; } // Currently without astral characters support. function isPunctChar(ch) { return P.test(ch) || regex.test(ch); } function isPunctCharCode(code) { return isPunctChar(fromCodePoint(code)); } // Markdown ASCII punctuation characters. // // !, ", #, $, %, &, ', (, ), *, +, ,, -, ., /, :, ;, <, =, >, ?, @, [, \, ], ^, _, `, {, |, }, or ~ // http://spec.commonmark.org/0.15/#ascii-punctuation-character // // Don't confuse with unicode punctuation !!! It lacks some chars in ascii range. // function isMdAsciiPunct(ch) { switch (ch) { case 0x21 /* ! */: case 0x22 /* " */: case 0x23 /* # */: case 0x24 /* $ */: case 0x25 /* % */: case 0x26 /* & */: case 0x27 /* ' */: case 0x28 /* ( */: case 0x29 /* ) */: case 0x2a /* * */: case 0x2b /* + */: case 0x2c /* , */: case 0x2d /* - */: case 0x2e /* . */: case 0x2f /* / */: case 0x3a /* : */: case 0x3b /* ; */: case 0x3c /* < */: case 0x3d /* = */: case 0x3e /* > */: case 0x3f /* ? */: case 0x40 /* @ */: case 0x5b /* [ */: case 0x5c /* \ */: case 0x5d /* ] */: case 0x5e /* ^ */: case 0x5f /* _ */: case 0x60 /* ` */: case 0x7b /* { */: case 0x7c /* | */: case 0x7d /* } */: case 0x7e /* ~ */: return true; default: return false; } } // Hepler to unify [reference labels]. // function normalizeReference(str) { // Trim and collapse whitespace // str = str.trim().replace(/\s+/g, " "); // In node v10 'ẞ'.toLowerCase() === 'Ṿ', which is presumed to be a bug // fixed in v12 (couldn't find any details). // // So treat this one as a special case // (remove this when node v10 is no longer supported). // if ("ẞ".toLowerCase() === "Ṿ") { /* c8 ignore next 2 */ str = str.replace(/ẞ/g, "ß"); } // .toLowerCase().toUpperCase() should get rid of all differences // between letter variants. // // Simple .toLowerCase() doesn't normalize 125 code points correctly, // and .toUpperCase doesn't normalize 6 of them (list of exceptions: // İ, ϴ, ẞ, Ω, K, Å - those are already uppercased, but have differently // uppercased versions). // // Here's an example showing how it happens. Lets take greek letter omega: // uppercase U+0398 (Θ), U+03f4 (ϴ) and lowercase U+03b8 (θ), U+03d1 (ϑ) // // Unicode entries: // 0398;GREEK CAPITAL LETTER THETA;Lu;0;L;;;;;N;;;;03B8; // 03B8;GREEK SMALL LETTER THETA;Ll;0;L;;;;;N;;;0398;;0398 // 03D1;GREEK THETA SYMBOL;Ll;0;L; 03B8;;;;N;GREEK SMALL LETTER SCRIPT THETA;;0398;;0398 // 03F4;GREEK CAPITAL THETA SYMBOL;Lu;0;L; 0398;;;;N;;;;03B8; // // Case-insensitive comparison should treat all of them as equivalent. // // But .toLowerCase() doesn't change ϑ (it's already lowercase), // and .toUpperCase() doesn't change ϴ (already uppercase). // // Applying first lower then upper case normalizes any character: // '\u0398\u03f4\u03b8\u03d1'.toLowerCase().toUpperCase() === '\u0398\u0398\u0398\u0398' // // Note: this is equivalent to unicode case folding; unicode normalization // is a different step that is not required here. // // Final result should be uppercased, because it's later stored in an object // (this avoid a conflict with Object.prototype members, // most notably, `__proto__`) // return str.toLowerCase().toUpperCase(); } function isAsciiTrimmable(c) { return c === 0x20 || c === 0x09 || c === 0x0a || c === 0x0d; } // "Light" .trim() for blocks (headers, paragraphs), where unicode spaces // should be preserved. function asciiTrim(str) { let start = 0; for (; start < str.length; start++) { if (!isAsciiTrimmable(str.charCodeAt(start))) { break; } } let end = str.length - 1; for (; end >= start; end--) { if (!isAsciiTrimmable(str.charCodeAt(end))) { break; } } return str.slice(start, end + 1); } // Re-export libraries commonly used in both markdown-it and its plugins, // so plugins won't have to depend on them explicitly, which reduces their // bundled size (e.g. a browser build). // const lib = { mdurl, ucmicro }; const utils = /*#__PURE__*/ Object.freeze( /*#__PURE__*/ Object.defineProperty( { __proto__: null, arrayReplaceAt, asciiTrim, assign: assign$1, escapeHtml, escapeRE: escapeRE$1, fromCodePoint, has, isMdAsciiPunct, isPunctChar, isPunctCharCode, isSpace, isString: isString$1, isValidEntityCode, isWhiteSpace, lib, normalizeReference, unescapeAll, unescapeMd, }, Symbol.toStringTag, { value: "Module" }, ), ); // Parse link label // // this function assumes that first character ("[") already matches; // returns the end of the label // function parseLinkLabel(state, start, disableNested) { let level, found, marker, prevPos; const max = state.posMax; const oldPos = state.pos; state.pos = start + 1; level = 1; while (state.pos < max) { marker = state.src.charCodeAt(state.pos); if (marker === 0x5d /* ] */) { level--; if (level === 0) { found = true; break; } } prevPos = state.pos; state.md.inline.skipToken(state); if (marker === 0x5b /* [ */) { if (prevPos === state.pos - 1) { // increase level if we find text `[`, which is not a part of any token level++; } else if (disableNested) { state.pos = oldPos; return -1; } } } let labelEnd = -1; if (found) { labelEnd = state.pos; } // restore old state state.pos = oldPos; return labelEnd; } // Parse link destination // function parseLinkDestination(str, start, max) { let code; let pos = start; const result = { ok: false, pos: 0, str: "", }; if (str.charCodeAt(pos) === 0x3c /* < */) { pos++; while (pos < max) { code = str.charCodeAt(pos); if (code === 0x0a /* \n */) { return result; } if (code === 0x3c /* < */) { return result; } if (code === 0x3e /* > */) { result.pos = pos + 1; result.str = unescapeAll(str.slice(start + 1, pos)); result.ok = true; return result; } if (code === 0x5c /* \ */ && pos + 1 < max) { pos += 2; continue; } pos++; } // no closing '>' return result; } // this should be ... } else { ... branch let level = 0; while (pos < max) { code = str.charCodeAt(pos); if (code === 0x20) { break; } // ascii control characters if (code < 0x20 || code === 0x7f) { break; } if (code === 0x5c /* \ */ && pos + 1 < max) { if (str.charCodeAt(pos + 1) === 0x20) { break; } pos += 2; continue; } if (code === 0x28 /* ( */) { level++; if (level > 32) { return result; } } if (code === 0x29 /* ) */) { if (level === 0) { break; } level--; } pos++; } if (start === pos) { return result; } if (level !== 0) { return result; } result.str = unescapeAll(str.slice(start, pos)); result.pos = pos; result.ok = true; return result; } // Parse link title // // Parse link title within `str` in [start, max] range, // or continue previous parsing if `prev_state` is defined (equal to result of last execution). // function parseLinkTitle(str, start, max, prev_state) { let code; let pos = start; const state = { // if `true`, this is a valid link title ok: false, // if `true`, this link can be continued on the next line can_continue: false, // if `ok`, it's the position of the first character after the closing marker pos: 0, // if `ok`, it's the unescaped title str: "", // expected closing marker character code marker: 0, }; if (prev_state) { // this is a continuation of a previous parseLinkTitle call on the next line, // used in reference links only state.str = prev_state.str; state.marker = prev_state.marker; } else { if (pos >= max) { return state; } let marker = str.charCodeAt(pos); if (marker !== 0x22 /* " */ && marker !== 0x27 /* ' */ && marker !== 0x28 /* ( */) { return state; } start++; pos++; // if opening marker is "(", switch it to closing marker ")" if (marker === 0x28) { marker = 0x29; } state.marker = marker; } while (pos < max) { code = str.charCodeAt(pos); if (code === state.marker) { state.pos = pos + 1; state.str += unescapeAll(str.slice(start, pos)); state.ok = true; return state; } else if (code === 0x28 /* ( */ && state.marker === 0x29 /* ) */) { return state; } else if (code === 0x5c /* \ */ && pos + 1 < max) { pos++; } pos++; } // no closing marker found, but this link title may continue on the next line (for references) state.can_continue = true; state.str += unescapeAll(str.slice(start, pos)); return state; } // Just a shortcut for bulk export const helpers = /*#__PURE__*/ Object.freeze( /*#__PURE__*/ Object.defineProperty( { __proto__: null, parseLinkDestination, parseLinkLabel, parseLinkTitle, }, Symbol.toStringTag, { value: "Module" }, ), ); /** * class Renderer * * Generates HTML from parsed token stream. Each instance has independent * copy of rules. Those can be rewritten with ease. Also, you can add new * rules if you create plugin and adds new token types. **/ const default_rules = {}; default_rules.code_inline = function (tokens, idx, options, env, slf) { const token = tokens[idx]; return "" + escapeHtml(token.content) + ""; }; default_rules.code_block = function (tokens, idx, options, env, slf) { const token = tokens[idx]; return "" + escapeHtml(tokens[idx].content) + "\n"; }; default_rules.fence = function (tokens, idx, options, env, slf) { const token = tokens[idx]; const info = token.info ? unescapeAll(token.info).trim() : ""; let langName = ""; let langAttrs = ""; if (info) { const arr = info.split(/(\s+)/g); langName = arr[0]; langAttrs = arr.slice(2).join(""); } let highlighted; if (options.highlight) { highlighted = options.highlight(token.content, langName, langAttrs) || escapeHtml(token.content); } else { highlighted = escapeHtml(token.content); } if (highlighted.indexOf("${highlighted}\n`; } return `
${highlighted}
\n`; }; default_rules.image = function (tokens, idx, options, env, slf) { const token = tokens[idx]; // "alt" attr MUST be set, even if empty. Because it's mandatory and // should be placed on proper position for tests. // // Replace content with actual value token.attrs[token.attrIndex("alt")][1] = slf.renderInlineAsText(token.children, options, env); return slf.renderToken(tokens, idx, options); }; default_rules.hardbreak = function (tokens, idx, options /*, env */) { return options.xhtmlOut ? "
\n" : "
\n"; }; default_rules.softbreak = function (tokens, idx, options /*, env */) { return ( options.breaks ? options.xhtmlOut ? "
\n" : "
\n" : "\n" ); }; default_rules.text = function (tokens, idx /*, options, env */) { return escapeHtml(tokens[idx].content); }; default_rules.html_block = function (tokens, idx /*, options, env */) { return tokens[idx].content; }; default_rules.html_inline = function (tokens, idx /*, options, env */) { return tokens[idx].content; }; /** * new Renderer() * * Creates new [[Renderer]] instance and fill [[Renderer#rules]] with defaults. **/ function Renderer() { /** * Renderer#rules -> Object * * Contains render rules for tokens. Can be updated and extended. * * ##### Example * * ```javascript * var md = require('markdown-it')(); * * md.renderer.rules.strong_open = function () { return ''; }; * md.renderer.rules.strong_close = function () { return ''; }; * * var result = md.renderInline(...); * ``` * * Each rule is called as independent static function with fixed signature: * * ```javascript * function my_token_render(tokens, idx, options, env, renderer) { * // ... * return renderedHTML; * } * ``` * * See [source code](https://github.com/markdown-it/markdown-it/blob/master/lib/renderer.mjs) * for more details and examples. **/ this.rules = assign$1({}, default_rules); } /** * Renderer.renderAttrs(token) -> String * * Render token attributes to string. **/ Renderer.prototype.renderAttrs = function renderAttrs(token) { let i, l, result; if (!token.attrs) { return ""; } result = ""; for (i = 0, l = token.attrs.length; i < l; i++) { result += " " + escapeHtml(token.attrs[i][0]) + '="' + escapeHtml(token.attrs[i][1]) + '"'; } return result; }; /** * Renderer.renderToken(tokens, idx, options) -> String * - tokens (Array): list of tokens * - idx (Numbed): token index to render * - options (Object): params of parser instance * * Default token renderer. Can be overriden by custom function * in [[Renderer#rules]]. **/ Renderer.prototype.renderToken = function renderToken(tokens, idx, options) { const token = tokens[idx]; let result = ""; // Tight list paragraphs if (token.hidden) { return ""; } // Insert a newline between hidden paragraph and subsequent opening // block-level tag. // // For example, here we should insert a newline before blockquote: // - a // > // if (token.block && token.nesting !== -1 && idx && tokens[idx - 1].hidden) { result += "\n"; } // Add token name, e.g. ``. // needLf = false; } } } } result += needLf ? ">\n" : ">"; return result; }; /** * Renderer.renderInline(tokens, options, env) -> String * - tokens (Array): list on block tokens to render * - options (Object): params of parser instance * - env (Object): additional data from parsed input (references, for example) * * The same as [[Renderer.render]], but for single token of `inline` type. **/ Renderer.prototype.renderInline = function (tokens, options, env) { let result = ""; const rules = this.rules; for (let i = 0, len = tokens.length; i < len; i++) { const type = tokens[i].type; if (typeof rules[type] !== "undefined") { result += rules[type](tokens, i, options, env, this); } else { result += this.renderToken(tokens, i, options); } } return result; }; /** internal * Renderer.renderInlineAsText(tokens, options, env) -> String * - tokens (Array): list on block tokens to render * - options (Object): params of parser instance * - env (Object): additional data from parsed input (references, for example) * * Special kludge for image `alt` attributes to conform CommonMark spec. * Don't try to use it! Spec requires to show `alt` content with stripped markup, * instead of simple escaping. **/ Renderer.prototype.renderInlineAsText = function (tokens, options, env) { let result = ""; for (let i = 0, len = tokens.length; i < len; i++) { switch (tokens[i].type) { case "text": result += tokens[i].content; break; case "image": result += this.renderInlineAsText(tokens[i].children, options, env); break; case "html_inline": case "html_block": result += tokens[i].content; break; case "softbreak": case "hardbreak": result += "\n"; break; // all other tokens are skipped } } return result; }; /** * Renderer.render(tokens, options, env) -> String * - tokens (Array): list on block tokens to render * - options (Object): params of parser instance * - env (Object): additional data from parsed input (references, for example) * * Takes token stream and generates HTML. Probably, you will never need to call * this method directly. **/ Renderer.prototype.render = function (tokens, options, env) { let result = ""; const rules = this.rules; for (let i = 0, len = tokens.length; i < len; i++) { const type = tokens[i].type; if (type === "inline") { result += this.renderInline(tokens[i].children, options, env); } else if (typeof rules[type] !== "undefined") { result += rules[type](tokens, i, options, env, this); } else { result += this.renderToken(tokens, i, options, env); } } return result; }; /** * class Ruler * * Helper class, used by [[MarkdownIt#core]], [[MarkdownIt#block]] and * [[MarkdownIt#inline]] to manage sequences of functions (rules): * * - keep rules in defined order * - assign the name to each rule * - enable/disable rules * - add/replace rules * - allow assign rules to additional named chains (in the same) * - cacheing lists of active rules * * You will not need use this class directly until write plugins. For simple * rules control use [[MarkdownIt.disable]], [[MarkdownIt.enable]] and * [[MarkdownIt.use]]. **/ /** * new Ruler() **/ function Ruler() { // List of added rules. Each element is: // // { // name: XXX, // enabled: Boolean, // fn: Function(), // alt: [ name2, name3 ] // } // this.__rules__ = []; // Cached rule chains. // // First level - chain name, '' for default. // Second level - diginal anchor for fast filtering by charcodes. // this.__cache__ = null; } // Helper methods, should not be used directly // Find rule index by name // Ruler.prototype.__find__ = function (name) { for (let i = 0; i < this.__rules__.length; i++) { if (this.__rules__[i].name === name) { return i; } } return -1; }; // Build rules lookup cache // Ruler.prototype.__compile__ = function () { const self = this; const chains = [""]; // collect unique names self.__rules__.forEach(function (rule) { if (!rule.enabled) { return; } rule.alt.forEach(function (altName) { if (chains.indexOf(altName) < 0) { chains.push(altName); } }); }); self.__cache__ = {}; chains.forEach(function (chain) { self.__cache__[chain] = []; self.__rules__.forEach(function (rule) { if (!rule.enabled) { return; } if (chain && rule.alt.indexOf(chain) < 0) { return; } self.__cache__[chain].push(rule.fn); }); }); }; /** * Ruler.at(name, fn [, options]) * - name (String): rule name to replace. * - fn (Function): new rule function. * - options (Object): new rule options (not mandatory). * * Replace rule by name with new function & options. Throws error if name not * found. * * ##### Options: * * - __alt__ - array with names of "alternate" chains. * * ##### Example * * Replace existing typographer replacement rule with new one: * * ```javascript * var md = require('markdown-it')(); * * md.core.ruler.at('replacements', function replace(state) { * //... * }); * ``` **/ Ruler.prototype.at = function (name, fn, options) { const index = this.__find__(name); const opt = options || {}; if (index === -1) { throw new Error("Parser rule not found: " + name); } this.__rules__[index].fn = fn; this.__rules__[index].alt = opt.alt || []; this.__cache__ = null; }; /** * Ruler.before(beforeName, ruleName, fn [, options]) * - beforeName (String): new rule will be added before this one. * - ruleName (String): name of added rule. * - fn (Function): rule function. * - options (Object): rule options (not mandatory). * * Add new rule to chain before one with given name. See also * [[Ruler.after]], [[Ruler.push]]. * * ##### Options: * * - __alt__ - array with names of "alternate" chains. * * ##### Example * * ```javascript * var md = require('markdown-it')(); * * md.block.ruler.before('paragraph', 'my_rule', function replace(state) { * //... * }); * ``` **/ Ruler.prototype.before = function (beforeName, ruleName, fn, options) { const index = this.__find__(beforeName); const opt = options || {}; if (index === -1) { throw new Error("Parser rule not found: " + beforeName); } this.__rules__.splice(index, 0, { name: ruleName, enabled: true, fn, alt: opt.alt || [], }); this.__cache__ = null; }; /** * Ruler.after(afterName, ruleName, fn [, options]) * - afterName (String): new rule will be added after this one. * - ruleName (String): name of added rule. * - fn (Function): rule function. * - options (Object): rule options (not mandatory). * * Add new rule to chain after one with given name. See also * [[Ruler.before]], [[Ruler.push]]. * * ##### Options: * * - __alt__ - array with names of "alternate" chains. * * ##### Example * * ```javascript * var md = require('markdown-it')(); * * md.inline.ruler.after('text', 'my_rule', function replace(state) { * //... * }); * ``` **/ Ruler.prototype.after = function (afterName, ruleName, fn, options) { const index = this.__find__(afterName); const opt = options || {}; if (index === -1) { throw new Error("Parser rule not found: " + afterName); } this.__rules__.splice(index + 1, 0, { name: ruleName, enabled: true, fn, alt: opt.alt || [], }); this.__cache__ = null; }; /** * Ruler.push(ruleName, fn [, options]) * - ruleName (String): name of added rule. * - fn (Function): rule function. * - options (Object): rule options (not mandatory). * * Push new rule to the end of chain. See also * [[Ruler.before]], [[Ruler.after]]. * * ##### Options: * * - __alt__ - array with names of "alternate" chains. * * ##### Example * * ```javascript * var md = require('markdown-it')(); * * md.core.ruler.push('my_rule', function replace(state) { * //... * }); * ``` **/ Ruler.prototype.push = function (ruleName, fn, options) { const opt = options || {}; this.__rules__.push({ name: ruleName, enabled: true, fn, alt: opt.alt || [], }); this.__cache__ = null; }; /** * Ruler.enable(list [, ignoreInvalid]) -> Array * - list (String|Array): list of rule names to enable. * - ignoreInvalid (Boolean): set `true` to ignore errors when rule not found. * * Enable rules with given names. If any rule name not found - throw Error. * Errors can be disabled by second param. * * Returns list of found rule names (if no exception happened). * * See also [[Ruler.disable]], [[Ruler.enableOnly]]. **/ Ruler.prototype.enable = function (list, ignoreInvalid) { if (!Array.isArray(list)) { list = [list]; } const result = []; // Search by name and enable list.forEach(function (name) { const idx = this.__find__(name); if (idx < 0) { if (ignoreInvalid) { return; } throw new Error("Rules manager: invalid rule name " + name); } this.__rules__[idx].enabled = true; result.push(name); }, this); this.__cache__ = null; return result; }; /** * Ruler.enableOnly(list [, ignoreInvalid]) * - list (String|Array): list of rule names to enable (whitelist). * - ignoreInvalid (Boolean): set `true` to ignore errors when rule not found. * * Enable rules with given names, and disable everything else. If any rule name * not found - throw Error. Errors can be disabled by second param. * * See also [[Ruler.disable]], [[Ruler.enable]]. **/ Ruler.prototype.enableOnly = function (list, ignoreInvalid) { if (!Array.isArray(list)) { list = [list]; } this.__rules__.forEach(function (rule) { rule.enabled = false; }); this.enable(list, ignoreInvalid); }; /** * Ruler.disable(list [, ignoreInvalid]) -> Array * - list (String|Array): list of rule names to disable. * - ignoreInvalid (Boolean): set `true` to ignore errors when rule not found. * * Disable rules with given names. If any rule name not found - throw Error. * Errors can be disabled by second param. * * Returns list of found rule names (if no exception happened). * * See also [[Ruler.enable]], [[Ruler.enableOnly]]. **/ Ruler.prototype.disable = function (list, ignoreInvalid) { if (!Array.isArray(list)) { list = [list]; } const result = []; // Search by name and disable list.forEach(function (name) { const idx = this.__find__(name); if (idx < 0) { if (ignoreInvalid) { return; } throw new Error("Rules manager: invalid rule name " + name); } this.__rules__[idx].enabled = false; result.push(name); }, this); this.__cache__ = null; return result; }; /** * Ruler.getRules(chainName) -> Array * * Return array of active functions (rules) for given chain name. It analyzes * rules configuration, compiles caches if not exists and returns result. * * Default chain name is `''` (empty string). It can't be skipped. That's * done intentionally, to keep signature monomorphic for high speed. **/ Ruler.prototype.getRules = function (chainName) { if (this.__cache__ === null) { this.__compile__(); } // Chain can be empty, if rules disabled. But we still have to return Array. return this.__cache__[chainName] || []; }; // Token class /** * class Token **/ /** * new Token(type, tag, nesting) * * Create new token and fill passed properties. **/ function Token(type, tag, nesting) { /** * Token#type -> String * * Type of the token (string, e.g. "paragraph_open") **/ this.type = type; /** * Token#tag -> String * * html tag name, e.g. "p" **/ this.tag = tag; /** * Token#attrs -> Array * * Html attributes. Format: `[ [ name1, value1 ], [ name2, value2 ] ]` **/ this.attrs = null; /** * Token#map -> Array * * Source map info. Format: `[ line_begin, line_end ]` **/ this.map = null; /** * Token#nesting -> Number * * Level change (number in {-1, 0, 1} set), where: * * - `1` means the tag is opening * - `0` means the tag is self-closing * - `-1` means the tag is closing **/ this.nesting = nesting; /** * Token#level -> Number * * nesting level, the same as `state.level` **/ this.level = 0; /** * Token#children -> Array * * An array of child nodes (inline and img tokens) **/ this.children = null; /** * Token#content -> String * * In a case of self-closing tag (code, html, fence, etc.), * it has contents of this tag. **/ this.content = ""; /** * Token#markup -> String * * '*' or '_' for emphasis, fence string for fence, etc. **/ this.markup = ""; /** * Token#info -> String * * Additional information: * * - Info string for "fence" tokens * - The value "auto" for autolink "link_open" and "link_close" tokens * - The string value of the item marker for ordered-list "list_item_open" tokens **/ this.info = ""; /** * Token#meta -> Object * * A place for plugins to store an arbitrary data **/ this.meta = null; /** * Token#block -> Boolean * * True for block-level tokens, false for inline tokens. * Used in renderer to calculate line breaks **/ this.block = false; /** * Token#hidden -> Boolean * * If it's true, ignore this element when rendering. Used for tight lists * to hide paragraphs. **/ this.hidden = false; } /** * Token.attrIndex(name) -> Number * * Search attribute index by name. **/ Token.prototype.attrIndex = function attrIndex(name) { if (!this.attrs) { return -1; } const attrs = this.attrs; for (let i = 0, len = attrs.length; i < len; i++) { if (attrs[i][0] === name) { return i; } } return -1; }; /** * Token.attrPush(attrData) * * Add `[ name, value ]` attribute to list. Init attrs if necessary **/ Token.prototype.attrPush = function attrPush(attrData) { if (this.attrs) { this.attrs.push(attrData); } else { this.attrs = [attrData]; } }; /** * Token.attrSet(name, value) * * Set `name` attribute to `value`. Override old value if exists. **/ Token.prototype.attrSet = function attrSet(name, value) { const idx = this.attrIndex(name); const attrData = [name, value]; if (idx < 0) { this.attrPush(attrData); } else { this.attrs[idx] = attrData; } }; /** * Token.attrGet(name) * * Get the value of attribute `name`, or null if it does not exist. **/ Token.prototype.attrGet = function attrGet(name) { const idx = this.attrIndex(name); let value = null; if (idx >= 0) { value = this.attrs[idx][1]; } return value; }; /** * Token.attrJoin(name, value) * * Join value to existing attribute via space. Or create new attribute if not * exists. Useful to operate with token classes. **/ Token.prototype.attrJoin = function attrJoin(name, value) { const idx = this.attrIndex(name); if (idx < 0) { this.attrPush([name, value]); } else { this.attrs[idx][1] = this.attrs[idx][1] + " " + value; } }; // Core state object // function StateCore(src, md, env) { this.src = src; this.env = env; this.tokens = []; this.inlineMode = false; this.md = md; // link to parser instance } // re-export Token class to use in core rules StateCore.prototype.Token = Token; // Normalize input string // https://spec.commonmark.org/0.29/#line-ending const NEWLINES_RE = /\r\n?|\n/g; const NULL_RE = /\0/g; function normalize(state) { let str; // Normalize newlines str = state.src.replace(NEWLINES_RE, "\n"); // Replace NULL characters str = str.replace(NULL_RE, "\uFFFD"); state.src = str; } function block(state) { let token; if (state.inlineMode) { token = new state.Token("inline", "", 0); token.content = state.src; token.map = [0, 1]; token.children = []; state.tokens.push(token); } else { state.md.block.parse(state.src, state.md, state.env, state.tokens); } } function inline(state) { const tokens = state.tokens; // Parse inlines for (let i = 0, l = tokens.length; i < l; i++) { const tok = tokens[i]; if (tok.type === "inline") { state.md.inline.parse(tok.content, state.md, state.env, tok.children); } } } // Replace link-like texts with link nodes. // // Currently restricted by `md.validateLink()` to http/https/ftp // function isLinkOpen$1(str) { return /^\s]/i.test(str); } function isLinkClose$1(str) { return /^<\/a\s*>/i.test(str); } function linkify$1(state) { const blockTokens = state.tokens; if (!state.md.options.linkify) { return; } for (let j = 0, l = blockTokens.length; j < l; j++) { if (blockTokens[j].type !== "inline" || !state.md.linkify.pretest(blockTokens[j].content)) { continue; } let tokens = blockTokens[j].children; let htmlLinkLevel = 0; // We scan from the end, to keep position when new tags added. // Use reversed logic in links start/end match for (let i = tokens.length - 1; i >= 0; i--) { const currentToken = tokens[i]; // Skip content of markdown links if (currentToken.type === "link_close") { i--; while (tokens[i].level !== currentToken.level && tokens[i].type !== "link_open") { i--; } continue; } // Skip content of html tag links if (currentToken.type === "html_inline") { if (isLinkOpen$1(currentToken.content) && htmlLinkLevel > 0) { htmlLinkLevel--; } if (isLinkClose$1(currentToken.content)) { htmlLinkLevel++; } } if (htmlLinkLevel > 0) { continue; } if (currentToken.type === "text" && state.md.linkify.test(currentToken.content)) { const text = currentToken.content; let links = state.md.linkify.match(text); // Now split string to nodes const nodes = []; let level = currentToken.level; let lastPos = 0; // forbid escape sequence at the start of the string, // this avoids http\://example.com/ from being linkified as // http://example.com/ if (links.length > 0 && links[0].index === 0 && i > 0 && tokens[i - 1].type === "text_special") { links = links.slice(1); } for (let ln = 0; ln < links.length; ln++) { const url = links[ln].url; const fullUrl = state.md.normalizeLink(url); if (!state.md.validateLink(fullUrl)) { continue; } let urlText = links[ln].text; // Linkifier might send raw hostnames like "example.com", where url // starts with domain name. So we prepend http:// in those cases, // and remove it afterwards. // if (!links[ln].schema) { urlText = state.md.normalizeLinkText("http://" + urlText).replace(/^http:\/\//, ""); } else if (links[ln].schema === "mailto:" && !/^mailto:/i.test(urlText)) { urlText = state.md.normalizeLinkText("mailto:" + urlText).replace(/^mailto:/, ""); } else { urlText = state.md.normalizeLinkText(urlText); } const pos = links[ln].index; if (pos > lastPos) { const token = new state.Token("text", "", 0); token.content = text.slice(lastPos, pos); token.level = level; nodes.push(token); } const token_o = new state.Token("link_open", "a", 1); token_o.attrs = [["href", fullUrl]]; token_o.level = level++; token_o.markup = "linkify"; token_o.info = "auto"; nodes.push(token_o); const token_t = new state.Token("text", "", 0); token_t.content = urlText; token_t.level = level; nodes.push(token_t); const token_c = new state.Token("link_close", "a", -1); token_c.level = --level; token_c.markup = "linkify"; token_c.info = "auto"; nodes.push(token_c); lastPos = links[ln].lastIndex; } if (lastPos < text.length) { const token = new state.Token("text", "", 0); token.content = text.slice(lastPos); token.level = level; nodes.push(token); } // replace current node blockTokens[j].children = tokens = arrayReplaceAt(tokens, i, nodes); } } } } // Simple typographic replacements // // (c) (C) → © // (tm) (TM) → ™ // (r) (R) → ® // +- → ± // ... → … (also ?.... → ?.., !.... → !..) // ???????? → ???, !!!!! → !!!, `,,` → `,` // -- → –, --- → — // // TODO: // - fractionals 1/2, 1/4, 3/4 -> ½, ¼, ¾ // - multiplications 2 x 4 -> 2 × 4 const RARE_RE = /\+-|\.\.|\?\?\?\?|!!!!|,,|--/; // Workaround for phantomjs - need regex without /g flag, // or root check will fail every second time const SCOPED_ABBR_TEST_RE = /\((c|tm|r)\)/i; const SCOPED_ABBR_RE = /\((c|tm|r)\)/gi; const SCOPED_ABBR = { c: "©", r: "®", tm: "™", }; function replaceFn(match, name) { return SCOPED_ABBR[name.toLowerCase()]; } function replace_scoped(inlineTokens) { let inside_autolink = 0; for (let i = inlineTokens.length - 1; i >= 0; i--) { const token = inlineTokens[i]; if (token.type === "text" && !inside_autolink) { token.content = token.content.replace(SCOPED_ABBR_RE, replaceFn); } if (token.type === "link_open" && token.info === "auto") { inside_autolink--; } if (token.type === "link_close" && token.info === "auto") { inside_autolink++; } } } function replace_rare(inlineTokens) { let inside_autolink = 0; for (let i = inlineTokens.length - 1; i >= 0; i--) { const token = inlineTokens[i]; if (token.type === "text" && !inside_autolink) { if (RARE_RE.test(token.content)) { token.content = token.content .replace(/\+-/g, "±") // .., ..., ....... -> … // but ?..... & !..... -> ?.. & !.. .replace(/\.{2,}/g, "…") .replace(/([?!])…/g, "$1..") .replace(/([?!]){4,}/g, "$1$1$1") .replace(/,{2,}/g, ",") // em-dash .replace(/(^|[^-])---(?=[^-]|$)/gm, "$1\u2014") // en-dash .replace(/(^|\s)--(?=\s|$)/gm, "$1\u2013") .replace(/(^|[^-\s])--(?=[^-\s]|$)/gm, "$1\u2013"); } } if (token.type === "link_open" && token.info === "auto") { inside_autolink--; } if (token.type === "link_close" && token.info === "auto") { inside_autolink++; } } } function replace(state) { let blkIdx; if (!state.md.options.typographer) { return; } for (blkIdx = state.tokens.length - 1; blkIdx >= 0; blkIdx--) { if (state.tokens[blkIdx].type !== "inline") { continue; } if (SCOPED_ABBR_TEST_RE.test(state.tokens[blkIdx].content)) { replace_scoped(state.tokens[blkIdx].children); } if (RARE_RE.test(state.tokens[blkIdx].content)) { replace_rare(state.tokens[blkIdx].children); } } } // Convert straight quotation marks to typographic ones // const QUOTE_TEST_RE = /['"]/; const QUOTE_RE = /['"]/g; const APOSTROPHE = "\u2019"; /* ’ */ function addReplacement(replacements, tokenIdx, pos, ch) { if (!replacements[tokenIdx]) { replacements[tokenIdx] = []; } replacements[tokenIdx].push({ pos, ch }); } function applyReplacements(str, replacements) { let result = ""; let lastPos = 0; replacements.sort((a, b) => a.pos - b.pos); for (let i = 0; i < replacements.length; i++) { const replacement = replacements[i]; result += str.slice(lastPos, replacement.pos) + replacement.ch; lastPos = replacement.pos + 1; } return result + str.slice(lastPos); } function process_inlines(tokens, state) { let j; const stack = []; // token index -> list of replacements in the original token content const replacements = {}; for (let i = 0; i < tokens.length; i++) { const token = tokens[i]; const thisLevel = tokens[i].level; for (j = stack.length - 1; j >= 0; j--) { if (stack[j].level <= thisLevel) { break; } } stack.length = j + 1; if (token.type !== "text") { continue; } const text = token.content; let pos = 0; const max = text.length; /* eslint no-labels:0,block-scoped-var:0 */ OUTER: while (pos < max) { QUOTE_RE.lastIndex = pos; const t = QUOTE_RE.exec(text); if (!t) { break; } let canOpen = true; let canClose = true; pos = t.index + 1; const isSingle = t[0] === "'"; // Find previous character, // default to space if it's the beginning of the line // let lastChar = 0x20; if (t.index - 1 >= 0) { lastChar = text.charCodeAt(t.index - 1); } else { for (j = i - 1; j >= 0; j--) { if (tokens[j].type === "softbreak" || tokens[j].type === "hardbreak") break; // lastChar defaults to 0x20 if (!tokens[j].content) continue; // should skip all tokens except 'text', 'html_inline' or 'code_inline' lastChar = tokens[j].content.charCodeAt(tokens[j].content.length - 1); break; } } // Find next character, // default to space if it's the end of the line // let nextChar = 0x20; if (pos < max) { nextChar = text.charCodeAt(pos); } else { for (j = i + 1; j < tokens.length; j++) { if (tokens[j].type === "softbreak" || tokens[j].type === "hardbreak") break; // nextChar defaults to 0x20 if (!tokens[j].content) continue; // should skip all tokens except 'text', 'html_inline' or 'code_inline' nextChar = tokens[j].content.charCodeAt(0); break; } } const isLastPunctChar = isMdAsciiPunct(lastChar) || isPunctCharCode(lastChar); const isNextPunctChar = isMdAsciiPunct(nextChar) || isPunctCharCode(nextChar); const isLastWhiteSpace = isWhiteSpace(lastChar); const isNextWhiteSpace = isWhiteSpace(nextChar); if (isNextWhiteSpace) { canOpen = false; } else if (isNextPunctChar) { if (!(isLastWhiteSpace || isLastPunctChar)) { canOpen = false; } } if (isLastWhiteSpace) { canClose = false; } else if (isLastPunctChar) { if (!(isNextWhiteSpace || isNextPunctChar)) { canClose = false; } } if (nextChar === 0x22 /* " */ && t[0] === '"') { if (lastChar >= 0x30 /* 0 */ && lastChar <= 0x39 /* 9 */) { // special case: 1"" - count first quote as an inch canClose = canOpen = false; } } if (canOpen && canClose) { // Replace quotes in the middle of punctuation sequence, but not // in the middle of the words, i.e.: // // 1. foo " bar " baz - not replaced // 2. foo-"-bar-"-baz - replaced // 3. foo"bar"baz - not replaced // canOpen = isLastPunctChar; canClose = isNextPunctChar; } if (!canOpen && !canClose) { // middle of word if (isSingle) { addReplacement(replacements, i, t.index, APOSTROPHE); } continue; } if (canClose) { // this could be a closing quote, rewind the stack to get a match for (j = stack.length - 1; j >= 0; j--) { let item = stack[j]; if (stack[j].level < thisLevel) { break; } if (item.single === isSingle && stack[j].level === thisLevel) { item = stack[j]; let openQuote; let closeQuote; if (isSingle) { openQuote = state.md.options.quotes[2]; closeQuote = state.md.options.quotes[3]; } else { openQuote = state.md.options.quotes[0]; closeQuote = state.md.options.quotes[1]; } addReplacement(replacements, i, t.index, closeQuote); addReplacement(replacements, item.token, item.pos, openQuote); stack.length = j; continue OUTER; } } } if (canOpen) { stack.push({ token: i, pos: t.index, single: isSingle, level: thisLevel, }); } else if (canClose && isSingle) { addReplacement(replacements, i, t.index, APOSTROPHE); } } } Object.keys(replacements).forEach(function (tokenIdx) { tokens[tokenIdx].content = applyReplacements(tokens[tokenIdx].content, replacements[tokenIdx]); }); } function smartquotes(state) { /* eslint max-depth:0 */ if (!state.md.options.typographer) { return; } for (let blkIdx = state.tokens.length - 1; blkIdx >= 0; blkIdx--) { if (state.tokens[blkIdx].type !== "inline" || !QUOTE_TEST_RE.test(state.tokens[blkIdx].content)) { continue; } process_inlines(state.tokens[blkIdx].children, state); } } // Join raw text tokens with the rest of the text // // This is set as a separate rule to provide an opportunity for plugins // to run text replacements after text join, but before escape join. // // For example, `\:)` shouldn't be replaced with an emoji. // function text_join(state) { let curr, last; const blockTokens = state.tokens; const l = blockTokens.length; for (let j = 0; j < l; j++) { if (blockTokens[j].type !== "inline") continue; const tokens = blockTokens[j].children; const max = tokens.length; for (curr = 0; curr < max; curr++) { if (tokens[curr].type === "text_special") { tokens[curr].type = "text"; } } for (curr = last = 0; curr < max; curr++) { if (tokens[curr].type === "text" && curr + 1 < max && tokens[curr + 1].type === "text") { // collapse two adjacent text nodes tokens[curr + 1].content = tokens[curr].content + tokens[curr + 1].content; } else { if (curr !== last) { tokens[last] = tokens[curr]; } last++; } } if (curr !== last) { tokens.length = last; } } } /** internal * class Core * * Top-level rules executor. Glues block/inline parsers and does intermediate * transformations. **/ const _rules$2 = [ ["normalize", normalize], ["block", block], ["inline", inline], ["linkify", linkify$1], ["replacements", replace], ["smartquotes", smartquotes], // `text_join` finds `text_special` tokens (for escape sequences) // and joins them with the rest of the text ["text_join", text_join], ]; /** * new Core() **/ function Core() { /** * Core#ruler -> Ruler * * [[Ruler]] instance. Keep configuration of core rules. **/ this.ruler = new Ruler(); for (let i = 0; i < _rules$2.length; i++) { this.ruler.push(_rules$2[i][0], _rules$2[i][1]); } } /** * Core.process(state) * * Executes core chain rules. **/ Core.prototype.process = function (state) { const rules = this.ruler.getRules(""); for (let i = 0, l = rules.length; i < l; i++) { rules[i](state); } }; Core.prototype.State = StateCore; // Parser state class function StateBlock(src, md, env, tokens) { this.src = src; // link to parser instance this.md = md; this.env = env; // // Internal state vartiables // this.tokens = tokens; this.bMarks = []; // line begin offsets for fast jumps this.eMarks = []; // line end offsets for fast jumps this.tShift = []; // offsets of the first non-space characters (tabs not expanded) this.sCount = []; // indents for each line (tabs expanded) // An amount of virtual spaces (tabs expanded) between beginning // of each line (bMarks) and real beginning of that line. // // It exists only as a hack because blockquotes override bMarks // losing information in the process. // // It's used only when expanding tabs, you can think about it as // an initial tab length, e.g. bsCount=21 applied to string `\t123` // means first tab should be expanded to 4-21%4 === 3 spaces. // this.bsCount = []; // block parser variables // required block content indent (for example, if we are // inside a list, it would be positioned after list marker) this.blkIndent = 0; this.line = 0; // line index in src this.lineMax = 0; // lines count this.tight = false; // loose/tight mode for lists this.ddIndent = -1; // indent of the current dd block (-1 if there isn't any) this.listIndent = -1; // indent of the current list block (-1 if there isn't any) // can be 'blockquote', 'list', 'root', 'paragraph' or 'reference' // used in lists to determine if they interrupt a paragraph this.parentType = "root"; this.level = 0; // Create caches // Generate markers. const s = this.src; for (let start = 0, pos = 0, indent = 0, offset = 0, len = s.length, indent_found = false; pos < len; pos++) { const ch = s.charCodeAt(pos); if (!indent_found) { if (isSpace(ch)) { indent++; if (ch === 0x09) { offset += 4 - (offset % 4); } else { offset++; } continue; } else { indent_found = true; } } if (ch === 0x0a || pos === len - 1) { if (ch !== 0x0a) { pos++; } this.bMarks.push(start); this.eMarks.push(pos); this.tShift.push(indent); this.sCount.push(offset); this.bsCount.push(0); indent_found = false; indent = 0; offset = 0; start = pos + 1; } } // Push fake entry to simplify cache bounds checks this.bMarks.push(s.length); this.eMarks.push(s.length); this.tShift.push(0); this.sCount.push(0); this.bsCount.push(0); this.lineMax = this.bMarks.length - 1; // don't count last fake line } // Push new token to "stream". // StateBlock.prototype.push = function (type, tag, nesting) { const token = new Token(type, tag, nesting); token.block = true; if (nesting < 0) this.level--; // closing tag token.level = this.level; if (nesting > 0) this.level++; // opening tag this.tokens.push(token); return token; }; StateBlock.prototype.isEmpty = function isEmpty(line) { return this.bMarks[line] + this.tShift[line] >= this.eMarks[line]; }; StateBlock.prototype.skipEmptyLines = function skipEmptyLines(from) { for (let max = this.lineMax; from < max; from++) { if (this.bMarks[from] + this.tShift[from] < this.eMarks[from]) { break; } } return from; }; // Skip spaces from given position. StateBlock.prototype.skipSpaces = function skipSpaces(pos) { for (let max = this.src.length; pos < max; pos++) { const ch = this.src.charCodeAt(pos); if (!isSpace(ch)) { break; } } return pos; }; // Skip spaces from given position in reverse. StateBlock.prototype.skipSpacesBack = function skipSpacesBack(pos, min) { if (pos <= min) { return pos; } while (pos > min) { if (!isSpace(this.src.charCodeAt(--pos))) { return pos + 1; } } return pos; }; // Skip char codes from given position StateBlock.prototype.skipChars = function skipChars(pos, code) { for (let max = this.src.length; pos < max; pos++) { if (this.src.charCodeAt(pos) !== code) { break; } } return pos; }; // Skip char codes reverse from given position - 1 StateBlock.prototype.skipCharsBack = function skipCharsBack(pos, code, min) { if (pos <= min) { return pos; } while (pos > min) { if (code !== this.src.charCodeAt(--pos)) { return pos + 1; } } return pos; }; // cut lines range from source. StateBlock.prototype.getLines = function getLines(begin, end, indent, keepLastLF) { if (begin >= end) { return ""; } const queue = new Array(end - begin); for (let i = 0, line = begin; line < end; line++, i++) { let lineIndent = 0; const lineStart = this.bMarks[line]; let first = lineStart; let last; if (line + 1 < end || keepLastLF) { // No need for bounds check because we have fake entry on tail. last = this.eMarks[line] + 1; } else { last = this.eMarks[line]; } while (first < last && lineIndent < indent) { const ch = this.src.charCodeAt(first); if (isSpace(ch)) { if (ch === 0x09) { lineIndent += 4 - ((lineIndent + this.bsCount[line]) % 4); } else { lineIndent++; } } else if (first - lineStart < this.tShift[line]) { // patched tShift masked characters to look like spaces (blockquotes, list markers) lineIndent++; } else { break; } first++; } if (lineIndent > indent) { // partially expanding tabs in code blocks, e.g '\t\tfoobar' // with indent=2 becomes ' \tfoobar' queue[i] = new Array(lineIndent - indent + 1).join(" ") + this.src.slice(first, last); } else { queue[i] = this.src.slice(first, last); } } return queue.join(""); }; // re-export Token class to use in block rules StateBlock.prototype.Token = Token; // GFM table, https://github.github.com/gfm/#tables-extension- // Limit the amount of empty autocompleted cells in a table, // see https://github.com/markdown-it/markdown-it/issues/1000, // // Both pulldown-cmark and commonmark-hs limit the number of cells this way to ~200k. // We set it to 65k, which can expand user input by a factor of x370 // (256x256 square is 1.8kB expanded into 650kB). const MAX_AUTOCOMPLETED_CELLS = 0x10000; function getLine(state, line) { const pos = state.bMarks[line] + state.tShift[line]; const max = state.eMarks[line]; return state.src.slice(pos, max); } function escapedSplit(str) { const result = []; const max = str.length; let pos = 0; let ch = str.charCodeAt(pos); let isEscaped = false; let lastPos = 0; let current = ""; while (pos < max) { if (ch === 0x7c /* | */) { if (!isEscaped) { // pipe separating cells, '|' result.push(current + str.substring(lastPos, pos)); current = ""; lastPos = pos + 1; } else { // escaped pipe, '\|' current += str.substring(lastPos, pos - 1); lastPos = pos; } } isEscaped = ch === 0x5c /* \ */; pos++; ch = str.charCodeAt(pos); } result.push(current + str.substring(lastPos)); return result; } function table(state, startLine, endLine, silent) { // should have at least two lines if (startLine + 2 > endLine) { return false; } let nextLine = startLine + 1; if (state.sCount[nextLine] < state.blkIndent) { return false; } // if it's indented more than 3 spaces, it should be a code block if (state.sCount[nextLine] - state.blkIndent >= 4) { return false; } // first character of the second line should be '|', '-', ':', // and no other characters are allowed but spaces; // basically, this is the equivalent of /^[-:|][-:|\s]*$/ regexp let pos = state.bMarks[nextLine] + state.tShift[nextLine]; if (pos >= state.eMarks[nextLine]) { return false; } const firstCh = state.src.charCodeAt(pos++); if (firstCh !== 0x7c /* | */ && firstCh !== 0x2d /* - */ && firstCh !== 0x3a /* : */) { return false; } if (pos >= state.eMarks[nextLine]) { return false; } const secondCh = state.src.charCodeAt(pos++); if (secondCh !== 0x7c /* | */ && secondCh !== 0x2d /* - */ && secondCh !== 0x3a /* : */ && !isSpace(secondCh)) { return false; } // if first character is '-', then second character must not be a space // (due to parsing ambiguity with list) if (firstCh === 0x2d /* - */ && isSpace(secondCh)) { return false; } while (pos < state.eMarks[nextLine]) { const ch = state.src.charCodeAt(pos); if (ch !== 0x7c /* | */ && ch !== 0x2d /* - */ && ch !== 0x3a /* : */ && !isSpace(ch)) { return false; } pos++; } let lineText = getLine(state, startLine + 1); let columns = lineText.split("|"); const aligns = []; for (let i = 0; i < columns.length; i++) { const t = columns[i].trim(); if (!t) { // allow empty columns before and after table, but not in between columns; // e.g. allow ` |---| `, disallow ` ---||--- ` if (i === 0 || i === columns.length - 1) { continue; } else { return false; } } if (!/^:?-+:?$/.test(t)) { return false; } if (t.charCodeAt(t.length - 1) === 0x3a /* : */) { aligns.push(t.charCodeAt(0) === 0x3a /* : */ ? "center" : "right"); } else if (t.charCodeAt(0) === 0x3a /* : */) { aligns.push("left"); } else { aligns.push(""); } } lineText = getLine(state, startLine).trim(); if (lineText.indexOf("|") === -1) { return false; } if (state.sCount[startLine] - state.blkIndent >= 4) { return false; } columns = escapedSplit(lineText); if (columns.length && columns[0] === "") columns.shift(); if (columns.length && columns[columns.length - 1] === "") columns.pop(); // header row will define an amount of columns in the entire table, // and align row should be exactly the same (the rest of the rows can differ) const columnCount = columns.length; if (columnCount === 0 || columnCount !== aligns.length) { return false; } if (silent) { return true; } const oldParentType = state.parentType; state.parentType = "table"; // use 'blockquote' lists for termination because it's // the most similar to tables const terminatorRules = state.md.block.ruler.getRules("blockquote"); const token_to = state.push("table_open", "table", 1); const tableLines = [startLine, 0]; token_to.map = tableLines; const token_tho = state.push("thead_open", "thead", 1); token_tho.map = [startLine, startLine + 1]; const token_htro = state.push("tr_open", "tr", 1); token_htro.map = [startLine, startLine + 1]; for (let i = 0; i < columns.length; i++) { const token_ho = state.push("th_open", "th", 1); if (aligns[i]) { token_ho.attrs = [["style", "text-align:" + aligns[i]]]; } const token_il = state.push("inline", "", 0); token_il.content = columns[i].trim(); token_il.children = []; state.push("th_close", "th", -1); } state.push("tr_close", "tr", -1); state.push("thead_close", "thead", -1); let tbodyLines; let autocompletedCells = 0; for (nextLine = startLine + 2; nextLine < endLine; nextLine++) { if (state.sCount[nextLine] < state.blkIndent) { break; } let terminate = false; for (let i = 0, l = terminatorRules.length; i < l; i++) { if (terminatorRules[i](state, nextLine, endLine, true)) { terminate = true; break; } } if (terminate) { break; } lineText = getLine(state, nextLine).trim(); if (!lineText) { break; } if (state.sCount[nextLine] - state.blkIndent >= 4) { break; } columns = escapedSplit(lineText); if (columns.length && columns[0] === "") columns.shift(); if (columns.length && columns[columns.length - 1] === "") columns.pop(); // note: autocomplete count can be negative if user specifies more columns than header, // but that does not affect intended use (which is limiting expansion) autocompletedCells += columnCount - columns.length; if (autocompletedCells > MAX_AUTOCOMPLETED_CELLS) { break; } if (nextLine === startLine + 2) { const token_tbo = state.push("tbody_open", "tbody", 1); token_tbo.map = tbodyLines = [startLine + 2, 0]; } const token_tro = state.push("tr_open", "tr", 1); token_tro.map = [nextLine, nextLine + 1]; for (let i = 0; i < columnCount; i++) { const token_tdo = state.push("td_open", "td", 1); if (aligns[i]) { token_tdo.attrs = [["style", "text-align:" + aligns[i]]]; } const token_il = state.push("inline", "", 0); token_il.content = columns[i] ? columns[i].trim() : ""; token_il.children = []; state.push("td_close", "td", -1); } state.push("tr_close", "tr", -1); } if (tbodyLines) { state.push("tbody_close", "tbody", -1); tbodyLines[1] = nextLine; } state.push("table_close", "table", -1); tableLines[1] = nextLine; state.parentType = oldParentType; state.line = nextLine; return true; } // Code block (4 spaces padded) function code(state, startLine, endLine /*, silent */) { if (state.sCount[startLine] - state.blkIndent < 4) { return false; } let nextLine = startLine + 1; let last = nextLine; while (nextLine < endLine) { if (state.isEmpty(nextLine)) { nextLine++; continue; } if (state.sCount[nextLine] - state.blkIndent >= 4) { nextLine++; last = nextLine; continue; } break; } state.line = last; const token = state.push("code_block", "code", 0); token.content = state.getLines(startLine, last, 4 + state.blkIndent, false) + "\n"; token.map = [startLine, state.line]; return true; } // fences (``` lang, ~~~ lang) function fence(state, startLine, endLine, silent) { let pos = state.bMarks[startLine] + state.tShift[startLine]; let max = state.eMarks[startLine]; // if it's indented more than 3 spaces, it should be a code block if (state.sCount[startLine] - state.blkIndent >= 4) { return false; } if (pos + 3 > max) { return false; } const marker = state.src.charCodeAt(pos); if (marker !== 0x7e /* ~ */ && marker !== 0x60 /* ` */) { return false; } // scan marker length let mem = pos; pos = state.skipChars(pos, marker); let len = pos - mem; if (len < 3) { return false; } const markup = state.src.slice(mem, pos); const params = state.src.slice(pos, max); if (marker === 0x60 /* ` */) { if (params.indexOf(String.fromCharCode(marker)) >= 0) { return false; } } // Since start is found, we can report success here in validation mode if (silent) { return true; } // search end of block let nextLine = startLine; let haveEndMarker = false; for (;;) { nextLine++; if (nextLine >= endLine) { // unclosed block should be autoclosed by end of document. // also block seems to be autoclosed by end of parent break; } pos = mem = state.bMarks[nextLine] + state.tShift[nextLine]; max = state.eMarks[nextLine]; if (pos < max && state.sCount[nextLine] < state.blkIndent) { // non-empty line with negative indent should stop the list: // - ``` // test break; } if (state.src.charCodeAt(pos) !== marker) { continue; } if (state.sCount[nextLine] - state.blkIndent >= 4) { // closing fence should be indented less than 4 spaces continue; } pos = state.skipChars(pos, marker); // closing code fence must be at least as long as the opening one if (pos - mem < len) { continue; } // make sure tail has spaces only pos = state.skipSpaces(pos); if (pos < max) { continue; } haveEndMarker = true; // found! break; } // If a fence has heading spaces, they should be removed from its inner block len = state.sCount[startLine]; state.line = nextLine + (haveEndMarker ? 1 : 0); const token = state.push("fence", "code", 0); token.info = params; token.content = state.getLines(startLine + 1, nextLine, len, true); token.markup = markup; token.map = [startLine, state.line]; return true; } // Block quotes function blockquote(state, startLine, endLine, silent) { let pos = state.bMarks[startLine] + state.tShift[startLine]; let max = state.eMarks[startLine]; const oldLineMax = state.lineMax; // if it's indented more than 3 spaces, it should be a code block if (state.sCount[startLine] - state.blkIndent >= 4) { return false; } // check the block quote marker if (state.src.charCodeAt(pos) !== 0x3e /* > */) { return false; } // we know that it's going to be a valid blockquote, // so no point trying to find the end of it in silent mode if (silent) { return true; } const oldBMarks = []; const oldBSCount = []; const oldSCount = []; const oldTShift = []; const terminatorRules = state.md.block.ruler.getRules("blockquote"); const oldParentType = state.parentType; state.parentType = "blockquote"; let lastLineEmpty = false; let nextLine; // Search the end of the block // // Block ends with either: // 1. an empty line outside: // ``` // > test // // ``` // 2. an empty line inside: // ``` // > // test // ``` // 3. another tag: // ``` // > test // - - - // ``` for (nextLine = startLine; nextLine < endLine; nextLine++) { // check if it's outdented, i.e. it's inside list item and indented // less than said list item: // // ``` // 1. anything // > current blockquote // 2. checking this line // ``` const isOutdented = state.sCount[nextLine] < state.blkIndent; pos = state.bMarks[nextLine] + state.tShift[nextLine]; max = state.eMarks[nextLine]; if (pos >= max) { // Case 1: line is not inside the blockquote, and this line is empty. break; } if (state.src.charCodeAt(pos++) === 0x3e /* > */ && !isOutdented) { // This line is inside the blockquote. // set offset past spaces and ">" let initial = state.sCount[nextLine] + 1; let spaceAfterMarker; let adjustTab; // skip one optional space after '>' if (state.src.charCodeAt(pos) === 0x20 /* space */) { // ' > test ' // ^ -- position start of line here: pos++; initial++; adjustTab = false; spaceAfterMarker = true; } else if (state.src.charCodeAt(pos) === 0x09 /* tab */) { spaceAfterMarker = true; if ((state.bsCount[nextLine] + initial) % 4 === 3) { // ' >\t test ' // ^ -- position start of line here (tab has width===1) pos++; initial++; adjustTab = false; } else { // ' >\t test ' // ^ -- position start of line here + shift bsCount slightly // to make extra space appear adjustTab = true; } } else { spaceAfterMarker = false; } let offset = initial; oldBMarks.push(state.bMarks[nextLine]); state.bMarks[nextLine] = pos; while (pos < max) { const ch = state.src.charCodeAt(pos); if (isSpace(ch)) { if (ch === 0x09) { offset += 4 - ((offset + state.bsCount[nextLine] + (adjustTab ? 1 : 0)) % 4); } else { offset++; } } else { break; } pos++; } lastLineEmpty = pos >= max; oldBSCount.push(state.bsCount[nextLine]); state.bsCount[nextLine] = state.sCount[nextLine] + 1 + (spaceAfterMarker ? 1 : 0); oldSCount.push(state.sCount[nextLine]); state.sCount[nextLine] = offset - initial; oldTShift.push(state.tShift[nextLine]); state.tShift[nextLine] = pos - state.bMarks[nextLine]; continue; } // Case 2: line is not inside the blockquote, and the last line was empty. if (lastLineEmpty) { break; } // Case 3: another tag found. let terminate = false; for (let i = 0, l = terminatorRules.length; i < l; i++) { if (terminatorRules[i](state, nextLine, endLine, true)) { terminate = true; break; } } if (terminate) { // Quirk to enforce "hard termination mode" for paragraphs; // normally if you call `tokenize(state, startLine, nextLine)`, // paragraphs will look below nextLine for paragraph continuation, // but if blockquote is terminated by another tag, they shouldn't state.lineMax = nextLine; if (state.blkIndent !== 0) { // state.blkIndent was non-zero, we now set it to zero, // so we need to re-calculate all offsets to appear as // if indent wasn't changed oldBMarks.push(state.bMarks[nextLine]); oldBSCount.push(state.bsCount[nextLine]); oldTShift.push(state.tShift[nextLine]); oldSCount.push(state.sCount[nextLine]); state.sCount[nextLine] -= state.blkIndent; } break; } oldBMarks.push(state.bMarks[nextLine]); oldBSCount.push(state.bsCount[nextLine]); oldTShift.push(state.tShift[nextLine]); oldSCount.push(state.sCount[nextLine]); // A negative indentation means that this is a paragraph continuation // state.sCount[nextLine] = -1; } const oldIndent = state.blkIndent; state.blkIndent = 0; const token_o = state.push("blockquote_open", "blockquote", 1); token_o.markup = ">"; const lines = [startLine, 0]; token_o.map = lines; state.md.block.tokenize(state, startLine, nextLine); const token_c = state.push("blockquote_close", "blockquote", -1); token_c.markup = ">"; state.lineMax = oldLineMax; state.parentType = oldParentType; lines[1] = state.line; // Restore original tShift; this might not be necessary since the parser // has already been here, but just to make sure we can do that. for (let i = 0; i < oldTShift.length; i++) { state.bMarks[i + startLine] = oldBMarks[i]; state.tShift[i + startLine] = oldTShift[i]; state.sCount[i + startLine] = oldSCount[i]; state.bsCount[i + startLine] = oldBSCount[i]; } state.blkIndent = oldIndent; return true; } // Horizontal rule function hr(state, startLine, endLine, silent) { const max = state.eMarks[startLine]; // if it's indented more than 3 spaces, it should be a code block if (state.sCount[startLine] - state.blkIndent >= 4) { return false; } let pos = state.bMarks[startLine] + state.tShift[startLine]; const marker = state.src.charCodeAt(pos++); // Check hr marker if (marker !== 0x2a /* * */ && marker !== 0x2d /* - */ && marker !== 0x5f /* _ */) { return false; } // markers can be mixed with spaces, but there should be at least 3 of them let cnt = 1; while (pos < max) { const ch = state.src.charCodeAt(pos++); if (ch !== marker && !isSpace(ch)) { return false; } if (ch === marker) { cnt++; } } if (cnt < 3) { return false; } if (silent) { return true; } state.line = startLine + 1; const token = state.push("hr", "hr", 0); token.map = [startLine, state.line]; token.markup = Array(cnt + 1).join(String.fromCharCode(marker)); return true; } // Lists // Search `[-+*][\n ]`, returns next pos after marker on success // or -1 on fail. function skipBulletListMarker(state, startLine) { const max = state.eMarks[startLine]; let pos = state.bMarks[startLine] + state.tShift[startLine]; const marker = state.src.charCodeAt(pos++); // Check bullet if (marker !== 0x2a /* * */ && marker !== 0x2d /* - */ && marker !== 0x2b /* + */) { return -1; } if (pos < max) { const ch = state.src.charCodeAt(pos); if (!isSpace(ch)) { // " -test " - is not a list item return -1; } } return pos; } // Search `\d+[.)][\n ]`, returns next pos after marker on success // or -1 on fail. function skipOrderedListMarker(state, startLine) { const start = state.bMarks[startLine] + state.tShift[startLine]; const max = state.eMarks[startLine]; let pos = start; // List marker should have at least 2 chars (digit + dot) if (pos + 1 >= max) { return -1; } let ch = state.src.charCodeAt(pos++); if (ch < 0x30 /* 0 */ || ch > 0x39 /* 9 */) { return -1; } for (;;) { // EOL -> fail if (pos >= max) { return -1; } ch = state.src.charCodeAt(pos++); if (ch >= 0x30 /* 0 */ && ch <= 0x39 /* 9 */) { // List marker should have no more than 9 digits // (prevents integer overflow in browsers) if (pos - start >= 10) { return -1; } continue; } // found valid marker if (ch === 0x29 /* ) */ || ch === 0x2e /* . */) { break; } return -1; } if (pos < max) { ch = state.src.charCodeAt(pos); if (!isSpace(ch)) { // " 1.test " - is not a list item return -1; } } return pos; } function markTightParagraphs(state, idx) { const level = state.level + 2; for (let i = idx + 2, l = state.tokens.length - 2; i < l; i++) { if (state.tokens[i].level === level && state.tokens[i].type === "paragraph_open") { state.tokens[i + 2].hidden = true; state.tokens[i].hidden = true; i += 2; } } } function list(state, startLine, endLine, silent) { let max, pos, start, token; let nextLine = startLine; let tight = true; // if it's indented more than 3 spaces, it should be a code block if (state.sCount[nextLine] - state.blkIndent >= 4) { return false; } // Special case: // - item 1 // - item 2 // - item 3 // - item 4 // - this one is a paragraph continuation if (state.listIndent >= 0 && state.sCount[nextLine] - state.listIndent >= 4 && state.sCount[nextLine] < state.blkIndent) { return false; } let isTerminatingParagraph = false; // limit conditions when list can interrupt // a paragraph (validation mode only) if (silent && state.parentType === "paragraph") { // Next list item should still terminate previous list item; // // This code can fail if plugins use blkIndent as well as lists, // but I hope the spec gets fixed long before that happens. // if (state.sCount[nextLine] >= state.blkIndent) { isTerminatingParagraph = true; } } // Detect list type and position after marker let isOrdered; let markerValue; let posAfterMarker; if ((posAfterMarker = skipOrderedListMarker(state, nextLine)) >= 0) { isOrdered = true; start = state.bMarks[nextLine] + state.tShift[nextLine]; markerValue = Number(state.src.slice(start, posAfterMarker - 1)); // If we're starting a new ordered list right after // a paragraph, it should start with 1. if (isTerminatingParagraph && markerValue !== 1) return false; } else if ((posAfterMarker = skipBulletListMarker(state, nextLine)) >= 0) { isOrdered = false; } else { return false; } // If we're starting a new unordered list right after // a paragraph, first line should not be empty. if (isTerminatingParagraph) { if (state.skipSpaces(posAfterMarker) >= state.eMarks[nextLine]) return false; } // For validation mode we can terminate immediately if (silent) { return true; } // We should terminate list on style change. Remember first one to compare. const markerCharCode = state.src.charCodeAt(posAfterMarker - 1); // Start list const listTokIdx = state.tokens.length; if (isOrdered) { token = state.push("ordered_list_open", "ol", 1); if (markerValue !== 1) { token.attrs = [["start", markerValue]]; } } else { token = state.push("bullet_list_open", "ul", 1); } const listLines = [nextLine, 0]; token.map = listLines; token.markup = String.fromCharCode(markerCharCode); // // Iterate list items // let prevEmptyEnd = false; const terminatorRules = state.md.block.ruler.getRules("list"); const oldParentType = state.parentType; state.parentType = "list"; while (nextLine < endLine) { pos = posAfterMarker; max = state.eMarks[nextLine]; const initial = state.sCount[nextLine] + posAfterMarker - (state.bMarks[nextLine] + state.tShift[nextLine]); let offset = initial; while (pos < max) { const ch = state.src.charCodeAt(pos); if (ch === 0x09) { offset += 4 - ((offset + state.bsCount[nextLine]) % 4); } else if (ch === 0x20) { offset++; } else { break; } pos++; } const contentStart = pos; let indentAfterMarker; if (contentStart >= max) { // trimming space in "- \n 3" case, indent is 1 here indentAfterMarker = 1; } else { indentAfterMarker = offset - initial; } // If we have more than 4 spaces, the indent is 1 // (the rest is just indented code block) if (indentAfterMarker > 4) { indentAfterMarker = 1; } // " - test" // ^^^^^ - calculating total length of this thing const indent = initial + indentAfterMarker; // Run subparser & write tokens token = state.push("list_item_open", "li", 1); token.markup = String.fromCharCode(markerCharCode); const itemLines = [nextLine, 0]; token.map = itemLines; if (isOrdered) { token.info = state.src.slice(start, posAfterMarker - 1); } // change current state, then restore it after parser subcall const oldTight = state.tight; const oldTShift = state.tShift[nextLine]; const oldSCount = state.sCount[nextLine]; // - example list // ^ listIndent position will be here // ^ blkIndent position will be here // const oldListIndent = state.listIndent; state.listIndent = state.blkIndent; state.blkIndent = indent; state.tight = true; state.tShift[nextLine] = contentStart - state.bMarks[nextLine]; state.sCount[nextLine] = offset; if (contentStart >= max && state.isEmpty(nextLine + 1)) { // workaround for this case // (list item is empty, list terminates before "foo"): // ~~~~~~~~ // - // // foo // ~~~~~~~~ state.line = Math.min(state.line + 2, endLine); } else { state.md.block.tokenize(state, nextLine, endLine, true); } // If any of list item is tight, mark list as tight if (!state.tight || prevEmptyEnd) { tight = false; } // Item become loose if finish with empty line, // but we should filter last element, because it means list finish prevEmptyEnd = state.line - nextLine > 1 && state.isEmpty(state.line - 1); state.blkIndent = state.listIndent; state.listIndent = oldListIndent; state.tShift[nextLine] = oldTShift; state.sCount[nextLine] = oldSCount; state.tight = oldTight; token = state.push("list_item_close", "li", -1); token.markup = String.fromCharCode(markerCharCode); nextLine = state.line; itemLines[1] = nextLine; if (nextLine >= endLine) { break; } // // Try to check if list is terminated or continued. // if (state.sCount[nextLine] < state.blkIndent) { break; } // if it's indented more than 3 spaces, it should be a code block if (state.sCount[nextLine] - state.blkIndent >= 4) { break; } // fail if terminating block found let terminate = false; for (let i = 0, l = terminatorRules.length; i < l; i++) { if (terminatorRules[i](state, nextLine, endLine, true)) { terminate = true; break; } } if (terminate) { break; } // fail if list has another type if (isOrdered) { posAfterMarker = skipOrderedListMarker(state, nextLine); if (posAfterMarker < 0) { break; } start = state.bMarks[nextLine] + state.tShift[nextLine]; } else { posAfterMarker = skipBulletListMarker(state, nextLine); if (posAfterMarker < 0) { break; } } if (markerCharCode !== state.src.charCodeAt(posAfterMarker - 1)) { break; } } // Finalize list if (isOrdered) { token = state.push("ordered_list_close", "ol", -1); } else { token = state.push("bullet_list_close", "ul", -1); } token.markup = String.fromCharCode(markerCharCode); listLines[1] = nextLine; state.line = nextLine; state.parentType = oldParentType; // mark paragraphs tight if needed if (tight) { markTightParagraphs(state, listTokIdx); } return true; } function reference(state, startLine, _endLine, silent) { let pos = state.bMarks[startLine] + state.tShift[startLine]; let max = state.eMarks[startLine]; let nextLine = startLine + 1; // if it's indented more than 3 spaces, it should be a code block if (state.sCount[startLine] - state.blkIndent >= 4) { return false; } if (state.src.charCodeAt(pos) !== 0x5b /* [ */) { return false; } function getNextLine(nextLine) { const endLine = state.lineMax; if (nextLine >= endLine || state.isEmpty(nextLine)) { // empty line or end of input return null; } let isContinuation = false; // this would be a code block normally, but after paragraph // it's considered a lazy continuation regardless of what's there if (state.sCount[nextLine] - state.blkIndent > 3) { isContinuation = true; } // quirk for blockquotes, this line should already be checked by that rule if (state.sCount[nextLine] < 0) { isContinuation = true; } if (!isContinuation) { const terminatorRules = state.md.block.ruler.getRules("reference"); const oldParentType = state.parentType; state.parentType = "reference"; // Some tags can terminate paragraph without empty line. let terminate = false; for (let i = 0, l = terminatorRules.length; i < l; i++) { if (terminatorRules[i](state, nextLine, endLine, true)) { terminate = true; break; } } state.parentType = oldParentType; if (terminate) { // terminated by another block return null; } } const pos = state.bMarks[nextLine] + state.tShift[nextLine]; const max = state.eMarks[nextLine]; // max + 1 explicitly includes the newline return state.src.slice(pos, max + 1); } let str = state.src.slice(pos, max + 1); max = str.length; let labelEnd = -1; for (pos = 1; pos < max; pos++) { const ch = str.charCodeAt(pos); if (ch === 0x5b /* [ */) { return false; } else if (ch === 0x5d /* ] */) { labelEnd = pos; break; } else if (ch === 0x0a /* \n */) { const lineContent = getNextLine(nextLine); if (lineContent !== null) { str += lineContent; max = str.length; nextLine++; } } else if (ch === 0x5c /* \ */) { pos++; if (pos < max && str.charCodeAt(pos) === 0x0a) { const lineContent = getNextLine(nextLine); if (lineContent !== null) { str += lineContent; max = str.length; nextLine++; } } } } if (labelEnd < 0 || str.charCodeAt(labelEnd + 1) !== 0x3a /* : */) { return false; } // [label]: destination 'title' // ^^^ skip optional whitespace here for (pos = labelEnd + 2; pos < max; pos++) { const ch = str.charCodeAt(pos); if (ch === 0x0a) { const lineContent = getNextLine(nextLine); if (lineContent !== null) { str += lineContent; max = str.length; nextLine++; } } else if (isSpace(ch)); else { break; } } // [label]: destination 'title' // ^^^^^^^^^^^ parse this const destRes = state.md.helpers.parseLinkDestination(str, pos, max); if (!destRes.ok) { return false; } const href = state.md.normalizeLink(destRes.str); if (!state.md.validateLink(href)) { return false; } pos = destRes.pos; // save cursor state, we could require to rollback later const destEndPos = pos; const destEndLineNo = nextLine; // [label]: destination 'title' // ^^^ skipping those spaces const start = pos; for (; pos < max; pos++) { const ch = str.charCodeAt(pos); if (ch === 0x0a) { const lineContent = getNextLine(nextLine); if (lineContent !== null) { str += lineContent; max = str.length; nextLine++; } } else if (isSpace(ch)); else { break; } } // [label]: destination 'title' // ^^^^^^^ parse this let titleRes = state.md.helpers.parseLinkTitle(str, pos, max); while (titleRes.can_continue) { const lineContent = getNextLine(nextLine); if (lineContent === null) break; str += lineContent; pos = max; max = str.length; nextLine++; titleRes = state.md.helpers.parseLinkTitle(str, pos, max, titleRes); } let title; if (pos < max && start !== pos && titleRes.ok) { title = titleRes.str; pos = titleRes.pos; } else { title = ""; pos = destEndPos; nextLine = destEndLineNo; } // skip trailing spaces until the rest of the line while (pos < max) { const ch = str.charCodeAt(pos); if (!isSpace(ch)) { break; } pos++; } if (pos < max && str.charCodeAt(pos) !== 0x0a) { if (title) { // garbage at the end of the line after title, // but it could still be a valid reference if we roll back title = ""; pos = destEndPos; nextLine = destEndLineNo; while (pos < max) { const ch = str.charCodeAt(pos); if (!isSpace(ch)) { break; } pos++; } } } if (pos < max && str.charCodeAt(pos) !== 0x0a) { // garbage at the end of the line return false; } const label = normalizeReference(str.slice(1, labelEnd)); if (!label) { // CommonMark 0.20 disallows empty labels return false; } // Reference can not terminate anything. This check is for safety only. /* istanbul ignore if */ if (silent) { return true; } if (typeof state.env.references === "undefined") { state.env.references = {}; } if (typeof state.env.references[label] === "undefined") { state.env.references[label] = { title, href }; } state.line = nextLine; return true; } // List of valid html blocks names, according to commonmark spec // https://spec.commonmark.org/0.30/#html-blocks const block_names = [ "address", "article", "aside", "base", "basefont", "blockquote", "body", "caption", "center", "col", "colgroup", "dd", "details", "dialog", "dir", "div", "dl", "dt", "fieldset", "figcaption", "figure", "footer", "form", "frame", "frameset", "h1", "h2", "h3", "h4", "h5", "h6", "head", "header", "hr", "html", "iframe", "legend", "li", "link", "main", "menu", "menuitem", "nav", "noframes", "ol", "optgroup", "option", "p", "param", "search", "section", "summary", "table", "tbody", "td", "tfoot", "th", "thead", "title", "tr", "track", "ul", ]; // Regexps to match html elements const attr_name = "[a-zA-Z_:][a-zA-Z0-9:._-]*"; const unquoted = "[^\"'=<>`\\x00-\\x20]+"; const single_quoted = "'[^']*'"; const double_quoted = '"[^"]*"'; const attr_value = "(?:" + unquoted + "|" + single_quoted + "|" + double_quoted + ")"; const attribute = "(?:\\s+" + attr_name + "(?:\\s*=\\s*" + attr_value + ")?)"; const open_tag = "<[A-Za-z][A-Za-z0-9\\-]*" + attribute + "*\\s*\\/?>"; const close_tag = "<\\/[A-Za-z][A-Za-z0-9\\-]*\\s*>"; const comment = ""; const processing = "<[?][\\s\\S]*?[?]>"; const declaration = "]*>"; const cdata = ""; const HTML_TAG_RE = new RegExp("^(?:" + open_tag + "|" + close_tag + "|" + comment + "|" + processing + "|" + declaration + "|" + cdata + ")"); const HTML_OPEN_CLOSE_TAG_RE = new RegExp("^(?:" + open_tag + "|" + close_tag + ")"); // HTML block // An array of opening and corresponding closing sequences for html tags, // last argument defines whether it can terminate a paragraph or not // const HTML_SEQUENCES = [ [/^<(script|pre|style|textarea)(?=(\s|>|$))/i, /<\/(script|pre|style|textarea)>/i, true], [/^/, true], [/^<\?/, /\?>/, true], [/^/, true], [/^/, true], [new RegExp("^|$))", "i"), /^$/, true], [new RegExp(HTML_OPEN_CLOSE_TAG_RE.source + "\\s*$"), /^$/, false], ]; function html_block(state, startLine, endLine, silent) { let pos = state.bMarks[startLine] + state.tShift[startLine]; let max = state.eMarks[startLine]; // if it's indented more than 3 spaces, it should be a code block if (state.sCount[startLine] - state.blkIndent >= 4) { return false; } if (!state.md.options.html) { return false; } if (state.src.charCodeAt(pos) !== 0x3c /* < */) { return false; } let lineText = state.src.slice(pos, max); let i = 0; for (; i < HTML_SEQUENCES.length; i++) { if (HTML_SEQUENCES[i][0].test(lineText)) { break; } } if (i === HTML_SEQUENCES.length) { return false; } if (silent) { // true if this sequence can be a terminator, false otherwise return HTML_SEQUENCES[i][2]; } let nextLine = startLine + 1; // Block types 6 and 7 (the only ones whose end condition is a blank line) // have `/^$/` as their closing regexp. For all other types (1-5, e.g. // `