@aicayzer/inkkit 0.1.4 → 0.1.6
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/dist/THIRD-PARTY-NOTICES.txt +1 -1
- package/dist/chunks/{core-DP-izJDM.js → core-nIObsA9D.js} +637 -213
- package/dist/chunks/{highlight-DOQk0Eyj.js → highlight-CeNqcHAb.js} +4 -3
- package/dist/core.js +1 -1
- package/dist/index.js +1 -1
- package/dist/style.css +87 -16
- package/dist/types.d.ts +4 -0
- package/package.json +2 -2
|
@@ -28357,6 +28357,10 @@ var defaultLabels = {
|
|
|
28357
28357
|
sourceEditor: "Markdown source editor",
|
|
28358
28358
|
placeholder: "Start typing…",
|
|
28359
28359
|
copyCode: "Copy",
|
|
28360
|
+
codeLanguage: "Code language",
|
|
28361
|
+
codeLanguageSearch: "Search languages",
|
|
28362
|
+
codeAuto: "Auto",
|
|
28363
|
+
codePlainText: "Plain text",
|
|
28360
28364
|
expandCallout: "Expand",
|
|
28361
28365
|
collapseCallout: "Collapse",
|
|
28362
28366
|
diagramRendering: "Rendering diagram…",
|
|
@@ -28387,8 +28391,8 @@ function editorLabels(ctx) {
|
|
|
28387
28391
|
return ctx.isInjected(labelsCtx.key) ? ctx.get(labelsCtx.key) : defaultLabels;
|
|
28388
28392
|
}
|
|
28389
28393
|
//#endregion
|
|
28390
|
-
//#region node_modules/.pnpm/dompurify@3.4.
|
|
28391
|
-
/*! @license DOMPurify 3.4.
|
|
28394
|
+
//#region node_modules/.pnpm/dompurify@3.4.17/node_modules/dompurify/dist/purify.es.mjs
|
|
28395
|
+
/*! @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 */
|
|
28392
28396
|
function _OverloadYield(e, d) {
|
|
28393
28397
|
this.v = e, this.k = d;
|
|
28394
28398
|
}
|
|
@@ -28512,9 +28516,12 @@ Array.prototype.indexOf;
|
|
|
28512
28516
|
var arrayLastIndexOf = unapply(Array.prototype.lastIndexOf);
|
|
28513
28517
|
var arrayPop = unapply(Array.prototype.pop);
|
|
28514
28518
|
var arrayPush = unapply(Array.prototype.push);
|
|
28515
|
-
Array.prototype.slice;
|
|
28519
|
+
var arraySlice = unapply(Array.prototype.slice);
|
|
28516
28520
|
var arraySplice = unapply(Array.prototype.splice);
|
|
28517
28521
|
var arrayIsArray = Array.isArray;
|
|
28522
|
+
var setCreate = unconstruct(Set);
|
|
28523
|
+
var setAdd = unapply(Set.prototype.add);
|
|
28524
|
+
var setHas = unapply(Set.prototype.has);
|
|
28518
28525
|
var stringToLowerCase = unapply(String.prototype.toLowerCase);
|
|
28519
28526
|
var stringToString = unapply(String.prototype.toString);
|
|
28520
28527
|
var stringMatch = unapply(String.prototype.match);
|
|
@@ -28659,6 +28666,25 @@ function lookupGetter(object, prop) {
|
|
|
28659
28666
|
}
|
|
28660
28667
|
return fallbackValue;
|
|
28661
28668
|
}
|
|
28669
|
+
/**
|
|
28670
|
+
* Like lookupGetter, but for accessor properties only, and returns null
|
|
28671
|
+
* instead of a null-returning fallback when no getter exists on the chain.
|
|
28672
|
+
* Lets a caller fall back to a direct property read in environments that
|
|
28673
|
+
* lack the getter, where a fallback returning null would be indistinguishable
|
|
28674
|
+
* from a legitimate null value (e.g. namespaceURI of a no-namespace element).
|
|
28675
|
+
*
|
|
28676
|
+
* @param object - The object (usually a prototype) to start the lookup at.
|
|
28677
|
+
* @param prop - The accessor property name.
|
|
28678
|
+
* @returns The unapplied getter, or null if none was found.
|
|
28679
|
+
*/
|
|
28680
|
+
function lookupAccessor(object, prop) {
|
|
28681
|
+
while (object !== null) {
|
|
28682
|
+
const desc = getOwnPropertyDescriptor(object, prop);
|
|
28683
|
+
if (desc) return desc.get ? unapply(desc.get) : null;
|
|
28684
|
+
object = getPrototypeOf(object);
|
|
28685
|
+
}
|
|
28686
|
+
return null;
|
|
28687
|
+
}
|
|
28662
28688
|
function isRegex(value) {
|
|
28663
28689
|
try {
|
|
28664
28690
|
regExpTest(value, "");
|
|
@@ -29343,7 +29369,7 @@ var NODE_TYPE = {
|
|
|
29343
29369
|
documentFragment: 11,
|
|
29344
29370
|
notation: 12
|
|
29345
29371
|
};
|
|
29346
|
-
var
|
|
29372
|
+
var LITERAL_TEXT_ELEMENTS = freeze(addToSet({}, [
|
|
29347
29373
|
"style",
|
|
29348
29374
|
"script",
|
|
29349
29375
|
"xmp",
|
|
@@ -29352,15 +29378,7 @@ var LITERAL_TEXT_ELEMENT_NAMES = [
|
|
|
29352
29378
|
"noframes",
|
|
29353
29379
|
"plaintext",
|
|
29354
29380
|
"noscript"
|
|
29355
|
-
];
|
|
29356
|
-
var LITERAL_TEXT_ELEMENTS = freeze(addToSet({}, LITERAL_TEXT_ELEMENT_NAMES));
|
|
29357
|
-
var LITERAL_TEXT_CLOSE = function() {
|
|
29358
|
-
const map = {};
|
|
29359
|
-
arrayForEach(LITERAL_TEXT_ELEMENT_NAMES, (name) => {
|
|
29360
|
-
map[name] = seal(new RegExp("</" + name + "(?=[\\t\\n\\f\\r />])", "i"));
|
|
29361
|
-
});
|
|
29362
|
-
return freeze(map);
|
|
29363
|
-
}();
|
|
29381
|
+
]));
|
|
29364
29382
|
var getGlobal = function getGlobal() {
|
|
29365
29383
|
return typeof window === "undefined" ? null : window;
|
|
29366
29384
|
};
|
|
@@ -29438,7 +29456,7 @@ var _resolveObjectOption = function _resolveObjectOption(cfg, key, makeFallback)
|
|
|
29438
29456
|
function createDOMPurify() {
|
|
29439
29457
|
let window = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : getGlobal();
|
|
29440
29458
|
const DOMPurify = (root) => createDOMPurify(root);
|
|
29441
|
-
DOMPurify.version = "3.4.
|
|
29459
|
+
DOMPurify.version = "3.4.17";
|
|
29442
29460
|
DOMPurify.removed = [];
|
|
29443
29461
|
if (!window || !window.document || window.document.nodeType !== NODE_TYPE.document || !window.Element) {
|
|
29444
29462
|
DOMPurify.isSupported = false;
|
|
@@ -29470,6 +29488,10 @@ function createDOMPurify() {
|
|
|
29470
29488
|
const _readNodeName = function _readNodeName(node) {
|
|
29471
29489
|
return getNodeName ? getNodeName(node) : node.nodeName;
|
|
29472
29490
|
};
|
|
29491
|
+
const getNamespaceURI = lookupAccessor(ElementPrototype, "namespaceURI");
|
|
29492
|
+
const _readNamespaceURI = function _readNamespaceURI(element) {
|
|
29493
|
+
return getNamespaceURI ? getNamespaceURI(element) : element.namespaceURI;
|
|
29494
|
+
};
|
|
29473
29495
|
if (typeof HTMLTemplateElement === "function") {
|
|
29474
29496
|
const template = document.createElement("template");
|
|
29475
29497
|
if (template.content && template.content.ownerDocument) document = template.content.ownerDocument;
|
|
@@ -29590,6 +29612,7 @@ function createDOMPurify() {
|
|
|
29590
29612
|
const SANITIZE_NAMED_PROPS_PREFIX = "user-content-";
|
|
29591
29613
|
let KEEP_CONTENT = true;
|
|
29592
29614
|
let IN_PLACE = false;
|
|
29615
|
+
let IN_PLACE_VISITED = null;
|
|
29593
29616
|
let USE_PROFILES = {};
|
|
29594
29617
|
let FORBID_CONTENTS = null;
|
|
29595
29618
|
const DEFAULT_FORBID_CONTENTS = addToSet({}, [
|
|
@@ -29863,18 +29886,23 @@ function createDOMPurify() {
|
|
|
29863
29886
|
* return. Return true otherwise.
|
|
29864
29887
|
*/
|
|
29865
29888
|
const _checkValidNamespace = function _checkValidNamespace(element) {
|
|
29866
|
-
|
|
29867
|
-
|
|
29868
|
-
|
|
29869
|
-
|
|
29870
|
-
|
|
29871
|
-
|
|
29872
|
-
|
|
29873
|
-
|
|
29874
|
-
|
|
29875
|
-
|
|
29876
|
-
|
|
29877
|
-
|
|
29889
|
+
const elementNamespace = _readNamespaceURI(element);
|
|
29890
|
+
const tagName = stringToLowerCase(_readNodeName(element));
|
|
29891
|
+
const parentNode = getParentNode(element);
|
|
29892
|
+
let parent;
|
|
29893
|
+
let parentTagName;
|
|
29894
|
+
if (parentNode && _readNodeType(parentNode) === NODE_TYPE.element) {
|
|
29895
|
+
parent = { namespaceURI: _readNamespaceURI(parentNode) };
|
|
29896
|
+
parentTagName = stringToLowerCase(_readNodeName(parentNode));
|
|
29897
|
+
} else {
|
|
29898
|
+
parent = { namespaceURI: NAMESPACE };
|
|
29899
|
+
parentTagName = "template";
|
|
29900
|
+
}
|
|
29901
|
+
if (!ALLOWED_NAMESPACES[elementNamespace]) return false;
|
|
29902
|
+
if (elementNamespace === SVG_NAMESPACE) return _checkSvgNamespace(tagName, parent, parentTagName);
|
|
29903
|
+
if (elementNamespace === MATHML_NAMESPACE) return _checkMathMlNamespace(tagName, parent, parentTagName);
|
|
29904
|
+
if (elementNamespace === HTML_NAMESPACE) return _checkHtmlNamespace(tagName, parent, parentTagName);
|
|
29905
|
+
if (PARSER_MEDIA_TYPE === "application/xhtml+xml" && ALLOWED_NAMESPACES[elementNamespace]) return true;
|
|
29878
29906
|
return false;
|
|
29879
29907
|
};
|
|
29880
29908
|
/**
|
|
@@ -29918,36 +29946,21 @@ function createDOMPurify() {
|
|
|
29918
29946
|
}
|
|
29919
29947
|
};
|
|
29920
29948
|
/**
|
|
29921
|
-
*
|
|
29922
|
-
*
|
|
29923
|
-
*
|
|
29924
|
-
* (campaign-3 F2). An internal throw mid-walk — e.g. a page-registered
|
|
29925
|
-
* custom element's reaction detaches a node so `_forceRemove`'s deliberate
|
|
29926
|
-
* parentless guard throws, or any other re-entrant engine mutation — would
|
|
29927
|
-
* otherwise leave the caller's *live* tree half-sanitized, with everything
|
|
29928
|
-
* after the abort point still carrying its handlers. There is no safe way
|
|
29929
|
-
* to resume the walk (the tree mutated under us), so we strip the root bare:
|
|
29930
|
-
* remove every child and every attribute, then let the caller's catch see
|
|
29931
|
-
* the original error. Clobber-safe (cached `remove`/`childNodes`/`attributes`
|
|
29932
|
-
* getters; the root was already clobber-pre-flighted at the IN_PLACE entry).
|
|
29949
|
+
* Fail-closed teardown of an in-place root: strip every disallowed attribute
|
|
29950
|
+
* in the subtree first, so nothing armed is left behind, then remove all
|
|
29951
|
+
* children and the root's own attributes.
|
|
29933
29952
|
*
|
|
29934
29953
|
* @param root the in-place root to empty
|
|
29935
29954
|
*/
|
|
29936
29955
|
const _neutralizeRoot = function _neutralizeRoot(root) {
|
|
29937
29956
|
_neutralizeSubtree(root);
|
|
29938
29957
|
const childNodes = getChildNodes(root);
|
|
29939
|
-
if (childNodes) {
|
|
29940
|
-
|
|
29941
|
-
|
|
29942
|
-
|
|
29943
|
-
|
|
29944
|
-
|
|
29945
|
-
try {
|
|
29946
|
-
remove(child);
|
|
29947
|
-
} catch (_) {}
|
|
29948
|
-
});
|
|
29949
|
-
}
|
|
29950
|
-
const attributes = getAttributes(root);
|
|
29958
|
+
if (childNodes) arrayForEach(arraySlice(childNodes), (child) => {
|
|
29959
|
+
try {
|
|
29960
|
+
remove(child);
|
|
29961
|
+
} catch (_) {}
|
|
29962
|
+
});
|
|
29963
|
+
const attributes = _readNodeType(root) === NODE_TYPE.element ? getAttributes(root) : null;
|
|
29951
29964
|
if (attributes) for (let i = attributes.length - 1; i >= 0; --i) {
|
|
29952
29965
|
const attribute = attributes[i];
|
|
29953
29966
|
const name = attribute && attribute.name;
|
|
@@ -29955,18 +29968,9 @@ function createDOMPurify() {
|
|
|
29955
29968
|
}
|
|
29956
29969
|
};
|
|
29957
29970
|
/**
|
|
29958
|
-
*
|
|
29959
|
-
*
|
|
29960
|
-
*
|
|
29961
|
-
* lookup key for HTML elements in an HTML document, so they silently miss an
|
|
29962
|
-
* attribute whose stored qualified name still contains uppercase ASCII
|
|
29963
|
-
* letters. That happens when the node came from a case-preserving source
|
|
29964
|
-
* (an XML/XHTML document imported via importNode(), or createAttributeNS()),
|
|
29965
|
-
* where e.g. `ONERROR` survives the walk: the policy check lowercases to
|
|
29966
|
-
* `onerror` and rejects it, but `removeAttribute('ONERROR')` looks up
|
|
29967
|
-
* `onerror` and finds nothing. Remove the exact Attr node instead, which is
|
|
29968
|
-
* case- and namespace-exact, and fall back to name-based removal only when
|
|
29969
|
-
* the caller could not supply the node.
|
|
29971
|
+
* Removes an attribute, preferably as its exact Attr node: name-based
|
|
29972
|
+
* removal lowercases the name in HTML documents and misses case-preserved
|
|
29973
|
+
* names, e.g. from imported XHTML.
|
|
29970
29974
|
*
|
|
29971
29975
|
* @param name an Attribute name
|
|
29972
29976
|
* @param element a DOM node
|
|
@@ -30000,12 +30004,8 @@ function createDOMPurify() {
|
|
|
30000
30004
|
}
|
|
30001
30005
|
};
|
|
30002
30006
|
/**
|
|
30003
|
-
*
|
|
30004
|
-
*
|
|
30005
|
-
* Removes every attribute the active configuration does not allow from a
|
|
30006
|
-
* single element, using the same allowlist as the main attribute pass (so
|
|
30007
|
-
* `on*` handlers go, but no `/^on/` blocklist is introduced). Used only to
|
|
30008
|
-
* neutralise nodes that are being discarded from an in-place tree.
|
|
30007
|
+
* Removes every attribute the configuration does not allow. Used to
|
|
30008
|
+
* neutralize nodes discarded from an in-place tree.
|
|
30009
30009
|
*
|
|
30010
30010
|
* @param element the element to strip
|
|
30011
30011
|
*/
|
|
@@ -30020,77 +30020,158 @@ function createDOMPurify() {
|
|
|
30020
30020
|
}
|
|
30021
30021
|
};
|
|
30022
30022
|
/**
|
|
30023
|
-
*
|
|
30024
|
-
*
|
|
30025
|
-
*
|
|
30026
|
-
*
|
|
30027
|
-
* namespace, comment, processing-instruction and KEEP_CONTENT:false removals
|
|
30028
|
-
* all drop their subtree wholesale via `_forceRemove`. On the IN_PLACE path
|
|
30029
|
-
* those dropped nodes are detached from the caller's LIVE tree but a
|
|
30030
|
-
* handler-bearing original among them (an `<img onerror>`/`<video>` that was
|
|
30031
|
-
* loading) keeps its queued resource event, which fires in page scope after
|
|
30032
|
-
* sanitize returns. This walks a removed subtree and strips every attribute
|
|
30033
|
-
* the active configuration does not allow — so `on*` handlers are cancelled
|
|
30034
|
-
* through the SAME allowlist that governs kept nodes, not a separate `/^on/`
|
|
30035
|
-
* blocklist. Run synchronously before sanitize returns, i.e. before any
|
|
30036
|
-
* queued event can fire. Hook-free by design: these nodes leave the output,
|
|
30037
|
-
* so firing attribute hooks for them would be surprising. Clobber-safe reads;
|
|
30038
|
-
* a doomed clobbered node may shadow `removeAttribute` (its own attributes are
|
|
30039
|
-
* irrelevant — it is discarded — while its non-clobbered descendants, e.g.
|
|
30040
|
-
* the `<img>`, are reached and scrubbed).
|
|
30023
|
+
* Iterative depth-first walk, so deep trees cannot overflow the call stack,
|
|
30024
|
+
* reading through the cached getters, so clobbering cannot hide children.
|
|
30025
|
+
* When `visit` returns false, the node's children are skipped. With
|
|
30026
|
+
* `intoShadow`, open shadow roots are walked after their host's children.
|
|
30041
30027
|
*
|
|
30042
|
-
* @param root
|
|
30028
|
+
* @param root where to start
|
|
30029
|
+
* @param visit called for every node
|
|
30030
|
+
* @param intoShadow whether to enter open shadow roots
|
|
30043
30031
|
*/
|
|
30044
|
-
const
|
|
30032
|
+
const _walkTree = function _walkTree(root, visit) {
|
|
30033
|
+
let intoShadow = arguments.length > 2 && arguments[2] !== void 0 ? arguments[2] : false;
|
|
30045
30034
|
const stack = [root];
|
|
30046
30035
|
while (stack.length > 0) {
|
|
30047
30036
|
const node = stack.pop();
|
|
30048
|
-
if (
|
|
30037
|
+
if (visit(node) === false) continue;
|
|
30038
|
+
if (intoShadow && _readNodeType(node) === NODE_TYPE.element) {
|
|
30039
|
+
const shadowRoot = getShadowRoot(node);
|
|
30040
|
+
if (_isDocumentFragment(shadowRoot)) stack.push(shadowRoot);
|
|
30041
|
+
}
|
|
30049
30042
|
const childNodes = getChildNodes(node);
|
|
30050
30043
|
if (childNodes) for (let i = childNodes.length - 1; i >= 0; --i) stack.push(childNodes[i]);
|
|
30051
30044
|
}
|
|
30052
30045
|
};
|
|
30053
30046
|
/**
|
|
30054
|
-
*
|
|
30047
|
+
* Strips disallowed attributes from every element of a discarded subtree,
|
|
30048
|
+
* so a handler that is already queued (a loading image) cannot fire.
|
|
30055
30049
|
*
|
|
30056
|
-
*
|
|
30057
|
-
|
|
30050
|
+
* @param root the root of a removed subtree
|
|
30051
|
+
*/
|
|
30052
|
+
const _neutralizeSubtree = function _neutralizeSubtree(root) {
|
|
30053
|
+
_walkTree(root, (node) => {
|
|
30054
|
+
if (_readNodeType(node) === NODE_TYPE.element) _stripDisallowedAttributes(node);
|
|
30055
|
+
});
|
|
30056
|
+
};
|
|
30057
|
+
/**
|
|
30058
|
+
* All nodes under `root`, root first, including open shadow roots. Template
|
|
30059
|
+
* content is not entered, it is inert.
|
|
30058
30060
|
*
|
|
30059
|
-
*
|
|
30060
|
-
*
|
|
30061
|
-
|
|
30062
|
-
|
|
30063
|
-
|
|
30064
|
-
|
|
30065
|
-
|
|
30066
|
-
|
|
30067
|
-
|
|
30068
|
-
|
|
30061
|
+
* @param root the in-place root
|
|
30062
|
+
* @return the nodes
|
|
30063
|
+
*/
|
|
30064
|
+
const _collectLiveNodes = function _collectLiveNodes(root) {
|
|
30065
|
+
const nodes = [];
|
|
30066
|
+
_walkTree(root, (node) => {
|
|
30067
|
+
nodes.push(node);
|
|
30068
|
+
}, true);
|
|
30069
|
+
return nodes;
|
|
30070
|
+
};
|
|
30071
|
+
/**
|
|
30072
|
+
* Strips disallowed attributes from every element that was under the
|
|
30073
|
+
* in-place root when the call started and no longer is, whatever removed
|
|
30074
|
+
* or moved it.
|
|
30069
30075
|
*
|
|
30070
|
-
*
|
|
30071
|
-
*
|
|
30072
|
-
|
|
30073
|
-
|
|
30074
|
-
|
|
30076
|
+
* @param root the in-place root
|
|
30077
|
+
* @param before the root's nodes, collected before the walk
|
|
30078
|
+
*/
|
|
30079
|
+
const _neutralizeEscapedElements = function _neutralizeEscapedElements(root, before) {
|
|
30080
|
+
const reachable = setCreate();
|
|
30081
|
+
arrayForEach(_collectLiveNodes(root), (node) => {
|
|
30082
|
+
if (_readNodeType(node) === NODE_TYPE.element) setAdd(reachable, node);
|
|
30083
|
+
});
|
|
30084
|
+
arrayForEach(before, (node) => {
|
|
30085
|
+
if (_readNodeType(node) === NODE_TYPE.element && !setHas(reachable, node)) _stripDisallowedAttributes(node);
|
|
30086
|
+
});
|
|
30087
|
+
};
|
|
30088
|
+
/**
|
|
30089
|
+
* Neutralizes everything that left the in-place tree during the call: what
|
|
30090
|
+
* DOMPurify removed, and what hooks or custom elements removed or moved out.
|
|
30075
30091
|
*
|
|
30076
|
-
*
|
|
30077
|
-
*
|
|
30092
|
+
* @param root the in-place root
|
|
30093
|
+
* @param before the root's nodes, collected before the walk
|
|
30094
|
+
* @return true if the root itself was removed
|
|
30095
|
+
*/
|
|
30096
|
+
const _neutralizeDiscarded = function _neutralizeDiscarded(root, before) {
|
|
30097
|
+
let rootRemoved = false;
|
|
30098
|
+
arrayForEach(DOMPurify.removed, (entry) => {
|
|
30099
|
+
if (entry.element) {
|
|
30100
|
+
rootRemoved = rootRemoved || entry.element === root;
|
|
30101
|
+
_neutralizeSubtree(entry.element);
|
|
30102
|
+
}
|
|
30103
|
+
});
|
|
30104
|
+
_neutralizeEscapedElements(root, before);
|
|
30105
|
+
return rootRemoved;
|
|
30106
|
+
};
|
|
30107
|
+
const MAX_UNVISITED_ROUNDS = 8;
|
|
30108
|
+
/**
|
|
30109
|
+
* The walk only moves forward, so a node moved to a position it already
|
|
30110
|
+
* passed is never visited. Give every original node that is still in the
|
|
30111
|
+
* tree but unvisited the normal treatment, hooks included. Nodes created
|
|
30112
|
+
* during the call are left alone. Fails closed if the tree keeps changing.
|
|
30113
|
+
*
|
|
30114
|
+
* @param root the in-place root
|
|
30115
|
+
* @param before the root's nodes, collected before the walk
|
|
30116
|
+
*/
|
|
30117
|
+
const _sanitizeUnvisitedNodes = function _sanitizeUnvisitedNodes(root, before) {
|
|
30118
|
+
const visited = IN_PLACE_VISITED;
|
|
30119
|
+
let original = null;
|
|
30120
|
+
const getOriginal = () => {
|
|
30121
|
+
if (!original) {
|
|
30122
|
+
original = setCreate();
|
|
30123
|
+
arrayForEach(before, (beforeNode) => {
|
|
30124
|
+
setAdd(original, beforeNode);
|
|
30125
|
+
});
|
|
30126
|
+
}
|
|
30127
|
+
return original;
|
|
30128
|
+
};
|
|
30129
|
+
for (let round = 0;; round++) {
|
|
30130
|
+
const live = _collectLiveNodes(root);
|
|
30131
|
+
const containers = [root];
|
|
30132
|
+
let pending = false;
|
|
30133
|
+
arrayForEach(live, (node) => {
|
|
30134
|
+
if (_readNodeType(node) === NODE_TYPE.documentFragment) arrayPush(containers, node);
|
|
30135
|
+
else if (!pending && !setHas(visited, node)) pending = setHas(getOriginal(), node);
|
|
30136
|
+
});
|
|
30137
|
+
if (!pending) return;
|
|
30138
|
+
if (round >= MAX_UNVISITED_ROUNDS) throw typeErrorCreate("the tree kept changing during in-place sanitization; refusing to return it");
|
|
30139
|
+
arrayForEach(containers, (container, index) => {
|
|
30140
|
+
_sanitizeUnvisitedIn(container, index > 0, root, getOriginal());
|
|
30141
|
+
});
|
|
30142
|
+
}
|
|
30143
|
+
};
|
|
30144
|
+
/**
|
|
30145
|
+
* One _sanitizeUnvisitedNodes pass over the root or an open shadow root.
|
|
30078
30146
|
*
|
|
30079
|
-
*
|
|
30080
|
-
*
|
|
30081
|
-
*
|
|
30082
|
-
*
|
|
30083
|
-
|
|
30084
|
-
|
|
30147
|
+
* @param container the root or an open shadow root
|
|
30148
|
+
* @param inShadow whether container is a shadow root
|
|
30149
|
+
* @param root the in-place root
|
|
30150
|
+
* @param original the root's nodes collected before the walk, as a set
|
|
30151
|
+
*/
|
|
30152
|
+
const _sanitizeUnvisitedIn = function _sanitizeUnvisitedIn(container, inShadow, root, original) {
|
|
30153
|
+
const iterator = _createNodeIterator(container);
|
|
30154
|
+
let node;
|
|
30155
|
+
while (node = iterator.nextNode()) {
|
|
30156
|
+
if (!setHas(original, node) || setHas(IN_PLACE_VISITED, node)) continue;
|
|
30157
|
+
if (inShadow) _executeHooks(hooks.uponSanitizeShadowNode, node, null);
|
|
30158
|
+
_sanitizeElements(node, root);
|
|
30159
|
+
_sanitizeAttributes(node, root);
|
|
30160
|
+
if (_isDocumentFragment(node.content)) _sanitizeShadowDOM2(node.content);
|
|
30161
|
+
}
|
|
30162
|
+
};
|
|
30163
|
+
/**
|
|
30164
|
+
* Records that _sanitizeElements processed `node`. No-op outside IN_PLACE.
|
|
30085
30165
|
*
|
|
30086
|
-
* @param
|
|
30166
|
+
* @param node the node being processed
|
|
30087
30167
|
*/
|
|
30168
|
+
const _markVisited = function _markVisited(node) {
|
|
30169
|
+
if (IN_PLACE_VISITED) setAdd(IN_PLACE_VISITED, node);
|
|
30170
|
+
};
|
|
30088
30171
|
/**
|
|
30089
|
-
*
|
|
30090
|
-
*
|
|
30091
|
-
*
|
|
30092
|
-
* everywhere except on <label>/<output>, and the whole policy is gated on
|
|
30093
|
-
* SAFE_FOR_XML (see the rationale block in _isValidAttribute).
|
|
30172
|
+
* Patch-linkage attributes (declarative partial updates): `patchsrc`
|
|
30173
|
+
* always, `for` everywhere except on <label> and <output>. Gated on
|
|
30174
|
+
* SAFE_FOR_XML. Shared by the IN_PLACE pre-pass and _isValidAttribute.
|
|
30094
30175
|
*
|
|
30095
30176
|
* @param lcName the transformCaseFunc'd attribute name
|
|
30096
30177
|
* @param lcTag the transformCaseFunc'd tag name of the carrying element
|
|
@@ -30101,17 +30182,26 @@ function createDOMPurify() {
|
|
|
30101
30182
|
if (lcName === "patchsrc") return true;
|
|
30102
30183
|
return lcName === "for" && lcTag !== "label" && lcTag !== "output";
|
|
30103
30184
|
};
|
|
30185
|
+
/**
|
|
30186
|
+
* IN_PLACE pre-pass: sever all patch linkage before the walk, so no patch
|
|
30187
|
+
* can fire mid-walk into a part of the tree the walker already passed.
|
|
30188
|
+
* Removes range markers (processing instructions, comments with markup)
|
|
30189
|
+
* and strips linkage attributes. It cannot undo a patch that already fired.
|
|
30190
|
+
*
|
|
30191
|
+
* TODO: if the shipped feature can patch through `for` on <label>/<output>,
|
|
30192
|
+
* drop that pair here too (see test/declarative-patch-probe.html).
|
|
30193
|
+
*
|
|
30194
|
+
* @param root the in-place root to sweep
|
|
30195
|
+
*/
|
|
30104
30196
|
const _neutralizePatchLinkage = function _neutralizePatchLinkage(root) {
|
|
30105
30197
|
if (!SAFE_FOR_XML) return;
|
|
30106
|
-
|
|
30107
|
-
while (stack.length > 0) {
|
|
30108
|
-
const node = stack.pop();
|
|
30198
|
+
_walkTree(root, (node) => {
|
|
30109
30199
|
const nodeType = _readNodeType(node);
|
|
30110
30200
|
if (nodeType === NODE_TYPE.processingInstruction || nodeType === NODE_TYPE.comment && regExpTest(COMMENT_MARKUP_PROBE, node.data)) {
|
|
30111
30201
|
try {
|
|
30112
30202
|
remove(node);
|
|
30113
30203
|
} catch (_) {}
|
|
30114
|
-
|
|
30204
|
+
return false;
|
|
30115
30205
|
}
|
|
30116
30206
|
if (nodeType === NODE_TYPE.element) {
|
|
30117
30207
|
const element = node;
|
|
@@ -30121,9 +30211,8 @@ function createDOMPurify() {
|
|
|
30121
30211
|
if (element.hasAttribute && element.hasAttribute("for") && _isPatchLinkageAttribute("for", lcTag)) element.removeAttribute("for");
|
|
30122
30212
|
} catch (_) {}
|
|
30123
30213
|
}
|
|
30124
|
-
|
|
30125
|
-
|
|
30126
|
-
}
|
|
30214
|
+
return true;
|
|
30215
|
+
});
|
|
30127
30216
|
};
|
|
30128
30217
|
/**
|
|
30129
30218
|
* _initDocument
|
|
@@ -30200,6 +30289,27 @@ function createDOMPurify() {
|
|
|
30200
30289
|
*/
|
|
30201
30290
|
const _scrubTemplateExpressions2 = function _scrubTemplateExpressions(node) {
|
|
30202
30291
|
var _node$querySelectorAl;
|
|
30292
|
+
_scrubOwnCharacterData(node);
|
|
30293
|
+
const templates = (_node$querySelectorAl = node.querySelectorAll) === null || _node$querySelectorAl === void 0 ? void 0 : _node$querySelectorAl.call(node, "template");
|
|
30294
|
+
if (templates) arrayForEach(templates, (tmpl) => {
|
|
30295
|
+
if (_isDocumentFragment(tmpl.content)) _scrubTemplateExpressions2(tmpl.content);
|
|
30296
|
+
});
|
|
30297
|
+
};
|
|
30298
|
+
/**
|
|
30299
|
+
* _scrubOwnCharacterData
|
|
30300
|
+
*
|
|
30301
|
+
* Non-recursive core of _scrubTemplateExpressions: normalize `node` and
|
|
30302
|
+
* strip template expressions from its own character data only. Does not
|
|
30303
|
+
* enter <template>.content or attached shadow roots (NodeIterator never
|
|
30304
|
+
* does). Used by _sanitizeShadowDOM, where every nested template content
|
|
30305
|
+
* and shadow root is already walked - and therefore scrubbed - by its own
|
|
30306
|
+
* recursive _sanitizeShadowDOM call; recursing here as well would rescrub
|
|
30307
|
+
* each nested fragment once per ancestor, which is quadratic in nesting
|
|
30308
|
+
* depth.
|
|
30309
|
+
*
|
|
30310
|
+
* @param node The root whose own character data should be scrubbed.
|
|
30311
|
+
*/
|
|
30312
|
+
const _scrubOwnCharacterData = function _scrubOwnCharacterData(node) {
|
|
30203
30313
|
node.normalize();
|
|
30204
30314
|
const doc = getOwnerDocument ? getOwnerDocument(node) : node.ownerDocument;
|
|
30205
30315
|
const walker = createNodeIterator.call(doc || node, node, NodeFilter.SHOW_TEXT | NodeFilter.SHOW_COMMENT | NodeFilter.SHOW_CDATA_SECTION | NodeFilter.SHOW_PROCESSING_INSTRUCTION, null);
|
|
@@ -30208,10 +30318,6 @@ function createDOMPurify() {
|
|
|
30208
30318
|
currentNode.data = _stripTemplateExpressions(currentNode.data);
|
|
30209
30319
|
currentNode = walker.nextNode();
|
|
30210
30320
|
}
|
|
30211
|
-
const templates = (_node$querySelectorAl = node.querySelectorAll) === null || _node$querySelectorAl === void 0 ? void 0 : _node$querySelectorAl.call(node, "template");
|
|
30212
|
-
if (templates) arrayForEach(templates, (tmpl) => {
|
|
30213
|
-
if (_isDocumentFragment(tmpl.content)) _scrubTemplateExpressions2(tmpl.content);
|
|
30214
|
-
});
|
|
30215
30321
|
};
|
|
30216
30322
|
/**
|
|
30217
30323
|
* _isClobbered
|
|
@@ -30287,7 +30393,7 @@ function createDOMPurify() {
|
|
|
30287
30393
|
*/
|
|
30288
30394
|
const _isUnsafeNode = function _isUnsafeNode(currentNode, tagName) {
|
|
30289
30395
|
if (SAFE_FOR_XML && currentNode.hasChildNodes() && !_isNode(currentNode.firstElementChild) && regExpTest(ELEMENT_MARKUP_PROBE, currentNode.textContent) && regExpTest(ELEMENT_MARKUP_PROBE, currentNode.innerHTML)) return true;
|
|
30290
|
-
if (SAFE_FOR_XML && currentNode
|
|
30396
|
+
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;
|
|
30291
30397
|
if (currentNode.nodeType === NODE_TYPE.processingInstruction) return true;
|
|
30292
30398
|
if (SAFE_FOR_XML && currentNode.nodeType === NODE_TYPE.comment && regExpTest(COMMENT_MARKUP_PROBE, currentNode.data)) return true;
|
|
30293
30399
|
return false;
|
|
@@ -30331,7 +30437,7 @@ function createDOMPurify() {
|
|
|
30331
30437
|
*/
|
|
30332
30438
|
const _sanitizeDisallowedNode = function _sanitizeDisallowedNode(currentNode, tagName, root) {
|
|
30333
30439
|
if (!FORBID_TAGS[tagName] && _isBasicCustomElement(tagName) && _matchesNameCheck(CUSTOM_ELEMENT_HANDLING.tagNameCheck, tagName)) return false;
|
|
30334
|
-
if (KEEP_CONTENT && !FORBID_CONTENTS[tagName]) {
|
|
30440
|
+
if (KEEP_CONTENT && !FORBID_CONTENTS[tagName] && !FORBID_CONTENTS[stringToLowerCase(tagName)]) {
|
|
30335
30441
|
const parentNode = getParentNode(currentNode);
|
|
30336
30442
|
const childNodes = getChildNodes(currentNode);
|
|
30337
30443
|
if (childNodes && parentNode) {
|
|
@@ -30364,35 +30470,13 @@ function createDOMPurify() {
|
|
|
30364
30470
|
return set === defaultSet || set === setConfigSet ? clone(set) : set;
|
|
30365
30471
|
};
|
|
30366
30472
|
/**
|
|
30367
|
-
*
|
|
30368
|
-
*
|
|
30369
|
-
*
|
|
30370
|
-
* filtering). Per the cached, unclobberable parentNode getter the node is
|
|
30371
|
-
* genuinely out of the tree, so it can reach neither the serialized
|
|
30372
|
-
* output nor an IN_PLACE live tree; treat it as removed and stop
|
|
30373
|
-
* processing it. Without this guard, the unsafe-node / namespace checks
|
|
30374
|
-
* would call _forceRemove on a parentless node and hit the REPORT-3
|
|
30375
|
-
* fail-closed throw — which exists for nodes DOMPurify wants gone but
|
|
30376
|
-
* *cannot* detach (clobbered / parentless roots), the opposite of a node
|
|
30377
|
-
* that is already safely gone. The walk root is exempt: a detached
|
|
30378
|
-
* IN_PLACE root is legitimate input and must still be fully sanitized,
|
|
30379
|
-
* and a kill-decision on it must keep hitting the REPORT-3 throw.
|
|
30380
|
-
*
|
|
30381
|
-
* Nodes detached by hooks stay the hook's responsibility for placement:
|
|
30382
|
-
* they are not recorded in DOMPurify.removed, so the post-walk IN_PLACE
|
|
30383
|
-
* pass (which iterates DOMPurify.removed) does not reach them. But a
|
|
30384
|
-
* hook-detached subtree can still hold a queued resource-event handler -
|
|
30385
|
-
* e.g. an <img onload> that began loading when the caller built the live
|
|
30386
|
-
* tree - which fires in page scope after sanitize returns even though the
|
|
30387
|
-
* handler never reached the returned tree. That is the audit-5 F1 hazard,
|
|
30388
|
-
* and the documented node.remove() hook pattern walks straight into it.
|
|
30389
|
-
* So on the IN_PLACE path we neutralize the detached subtree inline,
|
|
30390
|
-
* stripping its non-allow-listed attributes before returning, exactly as
|
|
30391
|
-
* the post-walk pass does for _forceRemove'd subtrees.
|
|
30473
|
+
* A hook may have detached the node it was called for. If so, treat it as
|
|
30474
|
+
* removed and stop processing it; the walk root is exempt. On the IN_PLACE
|
|
30475
|
+
* path the detached subtree is neutralized right away.
|
|
30392
30476
|
*
|
|
30393
30477
|
* @param currentNode the node a hook may have detached
|
|
30394
30478
|
* @param root the current walk root
|
|
30395
|
-
* @return true if the node
|
|
30479
|
+
* @return true if the node was detached
|
|
30396
30480
|
*/
|
|
30397
30481
|
const _handleHookDetachedNode = function _handleHookDetachedNode(currentNode, root) {
|
|
30398
30482
|
if (currentNode === root || getParentNode(currentNode) !== null) return false;
|
|
@@ -30409,6 +30493,7 @@ function createDOMPurify() {
|
|
|
30409
30493
|
* @return true if node was killed, false if left alive
|
|
30410
30494
|
*/
|
|
30411
30495
|
const _sanitizeElements = function _sanitizeElements(currentNode, root) {
|
|
30496
|
+
_markVisited(currentNode);
|
|
30412
30497
|
_executeHooks(hooks.beforeSanitizeElements, currentNode, null);
|
|
30413
30498
|
if (_handleHookDetachedNode(currentNode, root)) return true;
|
|
30414
30499
|
if (_isClobbered(currentNode)) {
|
|
@@ -30552,6 +30637,22 @@ function createDOMPurify() {
|
|
|
30552
30637
|
}
|
|
30553
30638
|
};
|
|
30554
30639
|
/**
|
|
30640
|
+
* _isUnsafeAttributeValue
|
|
30641
|
+
*
|
|
30642
|
+
* Value-based attribute checks that the attribute walk applies before any
|
|
30643
|
+
* hook can force-keep an attribute, shared with the public
|
|
30644
|
+
* isValidAttribute() so that it never approves a value sanitize() would
|
|
30645
|
+
* strip.
|
|
30646
|
+
*
|
|
30647
|
+
* @param name the attribute name as found on the node (case preserved)
|
|
30648
|
+
* @param value the attribute value
|
|
30649
|
+
* @returns true if the attribute must be removed because of its value
|
|
30650
|
+
*/
|
|
30651
|
+
const _isUnsafeAttributeValue = function _isUnsafeAttributeValue(name, value) {
|
|
30652
|
+
if (SAFE_FOR_XML && regExpTest(/((--!?|])>)|<\/(style|script|title|xmp|textarea|noscript|iframe|noembed|noframes)/i, value)) return true;
|
|
30653
|
+
return stringToLowerCase(name) === "attributename" && stringMatch(value, "href") !== null;
|
|
30654
|
+
};
|
|
30655
|
+
/**
|
|
30555
30656
|
* _sanitizeAttributes
|
|
30556
30657
|
*
|
|
30557
30658
|
* @protect attributes
|
|
@@ -30595,11 +30696,7 @@ function createDOMPurify() {
|
|
|
30595
30696
|
value = SANITIZE_NAMED_PROPS_PREFIX + value;
|
|
30596
30697
|
recreatedNamedProp = true;
|
|
30597
30698
|
}
|
|
30598
|
-
if (
|
|
30599
|
-
_removeAttribute(name, currentNode, attr);
|
|
30600
|
-
continue;
|
|
30601
|
-
}
|
|
30602
|
-
if (lcName === "attributename" && stringMatch(value, "href")) {
|
|
30699
|
+
if (_isUnsafeAttributeValue(name, value)) {
|
|
30603
30700
|
_removeAttribute(name, currentNode, attr);
|
|
30604
30701
|
continue;
|
|
30605
30702
|
}
|
|
@@ -30647,24 +30744,13 @@ function createDOMPurify() {
|
|
|
30647
30744
|
}
|
|
30648
30745
|
}
|
|
30649
30746
|
}
|
|
30747
|
+
if (SAFE_FOR_TEMPLATES) _scrubOwnCharacterData(fragment);
|
|
30650
30748
|
_executeHooks(hooks.afterSanitizeShadowDOM, fragment, null);
|
|
30651
30749
|
};
|
|
30652
30750
|
/**
|
|
30653
|
-
*
|
|
30654
|
-
*
|
|
30655
|
-
*
|
|
30656
|
-
* the existing _sanitizeShadowDOM pipeline. The default node iterator
|
|
30657
|
-
* does not descend into shadow trees, so nodes inside an attached
|
|
30658
|
-
* shadow root would otherwise be skipped entirely.
|
|
30659
|
-
*
|
|
30660
|
-
* Two real input paths put attached shadow roots in front of us:
|
|
30661
|
-
* 1. IN_PLACE on a DOM node that already has shadow roots attached.
|
|
30662
|
-
* 2. DOM-node input where importNode(dirty, true) deep-clones the
|
|
30663
|
-
* shadow root because it was created with `clonable: true`.
|
|
30664
|
-
*
|
|
30665
|
-
* This pass runs once, up front, so the main iteration loop (and the
|
|
30666
|
-
* existing _sanitizeShadowDOM template-content recursion) stay
|
|
30667
|
-
* untouched — string-input paths are not affected.
|
|
30751
|
+
* Sanitizes every attached shadow root under `root`, since the node
|
|
30752
|
+
* iterator does not enter shadow trees. Needed for IN_PLACE input and for
|
|
30753
|
+
* DOM input whose clonable shadow roots were copied by importNode().
|
|
30668
30754
|
*
|
|
30669
30755
|
* @param root the subtree root to walk for attached shadow roots
|
|
30670
30756
|
*/
|
|
@@ -30729,6 +30815,8 @@ function createDOMPurify() {
|
|
|
30729
30815
|
if (hooks.uponSanitizeAttribute.length > 0) ALLOWED_ATTR = clone(ALLOWED_ATTR);
|
|
30730
30816
|
DOMPurify.removed = [];
|
|
30731
30817
|
const inPlace = IN_PLACE && typeof dirty !== "string" && _isNode(dirty);
|
|
30818
|
+
let inPlaceNodes = [];
|
|
30819
|
+
const outerVisited = IN_PLACE_VISITED;
|
|
30732
30820
|
if (inPlace) {
|
|
30733
30821
|
_neutralizePatchLinkage(dirty);
|
|
30734
30822
|
const nn = _readNodeName(dirty);
|
|
@@ -30743,12 +30831,8 @@ function createDOMPurify() {
|
|
|
30743
30831
|
_neutralizeRoot(dirty);
|
|
30744
30832
|
throw typeErrorCreate("root node is clobbered and cannot be sanitized in-place");
|
|
30745
30833
|
}
|
|
30746
|
-
|
|
30747
|
-
|
|
30748
|
-
} catch (error) {
|
|
30749
|
-
_neutralizeRoot(dirty);
|
|
30750
|
-
throw error;
|
|
30751
|
-
}
|
|
30834
|
+
inPlaceNodes = _collectLiveNodes(dirty);
|
|
30835
|
+
IN_PLACE_VISITED = setCreate();
|
|
30752
30836
|
} else if (_isNode(dirty)) {
|
|
30753
30837
|
body = _initDocument("<!---->");
|
|
30754
30838
|
importedNode = body.ownerDocument.importNode(dirty, true);
|
|
@@ -30764,30 +30848,25 @@ function createDOMPurify() {
|
|
|
30764
30848
|
if (body && FORCE_BODY) _forceRemove(body.firstChild);
|
|
30765
30849
|
const walkRoot = inPlace ? dirty : body;
|
|
30766
30850
|
try {
|
|
30851
|
+
if (inPlace) _sanitizeAttachedShadowRoots(dirty);
|
|
30767
30852
|
const nodeIterator = _createNodeIterator(walkRoot);
|
|
30768
30853
|
while (currentNode = nodeIterator.nextNode()) {
|
|
30769
30854
|
_sanitizeElements(currentNode, walkRoot);
|
|
30770
30855
|
_sanitizeAttributes(currentNode, walkRoot);
|
|
30771
30856
|
if (_isDocumentFragment(currentNode.content)) _sanitizeShadowDOM2(currentNode.content);
|
|
30772
30857
|
}
|
|
30858
|
+
if (inPlace) _sanitizeUnvisitedNodes(dirty, inPlaceNodes);
|
|
30773
30859
|
} catch (error) {
|
|
30774
30860
|
if (inPlace) {
|
|
30775
30861
|
_neutralizeRoot(dirty);
|
|
30776
|
-
|
|
30777
|
-
if (entry.element) _neutralizeSubtree(entry.element);
|
|
30778
|
-
});
|
|
30862
|
+
_neutralizeDiscarded(dirty, inPlaceNodes);
|
|
30779
30863
|
}
|
|
30780
30864
|
throw error;
|
|
30865
|
+
} finally {
|
|
30866
|
+
IN_PLACE_VISITED = outerVisited;
|
|
30781
30867
|
}
|
|
30782
30868
|
if (inPlace) {
|
|
30783
|
-
|
|
30784
|
-
arrayForEach(DOMPurify.removed, (entry) => {
|
|
30785
|
-
if (entry.element) {
|
|
30786
|
-
if (entry.element === dirty) rootWasRemoved = true;
|
|
30787
|
-
_neutralizeSubtree(entry.element);
|
|
30788
|
-
}
|
|
30789
|
-
});
|
|
30790
|
-
if (rootWasRemoved) throw typeErrorCreate("a node selected for removal could not be safely returned; refusing to sanitize in place");
|
|
30869
|
+
if (_neutralizeDiscarded(dirty, inPlaceNodes)) throw typeErrorCreate("a node selected for removal could not be safely returned; refusing to sanitize in place");
|
|
30791
30870
|
if (SAFE_FOR_TEMPLATES) _scrubTemplateExpressions2(dirty);
|
|
30792
30871
|
return dirty;
|
|
30793
30872
|
}
|
|
@@ -30824,6 +30903,7 @@ function createDOMPurify() {
|
|
|
30824
30903
|
if (!CONFIG) _parseConfig({});
|
|
30825
30904
|
const lcTag = transformCaseFunc(tag);
|
|
30826
30905
|
const lcName = transformCaseFunc(attr);
|
|
30906
|
+
if (typeof value === "string" && (_isUnsafeAttributeValue(attr, value) || !ALLOW_SELF_CLOSE_IN_ATTR && regExpTest(SELF_CLOSING_TAG, value))) return false;
|
|
30827
30907
|
return _isValidAttribute(lcTag, lcName, value);
|
|
30828
30908
|
};
|
|
30829
30909
|
DOMPurify.addHook = function(entryPoint, hookFunction) {
|
|
@@ -44991,6 +45071,16 @@ function finalCodeEnding(doc) {
|
|
|
44991
45071
|
last = last.lastChild;
|
|
44992
45072
|
}
|
|
44993
45073
|
}
|
|
45074
|
+
/** Change only the language word, retaining fence spelling and extra metadata. */
|
|
45075
|
+
function languageFence(value, language) {
|
|
45076
|
+
const fence = readFence(value);
|
|
45077
|
+
if (!fence) return null;
|
|
45078
|
+
const match = /^(\s*)(\S+)?([\s\S]*)$/.exec(fence.info);
|
|
45079
|
+
return {
|
|
45080
|
+
...fence,
|
|
45081
|
+
info: match[1] + language + match[3]
|
|
45082
|
+
};
|
|
45083
|
+
}
|
|
44994
45084
|
//#endregion
|
|
44995
45085
|
//#region src/literals.ts
|
|
44996
45086
|
var unsupported = /* @__PURE__ */ new Set([
|
|
@@ -45027,6 +45117,7 @@ function containsUnsupported(node, images, inline = false) {
|
|
|
45027
45117
|
return unsupported.has(node.type) && !(images && node.type === "image") || "children" in node && node.children.some((child) => containsUnsupported(child, images, inline || node.type === "paragraph" || node.type === "heading"));
|
|
45028
45118
|
}
|
|
45029
45119
|
function containsUnresolved(node, source) {
|
|
45120
|
+
if (node.type === "link") return false;
|
|
45030
45121
|
if (node.type === "text") {
|
|
45031
45122
|
const start = node.position?.start.offset;
|
|
45032
45123
|
const end = node.position?.end.offset;
|
|
@@ -47375,7 +47466,7 @@ function completedAngle(tr, chars, from, to) {
|
|
|
47375
47466
|
let start = end - 1;
|
|
47376
47467
|
for (; start >= 0; start--) {
|
|
47377
47468
|
const char = chars[start];
|
|
47378
|
-
if (!char || char.marks.some((mark) => mark.type.spec.code) || char.link && char.form !== "literal" || !sameMarks(char.marks, closing.marks) || /[\s>]/.test(char.text)) return void 0;
|
|
47469
|
+
if (!char || char.marks.some((mark) => mark.type.spec.code) || char.link && char.form !== "literal" || char.text !== "<" && !sameMarks(char.marks, closing.marks) || /[\s>]/.test(char.text)) return void 0;
|
|
47379
47470
|
if (char.text === "<") break;
|
|
47380
47471
|
}
|
|
47381
47472
|
if (start < 0) return void 0;
|
|
@@ -47550,7 +47641,12 @@ function linked(tr, blocks) {
|
|
|
47550
47641
|
}
|
|
47551
47642
|
function readsBack(ctx, tr, pos) {
|
|
47552
47643
|
const block = tr.doc.nodeAt(pos);
|
|
47553
|
-
const
|
|
47644
|
+
const references = /* @__PURE__ */ new Set();
|
|
47645
|
+
block.descendants((node) => {
|
|
47646
|
+
for (const mark of node.marks) if (mark.type.name === "link" && mark.attrs.identifier) references.add(normaliseLabel(String(mark.attrs.identifier)));
|
|
47647
|
+
});
|
|
47648
|
+
const definitions = references.size ? [...referenceDefinitions(tr.doc)].filter(([identifier]) => references.has(identifier)).map(([, definition]) => definition.node) : [];
|
|
47649
|
+
const doc = tr.doc.type.create(null, [tr.doc.type.schema.nodes.paragraph.create(null, block.content), ...definitions]);
|
|
47554
47650
|
return semanticSignature(ctx.get(parserCtx)(serialize(ctx, doc))) === semanticSignature(doc);
|
|
47555
47651
|
}
|
|
47556
47652
|
var textblocks = (doc, from, to) => {
|
|
@@ -50584,16 +50680,16 @@ var literalReread = $prose((ctx) => new Plugin({
|
|
|
50584
50680
|
//#region src/decorations.ts
|
|
50585
50681
|
var copyIcon = "<svg width=\"14\" height=\"14\" viewBox=\"0 0 16 16\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><rect x=\"5.5\" y=\"5.5\" width=\"8\" height=\"8\" rx=\"1.5\"/><path d=\"M10.5 5.5v-2a1 1 0 0 0-1-1h-6a1 1 0 0 0-1 1v6a1 1 0 0 0 1 1h2\"/></svg>";
|
|
50586
50682
|
var doneIcon = "<svg width=\"14\" height=\"14\" viewBox=\"0 0 16 16\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M3.5 8.5l3 3 6-6.5\"/></svg>";
|
|
50587
|
-
function copyButton(onCopy, label) {
|
|
50683
|
+
function copyButton(onCopy, label, text) {
|
|
50588
50684
|
const button = document.createElement("button");
|
|
50589
50685
|
button.type = "button";
|
|
50686
|
+
button.tabIndex = 0;
|
|
50590
50687
|
button.className = "inkkit-copy";
|
|
50591
50688
|
button.setAttribute("aria-label", label);
|
|
50592
50689
|
button.innerHTML = copyIcon;
|
|
50593
50690
|
button.addEventListener("mousedown", (event) => event.preventDefault());
|
|
50594
50691
|
button.addEventListener("click", () => {
|
|
50595
|
-
|
|
50596
|
-
onCopy(code?.textContent ?? "");
|
|
50692
|
+
onCopy(text());
|
|
50597
50693
|
button.dataset.copied = "";
|
|
50598
50694
|
button.innerHTML = doneIcon;
|
|
50599
50695
|
window.setTimeout(() => {
|
|
@@ -50610,7 +50706,10 @@ function codeCopyPlugin(onCopy) {
|
|
|
50610
50706
|
const widgets = [];
|
|
50611
50707
|
state.doc.descendants((node, pos) => {
|
|
50612
50708
|
if (node.type.name !== "code_block") return true;
|
|
50613
|
-
widgets.push(Decoration.widget(pos + 1, () => copyButton(onCopy, editorLabels(ctx).copyCode
|
|
50709
|
+
widgets.push(Decoration.widget(pos + 1, (view, getPos) => copyButton(onCopy, editorLabels(ctx).copyCode, () => {
|
|
50710
|
+
const at = getPos();
|
|
50711
|
+
return at == null ? "" : view.state.doc.nodeAt(at - 1)?.textContent ?? "";
|
|
50712
|
+
}), {
|
|
50614
50713
|
side: -1,
|
|
50615
50714
|
key: "copy",
|
|
50616
50715
|
ignoreSelection: true
|
|
@@ -50633,6 +50732,315 @@ var placeholderPlugin = $prose((ctx) => new Plugin({
|
|
|
50633
50732
|
} }
|
|
50634
50733
|
}));
|
|
50635
50734
|
//#endregion
|
|
50735
|
+
//#region src/code-languages.ts
|
|
50736
|
+
var codeLanguages = [
|
|
50737
|
+
["arduino", "Arduino"],
|
|
50738
|
+
["bash", "Bash"],
|
|
50739
|
+
["c", "C"],
|
|
50740
|
+
["cpp", "C++"],
|
|
50741
|
+
["csharp", "C#"],
|
|
50742
|
+
["css", "CSS"],
|
|
50743
|
+
["diff", "Diff"],
|
|
50744
|
+
["go", "Go"],
|
|
50745
|
+
["graphql", "GraphQL"],
|
|
50746
|
+
["ini", "INI"],
|
|
50747
|
+
["java", "Java"],
|
|
50748
|
+
["javascript", "JavaScript"],
|
|
50749
|
+
["json", "JSON"],
|
|
50750
|
+
["kotlin", "Kotlin"],
|
|
50751
|
+
["less", "Less"],
|
|
50752
|
+
["lua", "Lua"],
|
|
50753
|
+
["makefile", "Makefile"],
|
|
50754
|
+
["markdown", "Markdown"],
|
|
50755
|
+
["objectivec", "Objective-C"],
|
|
50756
|
+
["perl", "Perl"],
|
|
50757
|
+
["php", "PHP"],
|
|
50758
|
+
["php-template", "PHP template"],
|
|
50759
|
+
["python", "Python"],
|
|
50760
|
+
["python-repl", "Python REPL"],
|
|
50761
|
+
["r", "R"],
|
|
50762
|
+
["ruby", "Ruby"],
|
|
50763
|
+
["rust", "Rust"],
|
|
50764
|
+
["scss", "SCSS"],
|
|
50765
|
+
["shell", "Shell"],
|
|
50766
|
+
["sql", "SQL"],
|
|
50767
|
+
["swift", "Swift"],
|
|
50768
|
+
["typescript", "TypeScript"],
|
|
50769
|
+
["vbnet", "Visual Basic .NET"],
|
|
50770
|
+
["wasm", "WebAssembly"],
|
|
50771
|
+
["xml", "HTML / XML"],
|
|
50772
|
+
["yaml", "YAML"]
|
|
50773
|
+
];
|
|
50774
|
+
//#endregion
|
|
50775
|
+
//#region src/code-controls.ts
|
|
50776
|
+
var codeControlCtx = $ctx((operation) => operation(), "inkkitCodeControl");
|
|
50777
|
+
function codeControlsPlugin(visibleRect) {
|
|
50778
|
+
return $prose((ctx) => new Plugin({
|
|
50779
|
+
key: new PluginKey("codeControls"),
|
|
50780
|
+
props: { decorations(state) {
|
|
50781
|
+
const widgets = [];
|
|
50782
|
+
state.doc.descendants((node, pos) => {
|
|
50783
|
+
if (node.type.name !== "code_block") return true;
|
|
50784
|
+
const language = String(node.attrs.language ?? "");
|
|
50785
|
+
widgets.push(Decoration.widget(pos + 1, (view, getPos) => {
|
|
50786
|
+
const labels = editorLabels(ctx);
|
|
50787
|
+
const wrapper = document.createElement("span");
|
|
50788
|
+
wrapper.className = "inkkit-code-controls";
|
|
50789
|
+
const button = document.createElement("button");
|
|
50790
|
+
button.type = "button";
|
|
50791
|
+
button.tabIndex = 0;
|
|
50792
|
+
button.className = "inkkit-code-language";
|
|
50793
|
+
button.textContent = language === "text" || language === "plaintext" ? labels.codePlainText : codeLanguages.find(([value]) => value === language)?.[1] ?? (language || labels.codeAuto);
|
|
50794
|
+
button.setAttribute("aria-label", labels.codeLanguage);
|
|
50795
|
+
button.setAttribute("aria-haspopup", "dialog");
|
|
50796
|
+
button.setAttribute("aria-expanded", "false");
|
|
50797
|
+
wrapper.append(button);
|
|
50798
|
+
let popup;
|
|
50799
|
+
let restore;
|
|
50800
|
+
const close = (focus = false) => {
|
|
50801
|
+
popup?.remove();
|
|
50802
|
+
popup = void 0;
|
|
50803
|
+
restore?.();
|
|
50804
|
+
restore = void 0;
|
|
50805
|
+
button.setAttribute("aria-expanded", "false");
|
|
50806
|
+
if (focus) button.focus();
|
|
50807
|
+
};
|
|
50808
|
+
button.addEventListener("mousedown", (event) => event.preventDefault());
|
|
50809
|
+
button.addEventListener("click", () => {
|
|
50810
|
+
if (popup) {
|
|
50811
|
+
close();
|
|
50812
|
+
return;
|
|
50813
|
+
}
|
|
50814
|
+
const captured = view.state.doc;
|
|
50815
|
+
popup = document.createElement("span");
|
|
50816
|
+
popup.className = "inkkit-code-language-menu";
|
|
50817
|
+
popup.setAttribute("role", "dialog");
|
|
50818
|
+
popup.setAttribute("aria-label", labels.codeLanguage);
|
|
50819
|
+
popup.contentEditable = "false";
|
|
50820
|
+
const search = document.createElement("input");
|
|
50821
|
+
search.type = "search";
|
|
50822
|
+
search.placeholder = labels.codeLanguageSearch;
|
|
50823
|
+
search.setAttribute("aria-label", labels.codeLanguageSearch);
|
|
50824
|
+
const list = document.createElement("span");
|
|
50825
|
+
list.className = "inkkit-code-language-list";
|
|
50826
|
+
popup.append(search, list);
|
|
50827
|
+
const choices = [
|
|
50828
|
+
["", labels.codeAuto],
|
|
50829
|
+
["text", labels.codePlainText],
|
|
50830
|
+
...codeLanguages
|
|
50831
|
+
];
|
|
50832
|
+
if (language && !choices.some(([value]) => value === language)) choices.push([language, language]);
|
|
50833
|
+
const render = () => {
|
|
50834
|
+
list.replaceChildren();
|
|
50835
|
+
for (const [value, name] of choices) {
|
|
50836
|
+
if (!`${value} ${name}`.toLowerCase().includes(search.value.toLowerCase())) continue;
|
|
50837
|
+
const option = document.createElement("button");
|
|
50838
|
+
option.type = "button";
|
|
50839
|
+
option.textContent = name;
|
|
50840
|
+
option.setAttribute("aria-pressed", String(value === language));
|
|
50841
|
+
option.addEventListener("click", () => {
|
|
50842
|
+
close();
|
|
50843
|
+
if (view.state.doc !== captured) return;
|
|
50844
|
+
ctx.get(codeControlCtx.key)(() => {
|
|
50845
|
+
const at = getPos();
|
|
50846
|
+
if (at == null) return false;
|
|
50847
|
+
const position = at - 1;
|
|
50848
|
+
const current = view.state.doc.nodeAt(position);
|
|
50849
|
+
if (current?.type.name !== "code_block") return false;
|
|
50850
|
+
if (value === language) {
|
|
50851
|
+
view.focus();
|
|
50852
|
+
return false;
|
|
50853
|
+
}
|
|
50854
|
+
const transaction = view.state.tr.setNodeMarkup(position, void 0, {
|
|
50855
|
+
...current.attrs,
|
|
50856
|
+
language: value,
|
|
50857
|
+
authoredFence: languageFence(current.attrs.authoredFence, value)
|
|
50858
|
+
});
|
|
50859
|
+
view.dispatch(closeHistory(transaction));
|
|
50860
|
+
view.dispatch(closeHistory(view.state.tr).setMeta("addToHistory", false));
|
|
50861
|
+
view.focus();
|
|
50862
|
+
return true;
|
|
50863
|
+
});
|
|
50864
|
+
});
|
|
50865
|
+
list.append(option);
|
|
50866
|
+
}
|
|
50867
|
+
};
|
|
50868
|
+
search.addEventListener("input", render);
|
|
50869
|
+
popup.addEventListener("keydown", (event) => {
|
|
50870
|
+
if (event.key === "Escape") {
|
|
50871
|
+
event.preventDefault();
|
|
50872
|
+
event.stopPropagation();
|
|
50873
|
+
close(true);
|
|
50874
|
+
}
|
|
50875
|
+
if (event.key === "ArrowDown") {
|
|
50876
|
+
event.preventDefault();
|
|
50877
|
+
const options = [...list.querySelectorAll("button")];
|
|
50878
|
+
options[(options.indexOf(document.activeElement) + 1) % options.length]?.focus();
|
|
50879
|
+
}
|
|
50880
|
+
if (event.key === "ArrowUp") {
|
|
50881
|
+
event.preventDefault();
|
|
50882
|
+
const options = [...list.querySelectorAll("button")];
|
|
50883
|
+
options[(options.indexOf(document.activeElement) - 1 + options.length) % options.length]?.focus();
|
|
50884
|
+
}
|
|
50885
|
+
if (event.key === "Enter" && event.target === search) {
|
|
50886
|
+
event.preventDefault();
|
|
50887
|
+
list.querySelector("button")?.click();
|
|
50888
|
+
}
|
|
50889
|
+
});
|
|
50890
|
+
wrapper.append(popup);
|
|
50891
|
+
render();
|
|
50892
|
+
const bounds = button.getBoundingClientRect();
|
|
50893
|
+
const safe = visibleRect();
|
|
50894
|
+
const left = Math.max(8, safe.left + 4);
|
|
50895
|
+
const right = Math.min(window.innerWidth - 8, safe.right - 4);
|
|
50896
|
+
const top = Math.max(8, safe.top + 4);
|
|
50897
|
+
const bottom = Math.min(window.innerHeight - 8, safe.bottom - 4);
|
|
50898
|
+
const below = Math.max(0, bottom - bounds.bottom - 4);
|
|
50899
|
+
const above = Math.max(0, bounds.top - top - 4);
|
|
50900
|
+
const height = Math.min(320, Math.max(below, above));
|
|
50901
|
+
popup.style.width = `${Math.max(0, Math.min(220, right - left))}px`;
|
|
50902
|
+
popup.style.maxHeight = `${height}px`;
|
|
50903
|
+
popup.style.left = `${Math.max(left, Math.min(bounds.right - 220, right - 220))}px`;
|
|
50904
|
+
const menuHeight = Math.min(height, popup.getBoundingClientRect().height);
|
|
50905
|
+
popup.style.top = `${Math.max(top, Math.min(below >= menuHeight ? bounds.bottom + 4 : bounds.top - menuHeight - 4, bottom - menuHeight))}px`;
|
|
50906
|
+
const outside = (event) => {
|
|
50907
|
+
if (!wrapper.contains(event.target)) close();
|
|
50908
|
+
};
|
|
50909
|
+
const scroll = (event) => {
|
|
50910
|
+
if (popup?.contains(event.target)) return;
|
|
50911
|
+
const current = button.getBoundingClientRect();
|
|
50912
|
+
if (current.top !== bounds.top || current.left !== bounds.left || current.bottom !== bounds.bottom || current.right !== bounds.right) close();
|
|
50913
|
+
};
|
|
50914
|
+
document.addEventListener("pointerdown", outside, true);
|
|
50915
|
+
window.addEventListener("scroll", scroll, true);
|
|
50916
|
+
restore = () => {
|
|
50917
|
+
document.removeEventListener("pointerdown", outside, true);
|
|
50918
|
+
window.removeEventListener("scroll", scroll, true);
|
|
50919
|
+
};
|
|
50920
|
+
button.setAttribute("aria-expanded", "true");
|
|
50921
|
+
render();
|
|
50922
|
+
search.focus({ preventScroll: true });
|
|
50923
|
+
});
|
|
50924
|
+
wrapper.cleanup = close;
|
|
50925
|
+
return wrapper;
|
|
50926
|
+
}, {
|
|
50927
|
+
side: -1,
|
|
50928
|
+
key: `language:${language}`,
|
|
50929
|
+
ignoreSelection: true,
|
|
50930
|
+
stopEvent: () => true,
|
|
50931
|
+
destroy: (dom) => dom.cleanup?.()
|
|
50932
|
+
}));
|
|
50933
|
+
return false;
|
|
50934
|
+
});
|
|
50935
|
+
return DecorationSet.create(state.doc, widgets);
|
|
50936
|
+
} },
|
|
50937
|
+
view: (view) => ({ update(next, previous) {
|
|
50938
|
+
if (next.state.doc !== previous.doc || !next.editable) view.dom.querySelectorAll(".inkkit-code-controls").forEach((element) => element.cleanup?.());
|
|
50939
|
+
} })
|
|
50940
|
+
}));
|
|
50941
|
+
}
|
|
50942
|
+
//#endregion
|
|
50943
|
+
//#region src/mode-selection.ts
|
|
50944
|
+
function modeSelectionMap(ctx, source, doc) {
|
|
50945
|
+
const points = [];
|
|
50946
|
+
const bom = source.startsWith("") ? 1 : 0;
|
|
50947
|
+
let text = "";
|
|
50948
|
+
const offsets = [];
|
|
50949
|
+
const ends = [];
|
|
50950
|
+
const append = (value, positions) => {
|
|
50951
|
+
for (let index = 0; index < value.length; index++) offsets[text.length + index] = positions[index];
|
|
50952
|
+
for (let index = 1; index <= value.length; index++) ends[text.length + index] = positions[index];
|
|
50953
|
+
text += value;
|
|
50954
|
+
};
|
|
50955
|
+
const visit = (node) => {
|
|
50956
|
+
if (node.type === "definition") return;
|
|
50957
|
+
if ("children" in node) {
|
|
50958
|
+
node.children.forEach((child) => visit(child));
|
|
50959
|
+
return;
|
|
50960
|
+
}
|
|
50961
|
+
const start = node.position?.start.offset;
|
|
50962
|
+
const end = node.position?.end.offset;
|
|
50963
|
+
if (start == null || end == null || !("value" in node)) return;
|
|
50964
|
+
const raw = source.slice(start + bom, end + bom);
|
|
50965
|
+
if (node.type === "text") {
|
|
50966
|
+
const decoded = decodedOffsets(raw, true);
|
|
50967
|
+
if (decoded) append(decoded.decoded, decoded.offsets.map((at) => at + start + bom));
|
|
50968
|
+
} else if (node.type === "inlineCode") {
|
|
50969
|
+
const exact = raw.indexOf(node.value);
|
|
50970
|
+
if (exact >= 0) {
|
|
50971
|
+
append(node.value, Array.from({ length: node.value.length + 1 }, (_, index) => start + bom + exact + index));
|
|
50972
|
+
return;
|
|
50973
|
+
}
|
|
50974
|
+
const ticks = /^`+/.exec(raw)?.[0].length ?? 0;
|
|
50975
|
+
let value = "";
|
|
50976
|
+
const positions = [];
|
|
50977
|
+
for (let index = ticks; index < raw.length - ticks; index++) {
|
|
50978
|
+
positions.push(start + bom + index);
|
|
50979
|
+
if (raw[index] === "\r" && raw[index + 1] === "\n") index++;
|
|
50980
|
+
value += /[\r\n]/.test(raw[index]) ? " " : raw[index];
|
|
50981
|
+
}
|
|
50982
|
+
positions.push(start + bom + raw.length - ticks);
|
|
50983
|
+
if (value.startsWith(" ") && value.endsWith(" ") && /[^ ]/.test(value)) {
|
|
50984
|
+
value = value.slice(1, -1);
|
|
50985
|
+
positions.shift();
|
|
50986
|
+
positions.pop();
|
|
50987
|
+
}
|
|
50988
|
+
append(value, positions);
|
|
50989
|
+
} else if (node.type === "code") {
|
|
50990
|
+
const lines = [...raw.matchAll(/([^\r\n]*)(\r\n|\r|\n|$)/g)];
|
|
50991
|
+
let line = /^(`{3,}|~{3,})/.test(raw) ? 1 : 0;
|
|
50992
|
+
const value = node.value.replace(/\r\n?/g, "\n");
|
|
50993
|
+
const positions = [];
|
|
50994
|
+
let offset = 0;
|
|
50995
|
+
for (const content of value.split("\n")) {
|
|
50996
|
+
while (line < lines.length && !lines[line][1].endsWith(content)) line++;
|
|
50997
|
+
const match = lines[line++];
|
|
50998
|
+
if (!match) return;
|
|
50999
|
+
const at = match.index + match[1].length - content.length;
|
|
51000
|
+
for (let index = 0; index <= content.length; index++) positions[offset + index] = start + bom + at + index;
|
|
51001
|
+
offset += content.length + 1;
|
|
51002
|
+
}
|
|
51003
|
+
append(value, positions);
|
|
51004
|
+
} else if (node.type === "html" || node.type === "inkkitLiteral") {
|
|
51005
|
+
const value = node.value;
|
|
51006
|
+
const at = raw.indexOf(value);
|
|
51007
|
+
if (at >= 0) append(value, Array.from({ length: value.length + 1 }, (_, index) => start + bom + at + index));
|
|
51008
|
+
}
|
|
51009
|
+
};
|
|
51010
|
+
const processor = ctx.get(remarkCtx);
|
|
51011
|
+
processor.runSync(processor.parse(source), { value: source }).children.forEach((node) => visit(node));
|
|
51012
|
+
let cursor = 0;
|
|
51013
|
+
doc.descendants((node, position) => {
|
|
51014
|
+
if ([
|
|
51015
|
+
"comment_inline",
|
|
51016
|
+
"comment_block",
|
|
51017
|
+
"reference_definition"
|
|
51018
|
+
].includes(node.type.name)) return false;
|
|
51019
|
+
if (!node.isText) return true;
|
|
51020
|
+
const value = node.text;
|
|
51021
|
+
const at = text.indexOf(value, cursor);
|
|
51022
|
+
if (at < 0) return false;
|
|
51023
|
+
for (let index = 0; index <= value.length; index++) {
|
|
51024
|
+
const offset = index === value.length ? ends[at + index] : offsets[at + index];
|
|
51025
|
+
if (offset != null) points.push({
|
|
51026
|
+
formatted: position + index,
|
|
51027
|
+
source: offset
|
|
51028
|
+
});
|
|
51029
|
+
}
|
|
51030
|
+
cursor = at + value.length;
|
|
51031
|
+
return false;
|
|
51032
|
+
});
|
|
51033
|
+
const nearest = (position, from, to, bias = -1) => {
|
|
51034
|
+
let best;
|
|
51035
|
+
for (const point of points) if (!best || Math.abs(point[from] - position) < Math.abs(best[from] - position) || bias > 0 && Math.abs(point[from] - position) === Math.abs(best[from] - position)) best = point;
|
|
51036
|
+
return best?.[to] ?? (from === "formatted" ? 0 : 1);
|
|
51037
|
+
};
|
|
51038
|
+
return {
|
|
51039
|
+
source: (position, bias = -1) => nearest(position, "formatted", "source", bias),
|
|
51040
|
+
formatted: (offset) => nearest(offset, "source", "formatted")
|
|
51041
|
+
};
|
|
51042
|
+
}
|
|
51043
|
+
//#endregion
|
|
50636
51044
|
//#region src/reference-clipboard.ts
|
|
50637
51045
|
function key(node) {
|
|
50638
51046
|
if (node.type.name === "reference_definition") return `link:${node.attrs.identifier}`;
|
|
@@ -56240,14 +56648,24 @@ var InkKitEditor = class InkKitEditor {
|
|
|
56240
56648
|
expectOneOf(mode, editingModes, "mode");
|
|
56241
56649
|
this.assertCurrent(expectedGeneration);
|
|
56242
56650
|
if (this.formatType === "txt" || this.mode === mode) return false;
|
|
56651
|
+
const wasSource = this.literalSurface;
|
|
56652
|
+
const view = this.editor.ctx.get(editorViewCtx);
|
|
56653
|
+
const focused = wasSource ? this.plain.ownerDocument.activeElement === this.plain : view.hasFocus();
|
|
56654
|
+
const container = this.viewportContainer ?? this.root;
|
|
56655
|
+
const previousSurface = wasSource ? this.plain : container;
|
|
56656
|
+
const fraction = previousSurface.scrollTop / Math.max(1, previousSurface.scrollHeight - previousSurface.clientHeight);
|
|
56657
|
+
const left = previousSurface.scrollLeft;
|
|
56658
|
+
const sourceSelection = wasSource ? {
|
|
56659
|
+
anchor: this.plain.selectionDirection === "backward" ? this.plain.selectionEnd : this.plain.selectionStart,
|
|
56660
|
+
head: this.plain.selectionDirection === "backward" ? this.plain.selectionStart : this.plain.selectionEnd
|
|
56661
|
+
} : void 0;
|
|
56243
56662
|
this.settleSource();
|
|
56244
56663
|
this.undoneSteps = [];
|
|
56245
56664
|
this.formattedUndone = 0;
|
|
56246
|
-
const view = this.editor.ctx.get(editorViewCtx);
|
|
56247
|
-
const focused = this.literalSurface ? this.plain.ownerDocument.activeElement === this.plain : view.hasFocus();
|
|
56248
|
-
const container = this.viewportContainer ?? this.root;
|
|
56249
|
-
const top = container.scrollTop, left = container.scrollLeft;
|
|
56250
56665
|
const text = this.snapshot().text;
|
|
56666
|
+
const mapping = modeSelectionMap(this.editor.ctx, text, view.state.doc);
|
|
56667
|
+
const anchor = sourceSelection ? mapping.formatted(rawOffset(text, sourceSelection.anchor)) : normalizedOffset(text, mapping.source(view.state.selection.anchor, view.state.selection.anchor < view.state.selection.head ? 1 : -1));
|
|
56668
|
+
const head = sourceSelection ? mapping.formatted(rawOffset(text, sourceSelection.head)) : normalizedOffset(text, mapping.source(view.state.selection.head, view.state.selection.head < view.state.selection.anchor ? 1 : -1));
|
|
56251
56669
|
this.mode = mode;
|
|
56252
56670
|
this.session.modeChanged();
|
|
56253
56671
|
this.cancelExports();
|
|
@@ -56255,8 +56673,8 @@ var InkKitEditor = class InkKitEditor {
|
|
|
56255
56673
|
this.literalMatch = void 0;
|
|
56256
56674
|
if (mode === "source") {
|
|
56257
56675
|
this.plain.value = text;
|
|
56258
|
-
this.plain.setSelectionRange(
|
|
56259
|
-
}
|
|
56676
|
+
this.plain.setSelectionRange(Math.min(anchor, head), Math.max(anchor, head), anchor > head ? "backward" : "forward");
|
|
56677
|
+
} else view.dispatch(view.state.tr.setSelection(TextSelection.between(view.state.doc.resolve(Math.min(anchor, view.state.doc.content.size)), view.state.doc.resolve(Math.min(head, view.state.doc.content.size)))).setMeta("addToHistory", false));
|
|
56260
56678
|
const suppressed = this.reportingSuppressed;
|
|
56261
56679
|
this.reportingSuppressed = true;
|
|
56262
56680
|
try {
|
|
@@ -56264,12 +56682,14 @@ var InkKitEditor = class InkKitEditor {
|
|
|
56264
56682
|
} finally {
|
|
56265
56683
|
this.reportingSuppressed = suppressed;
|
|
56266
56684
|
}
|
|
56685
|
+
const surface = this.literalSurface ? this.plain : container;
|
|
56686
|
+
surface.scrollTop = fraction * Math.max(0, surface.scrollHeight - surface.clientHeight);
|
|
56687
|
+
surface.scrollLeft = left;
|
|
56267
56688
|
if (focused) {
|
|
56268
56689
|
if (this.literalSurface) this.plain.focus({ preventScroll: true });
|
|
56269
|
-
else view.focus();
|
|
56690
|
+
else view.dom.focus({ preventScroll: true });
|
|
56691
|
+
this.revealCurrentSelection();
|
|
56270
56692
|
}
|
|
56271
|
-
container.scrollTop = top;
|
|
56272
|
-
container.scrollLeft = left;
|
|
56273
56693
|
this.publishCommandState();
|
|
56274
56694
|
return true;
|
|
56275
56695
|
}
|
|
@@ -56777,6 +57197,10 @@ var InkKitEditor = class InkKitEditor {
|
|
|
56777
57197
|
...defaultLabels,
|
|
56778
57198
|
...options.labels
|
|
56779
57199
|
}));
|
|
57200
|
+
ctx.set(codeControlCtx.key, (operation) => instance.forPerson(() => instance.facadeCall(() => {
|
|
57201
|
+
instance.assertMutation();
|
|
57202
|
+
return !instance.literalSurface && operation();
|
|
57203
|
+
}), false));
|
|
56780
57204
|
ctx.set(tableControlCtx.key, (operation) => instance.forPerson(() => instance.facadeCall(() => {
|
|
56781
57205
|
instance.assertMutation();
|
|
56782
57206
|
return !instance.literalSurface && operation();
|
|
@@ -56841,7 +57265,7 @@ var InkKitEditor = class InkKitEditor {
|
|
|
56841
57265
|
return true;
|
|
56842
57266
|
}
|
|
56843
57267
|
}));
|
|
56844
|
-
}).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(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-
|
|
57268
|
+
}).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-CeNqcHAb.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, {
|
|
56845
57269
|
capture: () => instance.adapterCapture(),
|
|
56846
57270
|
active: () => instance.ready && !instance.destroyed && !instance.literalSurface,
|
|
56847
57271
|
labels: {
|