@aicayzer/inkkit 0.1.5 → 0.1.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -5
- package/dist/THIRD-PARTY-NOTICES.txt +1 -1
- package/dist/chunks/{core-D7n0IhAZ.js → core-DkL-wDqy.js} +499 -244
- package/dist/chunks/{highlight-BIasnL-b.js → highlight-TSUdUlMi.js} +1 -1
- package/dist/core.js +1 -1
- package/dist/editor.d.ts +7 -0
- package/dist/index.js +1 -1
- package/package.json +2 -2
|
@@ -22734,7 +22734,7 @@ var SerializerState = class extends Stack {
|
|
|
22734
22734
|
#closeMark;
|
|
22735
22735
|
};
|
|
22736
22736
|
//#endregion
|
|
22737
|
-
//#region node_modules/.pnpm/prosemirror-view@1.42.6_patch_hash=
|
|
22737
|
+
//#region node_modules/.pnpm/prosemirror-view@1.42.6_patch_hash=808819d35255b79fc131ebc4873ab2fbcd47cd8f5bc05cd11e8caab02ecee548/node_modules/prosemirror-view/dist/index.js
|
|
22738
22738
|
var domIndex = function(node) {
|
|
22739
22739
|
for (var index = 0;; index++) {
|
|
22740
22740
|
node = node.previousSibling;
|
|
@@ -25232,6 +25232,17 @@ editHandlers.keydown = (view, _event) => {
|
|
|
25232
25232
|
view.input.lastKeyCodeTime = Date.now();
|
|
25233
25233
|
if (android && chrome && event.keyCode == 13) return;
|
|
25234
25234
|
if (event.keyCode != 229) view.domObserver.forceFlush();
|
|
25235
|
+
if (event.keyCode != 229 && ![
|
|
25236
|
+
16,
|
|
25237
|
+
17,
|
|
25238
|
+
18,
|
|
25239
|
+
91,
|
|
25240
|
+
92,
|
|
25241
|
+
93
|
|
25242
|
+
].includes(event.keyCode)) {
|
|
25243
|
+
view.domObserver.flush();
|
|
25244
|
+
view.input.lastFocus = 0;
|
|
25245
|
+
}
|
|
25235
25246
|
if (ios && event.keyCode == 13 && !event.ctrlKey && !event.altKey && !event.metaKey) {
|
|
25236
25247
|
let now = Date.now();
|
|
25237
25248
|
view.input.lastIOSEnter = now;
|
|
@@ -28391,8 +28402,8 @@ function editorLabels(ctx) {
|
|
|
28391
28402
|
return ctx.isInjected(labelsCtx.key) ? ctx.get(labelsCtx.key) : defaultLabels;
|
|
28392
28403
|
}
|
|
28393
28404
|
//#endregion
|
|
28394
|
-
//#region node_modules/.pnpm/dompurify@3.4.
|
|
28395
|
-
/*! @license DOMPurify 3.4.
|
|
28405
|
+
//#region node_modules/.pnpm/dompurify@3.4.17/node_modules/dompurify/dist/purify.es.mjs
|
|
28406
|
+
/*! @license DOMPurify 3.4.17 | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/3.4.17/LICENSE */
|
|
28396
28407
|
function _OverloadYield(e, d) {
|
|
28397
28408
|
this.v = e, this.k = d;
|
|
28398
28409
|
}
|
|
@@ -28516,9 +28527,12 @@ Array.prototype.indexOf;
|
|
|
28516
28527
|
var arrayLastIndexOf = unapply(Array.prototype.lastIndexOf);
|
|
28517
28528
|
var arrayPop = unapply(Array.prototype.pop);
|
|
28518
28529
|
var arrayPush = unapply(Array.prototype.push);
|
|
28519
|
-
Array.prototype.slice;
|
|
28530
|
+
var arraySlice = unapply(Array.prototype.slice);
|
|
28520
28531
|
var arraySplice = unapply(Array.prototype.splice);
|
|
28521
28532
|
var arrayIsArray = Array.isArray;
|
|
28533
|
+
var setCreate = unconstruct(Set);
|
|
28534
|
+
var setAdd = unapply(Set.prototype.add);
|
|
28535
|
+
var setHas = unapply(Set.prototype.has);
|
|
28522
28536
|
var stringToLowerCase = unapply(String.prototype.toLowerCase);
|
|
28523
28537
|
var stringToString = unapply(String.prototype.toString);
|
|
28524
28538
|
var stringMatch = unapply(String.prototype.match);
|
|
@@ -28663,6 +28677,25 @@ function lookupGetter(object, prop) {
|
|
|
28663
28677
|
}
|
|
28664
28678
|
return fallbackValue;
|
|
28665
28679
|
}
|
|
28680
|
+
/**
|
|
28681
|
+
* Like lookupGetter, but for accessor properties only, and returns null
|
|
28682
|
+
* instead of a null-returning fallback when no getter exists on the chain.
|
|
28683
|
+
* Lets a caller fall back to a direct property read in environments that
|
|
28684
|
+
* lack the getter, where a fallback returning null would be indistinguishable
|
|
28685
|
+
* from a legitimate null value (e.g. namespaceURI of a no-namespace element).
|
|
28686
|
+
*
|
|
28687
|
+
* @param object - The object (usually a prototype) to start the lookup at.
|
|
28688
|
+
* @param prop - The accessor property name.
|
|
28689
|
+
* @returns The unapplied getter, or null if none was found.
|
|
28690
|
+
*/
|
|
28691
|
+
function lookupAccessor(object, prop) {
|
|
28692
|
+
while (object !== null) {
|
|
28693
|
+
const desc = getOwnPropertyDescriptor(object, prop);
|
|
28694
|
+
if (desc) return desc.get ? unapply(desc.get) : null;
|
|
28695
|
+
object = getPrototypeOf(object);
|
|
28696
|
+
}
|
|
28697
|
+
return null;
|
|
28698
|
+
}
|
|
28666
28699
|
function isRegex(value) {
|
|
28667
28700
|
try {
|
|
28668
28701
|
regExpTest(value, "");
|
|
@@ -29347,7 +29380,7 @@ var NODE_TYPE = {
|
|
|
29347
29380
|
documentFragment: 11,
|
|
29348
29381
|
notation: 12
|
|
29349
29382
|
};
|
|
29350
|
-
var
|
|
29383
|
+
var LITERAL_TEXT_ELEMENTS = freeze(addToSet({}, [
|
|
29351
29384
|
"style",
|
|
29352
29385
|
"script",
|
|
29353
29386
|
"xmp",
|
|
@@ -29356,15 +29389,7 @@ var LITERAL_TEXT_ELEMENT_NAMES = [
|
|
|
29356
29389
|
"noframes",
|
|
29357
29390
|
"plaintext",
|
|
29358
29391
|
"noscript"
|
|
29359
|
-
];
|
|
29360
|
-
var LITERAL_TEXT_ELEMENTS = freeze(addToSet({}, LITERAL_TEXT_ELEMENT_NAMES));
|
|
29361
|
-
var LITERAL_TEXT_CLOSE = function() {
|
|
29362
|
-
const map = {};
|
|
29363
|
-
arrayForEach(LITERAL_TEXT_ELEMENT_NAMES, (name) => {
|
|
29364
|
-
map[name] = seal(new RegExp("</" + name + "(?=[\\t\\n\\f\\r />])", "i"));
|
|
29365
|
-
});
|
|
29366
|
-
return freeze(map);
|
|
29367
|
-
}();
|
|
29392
|
+
]));
|
|
29368
29393
|
var getGlobal = function getGlobal() {
|
|
29369
29394
|
return typeof window === "undefined" ? null : window;
|
|
29370
29395
|
};
|
|
@@ -29442,7 +29467,7 @@ var _resolveObjectOption = function _resolveObjectOption(cfg, key, makeFallback)
|
|
|
29442
29467
|
function createDOMPurify() {
|
|
29443
29468
|
let window = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : getGlobal();
|
|
29444
29469
|
const DOMPurify = (root) => createDOMPurify(root);
|
|
29445
|
-
DOMPurify.version = "3.4.
|
|
29470
|
+
DOMPurify.version = "3.4.17";
|
|
29446
29471
|
DOMPurify.removed = [];
|
|
29447
29472
|
if (!window || !window.document || window.document.nodeType !== NODE_TYPE.document || !window.Element) {
|
|
29448
29473
|
DOMPurify.isSupported = false;
|
|
@@ -29474,6 +29499,10 @@ function createDOMPurify() {
|
|
|
29474
29499
|
const _readNodeName = function _readNodeName(node) {
|
|
29475
29500
|
return getNodeName ? getNodeName(node) : node.nodeName;
|
|
29476
29501
|
};
|
|
29502
|
+
const getNamespaceURI = lookupAccessor(ElementPrototype, "namespaceURI");
|
|
29503
|
+
const _readNamespaceURI = function _readNamespaceURI(element) {
|
|
29504
|
+
return getNamespaceURI ? getNamespaceURI(element) : element.namespaceURI;
|
|
29505
|
+
};
|
|
29477
29506
|
if (typeof HTMLTemplateElement === "function") {
|
|
29478
29507
|
const template = document.createElement("template");
|
|
29479
29508
|
if (template.content && template.content.ownerDocument) document = template.content.ownerDocument;
|
|
@@ -29594,6 +29623,7 @@ function createDOMPurify() {
|
|
|
29594
29623
|
const SANITIZE_NAMED_PROPS_PREFIX = "user-content-";
|
|
29595
29624
|
let KEEP_CONTENT = true;
|
|
29596
29625
|
let IN_PLACE = false;
|
|
29626
|
+
let IN_PLACE_VISITED = null;
|
|
29597
29627
|
let USE_PROFILES = {};
|
|
29598
29628
|
let FORBID_CONTENTS = null;
|
|
29599
29629
|
const DEFAULT_FORBID_CONTENTS = addToSet({}, [
|
|
@@ -29867,18 +29897,23 @@ function createDOMPurify() {
|
|
|
29867
29897
|
* return. Return true otherwise.
|
|
29868
29898
|
*/
|
|
29869
29899
|
const _checkValidNamespace = function _checkValidNamespace(element) {
|
|
29870
|
-
|
|
29871
|
-
|
|
29872
|
-
|
|
29873
|
-
|
|
29874
|
-
|
|
29875
|
-
|
|
29876
|
-
|
|
29877
|
-
|
|
29878
|
-
|
|
29879
|
-
|
|
29880
|
-
|
|
29881
|
-
|
|
29900
|
+
const elementNamespace = _readNamespaceURI(element);
|
|
29901
|
+
const tagName = stringToLowerCase(_readNodeName(element));
|
|
29902
|
+
const parentNode = getParentNode(element);
|
|
29903
|
+
let parent;
|
|
29904
|
+
let parentTagName;
|
|
29905
|
+
if (parentNode && _readNodeType(parentNode) === NODE_TYPE.element) {
|
|
29906
|
+
parent = { namespaceURI: _readNamespaceURI(parentNode) };
|
|
29907
|
+
parentTagName = stringToLowerCase(_readNodeName(parentNode));
|
|
29908
|
+
} else {
|
|
29909
|
+
parent = { namespaceURI: NAMESPACE };
|
|
29910
|
+
parentTagName = "template";
|
|
29911
|
+
}
|
|
29912
|
+
if (!ALLOWED_NAMESPACES[elementNamespace]) return false;
|
|
29913
|
+
if (elementNamespace === SVG_NAMESPACE) return _checkSvgNamespace(tagName, parent, parentTagName);
|
|
29914
|
+
if (elementNamespace === MATHML_NAMESPACE) return _checkMathMlNamespace(tagName, parent, parentTagName);
|
|
29915
|
+
if (elementNamespace === HTML_NAMESPACE) return _checkHtmlNamespace(tagName, parent, parentTagName);
|
|
29916
|
+
if (PARSER_MEDIA_TYPE === "application/xhtml+xml" && ALLOWED_NAMESPACES[elementNamespace]) return true;
|
|
29882
29917
|
return false;
|
|
29883
29918
|
};
|
|
29884
29919
|
/**
|
|
@@ -29922,36 +29957,21 @@ function createDOMPurify() {
|
|
|
29922
29957
|
}
|
|
29923
29958
|
};
|
|
29924
29959
|
/**
|
|
29925
|
-
*
|
|
29926
|
-
*
|
|
29927
|
-
*
|
|
29928
|
-
* (campaign-3 F2). An internal throw mid-walk — e.g. a page-registered
|
|
29929
|
-
* custom element's reaction detaches a node so `_forceRemove`'s deliberate
|
|
29930
|
-
* parentless guard throws, or any other re-entrant engine mutation — would
|
|
29931
|
-
* otherwise leave the caller's *live* tree half-sanitized, with everything
|
|
29932
|
-
* after the abort point still carrying its handlers. There is no safe way
|
|
29933
|
-
* to resume the walk (the tree mutated under us), so we strip the root bare:
|
|
29934
|
-
* remove every child and every attribute, then let the caller's catch see
|
|
29935
|
-
* the original error. Clobber-safe (cached `remove`/`childNodes`/`attributes`
|
|
29936
|
-
* getters; the root was already clobber-pre-flighted at the IN_PLACE entry).
|
|
29960
|
+
* Fail-closed teardown of an in-place root: strip every disallowed attribute
|
|
29961
|
+
* in the subtree first, so nothing armed is left behind, then remove all
|
|
29962
|
+
* children and the root's own attributes.
|
|
29937
29963
|
*
|
|
29938
29964
|
* @param root the in-place root to empty
|
|
29939
29965
|
*/
|
|
29940
29966
|
const _neutralizeRoot = function _neutralizeRoot(root) {
|
|
29941
29967
|
_neutralizeSubtree(root);
|
|
29942
29968
|
const childNodes = getChildNodes(root);
|
|
29943
|
-
if (childNodes) {
|
|
29944
|
-
|
|
29945
|
-
|
|
29946
|
-
|
|
29947
|
-
|
|
29948
|
-
|
|
29949
|
-
try {
|
|
29950
|
-
remove(child);
|
|
29951
|
-
} catch (_) {}
|
|
29952
|
-
});
|
|
29953
|
-
}
|
|
29954
|
-
const attributes = getAttributes(root);
|
|
29969
|
+
if (childNodes) arrayForEach(arraySlice(childNodes), (child) => {
|
|
29970
|
+
try {
|
|
29971
|
+
remove(child);
|
|
29972
|
+
} catch (_) {}
|
|
29973
|
+
});
|
|
29974
|
+
const attributes = _readNodeType(root) === NODE_TYPE.element ? getAttributes(root) : null;
|
|
29955
29975
|
if (attributes) for (let i = attributes.length - 1; i >= 0; --i) {
|
|
29956
29976
|
const attribute = attributes[i];
|
|
29957
29977
|
const name = attribute && attribute.name;
|
|
@@ -29959,18 +29979,9 @@ function createDOMPurify() {
|
|
|
29959
29979
|
}
|
|
29960
29980
|
};
|
|
29961
29981
|
/**
|
|
29962
|
-
*
|
|
29963
|
-
*
|
|
29964
|
-
*
|
|
29965
|
-
* lookup key for HTML elements in an HTML document, so they silently miss an
|
|
29966
|
-
* attribute whose stored qualified name still contains uppercase ASCII
|
|
29967
|
-
* letters. That happens when the node came from a case-preserving source
|
|
29968
|
-
* (an XML/XHTML document imported via importNode(), or createAttributeNS()),
|
|
29969
|
-
* where e.g. `ONERROR` survives the walk: the policy check lowercases to
|
|
29970
|
-
* `onerror` and rejects it, but `removeAttribute('ONERROR')` looks up
|
|
29971
|
-
* `onerror` and finds nothing. Remove the exact Attr node instead, which is
|
|
29972
|
-
* case- and namespace-exact, and fall back to name-based removal only when
|
|
29973
|
-
* the caller could not supply the node.
|
|
29982
|
+
* Removes an attribute, preferably as its exact Attr node: name-based
|
|
29983
|
+
* removal lowercases the name in HTML documents and misses case-preserved
|
|
29984
|
+
* names, e.g. from imported XHTML.
|
|
29974
29985
|
*
|
|
29975
29986
|
* @param name an Attribute name
|
|
29976
29987
|
* @param element a DOM node
|
|
@@ -30004,12 +30015,8 @@ function createDOMPurify() {
|
|
|
30004
30015
|
}
|
|
30005
30016
|
};
|
|
30006
30017
|
/**
|
|
30007
|
-
*
|
|
30008
|
-
*
|
|
30009
|
-
* Removes every attribute the active configuration does not allow from a
|
|
30010
|
-
* single element, using the same allowlist as the main attribute pass (so
|
|
30011
|
-
* `on*` handlers go, but no `/^on/` blocklist is introduced). Used only to
|
|
30012
|
-
* neutralise nodes that are being discarded from an in-place tree.
|
|
30018
|
+
* Removes every attribute the configuration does not allow. Used to
|
|
30019
|
+
* neutralize nodes discarded from an in-place tree.
|
|
30013
30020
|
*
|
|
30014
30021
|
* @param element the element to strip
|
|
30015
30022
|
*/
|
|
@@ -30024,77 +30031,158 @@ function createDOMPurify() {
|
|
|
30024
30031
|
}
|
|
30025
30032
|
};
|
|
30026
30033
|
/**
|
|
30027
|
-
*
|
|
30034
|
+
* Iterative depth-first walk, so deep trees cannot overflow the call stack,
|
|
30035
|
+
* reading through the cached getters, so clobbering cannot hide children.
|
|
30036
|
+
* When `visit` returns false, the node's children are skipped. With
|
|
30037
|
+
* `intoShadow`, open shadow roots are walked after their host's children.
|
|
30028
30038
|
*
|
|
30029
|
-
*
|
|
30030
|
-
*
|
|
30031
|
-
*
|
|
30032
|
-
* all drop their subtree wholesale via `_forceRemove`. On the IN_PLACE path
|
|
30033
|
-
* those dropped nodes are detached from the caller's LIVE tree but a
|
|
30034
|
-
* handler-bearing original among them (an `<img onerror>`/`<video>` that was
|
|
30035
|
-
* loading) keeps its queued resource event, which fires in page scope after
|
|
30036
|
-
* sanitize returns. This walks a removed subtree and strips every attribute
|
|
30037
|
-
* the active configuration does not allow — so `on*` handlers are cancelled
|
|
30038
|
-
* through the SAME allowlist that governs kept nodes, not a separate `/^on/`
|
|
30039
|
-
* blocklist. Run synchronously before sanitize returns, i.e. before any
|
|
30040
|
-
* queued event can fire. Hook-free by design: these nodes leave the output,
|
|
30041
|
-
* so firing attribute hooks for them would be surprising. Clobber-safe reads;
|
|
30042
|
-
* a doomed clobbered node may shadow `removeAttribute` (its own attributes are
|
|
30043
|
-
* irrelevant — it is discarded — while its non-clobbered descendants, e.g.
|
|
30044
|
-
* the `<img>`, are reached and scrubbed).
|
|
30045
|
-
*
|
|
30046
|
-
* @param root the root of a removed subtree to neutralise
|
|
30039
|
+
* @param root where to start
|
|
30040
|
+
* @param visit called for every node
|
|
30041
|
+
* @param intoShadow whether to enter open shadow roots
|
|
30047
30042
|
*/
|
|
30048
|
-
const
|
|
30043
|
+
const _walkTree = function _walkTree(root, visit) {
|
|
30044
|
+
let intoShadow = arguments.length > 2 && arguments[2] !== void 0 ? arguments[2] : false;
|
|
30049
30045
|
const stack = [root];
|
|
30050
30046
|
while (stack.length > 0) {
|
|
30051
30047
|
const node = stack.pop();
|
|
30052
|
-
if (
|
|
30048
|
+
if (visit(node) === false) continue;
|
|
30049
|
+
if (intoShadow && _readNodeType(node) === NODE_TYPE.element) {
|
|
30050
|
+
const shadowRoot = getShadowRoot(node);
|
|
30051
|
+
if (_isDocumentFragment(shadowRoot)) stack.push(shadowRoot);
|
|
30052
|
+
}
|
|
30053
30053
|
const childNodes = getChildNodes(node);
|
|
30054
30054
|
if (childNodes) for (let i = childNodes.length - 1; i >= 0; --i) stack.push(childNodes[i]);
|
|
30055
30055
|
}
|
|
30056
30056
|
};
|
|
30057
30057
|
/**
|
|
30058
|
-
*
|
|
30058
|
+
* Strips disallowed attributes from every element of a discarded subtree,
|
|
30059
|
+
* so a handler that is already queued (a loading image) cannot fire.
|
|
30059
30060
|
*
|
|
30060
|
-
*
|
|
30061
|
-
|
|
30061
|
+
* @param root the root of a removed subtree
|
|
30062
|
+
*/
|
|
30063
|
+
const _neutralizeSubtree = function _neutralizeSubtree(root) {
|
|
30064
|
+
_walkTree(root, (node) => {
|
|
30065
|
+
if (_readNodeType(node) === NODE_TYPE.element) _stripDisallowedAttributes(node);
|
|
30066
|
+
});
|
|
30067
|
+
};
|
|
30068
|
+
/**
|
|
30069
|
+
* All nodes under `root`, root first, including open shadow roots. Template
|
|
30070
|
+
* content is not entered, it is inert.
|
|
30062
30071
|
*
|
|
30063
|
-
*
|
|
30064
|
-
*
|
|
30065
|
-
|
|
30066
|
-
|
|
30067
|
-
|
|
30068
|
-
|
|
30069
|
-
|
|
30070
|
-
|
|
30071
|
-
|
|
30072
|
-
|
|
30072
|
+
* @param root the in-place root
|
|
30073
|
+
* @return the nodes
|
|
30074
|
+
*/
|
|
30075
|
+
const _collectLiveNodes = function _collectLiveNodes(root) {
|
|
30076
|
+
const nodes = [];
|
|
30077
|
+
_walkTree(root, (node) => {
|
|
30078
|
+
nodes.push(node);
|
|
30079
|
+
}, true);
|
|
30080
|
+
return nodes;
|
|
30081
|
+
};
|
|
30082
|
+
/**
|
|
30083
|
+
* Strips disallowed attributes from every element that was under the
|
|
30084
|
+
* in-place root when the call started and no longer is, whatever removed
|
|
30085
|
+
* or moved it.
|
|
30073
30086
|
*
|
|
30074
|
-
*
|
|
30075
|
-
*
|
|
30076
|
-
|
|
30077
|
-
|
|
30078
|
-
|
|
30087
|
+
* @param root the in-place root
|
|
30088
|
+
* @param before the root's nodes, collected before the walk
|
|
30089
|
+
*/
|
|
30090
|
+
const _neutralizeEscapedElements = function _neutralizeEscapedElements(root, before) {
|
|
30091
|
+
const reachable = setCreate();
|
|
30092
|
+
arrayForEach(_collectLiveNodes(root), (node) => {
|
|
30093
|
+
if (_readNodeType(node) === NODE_TYPE.element) setAdd(reachable, node);
|
|
30094
|
+
});
|
|
30095
|
+
arrayForEach(before, (node) => {
|
|
30096
|
+
if (_readNodeType(node) === NODE_TYPE.element && !setHas(reachable, node)) _stripDisallowedAttributes(node);
|
|
30097
|
+
});
|
|
30098
|
+
};
|
|
30099
|
+
/**
|
|
30100
|
+
* Neutralizes everything that left the in-place tree during the call: what
|
|
30101
|
+
* DOMPurify removed, and what hooks or custom elements removed or moved out.
|
|
30079
30102
|
*
|
|
30080
|
-
*
|
|
30081
|
-
*
|
|
30103
|
+
* @param root the in-place root
|
|
30104
|
+
* @param before the root's nodes, collected before the walk
|
|
30105
|
+
* @return true if the root itself was removed
|
|
30106
|
+
*/
|
|
30107
|
+
const _neutralizeDiscarded = function _neutralizeDiscarded(root, before) {
|
|
30108
|
+
let rootRemoved = false;
|
|
30109
|
+
arrayForEach(DOMPurify.removed, (entry) => {
|
|
30110
|
+
if (entry.element) {
|
|
30111
|
+
rootRemoved = rootRemoved || entry.element === root;
|
|
30112
|
+
_neutralizeSubtree(entry.element);
|
|
30113
|
+
}
|
|
30114
|
+
});
|
|
30115
|
+
_neutralizeEscapedElements(root, before);
|
|
30116
|
+
return rootRemoved;
|
|
30117
|
+
};
|
|
30118
|
+
const MAX_UNVISITED_ROUNDS = 8;
|
|
30119
|
+
/**
|
|
30120
|
+
* The walk only moves forward, so a node moved to a position it already
|
|
30121
|
+
* passed is never visited. Give every original node that is still in the
|
|
30122
|
+
* tree but unvisited the normal treatment, hooks included. Nodes created
|
|
30123
|
+
* during the call are left alone. Fails closed if the tree keeps changing.
|
|
30124
|
+
*
|
|
30125
|
+
* @param root the in-place root
|
|
30126
|
+
* @param before the root's nodes, collected before the walk
|
|
30127
|
+
*/
|
|
30128
|
+
const _sanitizeUnvisitedNodes = function _sanitizeUnvisitedNodes(root, before) {
|
|
30129
|
+
const visited = IN_PLACE_VISITED;
|
|
30130
|
+
let original = null;
|
|
30131
|
+
const getOriginal = () => {
|
|
30132
|
+
if (!original) {
|
|
30133
|
+
original = setCreate();
|
|
30134
|
+
arrayForEach(before, (beforeNode) => {
|
|
30135
|
+
setAdd(original, beforeNode);
|
|
30136
|
+
});
|
|
30137
|
+
}
|
|
30138
|
+
return original;
|
|
30139
|
+
};
|
|
30140
|
+
for (let round = 0;; round++) {
|
|
30141
|
+
const live = _collectLiveNodes(root);
|
|
30142
|
+
const containers = [root];
|
|
30143
|
+
let pending = false;
|
|
30144
|
+
arrayForEach(live, (node) => {
|
|
30145
|
+
if (_readNodeType(node) === NODE_TYPE.documentFragment) arrayPush(containers, node);
|
|
30146
|
+
else if (!pending && !setHas(visited, node)) pending = setHas(getOriginal(), node);
|
|
30147
|
+
});
|
|
30148
|
+
if (!pending) return;
|
|
30149
|
+
if (round >= MAX_UNVISITED_ROUNDS) throw typeErrorCreate("the tree kept changing during in-place sanitization; refusing to return it");
|
|
30150
|
+
arrayForEach(containers, (container, index) => {
|
|
30151
|
+
_sanitizeUnvisitedIn(container, index > 0, root, getOriginal());
|
|
30152
|
+
});
|
|
30153
|
+
}
|
|
30154
|
+
};
|
|
30155
|
+
/**
|
|
30156
|
+
* One _sanitizeUnvisitedNodes pass over the root or an open shadow root.
|
|
30082
30157
|
*
|
|
30083
|
-
*
|
|
30084
|
-
*
|
|
30085
|
-
*
|
|
30086
|
-
*
|
|
30087
|
-
|
|
30088
|
-
|
|
30158
|
+
* @param container the root or an open shadow root
|
|
30159
|
+
* @param inShadow whether container is a shadow root
|
|
30160
|
+
* @param root the in-place root
|
|
30161
|
+
* @param original the root's nodes collected before the walk, as a set
|
|
30162
|
+
*/
|
|
30163
|
+
const _sanitizeUnvisitedIn = function _sanitizeUnvisitedIn(container, inShadow, root, original) {
|
|
30164
|
+
const iterator = _createNodeIterator(container);
|
|
30165
|
+
let node;
|
|
30166
|
+
while (node = iterator.nextNode()) {
|
|
30167
|
+
if (!setHas(original, node) || setHas(IN_PLACE_VISITED, node)) continue;
|
|
30168
|
+
if (inShadow) _executeHooks(hooks.uponSanitizeShadowNode, node, null);
|
|
30169
|
+
_sanitizeElements(node, root);
|
|
30170
|
+
_sanitizeAttributes(node, root);
|
|
30171
|
+
if (_isDocumentFragment(node.content)) _sanitizeShadowDOM2(node.content);
|
|
30172
|
+
}
|
|
30173
|
+
};
|
|
30174
|
+
/**
|
|
30175
|
+
* Records that _sanitizeElements processed `node`. No-op outside IN_PLACE.
|
|
30089
30176
|
*
|
|
30090
|
-
* @param
|
|
30177
|
+
* @param node the node being processed
|
|
30091
30178
|
*/
|
|
30179
|
+
const _markVisited = function _markVisited(node) {
|
|
30180
|
+
if (IN_PLACE_VISITED) setAdd(IN_PLACE_VISITED, node);
|
|
30181
|
+
};
|
|
30092
30182
|
/**
|
|
30093
|
-
*
|
|
30094
|
-
*
|
|
30095
|
-
*
|
|
30096
|
-
* everywhere except on <label>/<output>, and the whole policy is gated on
|
|
30097
|
-
* SAFE_FOR_XML (see the rationale block in _isValidAttribute).
|
|
30183
|
+
* Patch-linkage attributes (declarative partial updates): `patchsrc`
|
|
30184
|
+
* always, `for` everywhere except on <label> and <output>. Gated on
|
|
30185
|
+
* SAFE_FOR_XML. Shared by the IN_PLACE pre-pass and _isValidAttribute.
|
|
30098
30186
|
*
|
|
30099
30187
|
* @param lcName the transformCaseFunc'd attribute name
|
|
30100
30188
|
* @param lcTag the transformCaseFunc'd tag name of the carrying element
|
|
@@ -30105,17 +30193,26 @@ function createDOMPurify() {
|
|
|
30105
30193
|
if (lcName === "patchsrc") return true;
|
|
30106
30194
|
return lcName === "for" && lcTag !== "label" && lcTag !== "output";
|
|
30107
30195
|
};
|
|
30196
|
+
/**
|
|
30197
|
+
* IN_PLACE pre-pass: sever all patch linkage before the walk, so no patch
|
|
30198
|
+
* can fire mid-walk into a part of the tree the walker already passed.
|
|
30199
|
+
* Removes range markers (processing instructions, comments with markup)
|
|
30200
|
+
* and strips linkage attributes. It cannot undo a patch that already fired.
|
|
30201
|
+
*
|
|
30202
|
+
* TODO: if the shipped feature can patch through `for` on <label>/<output>,
|
|
30203
|
+
* drop that pair here too (see test/declarative-patch-probe.html).
|
|
30204
|
+
*
|
|
30205
|
+
* @param root the in-place root to sweep
|
|
30206
|
+
*/
|
|
30108
30207
|
const _neutralizePatchLinkage = function _neutralizePatchLinkage(root) {
|
|
30109
30208
|
if (!SAFE_FOR_XML) return;
|
|
30110
|
-
|
|
30111
|
-
while (stack.length > 0) {
|
|
30112
|
-
const node = stack.pop();
|
|
30209
|
+
_walkTree(root, (node) => {
|
|
30113
30210
|
const nodeType = _readNodeType(node);
|
|
30114
30211
|
if (nodeType === NODE_TYPE.processingInstruction || nodeType === NODE_TYPE.comment && regExpTest(COMMENT_MARKUP_PROBE, node.data)) {
|
|
30115
30212
|
try {
|
|
30116
30213
|
remove(node);
|
|
30117
30214
|
} catch (_) {}
|
|
30118
|
-
|
|
30215
|
+
return false;
|
|
30119
30216
|
}
|
|
30120
30217
|
if (nodeType === NODE_TYPE.element) {
|
|
30121
30218
|
const element = node;
|
|
@@ -30125,9 +30222,8 @@ function createDOMPurify() {
|
|
|
30125
30222
|
if (element.hasAttribute && element.hasAttribute("for") && _isPatchLinkageAttribute("for", lcTag)) element.removeAttribute("for");
|
|
30126
30223
|
} catch (_) {}
|
|
30127
30224
|
}
|
|
30128
|
-
|
|
30129
|
-
|
|
30130
|
-
}
|
|
30225
|
+
return true;
|
|
30226
|
+
});
|
|
30131
30227
|
};
|
|
30132
30228
|
/**
|
|
30133
30229
|
* _initDocument
|
|
@@ -30204,6 +30300,27 @@ function createDOMPurify() {
|
|
|
30204
30300
|
*/
|
|
30205
30301
|
const _scrubTemplateExpressions2 = function _scrubTemplateExpressions(node) {
|
|
30206
30302
|
var _node$querySelectorAl;
|
|
30303
|
+
_scrubOwnCharacterData(node);
|
|
30304
|
+
const templates = (_node$querySelectorAl = node.querySelectorAll) === null || _node$querySelectorAl === void 0 ? void 0 : _node$querySelectorAl.call(node, "template");
|
|
30305
|
+
if (templates) arrayForEach(templates, (tmpl) => {
|
|
30306
|
+
if (_isDocumentFragment(tmpl.content)) _scrubTemplateExpressions2(tmpl.content);
|
|
30307
|
+
});
|
|
30308
|
+
};
|
|
30309
|
+
/**
|
|
30310
|
+
* _scrubOwnCharacterData
|
|
30311
|
+
*
|
|
30312
|
+
* Non-recursive core of _scrubTemplateExpressions: normalize `node` and
|
|
30313
|
+
* strip template expressions from its own character data only. Does not
|
|
30314
|
+
* enter <template>.content or attached shadow roots (NodeIterator never
|
|
30315
|
+
* does). Used by _sanitizeShadowDOM, where every nested template content
|
|
30316
|
+
* and shadow root is already walked - and therefore scrubbed - by its own
|
|
30317
|
+
* recursive _sanitizeShadowDOM call; recursing here as well would rescrub
|
|
30318
|
+
* each nested fragment once per ancestor, which is quadratic in nesting
|
|
30319
|
+
* depth.
|
|
30320
|
+
*
|
|
30321
|
+
* @param node The root whose own character data should be scrubbed.
|
|
30322
|
+
*/
|
|
30323
|
+
const _scrubOwnCharacterData = function _scrubOwnCharacterData(node) {
|
|
30207
30324
|
node.normalize();
|
|
30208
30325
|
const doc = getOwnerDocument ? getOwnerDocument(node) : node.ownerDocument;
|
|
30209
30326
|
const walker = createNodeIterator.call(doc || node, node, NodeFilter.SHOW_TEXT | NodeFilter.SHOW_COMMENT | NodeFilter.SHOW_CDATA_SECTION | NodeFilter.SHOW_PROCESSING_INSTRUCTION, null);
|
|
@@ -30212,10 +30329,6 @@ function createDOMPurify() {
|
|
|
30212
30329
|
currentNode.data = _stripTemplateExpressions(currentNode.data);
|
|
30213
30330
|
currentNode = walker.nextNode();
|
|
30214
30331
|
}
|
|
30215
|
-
const templates = (_node$querySelectorAl = node.querySelectorAll) === null || _node$querySelectorAl === void 0 ? void 0 : _node$querySelectorAl.call(node, "template");
|
|
30216
|
-
if (templates) arrayForEach(templates, (tmpl) => {
|
|
30217
|
-
if (_isDocumentFragment(tmpl.content)) _scrubTemplateExpressions2(tmpl.content);
|
|
30218
|
-
});
|
|
30219
30332
|
};
|
|
30220
30333
|
/**
|
|
30221
30334
|
* _isClobbered
|
|
@@ -30291,7 +30404,7 @@ function createDOMPurify() {
|
|
|
30291
30404
|
*/
|
|
30292
30405
|
const _isUnsafeNode = function _isUnsafeNode(currentNode, tagName) {
|
|
30293
30406
|
if (SAFE_FOR_XML && currentNode.hasChildNodes() && !_isNode(currentNode.firstElementChild) && regExpTest(ELEMENT_MARKUP_PROBE, currentNode.textContent) && regExpTest(ELEMENT_MARKUP_PROBE, currentNode.innerHTML)) return true;
|
|
30294
|
-
if (SAFE_FOR_XML && currentNode
|
|
30407
|
+
if (SAFE_FOR_XML && LITERAL_TEXT_ELEMENTS[tagName] && _readNodeType(currentNode) === NODE_TYPE.element && _readNamespaceURI(currentNode) === HTML_NAMESPACE && (_isNode(currentNode.firstElementChild) || typeof currentNode.textContent === "string" && regExpTest(ELEMENT_MARKUP_PROBE, currentNode.textContent))) return true;
|
|
30295
30408
|
if (currentNode.nodeType === NODE_TYPE.processingInstruction) return true;
|
|
30296
30409
|
if (SAFE_FOR_XML && currentNode.nodeType === NODE_TYPE.comment && regExpTest(COMMENT_MARKUP_PROBE, currentNode.data)) return true;
|
|
30297
30410
|
return false;
|
|
@@ -30335,7 +30448,7 @@ function createDOMPurify() {
|
|
|
30335
30448
|
*/
|
|
30336
30449
|
const _sanitizeDisallowedNode = function _sanitizeDisallowedNode(currentNode, tagName, root) {
|
|
30337
30450
|
if (!FORBID_TAGS[tagName] && _isBasicCustomElement(tagName) && _matchesNameCheck(CUSTOM_ELEMENT_HANDLING.tagNameCheck, tagName)) return false;
|
|
30338
|
-
if (KEEP_CONTENT && !FORBID_CONTENTS[tagName]) {
|
|
30451
|
+
if (KEEP_CONTENT && !FORBID_CONTENTS[tagName] && !FORBID_CONTENTS[stringToLowerCase(tagName)]) {
|
|
30339
30452
|
const parentNode = getParentNode(currentNode);
|
|
30340
30453
|
const childNodes = getChildNodes(currentNode);
|
|
30341
30454
|
if (childNodes && parentNode) {
|
|
@@ -30368,35 +30481,13 @@ function createDOMPurify() {
|
|
|
30368
30481
|
return set === defaultSet || set === setConfigSet ? clone(set) : set;
|
|
30369
30482
|
};
|
|
30370
30483
|
/**
|
|
30371
|
-
*
|
|
30372
|
-
*
|
|
30373
|
-
*
|
|
30374
|
-
* filtering). Per the cached, unclobberable parentNode getter the node is
|
|
30375
|
-
* genuinely out of the tree, so it can reach neither the serialized
|
|
30376
|
-
* output nor an IN_PLACE live tree; treat it as removed and stop
|
|
30377
|
-
* processing it. Without this guard, the unsafe-node / namespace checks
|
|
30378
|
-
* would call _forceRemove on a parentless node and hit the REPORT-3
|
|
30379
|
-
* fail-closed throw — which exists for nodes DOMPurify wants gone but
|
|
30380
|
-
* *cannot* detach (clobbered / parentless roots), the opposite of a node
|
|
30381
|
-
* that is already safely gone. The walk root is exempt: a detached
|
|
30382
|
-
* IN_PLACE root is legitimate input and must still be fully sanitized,
|
|
30383
|
-
* and a kill-decision on it must keep hitting the REPORT-3 throw.
|
|
30384
|
-
*
|
|
30385
|
-
* Nodes detached by hooks stay the hook's responsibility for placement:
|
|
30386
|
-
* they are not recorded in DOMPurify.removed, so the post-walk IN_PLACE
|
|
30387
|
-
* pass (which iterates DOMPurify.removed) does not reach them. But a
|
|
30388
|
-
* hook-detached subtree can still hold a queued resource-event handler -
|
|
30389
|
-
* e.g. an <img onload> that began loading when the caller built the live
|
|
30390
|
-
* tree - which fires in page scope after sanitize returns even though the
|
|
30391
|
-
* handler never reached the returned tree. That is the audit-5 F1 hazard,
|
|
30392
|
-
* and the documented node.remove() hook pattern walks straight into it.
|
|
30393
|
-
* So on the IN_PLACE path we neutralize the detached subtree inline,
|
|
30394
|
-
* stripping its non-allow-listed attributes before returning, exactly as
|
|
30395
|
-
* the post-walk pass does for _forceRemove'd subtrees.
|
|
30484
|
+
* A hook may have detached the node it was called for. If so, treat it as
|
|
30485
|
+
* removed and stop processing it; the walk root is exempt. On the IN_PLACE
|
|
30486
|
+
* path the detached subtree is neutralized right away.
|
|
30396
30487
|
*
|
|
30397
30488
|
* @param currentNode the node a hook may have detached
|
|
30398
30489
|
* @param root the current walk root
|
|
30399
|
-
* @return true if the node
|
|
30490
|
+
* @return true if the node was detached
|
|
30400
30491
|
*/
|
|
30401
30492
|
const _handleHookDetachedNode = function _handleHookDetachedNode(currentNode, root) {
|
|
30402
30493
|
if (currentNode === root || getParentNode(currentNode) !== null) return false;
|
|
@@ -30413,6 +30504,7 @@ function createDOMPurify() {
|
|
|
30413
30504
|
* @return true if node was killed, false if left alive
|
|
30414
30505
|
*/
|
|
30415
30506
|
const _sanitizeElements = function _sanitizeElements(currentNode, root) {
|
|
30507
|
+
_markVisited(currentNode);
|
|
30416
30508
|
_executeHooks(hooks.beforeSanitizeElements, currentNode, null);
|
|
30417
30509
|
if (_handleHookDetachedNode(currentNode, root)) return true;
|
|
30418
30510
|
if (_isClobbered(currentNode)) {
|
|
@@ -30556,6 +30648,22 @@ function createDOMPurify() {
|
|
|
30556
30648
|
}
|
|
30557
30649
|
};
|
|
30558
30650
|
/**
|
|
30651
|
+
* _isUnsafeAttributeValue
|
|
30652
|
+
*
|
|
30653
|
+
* Value-based attribute checks that the attribute walk applies before any
|
|
30654
|
+
* hook can force-keep an attribute, shared with the public
|
|
30655
|
+
* isValidAttribute() so that it never approves a value sanitize() would
|
|
30656
|
+
* strip.
|
|
30657
|
+
*
|
|
30658
|
+
* @param name the attribute name as found on the node (case preserved)
|
|
30659
|
+
* @param value the attribute value
|
|
30660
|
+
* @returns true if the attribute must be removed because of its value
|
|
30661
|
+
*/
|
|
30662
|
+
const _isUnsafeAttributeValue = function _isUnsafeAttributeValue(name, value) {
|
|
30663
|
+
if (SAFE_FOR_XML && regExpTest(/((--!?|])>)|<\/(style|script|title|xmp|textarea|noscript|iframe|noembed|noframes)/i, value)) return true;
|
|
30664
|
+
return stringToLowerCase(name) === "attributename" && stringMatch(value, "href") !== null;
|
|
30665
|
+
};
|
|
30666
|
+
/**
|
|
30559
30667
|
* _sanitizeAttributes
|
|
30560
30668
|
*
|
|
30561
30669
|
* @protect attributes
|
|
@@ -30599,11 +30707,7 @@ function createDOMPurify() {
|
|
|
30599
30707
|
value = SANITIZE_NAMED_PROPS_PREFIX + value;
|
|
30600
30708
|
recreatedNamedProp = true;
|
|
30601
30709
|
}
|
|
30602
|
-
if (
|
|
30603
|
-
_removeAttribute(name, currentNode, attr);
|
|
30604
|
-
continue;
|
|
30605
|
-
}
|
|
30606
|
-
if (lcName === "attributename" && stringMatch(value, "href")) {
|
|
30710
|
+
if (_isUnsafeAttributeValue(name, value)) {
|
|
30607
30711
|
_removeAttribute(name, currentNode, attr);
|
|
30608
30712
|
continue;
|
|
30609
30713
|
}
|
|
@@ -30651,24 +30755,13 @@ function createDOMPurify() {
|
|
|
30651
30755
|
}
|
|
30652
30756
|
}
|
|
30653
30757
|
}
|
|
30758
|
+
if (SAFE_FOR_TEMPLATES) _scrubOwnCharacterData(fragment);
|
|
30654
30759
|
_executeHooks(hooks.afterSanitizeShadowDOM, fragment, null);
|
|
30655
30760
|
};
|
|
30656
30761
|
/**
|
|
30657
|
-
*
|
|
30658
|
-
*
|
|
30659
|
-
*
|
|
30660
|
-
* the existing _sanitizeShadowDOM pipeline. The default node iterator
|
|
30661
|
-
* does not descend into shadow trees, so nodes inside an attached
|
|
30662
|
-
* shadow root would otherwise be skipped entirely.
|
|
30663
|
-
*
|
|
30664
|
-
* Two real input paths put attached shadow roots in front of us:
|
|
30665
|
-
* 1. IN_PLACE on a DOM node that already has shadow roots attached.
|
|
30666
|
-
* 2. DOM-node input where importNode(dirty, true) deep-clones the
|
|
30667
|
-
* shadow root because it was created with `clonable: true`.
|
|
30668
|
-
*
|
|
30669
|
-
* This pass runs once, up front, so the main iteration loop (and the
|
|
30670
|
-
* existing _sanitizeShadowDOM template-content recursion) stay
|
|
30671
|
-
* untouched — string-input paths are not affected.
|
|
30762
|
+
* Sanitizes every attached shadow root under `root`, since the node
|
|
30763
|
+
* iterator does not enter shadow trees. Needed for IN_PLACE input and for
|
|
30764
|
+
* DOM input whose clonable shadow roots were copied by importNode().
|
|
30672
30765
|
*
|
|
30673
30766
|
* @param root the subtree root to walk for attached shadow roots
|
|
30674
30767
|
*/
|
|
@@ -30733,6 +30826,8 @@ function createDOMPurify() {
|
|
|
30733
30826
|
if (hooks.uponSanitizeAttribute.length > 0) ALLOWED_ATTR = clone(ALLOWED_ATTR);
|
|
30734
30827
|
DOMPurify.removed = [];
|
|
30735
30828
|
const inPlace = IN_PLACE && typeof dirty !== "string" && _isNode(dirty);
|
|
30829
|
+
let inPlaceNodes = [];
|
|
30830
|
+
const outerVisited = IN_PLACE_VISITED;
|
|
30736
30831
|
if (inPlace) {
|
|
30737
30832
|
_neutralizePatchLinkage(dirty);
|
|
30738
30833
|
const nn = _readNodeName(dirty);
|
|
@@ -30747,12 +30842,8 @@ function createDOMPurify() {
|
|
|
30747
30842
|
_neutralizeRoot(dirty);
|
|
30748
30843
|
throw typeErrorCreate("root node is clobbered and cannot be sanitized in-place");
|
|
30749
30844
|
}
|
|
30750
|
-
|
|
30751
|
-
|
|
30752
|
-
} catch (error) {
|
|
30753
|
-
_neutralizeRoot(dirty);
|
|
30754
|
-
throw error;
|
|
30755
|
-
}
|
|
30845
|
+
inPlaceNodes = _collectLiveNodes(dirty);
|
|
30846
|
+
IN_PLACE_VISITED = setCreate();
|
|
30756
30847
|
} else if (_isNode(dirty)) {
|
|
30757
30848
|
body = _initDocument("<!---->");
|
|
30758
30849
|
importedNode = body.ownerDocument.importNode(dirty, true);
|
|
@@ -30768,30 +30859,25 @@ function createDOMPurify() {
|
|
|
30768
30859
|
if (body && FORCE_BODY) _forceRemove(body.firstChild);
|
|
30769
30860
|
const walkRoot = inPlace ? dirty : body;
|
|
30770
30861
|
try {
|
|
30862
|
+
if (inPlace) _sanitizeAttachedShadowRoots(dirty);
|
|
30771
30863
|
const nodeIterator = _createNodeIterator(walkRoot);
|
|
30772
30864
|
while (currentNode = nodeIterator.nextNode()) {
|
|
30773
30865
|
_sanitizeElements(currentNode, walkRoot);
|
|
30774
30866
|
_sanitizeAttributes(currentNode, walkRoot);
|
|
30775
30867
|
if (_isDocumentFragment(currentNode.content)) _sanitizeShadowDOM2(currentNode.content);
|
|
30776
30868
|
}
|
|
30869
|
+
if (inPlace) _sanitizeUnvisitedNodes(dirty, inPlaceNodes);
|
|
30777
30870
|
} catch (error) {
|
|
30778
30871
|
if (inPlace) {
|
|
30779
30872
|
_neutralizeRoot(dirty);
|
|
30780
|
-
|
|
30781
|
-
if (entry.element) _neutralizeSubtree(entry.element);
|
|
30782
|
-
});
|
|
30873
|
+
_neutralizeDiscarded(dirty, inPlaceNodes);
|
|
30783
30874
|
}
|
|
30784
30875
|
throw error;
|
|
30876
|
+
} finally {
|
|
30877
|
+
IN_PLACE_VISITED = outerVisited;
|
|
30785
30878
|
}
|
|
30786
30879
|
if (inPlace) {
|
|
30787
|
-
|
|
30788
|
-
arrayForEach(DOMPurify.removed, (entry) => {
|
|
30789
|
-
if (entry.element) {
|
|
30790
|
-
if (entry.element === dirty) rootWasRemoved = true;
|
|
30791
|
-
_neutralizeSubtree(entry.element);
|
|
30792
|
-
}
|
|
30793
|
-
});
|
|
30794
|
-
if (rootWasRemoved) throw typeErrorCreate("a node selected for removal could not be safely returned; refusing to sanitize in place");
|
|
30880
|
+
if (_neutralizeDiscarded(dirty, inPlaceNodes)) throw typeErrorCreate("a node selected for removal could not be safely returned; refusing to sanitize in place");
|
|
30795
30881
|
if (SAFE_FOR_TEMPLATES) _scrubTemplateExpressions2(dirty);
|
|
30796
30882
|
return dirty;
|
|
30797
30883
|
}
|
|
@@ -30828,6 +30914,7 @@ function createDOMPurify() {
|
|
|
30828
30914
|
if (!CONFIG) _parseConfig({});
|
|
30829
30915
|
const lcTag = transformCaseFunc(tag);
|
|
30830
30916
|
const lcName = transformCaseFunc(attr);
|
|
30917
|
+
if (typeof value === "string" && (_isUnsafeAttributeValue(attr, value) || !ALLOW_SELF_CLOSE_IN_ATTR && regExpTest(SELF_CLOSING_TAG, value))) return false;
|
|
30831
30918
|
return _isValidAttribute(lcTag, lcName, value);
|
|
30832
30919
|
};
|
|
30833
30920
|
DOMPurify.addHook = function(entryPoint, hookFunction) {
|
|
@@ -31167,6 +31254,18 @@ function mermaidPreview(onError) {
|
|
|
31167
31254
|
}));
|
|
31168
31255
|
}
|
|
31169
31256
|
//#endregion
|
|
31257
|
+
//#region src/literal-text.ts
|
|
31258
|
+
/** Literal lines stay in their textblock, including headings and table cells. */
|
|
31259
|
+
function literalTextNodes(schema, text, marks, isHTML) {
|
|
31260
|
+
const nodes = [];
|
|
31261
|
+
const lines = text.replace(/\r\n?/g, "\n").split("\n");
|
|
31262
|
+
lines.forEach((line, index) => {
|
|
31263
|
+
if (index) nodes.push(schema.nodes.hardbreak.create({ isHTML }, null, index === lines.length - 1 && line === "" ? [] : marks));
|
|
31264
|
+
if (line) nodes.push(schema.text(line, marks));
|
|
31265
|
+
});
|
|
31266
|
+
return nodes;
|
|
31267
|
+
}
|
|
31268
|
+
//#endregion
|
|
31170
31269
|
//#region node_modules/.pnpm/rope-sequence@1.3.4/node_modules/rope-sequence/dist/index.js
|
|
31171
31270
|
var GOOD_LEAF_SIZE = 200;
|
|
31172
31271
|
var RopeSequence = function RopeSequence() {};
|
|
@@ -31621,6 +31720,13 @@ A command function that redoes the last undone change, if any.
|
|
|
31621
31720
|
*/
|
|
31622
31721
|
var redo = buildCommand(true, true);
|
|
31623
31722
|
/**
|
|
31723
|
+
The amount of redoable events available in a given editor state.
|
|
31724
|
+
*/
|
|
31725
|
+
function redoDepth(state) {
|
|
31726
|
+
let hist = historyKey.getState(state);
|
|
31727
|
+
return hist ? hist.undone.eventCount : 0;
|
|
31728
|
+
}
|
|
31729
|
+
/**
|
|
31624
31730
|
Returns true if the given transaction was generated by the history
|
|
31625
31731
|
plugin.
|
|
31626
31732
|
*/
|
|
@@ -44042,7 +44148,7 @@ function nodeText(node, missingImages, fileDescriptions) {
|
|
|
44042
44148
|
}
|
|
44043
44149
|
return clipboardText(node.content, missingImages, fileDescriptions);
|
|
44044
44150
|
}
|
|
44045
|
-
function
|
|
44151
|
+
function clipboardDOM(content, schema) {
|
|
44046
44152
|
content = shareableFragment(content);
|
|
44047
44153
|
const container = document.createElement("div");
|
|
44048
44154
|
const serializer = DOMSerializer.fromSchema(schema);
|
|
@@ -44072,14 +44178,30 @@ function clipboardContent(content, schema) {
|
|
|
44072
44178
|
label.textContent = `[${definition.getAttribute("data-inkkit-label")}] `;
|
|
44073
44179
|
definition.prepend(label);
|
|
44074
44180
|
}
|
|
44181
|
+
return container;
|
|
44182
|
+
}
|
|
44183
|
+
function clipboardContent(content, schema) {
|
|
44075
44184
|
return {
|
|
44076
44185
|
text: clipboardText(content),
|
|
44077
|
-
html:
|
|
44186
|
+
html: clipboardDOM(content, schema).innerHTML
|
|
44078
44187
|
};
|
|
44079
44188
|
}
|
|
44080
|
-
|
|
44189
|
+
function clipboardSerializer(schema) {
|
|
44190
|
+
const serializer = DOMSerializer.fromSchema(schema);
|
|
44191
|
+
const safe = new DOMSerializer(serializer.nodes, serializer.marks);
|
|
44192
|
+
safe.serializeFragment = (content, options = {}, target) => {
|
|
44193
|
+
const output = target ?? (options.document ?? document).createDocumentFragment();
|
|
44194
|
+
output.append(...clipboardDOM(content, schema).childNodes);
|
|
44195
|
+
return output;
|
|
44196
|
+
};
|
|
44197
|
+
return safe;
|
|
44198
|
+
}
|
|
44199
|
+
var visibleClipboard = $prose((ctx) => new Plugin({
|
|
44081
44200
|
key: new PluginKey("visibleClipboard"),
|
|
44082
|
-
props: {
|
|
44201
|
+
props: {
|
|
44202
|
+
clipboardSerializer: clipboardSerializer(ctx.get(schemaCtx)),
|
|
44203
|
+
clipboardTextSerializer: (slice) => clipboardText(slice.content)
|
|
44204
|
+
}
|
|
44083
44205
|
}));
|
|
44084
44206
|
/** Export a frozen document fragment; presentation URLs never enter the clipboard. */
|
|
44085
44207
|
async function portableClipboard(content, schema, markdown, adapter, context, completeDiagrams, files, signal = new AbortController().signal) {
|
|
@@ -45023,13 +45145,16 @@ function lineBreakHTML(value) {
|
|
|
45023
45145
|
"<br/>"
|
|
45024
45146
|
].includes(value.trim());
|
|
45025
45147
|
}
|
|
45026
|
-
function restoreInlineBreaks(node) {
|
|
45027
|
-
|
|
45028
|
-
|
|
45029
|
-
|
|
45030
|
-
|
|
45031
|
-
|
|
45032
|
-
|
|
45148
|
+
function restoreInlineBreaks(node, inline = false) {
|
|
45149
|
+
const restore = node.type === "paragraph" && node.children.length > 1 || node.type === "heading" || node.type === "tableCell" || inline;
|
|
45150
|
+
if ("children" in node) {
|
|
45151
|
+
node.children = node.children.map((child) => restore && child.type === "html" && lineBreakHTML(child.value) ? {
|
|
45152
|
+
type: "break",
|
|
45153
|
+
data: { inkkitHTMLBreak: true },
|
|
45154
|
+
position: child.position
|
|
45155
|
+
} : child);
|
|
45156
|
+
node.children.forEach((child) => restoreInlineBreaks(child, restore));
|
|
45157
|
+
}
|
|
45033
45158
|
}
|
|
45034
45159
|
function containsUnsupported(node, images, inline = false) {
|
|
45035
45160
|
if (!inline && node.type === "html" && lineBreakHTML(node.value)) return false;
|
|
@@ -45934,6 +46059,12 @@ function retainSource(before, after, source, canonical, lineEnding, inTable = fa
|
|
|
45934
46059
|
}, source, canonical, lineEnding, inTable);
|
|
45935
46060
|
const first = before.children[0];
|
|
45936
46061
|
if (spacer(first)) return retained;
|
|
46062
|
+
const gap = source.slice(first.position.end.offset, before.children[1].position.start.offset);
|
|
46063
|
+
if (!/\r?\n[ \t]*\r?\n/.test(gap)) {
|
|
46064
|
+
const from = first.position.start.offset - start;
|
|
46065
|
+
const to = before.children[1].position.start.offset - start;
|
|
46066
|
+
return retained.startsWith(source.slice(start, start + to)) ? retained.slice(0, from) + retained.slice(to) : retained;
|
|
46067
|
+
}
|
|
45937
46068
|
const from = first.position.start.offset - start;
|
|
45938
46069
|
const to = first.position.end.offset - start;
|
|
45939
46070
|
return retained.startsWith(source.slice(start, before.children[1].position.start.offset)) ? retained.slice(0, from) + "<br />" + retained.slice(to) : retained;
|
|
@@ -50114,7 +50245,23 @@ function padShortRows(node) {
|
|
|
50114
50245
|
if ("children" in node) node.children.forEach(padShortRows);
|
|
50115
50246
|
}
|
|
50116
50247
|
var remarkDialectPlugin = $remark("remarkDialect", () => remarkDialect);
|
|
50117
|
-
var
|
|
50248
|
+
var preservingSoftBreaks = $remark("preservingSoftBreaks", () => () => (tree) => {
|
|
50249
|
+
const walk = (parent) => {
|
|
50250
|
+
parent.children = parent.children.flatMap((child) => {
|
|
50251
|
+
if ("children" in child) walk(child);
|
|
50252
|
+
if (child.type !== "text" || !/[\r\n]/.test(child.value)) return [child];
|
|
50253
|
+
return child.value.split(/(\r?\n|\r)/).flatMap((value) => !value ? [] : /^(?:\r?\n|\r)$/.test(value) ? [{
|
|
50254
|
+
type: "break",
|
|
50255
|
+
data: { isInline: true }
|
|
50256
|
+
}] : [{
|
|
50257
|
+
type: "text",
|
|
50258
|
+
value
|
|
50259
|
+
}]);
|
|
50260
|
+
});
|
|
50261
|
+
};
|
|
50262
|
+
walk(tree);
|
|
50263
|
+
});
|
|
50264
|
+
var commonmarkWithLiteralReferences = commonmark.filter((plugin) => !remarkInlineLinkPlugin.includes(plugin) && !remarkLineBreak.includes(plugin) && plugin !== hardbreakClearMarkPlugin && !hardbreakKeymap.some((part) => part === plugin) && plugin !== syncHeadingIdPlugin && plugin !== createCodeBlockInputRule);
|
|
50118
50265
|
var breaksWithoutMarks = $prose((ctx) => new Plugin({
|
|
50119
50266
|
key: new PluginKey("inkkitBreaksWithoutMarks"),
|
|
50120
50267
|
appendTransaction(transactions, oldState, newState) {
|
|
@@ -50124,9 +50271,24 @@ var breaksWithoutMarks = $prose((ctx) => new Plugin({
|
|
|
50124
50271
|
const end = oldState.doc.content.findDiffEnd(newState.doc.content).b;
|
|
50125
50272
|
const type = hardbreakSchema.type(ctx);
|
|
50126
50273
|
const tr = newState.tr;
|
|
50274
|
+
newState.doc.nodesBetween(start, Math.max(start, end), (node, pos) => {
|
|
50275
|
+
if (!node.isTextblock || !node.firstChild) return;
|
|
50276
|
+
let count = 0;
|
|
50277
|
+
for (let index = 0; index < node.childCount; index++) {
|
|
50278
|
+
const child = node.child(index);
|
|
50279
|
+
if (child.type !== type || !child.attrs.isInline || child.attrs.isHTML) break;
|
|
50280
|
+
let previous = pos + 1 + count;
|
|
50281
|
+
for (let transaction = transactions.length - 1; transaction >= 0; transaction--) previous = transactions[transaction].mapping.invert().map(previous, 1);
|
|
50282
|
+
const before = oldState.doc.nodeAt(previous);
|
|
50283
|
+
if (before?.type !== type || !before.attrs.isInline || before.attrs.isHTML || oldState.doc.resolve(previous).parentOffset === 0) break;
|
|
50284
|
+
count += child.nodeSize;
|
|
50285
|
+
}
|
|
50286
|
+
if (count) tr.delete(tr.mapping.map(pos + 1), tr.mapping.map(pos + 1 + count));
|
|
50287
|
+
return false;
|
|
50288
|
+
});
|
|
50127
50289
|
newState.doc.nodesBetween(start, Math.max(start, end), (node, pos) => {
|
|
50128
50290
|
if (node.type !== type) return;
|
|
50129
|
-
for (const mark of node.marks) if (mark.type.spec.code) tr.removeMark(pos, pos + 1, mark.type);
|
|
50291
|
+
for (const mark of node.marks) if (mark.type.spec.code) tr.removeMark(tr.mapping.map(pos), tr.mapping.map(pos + 1), mark.type);
|
|
50130
50292
|
});
|
|
50131
50293
|
return tr.steps.length ? tr : null;
|
|
50132
50294
|
}
|
|
@@ -50388,6 +50550,7 @@ function createDialect(images, wikiLinks = false, files = false) {
|
|
|
50388
50550
|
wikiLinks || files ? linkedSyntax(wikiLinks, files) : [],
|
|
50389
50551
|
createLiteralPreservation(images || files),
|
|
50390
50552
|
commonmarkWithLiteralReferences,
|
|
50553
|
+
preservingSoftBreaks,
|
|
50391
50554
|
$node("doc", () => ({
|
|
50392
50555
|
...docSchema.schema,
|
|
50393
50556
|
attrs: {
|
|
@@ -51343,7 +51506,8 @@ var ImageView = class {
|
|
|
51343
51506
|
return;
|
|
51344
51507
|
}
|
|
51345
51508
|
const presentation = state.presentation;
|
|
51346
|
-
if (presentation.kind === "missing"
|
|
51509
|
+
if (presentation.kind === "missing") this.card(label, this.runtime.labels.fileMissing, true);
|
|
51510
|
+
else if (presentation.kind === "error") this.card(label, presentation.message || this.runtime.labels.fileError, true);
|
|
51347
51511
|
else if (presentation.kind === "image") {
|
|
51348
51512
|
this.renderImage(presentation.url, label, width);
|
|
51349
51513
|
this.card(label);
|
|
@@ -53987,6 +54151,7 @@ var PasteController = class {
|
|
|
53987
54151
|
async run(input, files = [], target) {
|
|
53988
54152
|
if (this.dead) throw new InkKitError("destroyed", "The editor was destroyed.");
|
|
53989
54153
|
if (this.pending) throw new InkKitError("operation-pending", "An image paste is still pending.");
|
|
54154
|
+
input = this.options.restoreCopy?.(input) ?? input;
|
|
53990
54155
|
const context = { ...this.options.context() };
|
|
53991
54156
|
const ctx = this.options.ctx();
|
|
53992
54157
|
const view = ctx.get(editorViewCtx);
|
|
@@ -54027,8 +54192,12 @@ var PasteController = class {
|
|
|
54027
54192
|
this.dispatch(view, caretAfterInsertion(view.state.tr.insertText(input.text, anchor.from, anchor.to)).scrollIntoView());
|
|
54028
54193
|
return;
|
|
54029
54194
|
}
|
|
54030
|
-
const
|
|
54031
|
-
|
|
54195
|
+
const anchor = this.anchor(operation);
|
|
54196
|
+
const start = view.state.doc.resolve(anchor.from);
|
|
54197
|
+
const end = view.state.doc.resolve(anchor.to);
|
|
54198
|
+
const marks = view.state.storedMarks ?? (anchor.from === anchor.to ? start.marks() : start.marksAcross(end)) ?? [];
|
|
54199
|
+
const nodes = literalTextNodes(schema, input.text, marks, htmlBreakAt(start));
|
|
54200
|
+
slice = new Slice(Fragment.from(schema.nodes.paragraph.create(null, nodes)), 1, 1);
|
|
54032
54201
|
} else if (input.html) {
|
|
54033
54202
|
const container = visibleContent(ctx, input.html);
|
|
54034
54203
|
const imported = /* @__PURE__ */ new Map();
|
|
@@ -55019,14 +55188,7 @@ function replacementSlice(state, from, to, value) {
|
|
|
55019
55188
|
const nodes = [];
|
|
55020
55189
|
if (start.parent.type.spec.code) {
|
|
55021
55190
|
if (value) nodes.push(state.schema.text(value, marks));
|
|
55022
|
-
} else
|
|
55023
|
-
const isHTML = htmlBreakAt(start);
|
|
55024
|
-
const lines = value.split("\n");
|
|
55025
|
-
lines.forEach((line, index) => {
|
|
55026
|
-
if (index) nodes.push(state.schema.nodes.hardbreak.create({ isHTML }, null, index === lines.length - 1 && line === "" ? [] : marks));
|
|
55027
|
-
if (line) nodes.push(state.schema.text(line, marks));
|
|
55028
|
-
});
|
|
55029
|
-
}
|
|
55191
|
+
} else nodes.push(...literalTextNodes(state.schema, value, marks, htmlBreakAt(start)));
|
|
55030
55192
|
return new Slice(Fragment.fromArray(nodes), 0, 0);
|
|
55031
55193
|
}
|
|
55032
55194
|
function replaceFormatted(view, search, replacement, preflight, all = false) {
|
|
@@ -55853,6 +56015,15 @@ function caretState(state) {
|
|
|
55853
56015
|
quoted: quoted($from, $to)
|
|
55854
56016
|
};
|
|
55855
56017
|
}
|
|
56018
|
+
function terminalSelection(doc) {
|
|
56019
|
+
const $end = doc.resolve(doc.content.size);
|
|
56020
|
+
const gap = GapCursor;
|
|
56021
|
+
if (isImageBlock(doc.lastChild) && gap.valid($end)) return new GapCursor($end);
|
|
56022
|
+
const text = Selection.findFrom($end, -1, true);
|
|
56023
|
+
if (text) return text;
|
|
56024
|
+
if (gap.valid($end)) return new GapCursor($end);
|
|
56025
|
+
throw new InkKitError("preservation", "Document has no safe writing position");
|
|
56026
|
+
}
|
|
55856
56027
|
function quoted($from, $to) {
|
|
55857
56028
|
return $from.blockRange($to, (node) => node.type.name === "blockquote") != null;
|
|
55858
56029
|
}
|
|
@@ -56065,6 +56236,8 @@ var InkKitEditor = class InkKitEditor {
|
|
|
56065
56236
|
typedSteps = [];
|
|
56066
56237
|
undoneSteps = [];
|
|
56067
56238
|
formattedUndone = 0;
|
|
56239
|
+
formattedRedoLimit;
|
|
56240
|
+
historyDirection;
|
|
56068
56241
|
lastSourceInput = 0;
|
|
56069
56242
|
syncingSource = false;
|
|
56070
56243
|
mode = "formatted";
|
|
@@ -56082,6 +56255,52 @@ var InkKitEditor = class InkKitEditor {
|
|
|
56082
56255
|
auxClickHandler;
|
|
56083
56256
|
untrackLinkModifier;
|
|
56084
56257
|
footnoteKeyHandler;
|
|
56258
|
+
localCopies = /* @__PURE__ */ new Map();
|
|
56259
|
+
rememberCopy(output, markdown, content, privateSource = false, actualCopy = false) {
|
|
56260
|
+
let comments = privateSource;
|
|
56261
|
+
content.descendants((node) => {
|
|
56262
|
+
const labelled = (label) => typeof label === "string" && literalCommentSpans(label).length > 0;
|
|
56263
|
+
if (isComment(node) || /^(?:reference|footnote)_/.test(node.type.name) && labelled(node.attrs.label) || node.marks.some((mark) => mark.type.name === "link" && (labelled(mark.attrs.label) || commentProvenance(mark.attrs.referenceContent)))) comments = true;
|
|
56264
|
+
});
|
|
56265
|
+
if (!comments) {
|
|
56266
|
+
if (actualCopy) this.localCopies.clear();
|
|
56267
|
+
return output;
|
|
56268
|
+
}
|
|
56269
|
+
const key = JSON.stringify([
|
|
56270
|
+
output.text,
|
|
56271
|
+
output.html,
|
|
56272
|
+
markdown
|
|
56273
|
+
]);
|
|
56274
|
+
const previous = this.localCopies.get(key);
|
|
56275
|
+
if (previous) {
|
|
56276
|
+
output.html = previous.html;
|
|
56277
|
+
if (actualCopy) this.localCopies.clear();
|
|
56278
|
+
this.localCopies.set(key, previous);
|
|
56279
|
+
return output;
|
|
56280
|
+
}
|
|
56281
|
+
const wrapper = document.createElement("div");
|
|
56282
|
+
const token = Array.from(crypto.getRandomValues(/* @__PURE__ */ new Uint8Array(16)), (byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
56283
|
+
wrapper.setAttribute("data-inkkit-local-copy", token);
|
|
56284
|
+
wrapper.innerHTML = output.html;
|
|
56285
|
+
output.html = wrapper.outerHTML;
|
|
56286
|
+
if (actualCopy) this.localCopies.clear();
|
|
56287
|
+
this.localCopies.set(key, {
|
|
56288
|
+
text: output.text,
|
|
56289
|
+
html: output.html,
|
|
56290
|
+
markdown
|
|
56291
|
+
});
|
|
56292
|
+
return output;
|
|
56293
|
+
}
|
|
56294
|
+
retainCopy(html) {
|
|
56295
|
+
for (const [key, copy] of this.localCopies) if (copy.html !== html) this.localCopies.delete(key);
|
|
56296
|
+
}
|
|
56297
|
+
restoreCopy(input) {
|
|
56298
|
+
const copy = [...this.localCopies.values()].find((copy) => copy.text === input.text && copy.html === input.html);
|
|
56299
|
+
return copy && input.markdown == null && !input.plainText && copy.text === input.text && copy.html === input.html ? {
|
|
56300
|
+
...input,
|
|
56301
|
+
markdown: copy.markdown
|
|
56302
|
+
} : input;
|
|
56303
|
+
}
|
|
56085
56304
|
copyHandler;
|
|
56086
56305
|
originalSource = "";
|
|
56087
56306
|
reportingSuppressed = false;
|
|
@@ -56297,7 +56516,9 @@ var InkKitEditor = class InkKitEditor {
|
|
|
56297
56516
|
revision: this.session.mark(),
|
|
56298
56517
|
lastMarkdown: this.lastMarkdown,
|
|
56299
56518
|
literalMatch: this.literalMatch,
|
|
56300
|
-
textScope: this.textScope
|
|
56519
|
+
textScope: this.textScope,
|
|
56520
|
+
formattedRedoLimit: this.formattedRedoLimit,
|
|
56521
|
+
formattedUndone: this.formattedUndone
|
|
56301
56522
|
};
|
|
56302
56523
|
}
|
|
56303
56524
|
finish(call, before, failure) {
|
|
@@ -56319,6 +56540,8 @@ var InkKitEditor = class InkKitEditor {
|
|
|
56319
56540
|
this.lastMarkdown = before.lastMarkdown;
|
|
56320
56541
|
this.literalMatch = before.literalMatch;
|
|
56321
56542
|
this.textScope = before.textScope;
|
|
56543
|
+
this.formattedRedoLimit = before.formattedRedoLimit;
|
|
56544
|
+
this.formattedUndone = before.formattedUndone;
|
|
56322
56545
|
}
|
|
56323
56546
|
/** Reports a refused edit to the facade call that made it, or else to `events.error`. */
|
|
56324
56547
|
refuse(error) {
|
|
@@ -56459,7 +56682,7 @@ var InkKitEditor = class InkKitEditor {
|
|
|
56459
56682
|
table: this.literalSurface ? void 0 : tableContext(state),
|
|
56460
56683
|
commands: {
|
|
56461
56684
|
undo: mutable && (!!this.pendingSource || undo(state)),
|
|
56462
|
-
redo: mutable && (this.undoneSteps.length > 0 || !this.pendingSource && redo(state)),
|
|
56685
|
+
redo: mutable && (this.undoneSteps.length > 0 || !this.pendingSource && this.formattedRedoLimit !== 0 && redo(state)),
|
|
56463
56686
|
insertText: mutable,
|
|
56464
56687
|
paste: mutable,
|
|
56465
56688
|
replace: mutable,
|
|
@@ -56629,7 +56852,9 @@ var InkKitEditor = class InkKitEditor {
|
|
|
56629
56852
|
this.assertAlive();
|
|
56630
56853
|
expectString(text, "text");
|
|
56631
56854
|
this.assertMutation(expectedGeneration);
|
|
56632
|
-
|
|
56855
|
+
const changed = this.applySource(text, true);
|
|
56856
|
+
if (changed) this.localCopies.clear();
|
|
56857
|
+
return changed;
|
|
56633
56858
|
}
|
|
56634
56859
|
applySource(text, isolated, from, to, native = false) {
|
|
56635
56860
|
if (!this.writable) throw new InkKitError("read-only", "The editor is read-only");
|
|
@@ -56648,6 +56873,7 @@ var InkKitEditor = class InkKitEditor {
|
|
|
56648
56873
|
const previous = this.pendingSource;
|
|
56649
56874
|
const continuing = previous && !previous.closed && !closed && now - this.lastSourceInput <= sourceGroupDelay;
|
|
56650
56875
|
if (previous && !continuing) this.rememberTypedStep(previous);
|
|
56876
|
+
this.formattedRedoLimit = 0;
|
|
56651
56877
|
this.lastSourceInput = now;
|
|
56652
56878
|
this.undoneSteps = [];
|
|
56653
56879
|
this.formattedUndone = 0;
|
|
@@ -56775,7 +57001,7 @@ var InkKitEditor = class InkKitEditor {
|
|
|
56775
57001
|
}
|
|
56776
57002
|
}
|
|
56777
57003
|
let tr = view.state.tr.replaceWith(0, view.state.doc.content.size, parsed.content).setDocAttribute(sourceAttribute, provenance).setMeta(sourceReplacement, true);
|
|
56778
|
-
tr.setSelection(
|
|
57004
|
+
tr.setSelection(terminalSelection(tr.doc));
|
|
56779
57005
|
if (isolated) tr = closeHistory(tr);
|
|
56780
57006
|
view.dispatch(tr);
|
|
56781
57007
|
if (isolated) view.dispatch(closeHistory(view.state.tr).setMeta("addToHistory", false));
|
|
@@ -56786,7 +57012,7 @@ var InkKitEditor = class InkKitEditor {
|
|
|
56786
57012
|
this.assertMutation(expectedGeneration);
|
|
56787
57013
|
if (this.literalSurface && this.stepTypedSource("undo")) return true;
|
|
56788
57014
|
const view = this.editor.ctx.get(editorViewCtx);
|
|
56789
|
-
const undone = !this.literalSurface && undoSmartPunctuation(view.state, view.dispatch) || undo
|
|
57015
|
+
const undone = !this.literalSurface && undoSmartPunctuation(view.state, view.dispatch) || this.stepFormattedHistory("undo");
|
|
56790
57016
|
if (undone) {
|
|
56791
57017
|
if (this.literalSurface && this.undoneSteps.length) this.formattedUndone++;
|
|
56792
57018
|
else this.undoneSteps = [];
|
|
@@ -56799,14 +57025,30 @@ var InkKitEditor = class InkKitEditor {
|
|
|
56799
57025
|
const view = this.editor.ctx.get(editorViewCtx);
|
|
56800
57026
|
if (this.literalSurface) {
|
|
56801
57027
|
if (this.formattedUndone > 0) {
|
|
56802
|
-
this.
|
|
56803
|
-
|
|
57028
|
+
if (this.formattedRedoLimit !== 0 && redo(view.state)) {
|
|
57029
|
+
const redone = this.stepFormattedHistory("redo");
|
|
57030
|
+
if (redone) this.formattedUndone--;
|
|
57031
|
+
return redone;
|
|
57032
|
+
}
|
|
56804
57033
|
this.formattedUndone = 0;
|
|
56805
57034
|
}
|
|
56806
57035
|
if (this.stepTypedSource("redo")) return true;
|
|
56807
57036
|
if (this.pendingSource) return false;
|
|
56808
57037
|
}
|
|
56809
|
-
return redo
|
|
57038
|
+
return this.stepFormattedHistory("redo");
|
|
57039
|
+
}
|
|
57040
|
+
stepFormattedHistory(direction) {
|
|
57041
|
+
if (direction === "redo" && this.formattedRedoLimit === 0) return false;
|
|
57042
|
+
const view = this.editor.ctx.get(editorViewCtx);
|
|
57043
|
+
const before = view.state;
|
|
57044
|
+
const previous = this.historyDirection;
|
|
57045
|
+
this.historyDirection = direction;
|
|
57046
|
+
try {
|
|
57047
|
+
(direction === "undo" ? undo : redo)(before, view.dispatch);
|
|
57048
|
+
return view.state !== before;
|
|
57049
|
+
} finally {
|
|
57050
|
+
this.historyDirection = previous;
|
|
57051
|
+
}
|
|
56810
57052
|
}
|
|
56811
57053
|
replaceLiteralSelection(text, isolated = true, retainEndings = false) {
|
|
56812
57054
|
const value = this.plain.value;
|
|
@@ -56909,6 +57151,7 @@ var InkKitEditor = class InkKitEditor {
|
|
|
56909
57151
|
const metadata = referenceMetadata(this.editor.ctx, selectedDoc ?? doc, content);
|
|
56910
57152
|
if (metadata != null) result.html = withReferenceMetadata(result.html, metadata);
|
|
56911
57153
|
this.assertCaptured(captured);
|
|
57154
|
+
this.rememberCopy(result, markdown, content, selectedSource != null && selectedSource.markdown !== selectedSource.shareable);
|
|
56912
57155
|
return {
|
|
56913
57156
|
...version,
|
|
56914
57157
|
...result
|
|
@@ -56918,7 +57161,7 @@ var InkKitEditor = class InkKitEditor {
|
|
|
56918
57161
|
* clipboard again while this runs. */
|
|
56919
57162
|
async paste(input, expectedGeneration) {
|
|
56920
57163
|
this.assertAlive();
|
|
56921
|
-
input = clipboardInput(input);
|
|
57164
|
+
input = this.restoreCopy(clipboardInput(input));
|
|
56922
57165
|
this.assertMutation(expectedGeneration);
|
|
56923
57166
|
if (this.literalSurface) {
|
|
56924
57167
|
this.pasteAsPlainText(this.mode === "source" && this.formatType === "md" ? input.markdown ?? input.text : input.text);
|
|
@@ -56951,14 +57194,9 @@ var InkKitEditor = class InkKitEditor {
|
|
|
56951
57194
|
else {
|
|
56952
57195
|
const { schema } = view.state;
|
|
56953
57196
|
const marks = view.state.storedMarks ?? (view.state.selection.empty ? view.state.selection.$from.marks() : view.state.selection.$from.marksAcross(view.state.selection.$to)) ?? [];
|
|
56954
|
-
const children = [];
|
|
56955
57197
|
const isHTML = !inHiddenComment && htmlBreakAt(view.state.selection.$from);
|
|
56956
57198
|
const lines = text.replace(/\r\n?/g, "\n").split("\n");
|
|
56957
|
-
|
|
56958
|
-
const finalBreak = index === lines.length - 1 && line === "";
|
|
56959
|
-
if (index) children.push(schema.nodes.hardbreak.create({ isHTML }, null, finalBreak ? [] : marks));
|
|
56960
|
-
if (line) children.push(schema.text(line, marks));
|
|
56961
|
-
});
|
|
57199
|
+
const children = literalTextNodes(schema, text, marks, isHTML);
|
|
56962
57200
|
if (!children.length) {
|
|
56963
57201
|
view.dispatch(view.state.tr.deleteSelection());
|
|
56964
57202
|
return;
|
|
@@ -57001,12 +57239,14 @@ var InkKitEditor = class InkKitEditor {
|
|
|
57001
57239
|
async destroy() {
|
|
57002
57240
|
if (this.destroyed) return;
|
|
57003
57241
|
this.destroyed = true;
|
|
57242
|
+
this.localCopies.clear();
|
|
57004
57243
|
this.ready = false;
|
|
57005
57244
|
this.session.close();
|
|
57006
57245
|
this.pendingSource = void 0;
|
|
57007
57246
|
this.typedSteps = [];
|
|
57008
57247
|
this.undoneSteps = [];
|
|
57009
57248
|
this.formattedUndone = 0;
|
|
57249
|
+
this.formattedRedoLimit = void 0;
|
|
57010
57250
|
this.cancelExports();
|
|
57011
57251
|
this.mediaPolicy();
|
|
57012
57252
|
this.pasteController.destroy();
|
|
@@ -57046,6 +57286,7 @@ var InkKitEditor = class InkKitEditor {
|
|
|
57046
57286
|
if (portable && this.events.clipboard) this.clipboardSnapshot("selection").then(async (content) => {
|
|
57047
57287
|
if (cut) refuseUnportableCut(content);
|
|
57048
57288
|
await writeClipboard(this.events, content);
|
|
57289
|
+
this.retainCopy(content.html);
|
|
57049
57290
|
if (cut) {
|
|
57050
57291
|
this.assertCut(captured);
|
|
57051
57292
|
this.plain.setSelectionRange(from, to);
|
|
@@ -57055,6 +57296,7 @@ var InkKitEditor = class InkKitEditor {
|
|
|
57055
57296
|
else {
|
|
57056
57297
|
const output = clipboardContent(parsed.content, parsed.type.schema);
|
|
57057
57298
|
if (!event.clipboardData) return;
|
|
57299
|
+
this.rememberCopy(output, selected.markdown, parsed.content, selected.markdown !== selected.shareable, true);
|
|
57058
57300
|
event.clipboardData.setData("text/plain", output.text);
|
|
57059
57301
|
event.clipboardData.setData("text/html", output.html);
|
|
57060
57302
|
if (portable) {
|
|
@@ -57079,6 +57321,7 @@ var InkKitEditor = class InkKitEditor {
|
|
|
57079
57321
|
instance.ownsRootClass = !root.classList.contains("inkkit-root");
|
|
57080
57322
|
root.classList.add("inkkit-root");
|
|
57081
57323
|
instance.pasteController = new PasteController({
|
|
57324
|
+
restoreCopy: (input) => instance.restoreCopy(input),
|
|
57082
57325
|
ctx: () => instance.editor.ctx,
|
|
57083
57326
|
context: () => instance.context(),
|
|
57084
57327
|
adapter: options.images,
|
|
@@ -57182,6 +57425,14 @@ var InkKitEditor = class InkKitEditor {
|
|
|
57182
57425
|
return;
|
|
57183
57426
|
}
|
|
57184
57427
|
}
|
|
57428
|
+
if (instance.formattedRedoLimit !== void 0) {
|
|
57429
|
+
const depth = redoDepth(next);
|
|
57430
|
+
if (depth === 0) instance.formattedRedoLimit = void 0;
|
|
57431
|
+
else if (next !== this.state) {
|
|
57432
|
+
if (instance.historyDirection === "undo") instance.formattedRedoLimit = Math.min(depth, instance.formattedRedoLimit + 1);
|
|
57433
|
+
else if (instance.historyDirection === "redo") instance.formattedRedoLimit = Math.max(0, instance.formattedRedoLimit - 1);
|
|
57434
|
+
}
|
|
57435
|
+
}
|
|
57185
57436
|
this.updateState(next);
|
|
57186
57437
|
},
|
|
57187
57438
|
handleScrollToSelection: () => {
|
|
@@ -57189,7 +57440,7 @@ var InkKitEditor = class InkKitEditor {
|
|
|
57189
57440
|
return true;
|
|
57190
57441
|
}
|
|
57191
57442
|
}));
|
|
57192
|
-
}).use(labelsCtx).use(instance.pasteController.plugin).use(visibleClipboard).use($prose(() => instance.suggestions.plugin())).use(instance.keymapPlugin).use(createDialect(Boolean(options.images || options.files), Boolean(options.wikiLinks), Boolean(options.files))).use(codeControlCtx).use(codeControlsPlugin(() => instance.viewport().rect)).use(tablePlugins).use(tableBlock).use(instance.changePlugin).use(instance.historyInputPlugin).use(history).use(clipboard).use(cursor).use(taskListPlugin).use(quoteBackspace).use(calloutEnter).use(literalEnter).use(literalReread).use(listItemBackspace).use(listItemShiftTab).use(nestedItemEnter).use(rejoinLists).use(movedListItems).use(textInputPolicy).use(events.copy ? codeCopyPlugin((text) => events.copy?.(text)) : []).use(placeholderPlugin).use(options.rendering?.codeHighlighting === false ? [] : (await import("./highlight-
|
|
57443
|
+
}).use(labelsCtx).use(instance.pasteController.plugin).use(visibleClipboard).use($prose(() => instance.suggestions.plugin())).use(instance.keymapPlugin).use(createDialect(Boolean(options.images || options.files), Boolean(options.wikiLinks), Boolean(options.files))).use(codeControlCtx).use(codeControlsPlugin(() => instance.viewport().rect)).use(tablePlugins).use(tableBlock).use(instance.changePlugin).use(instance.historyInputPlugin).use(history).use(clipboard).use(cursor).use(taskListPlugin).use(quoteBackspace).use(calloutEnter).use(literalEnter).use(literalReread).use(listItemBackspace).use(listItemShiftTab).use(nestedItemEnter).use(rejoinLists).use(movedListItems).use(textInputPolicy).use(events.copy ? codeCopyPlugin((text) => events.copy?.(text)) : []).use(placeholderPlugin).use(options.rendering?.codeHighlighting === false ? [] : (await import("./highlight-TSUdUlMi.js")).highlightPlugin).use(options.rendering?.diagramPreview === false || !diagramsAvailable() ? [] : mermaidPreview((error) => instance.report(error))).use(selectionPlugin).use($prose(() => search())).use(instance.policyPlugin).use(options.wikiLinks ? wikiLinkView(options.wikiLinks, {
|
|
57193
57444
|
capture: () => instance.adapterCapture(),
|
|
57194
57445
|
active: () => instance.ready && !instance.destroyed && !instance.literalSurface,
|
|
57195
57446
|
labels: {
|
|
@@ -57269,6 +57520,7 @@ var InkKitEditor = class InkKitEditor {
|
|
|
57269
57520
|
event.preventDefault();
|
|
57270
57521
|
instance.paste({
|
|
57271
57522
|
text: event.clipboardData.getData("text/plain"),
|
|
57523
|
+
html: event.clipboardData.getData("text/html"),
|
|
57272
57524
|
markdown: event.clipboardData.getData("text/markdown") || void 0
|
|
57273
57525
|
}).catch((error) => instance.report(error));
|
|
57274
57526
|
});
|
|
@@ -57338,11 +57590,13 @@ var InkKitEditor = class InkKitEditor {
|
|
|
57338
57590
|
instance.snapshot();
|
|
57339
57591
|
const content = expanded;
|
|
57340
57592
|
const valid = fragment.firstChild?.isInline ? Fragment.from(view.state.schema.nodes.paragraph.create(null, fragment)) : fragment;
|
|
57341
|
-
selectionMarkdown(instance.editor.ctx, view.state.doc, valid);
|
|
57593
|
+
const markdown = selectionMarkdown(instance.editor.ctx, view.state.doc, valid);
|
|
57342
57594
|
const output = clipboardContent(content, view.state.schema);
|
|
57343
|
-
event.clipboardData.setData("text/plain", output.text);
|
|
57344
57595
|
const metadata = referenceMetadata(instance.editor.ctx, view.state.doc, valid);
|
|
57345
|
-
|
|
57596
|
+
if (metadata != null) output.html = withReferenceMetadata(output.html, metadata);
|
|
57597
|
+
instance.rememberCopy(output, markdown, fragment, false, true);
|
|
57598
|
+
event.clipboardData.setData("text/plain", output.text);
|
|
57599
|
+
event.clipboardData.setData("text/html", output.html);
|
|
57346
57600
|
if (event.type === "cut" && instance.writable) view.dispatch(view.state.tr.deleteSelection().scrollIntoView());
|
|
57347
57601
|
} catch (error) {
|
|
57348
57602
|
instance.report(error);
|
|
@@ -57358,6 +57612,7 @@ var InkKitEditor = class InkKitEditor {
|
|
|
57358
57612
|
instance.clipboardSnapshot("selection").then(async (content) => {
|
|
57359
57613
|
if (cut) refuseUnportableCut(content);
|
|
57360
57614
|
await writeClipboard(events, content);
|
|
57615
|
+
instance.retainCopy(content.html);
|
|
57361
57616
|
if (cut) {
|
|
57362
57617
|
instance.assertCut(captured);
|
|
57363
57618
|
view.dispatch(view.state.tr.deleteRange(from, to).scrollIntoView());
|
|
@@ -57405,6 +57660,7 @@ var InkKitEditor = class InkKitEditor {
|
|
|
57405
57660
|
this.typedSteps = [];
|
|
57406
57661
|
this.undoneSteps = [];
|
|
57407
57662
|
this.formattedUndone = 0;
|
|
57663
|
+
this.formattedRedoLimit = void 0;
|
|
57408
57664
|
this.mode = "formatted";
|
|
57409
57665
|
this.plainComposing = false;
|
|
57410
57666
|
this.beforePlainInput = void 0;
|
|
@@ -57784,6 +58040,7 @@ var InkKitEditor = class InkKitEditor {
|
|
|
57784
58040
|
}])[0]);
|
|
57785
58041
|
}
|
|
57786
58042
|
load(markdown, preservation) {
|
|
58043
|
+
this.localCopies.clear();
|
|
57787
58044
|
this.originalSource = markdown;
|
|
57788
58045
|
const { ctx } = this.editor;
|
|
57789
58046
|
const view = ctx.get(editorViewCtx);
|
|
@@ -57800,9 +58057,7 @@ var InkKitEditor = class InkKitEditor {
|
|
|
57800
58057
|
this.preservation = preservation;
|
|
57801
58058
|
this.carried = void 0;
|
|
57802
58059
|
this.lastMarkdown = markdown;
|
|
57803
|
-
const
|
|
57804
|
-
const $end = doc.resolve(doc.content.size);
|
|
57805
|
-
const end = isImageBlock(doc.lastChild) ? new GapCursor($end) : Selection.findFrom($end, -1, true) ?? Selection.atEnd(doc);
|
|
58060
|
+
const end = terminalSelection(view.state.doc);
|
|
57806
58061
|
view.dispatch(setSearchState(view.state.tr.setSelection(end), new SearchQuery({ search: "" })));
|
|
57807
58062
|
}
|
|
57808
58063
|
/** Selects the next match and returns whether there was one. */
|