plutonium 0.60.5 → 0.61.0
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.
- checksums.yaml +4 -4
- data/.claude/skills/plutonium/SKILL.md +19 -1
- data/.claude/skills/plutonium-app/SKILL.md +41 -0
- data/.claude/skills/plutonium-auth/SKILL.md +40 -0
- data/.claude/skills/plutonium-behavior/SKILL.md +47 -1
- data/.claude/skills/plutonium-kanban/SKILL.md +313 -0
- data/.claude/skills/plutonium-resource/SKILL.md +40 -0
- data/.claude/skills/plutonium-tenancy/SKILL.md +43 -0
- data/.claude/skills/plutonium-testing/SKILL.md +38 -0
- data/.claude/skills/plutonium-ui/SKILL.md +51 -0
- data/.claude/skills/plutonium-wizard/SKILL.md +469 -0
- data/.cliff.toml +6 -0
- data/Appraisals +3 -0
- data/CHANGELOG.md +549 -439
- data/CLAUDE.md +15 -7
- data/app/assets/plutonium.css +1 -1
- data/app/assets/plutonium.js +895 -193
- data/app/assets/plutonium.js.map +4 -4
- data/app/assets/plutonium.min.js +53 -53
- data/app/assets/plutonium.min.js.map +4 -4
- data/app/views/layouts/basic.html.erb +7 -0
- data/app/views/plutonium/_flash_toasts.html.erb +2 -46
- data/app/views/plutonium/_toast.html.erb +52 -0
- data/app/views/resource/_resource_kanban.html.erb +1 -0
- data/db/migrate/wizard/20260615000001_create_plutonium_wizard_sessions.rb +57 -0
- data/docs/.vitepress/config.ts +24 -0
- data/docs/guides/index.md +2 -0
- data/docs/guides/kanban.md +447 -0
- data/docs/guides/wizards.md +447 -0
- data/docs/public/images/guides/kanban-after-move.png +0 -0
- data/docs/public/images/guides/kanban-board-light.png +0 -0
- data/docs/public/images/guides/kanban-board.png +0 -0
- data/docs/public/images/guides/kanban-show-centered-modal.png +0 -0
- data/docs/public/images/guides/kanban-wip-toast.png +0 -0
- data/docs/public/images/guides/wizards-chooser.png +0 -0
- data/docs/public/images/guides/wizards-completed.png +0 -0
- data/docs/public/images/guides/wizards-index-action.png +0 -0
- data/docs/public/images/guides/wizards-repeater.png +0 -0
- data/docs/public/images/guides/wizards-review.png +0 -0
- data/docs/public/images/guides/wizards-step.png +0 -0
- data/docs/reference/behavior/policies.md +1 -1
- data/docs/reference/index.md +14 -0
- data/docs/reference/kanban/authorization.md +62 -0
- data/docs/reference/kanban/dsl.md +293 -0
- data/docs/reference/kanban/index.md +40 -0
- data/docs/reference/kanban/positioning.md +162 -0
- data/docs/reference/resource/definition.md +16 -0
- data/docs/reference/ui/forms.md +36 -0
- data/docs/reference/ui/pages.md +2 -0
- data/docs/reference/wizard/anchoring-resume.md +194 -0
- data/docs/reference/wizard/dsl.md +332 -0
- data/docs/reference/wizard/index.md +33 -0
- data/docs/reference/wizard/one-time.md +129 -0
- data/docs/reference/wizard/registration-launch.md +177 -0
- data/docs/reference/wizard/storage-config.md +151 -0
- data/docs/superpowers/plans/2026-06-14-form-sectioning.md +2 -2
- data/docs/superpowers/plans/2026-06-15-wizard-dsl.md +1619 -0
- data/docs/superpowers/plans/2026-06-15-wizard-dsl.md.tasks.json +68 -0
- data/docs/superpowers/plans/2026-06-26-kanban-dsl.md +1128 -0
- data/docs/superpowers/plans/2026-06-26-kanban-dsl.md.tasks.json +24 -0
- data/docs/superpowers/specs/2026-06-15-wizard-dsl-design.md +836 -0
- data/docs/superpowers/specs/2026-06-15-wizard-dsl-examples.rb +245 -0
- data/docs/superpowers/specs/2026-06-17-wizard-relaunch-prompt-design.md +86 -0
- data/docs/superpowers/specs/2026-06-18-wizard-attachments-design.md +101 -0
- data/docs/superpowers/specs/2026-06-18-wizard-hosting-design.md +220 -0
- data/docs/superpowers/specs/2026-06-26-kanban-dsl-design.md +388 -0
- data/gemfiles/postgres.gemfile +8 -0
- data/gemfiles/postgres.gemfile.lock +321 -0
- data/gemfiles/rails_7.gemfile +1 -0
- data/gemfiles/rails_7.gemfile.lock +1 -1
- data/gemfiles/rails_8.0.gemfile +1 -0
- data/gemfiles/rails_8.0.gemfile.lock +1 -1
- data/gemfiles/rails_8.1.gemfile +1 -0
- data/gemfiles/rails_8.1.gemfile.lock +14 -1
- data/lib/generators/pu/invites/templates/packages/invites/app/controllers/invites/user_invitations_controller.rb.tt +6 -1
- data/lib/plutonium/action/base.rb +9 -0
- data/lib/plutonium/auth/rodauth.rb +1 -2
- data/lib/plutonium/configuration.rb +4 -0
- data/lib/plutonium/core/controller.rb +20 -1
- data/lib/plutonium/definition/base.rb +25 -0
- data/lib/plutonium/definition/form_layout.rb +54 -35
- data/lib/plutonium/definition/index_views.rb +54 -1
- data/lib/plutonium/definition/wizards.rb +209 -0
- data/lib/plutonium/invites/concerns/invite_token.rb +9 -0
- data/lib/plutonium/invites/concerns/invite_user.rb +9 -0
- data/lib/plutonium/invites/controller.rb +4 -1
- data/lib/plutonium/kanban/action.rb +7 -0
- data/lib/plutonium/kanban/board.rb +40 -0
- data/lib/plutonium/kanban/broadcaster.rb +54 -0
- data/lib/plutonium/kanban/column.rb +69 -0
- data/lib/plutonium/kanban/context.rb +15 -0
- data/lib/plutonium/kanban/dsl.rb +71 -0
- data/lib/plutonium/kanban/grouping.rb +51 -0
- data/lib/plutonium/kanban/positioning.rb +75 -0
- data/lib/plutonium/kanban.rb +11 -0
- data/lib/plutonium/migrations.rb +40 -0
- data/lib/plutonium/positioning.rb +146 -0
- data/lib/plutonium/railtie.rb +33 -0
- data/lib/plutonium/resource/controller.rb +2 -0
- data/lib/plutonium/resource/controllers/crud_actions.rb +1 -1
- data/lib/plutonium/resource/controllers/kanban_actions.rb +455 -0
- data/lib/plutonium/resource/controllers/wizard_actions.rb +165 -0
- data/lib/plutonium/resource/policy.rb +8 -0
- data/lib/plutonium/routing/mapper_extensions.rb +44 -0
- data/lib/plutonium/routing/wizard_registration.rb +289 -0
- data/lib/plutonium/ui/display/resource.rb +17 -12
- data/lib/plutonium/ui/form/base.rb +19 -5
- data/lib/plutonium/ui/form/components/password.rb +126 -0
- data/lib/plutonium/ui/form/components/uppy.rb +6 -3
- data/lib/plutonium/ui/form/options/inferred_types.rb +20 -0
- data/lib/plutonium/ui/form/resource.rb +1 -1
- data/lib/plutonium/ui/form/wizard.rb +63 -0
- data/lib/plutonium/ui/grid/card.rb +16 -5
- data/lib/plutonium/ui/kanban/card.rb +67 -0
- data/lib/plutonium/ui/kanban/color_dot.rb +36 -0
- data/lib/plutonium/ui/kanban/column.rb +324 -0
- data/lib/plutonium/ui/kanban/resource.rb +212 -0
- data/lib/plutonium/ui/layout/resource_layout.rb +7 -1
- data/lib/plutonium/ui/modal/base.rb +30 -3
- data/lib/plutonium/ui/modal/centered.rb +5 -2
- data/lib/plutonium/ui/page/index.rb +1 -0
- data/lib/plutonium/ui/page/show.rb +23 -0
- data/lib/plutonium/ui/page/wizard.rb +371 -0
- data/lib/plutonium/ui/page/wizard_chooser.rb +97 -0
- data/lib/plutonium/ui/page/wizard_completed.rb +86 -0
- data/lib/plutonium/ui/table/base.rb +1 -1
- data/lib/plutonium/ui/table/components/view_switcher.rb +2 -1
- data/lib/plutonium/ui/wizard/review.rb +196 -0
- data/lib/plutonium/ui/wizard/stepper.rb +122 -0
- data/lib/plutonium/ui/wizard/summary_display.rb +59 -0
- data/lib/plutonium/version.rb +1 -1
- data/lib/plutonium/wizard/attachment_data.rb +42 -0
- data/lib/plutonium/wizard/attachments.rb +226 -0
- data/lib/plutonium/wizard/base.rb +216 -0
- data/lib/plutonium/wizard/base_controller.rb +31 -0
- data/lib/plutonium/wizard/configuration.rb +42 -0
- data/lib/plutonium/wizard/controller.rb +162 -0
- data/lib/plutonium/wizard/data.rb +134 -0
- data/lib/plutonium/wizard/driving.rb +639 -0
- data/lib/plutonium/wizard/dsl.rb +336 -0
- data/lib/plutonium/wizard/errors.rb +27 -0
- data/lib/plutonium/wizard/field_capture.rb +157 -0
- data/lib/plutonium/wizard/field_importer.rb +208 -0
- data/lib/plutonium/wizard/gate.rb +171 -0
- data/lib/plutonium/wizard/instance_key.rb +97 -0
- data/lib/plutonium/wizard/lazy_persisted.rb +77 -0
- data/lib/plutonium/wizard/resume.rb +250 -0
- data/lib/plutonium/wizard/review_step.rb +48 -0
- data/lib/plutonium/wizard/route_resolution.rb +40 -0
- data/lib/plutonium/wizard/runner.rb +684 -0
- data/lib/plutonium/wizard/session.rb +53 -0
- data/lib/plutonium/wizard/state.rb +35 -0
- data/lib/plutonium/wizard/step.rb +61 -0
- data/lib/plutonium/wizard/step_adapter.rb +103 -0
- data/lib/plutonium/wizard/store/active_record.rb +174 -0
- data/lib/plutonium/wizard/store/base.rb +42 -0
- data/lib/plutonium/wizard/store/memory.rb +44 -0
- data/lib/plutonium/wizard/sweep_job.rb +76 -0
- data/lib/plutonium/wizard.rb +86 -0
- data/lib/plutonium.rb +5 -0
- data/lib/rodauth/features/case_insensitive_login.rb +1 -1
- data/lib/tasks/release.rake +144 -191
- data/package.json +3 -3
- data/src/css/components.css +132 -0
- data/src/js/controllers/attachment_input_controller.js +15 -1
- data/src/js/controllers/dirty_form_guard_controller.js +155 -27
- data/src/js/controllers/kanban_controller.js +330 -0
- data/src/js/controllers/password_sentinel_controller.js +39 -0
- data/src/js/controllers/register_controllers.js +6 -0
- data/src/js/controllers/remote_modal_controller.js +10 -0
- data/src/js/controllers/row_click_controller.js +14 -1
- data/src/js/controllers/wizard_controller.js +54 -0
- data/src/js/turbo/turbo_confirm.js +1 -1
- data/yarn.lock +271 -282
- metadata +100 -1
data/app/assets/plutonium.js
CHANGED
|
@@ -6,7 +6,11 @@
|
|
|
6
6
|
var __getProtoOf = Object.getPrototypeOf;
|
|
7
7
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
8
|
var __commonJS = (cb, mod) => function __require() {
|
|
9
|
-
|
|
9
|
+
try {
|
|
10
|
+
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
|
11
|
+
} catch (e4) {
|
|
12
|
+
throw mod = 0, e4;
|
|
13
|
+
}
|
|
10
14
|
};
|
|
11
15
|
var __copyProps = (to, from, except, desc) => {
|
|
12
16
|
if (from && typeof from === "object" || typeof from === "function") {
|
|
@@ -13655,7 +13659,7 @@
|
|
|
13655
13659
|
var mathMl$1 = freeze(["math", "menclose", "merror", "mfenced", "mfrac", "mglyph", "mi", "mlabeledtr", "mmultiscripts", "mn", "mo", "mover", "mpadded", "mphantom", "mroot", "mrow", "ms", "mspace", "msqrt", "mstyle", "msub", "msup", "msubsup", "mtable", "mtd", "mtext", "mtr", "munder", "munderover", "mprescripts"]);
|
|
13656
13660
|
var mathMlDisallowed = freeze(["maction", "maligngroup", "malignmark", "mlongdiv", "mscarries", "mscarry", "msgroup", "mstack", "msline", "msrow", "semantics", "annotation", "annotation-xml", "mprescripts", "none"]);
|
|
13657
13661
|
var text = freeze(["#text"]);
|
|
13658
|
-
var html = freeze(["accept", "action", "align", "alt", "autocapitalize", "autocomplete", "autopictureinpicture", "autoplay", "background", "bgcolor", "border", "capture", "cellpadding", "cellspacing", "checked", "cite", "class", "clear", "color", "cols", "colspan", "controls", "controlslist", "coords", "crossorigin", "datetime", "decoding", "default", "dir", "disabled", "disablepictureinpicture", "disableremoteplayback", "download", "draggable", "enctype", "enterkeyhint", "exportparts", "face", "for", "headers", "height", "hidden", "high", "href", "hreflang", "id", "inert", "inputmode", "integrity", "ismap", "kind", "label", "lang", "list", "loading", "loop", "low", "max", "maxlength", "media", "method", "min", "minlength", "multiple", "muted", "name", "nonce", "noshade", "novalidate", "nowrap", "open", "optimum", "part", "pattern", "placeholder", "playsinline", "popover", "popovertarget", "popovertargetaction", "poster", "preload", "pubdate", "radiogroup", "readonly", "rel", "required", "rev", "reversed", "role", "rows", "rowspan", "spellcheck", "scope", "selected", "shape", "size", "sizes", "slot", "span", "srclang", "start", "src", "srcset", "step", "style", "summary", "tabindex", "title", "translate", "type", "usemap", "valign", "value", "width", "wrap", "xmlns"]);
|
|
13662
|
+
var html = freeze(["accept", "action", "align", "alt", "autocapitalize", "autocomplete", "autopictureinpicture", "autoplay", "background", "bgcolor", "border", "capture", "cellpadding", "cellspacing", "checked", "cite", "class", "clear", "color", "cols", "colspan", "command", "commandfor", "controls", "controlslist", "coords", "crossorigin", "datetime", "decoding", "default", "dir", "disabled", "disablepictureinpicture", "disableremoteplayback", "download", "draggable", "enctype", "enterkeyhint", "exportparts", "face", "for", "headers", "height", "hidden", "high", "href", "hreflang", "id", "inert", "inputmode", "integrity", "ismap", "kind", "label", "lang", "list", "loading", "loop", "low", "max", "maxlength", "media", "method", "min", "minlength", "multiple", "muted", "name", "nonce", "noshade", "novalidate", "nowrap", "open", "optimum", "part", "pattern", "placeholder", "playsinline", "popover", "popovertarget", "popovertargetaction", "poster", "preload", "pubdate", "radiogroup", "readonly", "rel", "required", "rev", "reversed", "role", "rows", "rowspan", "spellcheck", "scope", "selected", "shape", "size", "sizes", "slot", "span", "srclang", "start", "src", "srcset", "step", "style", "summary", "tabindex", "title", "translate", "type", "usemap", "valign", "value", "width", "wrap", "xmlns"]);
|
|
13659
13663
|
var svg = freeze(["accent-height", "accumulate", "additive", "alignment-baseline", "amplitude", "ascent", "attributename", "attributetype", "azimuth", "basefrequency", "baseline-shift", "begin", "bias", "by", "class", "clip", "clippathunits", "clip-path", "clip-rule", "color", "color-interpolation", "color-interpolation-filters", "color-profile", "color-rendering", "cx", "cy", "d", "dx", "dy", "diffuseconstant", "direction", "display", "divisor", "dur", "edgemode", "elevation", "end", "exponent", "fill", "fill-opacity", "fill-rule", "filter", "filterunits", "flood-color", "flood-opacity", "font-family", "font-size", "font-size-adjust", "font-stretch", "font-style", "font-variant", "font-weight", "fx", "fy", "g1", "g2", "glyph-name", "glyphref", "gradientunits", "gradienttransform", "height", "href", "id", "image-rendering", "in", "in2", "intercept", "k", "k1", "k2", "k3", "k4", "kerning", "keypoints", "keysplines", "keytimes", "lang", "lengthadjust", "letter-spacing", "kernelmatrix", "kernelunitlength", "lighting-color", "local", "marker-end", "marker-mid", "marker-start", "markerheight", "markerunits", "markerwidth", "maskcontentunits", "maskunits", "max", "mask", "mask-type", "media", "method", "mode", "min", "name", "numoctaves", "offset", "operator", "opacity", "order", "orient", "orientation", "origin", "overflow", "paint-order", "path", "pathlength", "patterncontentunits", "patterntransform", "patternunits", "points", "preservealpha", "preserveaspectratio", "primitiveunits", "r", "rx", "ry", "radius", "refx", "refy", "repeatcount", "repeatdur", "restart", "result", "rotate", "scale", "seed", "shape-rendering", "slope", "specularconstant", "specularexponent", "spreadmethod", "startoffset", "stddeviation", "stitchtiles", "stop-color", "stop-opacity", "stroke-dasharray", "stroke-dashoffset", "stroke-linecap", "stroke-linejoin", "stroke-miterlimit", "stroke-opacity", "stroke", "stroke-width", "style", "surfacescale", "systemlanguage", "tabindex", "tablevalues", "targetx", "targety", "transform", "transform-origin", "text-anchor", "text-decoration", "text-rendering", "textlength", "type", "u1", "u2", "unicode", "values", "viewbox", "visibility", "version", "vert-adv-y", "vert-origin-x", "vert-origin-y", "width", "word-spacing", "wrap", "writing-mode", "xchannelselector", "ychannelselector", "x", "x1", "x2", "xmlns", "y", "y1", "y2", "z", "zoomandpan"]);
|
|
13660
13664
|
var mathMl = freeze(["accent", "accentunder", "align", "bevelled", "close", "columnalign", "columnlines", "columnspacing", "columnspan", "denomalign", "depth", "dir", "display", "displaystyle", "encoding", "fence", "frame", "height", "href", "id", "largeop", "length", "linethickness", "lquote", "lspace", "mathbackground", "mathcolor", "mathsize", "mathvariant", "maxsize", "minsize", "movablelimits", "notation", "numalign", "open", "rowalign", "rowlines", "rowspacing", "rowspan", "rspace", "rquote", "scriptlevel", "scriptminsize", "scriptsizemultiplier", "selection", "separator", "separators", "stretchy", "subscriptshift", "supscriptshift", "symmetric", "voffset", "width", "xmlns"]);
|
|
13661
13665
|
var xml = freeze(["xlink:href", "xml:id", "xlink:title", "xml:space", "xmlns:xlink"]);
|
|
@@ -13675,13 +13679,26 @@
|
|
|
13675
13679
|
);
|
|
13676
13680
|
var DOCTYPE_NAME = seal(/^html$/i);
|
|
13677
13681
|
var CUSTOM_ELEMENT = seal(/^[a-z][.\w]*(-[.\w]+)+$/i);
|
|
13682
|
+
var ELEMENT_MARKUP_PROBE = seal(/<[/\w!]/g);
|
|
13683
|
+
var COMMENT_MARKUP_PROBE = seal(/<[/\w]/g);
|
|
13684
|
+
var FALLBACK_TAG_CLOSE = seal(/<\/no(script|embed|frames)/i);
|
|
13685
|
+
var SELF_CLOSING_TAG = seal(/\/>/i);
|
|
13678
13686
|
var NODE_TYPE = {
|
|
13679
13687
|
element: 1,
|
|
13688
|
+
attribute: 2,
|
|
13680
13689
|
text: 3,
|
|
13690
|
+
cdataSection: 4,
|
|
13691
|
+
entityReference: 5,
|
|
13692
|
+
// Deprecated
|
|
13693
|
+
entityNode: 6,
|
|
13681
13694
|
// Deprecated
|
|
13682
|
-
|
|
13695
|
+
processingInstruction: 7,
|
|
13683
13696
|
comment: 8,
|
|
13684
|
-
document: 9
|
|
13697
|
+
document: 9,
|
|
13698
|
+
documentType: 10,
|
|
13699
|
+
documentFragment: 11,
|
|
13700
|
+
notation: 12
|
|
13701
|
+
// Deprecated
|
|
13685
13702
|
};
|
|
13686
13703
|
var getGlobal = function getGlobal2() {
|
|
13687
13704
|
return typeof window === "undefined" ? null : window;
|
|
@@ -13723,10 +13740,13 @@
|
|
|
13723
13740
|
uponSanitizeShadowNode: []
|
|
13724
13741
|
};
|
|
13725
13742
|
};
|
|
13743
|
+
var _resolveSetOption = function _resolveSetOption2(cfg, key, fallback, options2) {
|
|
13744
|
+
return objectHasOwnProperty(cfg, key) && arrayIsArray(cfg[key]) ? addToSet(options2.base ? clone(options2.base) : {}, cfg[key], options2.transform) : fallback;
|
|
13745
|
+
};
|
|
13726
13746
|
function createDOMPurify() {
|
|
13727
13747
|
let window2 = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : getGlobal();
|
|
13728
13748
|
const DOMPurify = (root) => createDOMPurify(root);
|
|
13729
|
-
DOMPurify.version = "3.4.
|
|
13749
|
+
DOMPurify.version = "3.4.11";
|
|
13730
13750
|
DOMPurify.removed = [];
|
|
13731
13751
|
if (!window2 || !window2.document || window2.document.nodeType !== NODE_TYPE.document || !window2.Element) {
|
|
13732
13752
|
DOMPurify.isSupported = false;
|
|
@@ -13735,13 +13755,21 @@
|
|
|
13735
13755
|
let document2 = window2.document;
|
|
13736
13756
|
const originalDocument = document2;
|
|
13737
13757
|
const currentScript = originalDocument.currentScript;
|
|
13738
|
-
|
|
13758
|
+
window2.DocumentFragment;
|
|
13759
|
+
const HTMLTemplateElement2 = window2.HTMLTemplateElement, Node2 = window2.Node, Element2 = window2.Element, NodeFilter = window2.NodeFilter, _window$NamedNodeMap = window2.NamedNodeMap;
|
|
13760
|
+
_window$NamedNodeMap === void 0 ? window2.NamedNodeMap || window2.MozNamedAttrMap : _window$NamedNodeMap;
|
|
13761
|
+
window2.HTMLFormElement;
|
|
13762
|
+
const DOMParser2 = window2.DOMParser, trustedTypes = window2.trustedTypes;
|
|
13739
13763
|
const ElementPrototype = Element2.prototype;
|
|
13740
13764
|
const cloneNode = lookupGetter(ElementPrototype, "cloneNode");
|
|
13741
13765
|
const remove = lookupGetter(ElementPrototype, "remove");
|
|
13742
13766
|
const getNextSibling = lookupGetter(ElementPrototype, "nextSibling");
|
|
13743
13767
|
const getChildNodes = lookupGetter(ElementPrototype, "childNodes");
|
|
13744
13768
|
const getParentNode2 = lookupGetter(ElementPrototype, "parentNode");
|
|
13769
|
+
const getShadowRoot = lookupGetter(ElementPrototype, "shadowRoot");
|
|
13770
|
+
const getAttributes = lookupGetter(ElementPrototype, "attributes");
|
|
13771
|
+
const getNodeType = Node2 && Node2.prototype ? lookupGetter(Node2.prototype, "nodeType") : null;
|
|
13772
|
+
const getNodeName2 = Node2 && Node2.prototype ? lookupGetter(Node2.prototype, "nodeName") : null;
|
|
13745
13773
|
if (typeof HTMLTemplateElement2 === "function") {
|
|
13746
13774
|
const template = document2.createElement("template");
|
|
13747
13775
|
if (template.content && template.content.ownerDocument) {
|
|
@@ -13750,6 +13778,39 @@
|
|
|
13750
13778
|
}
|
|
13751
13779
|
let trustedTypesPolicy;
|
|
13752
13780
|
let emptyHTML = "";
|
|
13781
|
+
let defaultTrustedTypesPolicy;
|
|
13782
|
+
let defaultTrustedTypesPolicyResolved = false;
|
|
13783
|
+
let IN_TRUSTED_TYPES_POLICY = 0;
|
|
13784
|
+
const _assertNotInTrustedTypesPolicy = function _assertNotInTrustedTypesPolicy2() {
|
|
13785
|
+
if (IN_TRUSTED_TYPES_POLICY > 0) {
|
|
13786
|
+
throw typeErrorCreate('A configured TRUSTED_TYPES_POLICY callback (createHTML or createScriptURL) must not call DOMPurify.sanitize, as that causes infinite recursion. Do not pass a policy whose callbacks wrap DOMPurify as TRUSTED_TYPES_POLICY; see the "DOMPurify and Trusted Types" section of the README.');
|
|
13787
|
+
}
|
|
13788
|
+
};
|
|
13789
|
+
const _createTrustedHTML = function _createTrustedHTML2(html3) {
|
|
13790
|
+
_assertNotInTrustedTypesPolicy();
|
|
13791
|
+
IN_TRUSTED_TYPES_POLICY++;
|
|
13792
|
+
try {
|
|
13793
|
+
return trustedTypesPolicy.createHTML(html3);
|
|
13794
|
+
} finally {
|
|
13795
|
+
IN_TRUSTED_TYPES_POLICY--;
|
|
13796
|
+
}
|
|
13797
|
+
};
|
|
13798
|
+
const _createTrustedScriptURL = function _createTrustedScriptURL2(scriptUrl) {
|
|
13799
|
+
_assertNotInTrustedTypesPolicy();
|
|
13800
|
+
IN_TRUSTED_TYPES_POLICY++;
|
|
13801
|
+
try {
|
|
13802
|
+
return trustedTypesPolicy.createScriptURL(scriptUrl);
|
|
13803
|
+
} finally {
|
|
13804
|
+
IN_TRUSTED_TYPES_POLICY--;
|
|
13805
|
+
}
|
|
13806
|
+
};
|
|
13807
|
+
const _getDefaultTrustedTypesPolicy = function _getDefaultTrustedTypesPolicy2() {
|
|
13808
|
+
if (!defaultTrustedTypesPolicyResolved) {
|
|
13809
|
+
defaultTrustedTypesPolicy = _createTrustedTypesPolicy(trustedTypes, currentScript);
|
|
13810
|
+
defaultTrustedTypesPolicyResolved = true;
|
|
13811
|
+
}
|
|
13812
|
+
return defaultTrustedTypesPolicy;
|
|
13813
|
+
};
|
|
13753
13814
|
const _document = document2, implementation = _document.implementation, createNodeIterator = _document.createNodeIterator, createDocumentFragment2 = _document.createDocumentFragment, getElementsByTagName = _document.getElementsByTagName;
|
|
13754
13815
|
const importNode = originalDocument.importNode;
|
|
13755
13816
|
let hooks = _createHooksMap();
|
|
@@ -13804,6 +13865,8 @@
|
|
|
13804
13865
|
let SAFE_FOR_XML = true;
|
|
13805
13866
|
let WHOLE_DOCUMENT = false;
|
|
13806
13867
|
let SET_CONFIG = false;
|
|
13868
|
+
let SET_CONFIG_ALLOWED_TAGS = null;
|
|
13869
|
+
let SET_CONFIG_ALLOWED_ATTR = null;
|
|
13807
13870
|
let FORCE_BODY = false;
|
|
13808
13871
|
let RETURN_DOM = false;
|
|
13809
13872
|
let RETURN_DOM_FRAGMENT = false;
|
|
@@ -13815,7 +13878,43 @@
|
|
|
13815
13878
|
let IN_PLACE = false;
|
|
13816
13879
|
let USE_PROFILES = {};
|
|
13817
13880
|
let FORBID_CONTENTS = null;
|
|
13818
|
-
const DEFAULT_FORBID_CONTENTS = addToSet({}, [
|
|
13881
|
+
const DEFAULT_FORBID_CONTENTS = addToSet({}, [
|
|
13882
|
+
"annotation-xml",
|
|
13883
|
+
"audio",
|
|
13884
|
+
"colgroup",
|
|
13885
|
+
"desc",
|
|
13886
|
+
"foreignobject",
|
|
13887
|
+
"head",
|
|
13888
|
+
"iframe",
|
|
13889
|
+
"math",
|
|
13890
|
+
"mi",
|
|
13891
|
+
"mn",
|
|
13892
|
+
"mo",
|
|
13893
|
+
"ms",
|
|
13894
|
+
"mtext",
|
|
13895
|
+
"noembed",
|
|
13896
|
+
"noframes",
|
|
13897
|
+
"noscript",
|
|
13898
|
+
"plaintext",
|
|
13899
|
+
"script",
|
|
13900
|
+
// <selectedcontent> mirrors the selected <option>'s subtree, cloned by
|
|
13901
|
+
// the UA (customizable <select>) — including any on* handlers — and the
|
|
13902
|
+
// engine re-mirrors synchronously whenever a removal changes which
|
|
13903
|
+
// option/selectedcontent is current, even inside DOMPurify's inert
|
|
13904
|
+
// DOMParser document. Hoisting its children on removal re-inserts a fresh
|
|
13905
|
+
// mirror target ahead of the walk, which the engine refills, looping
|
|
13906
|
+
// forever (DoS) and amplifying output. Dropping its content on removal
|
|
13907
|
+
// (rather than hoisting) breaks that cascade; the content is a duplicate
|
|
13908
|
+
// of the option, which is sanitized on its own. See campaign-3 F1/F6.
|
|
13909
|
+
"selectedcontent",
|
|
13910
|
+
"style",
|
|
13911
|
+
"svg",
|
|
13912
|
+
"template",
|
|
13913
|
+
"thead",
|
|
13914
|
+
"title",
|
|
13915
|
+
"video",
|
|
13916
|
+
"xmp"
|
|
13917
|
+
]);
|
|
13819
13918
|
let DATA_URI_TAGS = null;
|
|
13820
13919
|
const DEFAULT_DATA_URI_TAGS = addToSet({}, ["audio", "video", "img", "source", "image", "track"]);
|
|
13821
13920
|
let URI_SAFE_ATTRIBUTES = null;
|
|
@@ -13827,8 +13926,10 @@
|
|
|
13827
13926
|
let IS_EMPTY_INPUT = false;
|
|
13828
13927
|
let ALLOWED_NAMESPACES = null;
|
|
13829
13928
|
const DEFAULT_ALLOWED_NAMESPACES = addToSet({}, [MATHML_NAMESPACE, SVG_NAMESPACE, HTML_NAMESPACE], stringToString);
|
|
13830
|
-
|
|
13831
|
-
let
|
|
13929
|
+
const DEFAULT_MATHML_TEXT_INTEGRATION_POINTS = freeze(["mi", "mo", "mn", "ms", "mtext"]);
|
|
13930
|
+
let MATHML_TEXT_INTEGRATION_POINTS = addToSet({}, DEFAULT_MATHML_TEXT_INTEGRATION_POINTS);
|
|
13931
|
+
const DEFAULT_HTML_INTEGRATION_POINTS = freeze(["annotation-xml"]);
|
|
13932
|
+
let HTML_INTEGRATION_POINTS = addToSet({}, DEFAULT_HTML_INTEGRATION_POINTS);
|
|
13832
13933
|
const COMMON_SVG_AND_HTML_ELEMENTS = addToSet({}, ["title", "style", "font", "a", "script"]);
|
|
13833
13934
|
let PARSER_MEDIA_TYPE = null;
|
|
13834
13935
|
const SUPPORTED_PARSER_MEDIA_TYPES = ["application/xhtml+xml", "text/html"];
|
|
@@ -13851,14 +13952,32 @@
|
|
|
13851
13952
|
PARSER_MEDIA_TYPE = // eslint-disable-next-line unicorn/prefer-includes
|
|
13852
13953
|
SUPPORTED_PARSER_MEDIA_TYPES.indexOf(cfg.PARSER_MEDIA_TYPE) === -1 ? DEFAULT_PARSER_MEDIA_TYPE : cfg.PARSER_MEDIA_TYPE;
|
|
13853
13954
|
transformCaseFunc = PARSER_MEDIA_TYPE === "application/xhtml+xml" ? stringToString : stringToLowerCase;
|
|
13854
|
-
ALLOWED_TAGS =
|
|
13855
|
-
|
|
13856
|
-
|
|
13857
|
-
|
|
13858
|
-
|
|
13859
|
-
|
|
13860
|
-
|
|
13861
|
-
|
|
13955
|
+
ALLOWED_TAGS = _resolveSetOption(cfg, "ALLOWED_TAGS", DEFAULT_ALLOWED_TAGS, {
|
|
13956
|
+
transform: transformCaseFunc
|
|
13957
|
+
});
|
|
13958
|
+
ALLOWED_ATTR = _resolveSetOption(cfg, "ALLOWED_ATTR", DEFAULT_ALLOWED_ATTR, {
|
|
13959
|
+
transform: transformCaseFunc
|
|
13960
|
+
});
|
|
13961
|
+
ALLOWED_NAMESPACES = _resolveSetOption(cfg, "ALLOWED_NAMESPACES", DEFAULT_ALLOWED_NAMESPACES, {
|
|
13962
|
+
transform: stringToString
|
|
13963
|
+
});
|
|
13964
|
+
URI_SAFE_ATTRIBUTES = _resolveSetOption(cfg, "ADD_URI_SAFE_ATTR", DEFAULT_URI_SAFE_ATTRIBUTES, {
|
|
13965
|
+
transform: transformCaseFunc,
|
|
13966
|
+
base: DEFAULT_URI_SAFE_ATTRIBUTES
|
|
13967
|
+
});
|
|
13968
|
+
DATA_URI_TAGS = _resolveSetOption(cfg, "ADD_DATA_URI_TAGS", DEFAULT_DATA_URI_TAGS, {
|
|
13969
|
+
transform: transformCaseFunc,
|
|
13970
|
+
base: DEFAULT_DATA_URI_TAGS
|
|
13971
|
+
});
|
|
13972
|
+
FORBID_CONTENTS = _resolveSetOption(cfg, "FORBID_CONTENTS", DEFAULT_FORBID_CONTENTS, {
|
|
13973
|
+
transform: transformCaseFunc
|
|
13974
|
+
});
|
|
13975
|
+
FORBID_TAGS = _resolveSetOption(cfg, "FORBID_TAGS", clone({}), {
|
|
13976
|
+
transform: transformCaseFunc
|
|
13977
|
+
});
|
|
13978
|
+
FORBID_ATTR = _resolveSetOption(cfg, "FORBID_ATTR", clone({}), {
|
|
13979
|
+
transform: transformCaseFunc
|
|
13980
|
+
});
|
|
13862
13981
|
USE_PROFILES = objectHasOwnProperty(cfg, "USE_PROFILES") ? cfg.USE_PROFILES && typeof cfg.USE_PROFILES === "object" ? clone(cfg.USE_PROFILES) : cfg.USE_PROFILES : false;
|
|
13863
13982
|
ALLOW_ARIA_ATTR = cfg.ALLOW_ARIA_ATTR !== false;
|
|
13864
13983
|
ALLOW_DATA_ATTR = cfg.ALLOW_DATA_ATTR !== false;
|
|
@@ -13877,8 +13996,8 @@
|
|
|
13877
13996
|
IN_PLACE = cfg.IN_PLACE || false;
|
|
13878
13997
|
IS_ALLOWED_URI$1 = isRegex(cfg.ALLOWED_URI_REGEXP) ? cfg.ALLOWED_URI_REGEXP : IS_ALLOWED_URI;
|
|
13879
13998
|
NAMESPACE = typeof cfg.NAMESPACE === "string" ? cfg.NAMESPACE : HTML_NAMESPACE;
|
|
13880
|
-
MATHML_TEXT_INTEGRATION_POINTS = objectHasOwnProperty(cfg, "MATHML_TEXT_INTEGRATION_POINTS") && cfg.MATHML_TEXT_INTEGRATION_POINTS && typeof cfg.MATHML_TEXT_INTEGRATION_POINTS === "object" ? clone(cfg.MATHML_TEXT_INTEGRATION_POINTS) : addToSet({},
|
|
13881
|
-
HTML_INTEGRATION_POINTS = objectHasOwnProperty(cfg, "HTML_INTEGRATION_POINTS") && cfg.HTML_INTEGRATION_POINTS && typeof cfg.HTML_INTEGRATION_POINTS === "object" ? clone(cfg.HTML_INTEGRATION_POINTS) : addToSet({},
|
|
13999
|
+
MATHML_TEXT_INTEGRATION_POINTS = objectHasOwnProperty(cfg, "MATHML_TEXT_INTEGRATION_POINTS") && cfg.MATHML_TEXT_INTEGRATION_POINTS && typeof cfg.MATHML_TEXT_INTEGRATION_POINTS === "object" ? clone(cfg.MATHML_TEXT_INTEGRATION_POINTS) : addToSet({}, DEFAULT_MATHML_TEXT_INTEGRATION_POINTS);
|
|
14000
|
+
HTML_INTEGRATION_POINTS = objectHasOwnProperty(cfg, "HTML_INTEGRATION_POINTS") && cfg.HTML_INTEGRATION_POINTS && typeof cfg.HTML_INTEGRATION_POINTS === "object" ? clone(cfg.HTML_INTEGRATION_POINTS) : addToSet({}, DEFAULT_HTML_INTEGRATION_POINTS);
|
|
13882
14001
|
const customElementHandling = objectHasOwnProperty(cfg, "CUSTOM_ELEMENT_HANDLING") && cfg.CUSTOM_ELEMENT_HANDLING && typeof cfg.CUSTOM_ELEMENT_HANDLING === "object" ? clone(cfg.CUSTOM_ELEMENT_HANDLING) : create(null);
|
|
13883
14002
|
CUSTOM_ELEMENT_HANDLING = create(null);
|
|
13884
14003
|
if (objectHasOwnProperty(customElementHandling, "tagNameCheck") && isRegexOrFunction(customElementHandling.tagNameCheck)) {
|
|
@@ -13890,6 +14009,7 @@
|
|
|
13890
14009
|
if (objectHasOwnProperty(customElementHandling, "allowCustomizedBuiltInElements") && typeof customElementHandling.allowCustomizedBuiltInElements === "boolean") {
|
|
13891
14010
|
CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements = customElementHandling.allowCustomizedBuiltInElements;
|
|
13892
14011
|
}
|
|
14012
|
+
seal(CUSTOM_ELEMENT_HANDLING);
|
|
13893
14013
|
if (SAFE_FOR_TEMPLATES) {
|
|
13894
14014
|
ALLOW_DATA_ATTR = false;
|
|
13895
14015
|
}
|
|
@@ -13973,14 +14093,23 @@
|
|
|
13973
14093
|
if (typeof cfg.TRUSTED_TYPES_POLICY.createScriptURL !== "function") {
|
|
13974
14094
|
throw typeErrorCreate('TRUSTED_TYPES_POLICY configuration option must provide a "createScriptURL" hook.');
|
|
13975
14095
|
}
|
|
14096
|
+
const previousTrustedTypesPolicy = trustedTypesPolicy;
|
|
13976
14097
|
trustedTypesPolicy = cfg.TRUSTED_TYPES_POLICY;
|
|
13977
|
-
|
|
14098
|
+
try {
|
|
14099
|
+
emptyHTML = _createTrustedHTML("");
|
|
14100
|
+
} catch (error2) {
|
|
14101
|
+
trustedTypesPolicy = previousTrustedTypesPolicy;
|
|
14102
|
+
throw error2;
|
|
14103
|
+
}
|
|
14104
|
+
} else if (cfg.TRUSTED_TYPES_POLICY === null) {
|
|
14105
|
+
trustedTypesPolicy = void 0;
|
|
14106
|
+
emptyHTML = "";
|
|
13978
14107
|
} else {
|
|
13979
14108
|
if (trustedTypesPolicy === void 0) {
|
|
13980
|
-
trustedTypesPolicy =
|
|
14109
|
+
trustedTypesPolicy = _getDefaultTrustedTypesPolicy();
|
|
13981
14110
|
}
|
|
13982
|
-
if (trustedTypesPolicy
|
|
13983
|
-
emptyHTML =
|
|
14111
|
+
if (trustedTypesPolicy && typeof emptyHTML === "string") {
|
|
14112
|
+
emptyHTML = _createTrustedHTML("");
|
|
13984
14113
|
}
|
|
13985
14114
|
}
|
|
13986
14115
|
if (freeze) {
|
|
@@ -13990,6 +14119,33 @@
|
|
|
13990
14119
|
};
|
|
13991
14120
|
const ALL_SVG_TAGS = addToSet({}, [...svg$1, ...svgFilters, ...svgDisallowed]);
|
|
13992
14121
|
const ALL_MATHML_TAGS = addToSet({}, [...mathMl$1, ...mathMlDisallowed]);
|
|
14122
|
+
const _checkSvgNamespace = function _checkSvgNamespace2(tagName, parent, parentTagName) {
|
|
14123
|
+
if (parent.namespaceURI === HTML_NAMESPACE) {
|
|
14124
|
+
return tagName === "svg";
|
|
14125
|
+
}
|
|
14126
|
+
if (parent.namespaceURI === MATHML_NAMESPACE) {
|
|
14127
|
+
return tagName === "svg" && (parentTagName === "annotation-xml" || MATHML_TEXT_INTEGRATION_POINTS[parentTagName]);
|
|
14128
|
+
}
|
|
14129
|
+
return Boolean(ALL_SVG_TAGS[tagName]);
|
|
14130
|
+
};
|
|
14131
|
+
const _checkMathMlNamespace = function _checkMathMlNamespace2(tagName, parent, parentTagName) {
|
|
14132
|
+
if (parent.namespaceURI === HTML_NAMESPACE) {
|
|
14133
|
+
return tagName === "math";
|
|
14134
|
+
}
|
|
14135
|
+
if (parent.namespaceURI === SVG_NAMESPACE) {
|
|
14136
|
+
return tagName === "math" && HTML_INTEGRATION_POINTS[parentTagName];
|
|
14137
|
+
}
|
|
14138
|
+
return Boolean(ALL_MATHML_TAGS[tagName]);
|
|
14139
|
+
};
|
|
14140
|
+
const _checkHtmlNamespace = function _checkHtmlNamespace2(tagName, parent, parentTagName) {
|
|
14141
|
+
if (parent.namespaceURI === SVG_NAMESPACE && !HTML_INTEGRATION_POINTS[parentTagName]) {
|
|
14142
|
+
return false;
|
|
14143
|
+
}
|
|
14144
|
+
if (parent.namespaceURI === MATHML_NAMESPACE && !MATHML_TEXT_INTEGRATION_POINTS[parentTagName]) {
|
|
14145
|
+
return false;
|
|
14146
|
+
}
|
|
14147
|
+
return !ALL_MATHML_TAGS[tagName] && (COMMON_SVG_AND_HTML_ELEMENTS[tagName] || !ALL_SVG_TAGS[tagName]);
|
|
14148
|
+
};
|
|
13993
14149
|
const _checkValidNamespace = function _checkValidNamespace2(element) {
|
|
13994
14150
|
let parent = getParentNode2(element);
|
|
13995
14151
|
if (!parent || !parent.tagName) {
|
|
@@ -14004,31 +14160,13 @@
|
|
|
14004
14160
|
return false;
|
|
14005
14161
|
}
|
|
14006
14162
|
if (element.namespaceURI === SVG_NAMESPACE) {
|
|
14007
|
-
|
|
14008
|
-
return tagName === "svg";
|
|
14009
|
-
}
|
|
14010
|
-
if (parent.namespaceURI === MATHML_NAMESPACE) {
|
|
14011
|
-
return tagName === "svg" && (parentTagName === "annotation-xml" || MATHML_TEXT_INTEGRATION_POINTS[parentTagName]);
|
|
14012
|
-
}
|
|
14013
|
-
return Boolean(ALL_SVG_TAGS[tagName]);
|
|
14163
|
+
return _checkSvgNamespace(tagName, parent, parentTagName);
|
|
14014
14164
|
}
|
|
14015
14165
|
if (element.namespaceURI === MATHML_NAMESPACE) {
|
|
14016
|
-
|
|
14017
|
-
return tagName === "math";
|
|
14018
|
-
}
|
|
14019
|
-
if (parent.namespaceURI === SVG_NAMESPACE) {
|
|
14020
|
-
return tagName === "math" && HTML_INTEGRATION_POINTS[parentTagName];
|
|
14021
|
-
}
|
|
14022
|
-
return Boolean(ALL_MATHML_TAGS[tagName]);
|
|
14166
|
+
return _checkMathMlNamespace(tagName, parent, parentTagName);
|
|
14023
14167
|
}
|
|
14024
14168
|
if (element.namespaceURI === HTML_NAMESPACE) {
|
|
14025
|
-
|
|
14026
|
-
return false;
|
|
14027
|
-
}
|
|
14028
|
-
if (parent.namespaceURI === MATHML_NAMESPACE && !MATHML_TEXT_INTEGRATION_POINTS[parentTagName]) {
|
|
14029
|
-
return false;
|
|
14030
|
-
}
|
|
14031
|
-
return !ALL_MATHML_TAGS[tagName] && (COMMON_SVG_AND_HTML_ELEMENTS[tagName] || !ALL_SVG_TAGS[tagName]);
|
|
14169
|
+
return _checkHtmlNamespace(tagName, parent, parentTagName);
|
|
14032
14170
|
}
|
|
14033
14171
|
if (PARSER_MEDIA_TYPE === "application/xhtml+xml" && ALLOWED_NAMESPACES[element.namespaceURI]) {
|
|
14034
14172
|
return true;
|
|
@@ -14043,6 +14181,37 @@
|
|
|
14043
14181
|
getParentNode2(node).removeChild(node);
|
|
14044
14182
|
} catch (_4) {
|
|
14045
14183
|
remove(node);
|
|
14184
|
+
if (!getParentNode2(node)) {
|
|
14185
|
+
throw typeErrorCreate("a node selected for removal could not be detached from its tree and cannot be safely returned; refusing to sanitize in place");
|
|
14186
|
+
}
|
|
14187
|
+
}
|
|
14188
|
+
};
|
|
14189
|
+
const _neutralizeRoot = function _neutralizeRoot2(root) {
|
|
14190
|
+
const childNodes = getChildNodes(root);
|
|
14191
|
+
if (childNodes) {
|
|
14192
|
+
const snapshot = [];
|
|
14193
|
+
arrayForEach(childNodes, (child) => {
|
|
14194
|
+
arrayPush(snapshot, child);
|
|
14195
|
+
});
|
|
14196
|
+
arrayForEach(snapshot, (child) => {
|
|
14197
|
+
try {
|
|
14198
|
+
remove(child);
|
|
14199
|
+
} catch (_4) {
|
|
14200
|
+
}
|
|
14201
|
+
});
|
|
14202
|
+
}
|
|
14203
|
+
const attributes = getAttributes(root);
|
|
14204
|
+
if (attributes) {
|
|
14205
|
+
for (let i4 = attributes.length - 1; i4 >= 0; --i4) {
|
|
14206
|
+
const attribute = attributes[i4];
|
|
14207
|
+
const name = attribute && attribute.name;
|
|
14208
|
+
if (typeof name === "string") {
|
|
14209
|
+
try {
|
|
14210
|
+
root.removeAttribute(name);
|
|
14211
|
+
} catch (_4) {
|
|
14212
|
+
}
|
|
14213
|
+
}
|
|
14214
|
+
}
|
|
14046
14215
|
}
|
|
14047
14216
|
};
|
|
14048
14217
|
const _removeAttribute = function _removeAttribute2(name, element) {
|
|
@@ -14072,6 +14241,39 @@
|
|
|
14072
14241
|
}
|
|
14073
14242
|
}
|
|
14074
14243
|
};
|
|
14244
|
+
const _stripDisallowedAttributes = function _stripDisallowedAttributes2(element) {
|
|
14245
|
+
const attributes = getAttributes(element);
|
|
14246
|
+
if (!attributes) {
|
|
14247
|
+
return;
|
|
14248
|
+
}
|
|
14249
|
+
for (let i4 = attributes.length - 1; i4 >= 0; --i4) {
|
|
14250
|
+
const attribute = attributes[i4];
|
|
14251
|
+
const name = attribute && attribute.name;
|
|
14252
|
+
if (typeof name !== "string" || ALLOWED_ATTR[transformCaseFunc(name)]) {
|
|
14253
|
+
continue;
|
|
14254
|
+
}
|
|
14255
|
+
try {
|
|
14256
|
+
element.removeAttribute(name);
|
|
14257
|
+
} catch (_4) {
|
|
14258
|
+
}
|
|
14259
|
+
}
|
|
14260
|
+
};
|
|
14261
|
+
const _neutralizeSubtree = function _neutralizeSubtree2(root) {
|
|
14262
|
+
const stack = [root];
|
|
14263
|
+
while (stack.length > 0) {
|
|
14264
|
+
const node = stack.pop();
|
|
14265
|
+
const nodeType = getNodeType ? getNodeType(node) : node.nodeType;
|
|
14266
|
+
if (nodeType === NODE_TYPE.element) {
|
|
14267
|
+
_stripDisallowedAttributes(node);
|
|
14268
|
+
}
|
|
14269
|
+
const childNodes = getChildNodes(node);
|
|
14270
|
+
if (childNodes) {
|
|
14271
|
+
for (let i4 = childNodes.length - 1; i4 >= 0; --i4) {
|
|
14272
|
+
stack.push(childNodes[i4]);
|
|
14273
|
+
}
|
|
14274
|
+
}
|
|
14275
|
+
}
|
|
14276
|
+
};
|
|
14075
14277
|
const _initDocument = function _initDocument2(dirty) {
|
|
14076
14278
|
let doc = null;
|
|
14077
14279
|
let leadingWhitespace = null;
|
|
@@ -14084,7 +14286,7 @@
|
|
|
14084
14286
|
if (PARSER_MEDIA_TYPE === "application/xhtml+xml" && NAMESPACE === HTML_NAMESPACE) {
|
|
14085
14287
|
dirty = '<html xmlns="http://www.w3.org/1999/xhtml"><head></head><body>' + dirty + "</body></html>";
|
|
14086
14288
|
}
|
|
14087
|
-
const dirtyPayload = trustedTypesPolicy ?
|
|
14289
|
+
const dirtyPayload = trustedTypesPolicy ? _createTrustedHTML(dirty) : dirty;
|
|
14088
14290
|
if (NAMESPACE === HTML_NAMESPACE) {
|
|
14089
14291
|
try {
|
|
14090
14292
|
doc = new DOMParser2().parseFromString(dirtyPayload, PARSER_MEDIA_TYPE);
|
|
@@ -14116,81 +14318,164 @@
|
|
|
14116
14318
|
null
|
|
14117
14319
|
);
|
|
14118
14320
|
};
|
|
14321
|
+
const _stripTemplateExpressions = function _stripTemplateExpressions2(value) {
|
|
14322
|
+
value = stringReplace(value, MUSTACHE_EXPR$1, " ");
|
|
14323
|
+
value = stringReplace(value, ERB_EXPR$1, " ");
|
|
14324
|
+
value = stringReplace(value, TMPLIT_EXPR$1, " ");
|
|
14325
|
+
return value;
|
|
14326
|
+
};
|
|
14327
|
+
const _scrubTemplateExpressions2 = function _scrubTemplateExpressions(node) {
|
|
14328
|
+
var _node$querySelectorAl;
|
|
14329
|
+
node.normalize();
|
|
14330
|
+
const walker = createNodeIterator.call(
|
|
14331
|
+
node.ownerDocument || node,
|
|
14332
|
+
node,
|
|
14333
|
+
// eslint-disable-next-line no-bitwise
|
|
14334
|
+
NodeFilter.SHOW_TEXT | NodeFilter.SHOW_COMMENT | NodeFilter.SHOW_CDATA_SECTION | NodeFilter.SHOW_PROCESSING_INSTRUCTION,
|
|
14335
|
+
null
|
|
14336
|
+
);
|
|
14337
|
+
let currentNode = walker.nextNode();
|
|
14338
|
+
while (currentNode) {
|
|
14339
|
+
currentNode.data = _stripTemplateExpressions(currentNode.data);
|
|
14340
|
+
currentNode = walker.nextNode();
|
|
14341
|
+
}
|
|
14342
|
+
const templates = (_node$querySelectorAl = node.querySelectorAll) === null || _node$querySelectorAl === void 0 ? void 0 : _node$querySelectorAl.call(node, "template");
|
|
14343
|
+
if (templates) {
|
|
14344
|
+
arrayForEach(templates, (tmpl) => {
|
|
14345
|
+
if (_isDocumentFragment(tmpl.content)) {
|
|
14346
|
+
_scrubTemplateExpressions2(tmpl.content);
|
|
14347
|
+
}
|
|
14348
|
+
});
|
|
14349
|
+
}
|
|
14350
|
+
};
|
|
14119
14351
|
const _isClobbered = function _isClobbered2(element) {
|
|
14120
|
-
|
|
14352
|
+
const realTagName = getNodeName2 ? getNodeName2(element) : null;
|
|
14353
|
+
if (typeof realTagName !== "string") {
|
|
14354
|
+
return false;
|
|
14355
|
+
}
|
|
14356
|
+
if (transformCaseFunc(realTagName) !== "form") {
|
|
14357
|
+
return false;
|
|
14358
|
+
}
|
|
14359
|
+
return typeof element.nodeName !== "string" || typeof element.textContent !== "string" || typeof element.removeChild !== "function" || // Realm-safe NamedNodeMap detection: equality against the cached
|
|
14360
|
+
// prototype getter. Clobbered .attributes (e.g. <input name="attributes">)
|
|
14361
|
+
// makes the direct read diverge from the cached read; a clean form
|
|
14362
|
+
// (same-realm OR foreign-realm) has both reads pointing at the same
|
|
14363
|
+
// canonical NamedNodeMap.
|
|
14364
|
+
element.attributes !== getAttributes(element) || typeof element.removeAttribute !== "function" || typeof element.setAttribute !== "function" || typeof element.namespaceURI !== "string" || typeof element.insertBefore !== "function" || typeof element.hasChildNodes !== "function" || // NodeType clobbering probe. Cached Node.prototype.nodeType getter
|
|
14365
|
+
// returns the integer 1 for any Element regardless of realm; direct
|
|
14366
|
+
// read on a clobbered form (e.g. <input name="nodeType">) returns
|
|
14367
|
+
// the named child element. Cheap addition — nodeType is read from
|
|
14368
|
+
// an internal slot, no serialization cost — and removes a residual
|
|
14369
|
+
// clobbering surface used by several mXSS / PI / comment branches
|
|
14370
|
+
// in _sanitizeElements that compare currentNode.nodeType directly.
|
|
14371
|
+
element.nodeType !== getNodeType(element) || // HTMLFormElement has [LegacyOverrideBuiltIns]: a descendant named
|
|
14372
|
+
// "childNodes" shadows the prototype getter. Direct reads of
|
|
14373
|
+
// form.childNodes from a clobbered form return the named child
|
|
14374
|
+
// instead of the real NodeList, so any walk that reads it directly
|
|
14375
|
+
// skips the form's real children. Compare the direct read to the
|
|
14376
|
+
// cached Node.prototype getter — when the form's named-property
|
|
14377
|
+
// getter intercepts the read, the two values differ and we flag
|
|
14378
|
+
// the form. This catches every clobbering child type (input,
|
|
14379
|
+
// select, etc.) regardless of whether the named child happens to
|
|
14380
|
+
// carry a numeric .length, which a typeof-based probe would miss
|
|
14381
|
+
// (e.g. HTMLSelectElement.length is a defined unsigned-long).
|
|
14382
|
+
element.childNodes !== getChildNodes(element);
|
|
14383
|
+
};
|
|
14384
|
+
const _isDocumentFragment = function _isDocumentFragment2(value) {
|
|
14385
|
+
if (!getNodeType || typeof value !== "object" || value === null) {
|
|
14386
|
+
return false;
|
|
14387
|
+
}
|
|
14388
|
+
try {
|
|
14389
|
+
return getNodeType(value) === NODE_TYPE.documentFragment;
|
|
14390
|
+
} catch (_4) {
|
|
14391
|
+
return false;
|
|
14392
|
+
}
|
|
14121
14393
|
};
|
|
14122
14394
|
const _isNode = function _isNode2(value) {
|
|
14123
|
-
|
|
14395
|
+
if (!getNodeType || typeof value !== "object" || value === null) {
|
|
14396
|
+
return false;
|
|
14397
|
+
}
|
|
14398
|
+
try {
|
|
14399
|
+
return typeof getNodeType(value) === "number";
|
|
14400
|
+
} catch (_4) {
|
|
14401
|
+
return false;
|
|
14402
|
+
}
|
|
14124
14403
|
};
|
|
14125
14404
|
function _executeHooks(hooks2, currentNode, data) {
|
|
14405
|
+
if (hooks2.length === 0) {
|
|
14406
|
+
return;
|
|
14407
|
+
}
|
|
14126
14408
|
arrayForEach(hooks2, (hook) => {
|
|
14127
14409
|
hook.call(DOMPurify, currentNode, data, CONFIG);
|
|
14128
14410
|
});
|
|
14129
14411
|
}
|
|
14130
|
-
const
|
|
14131
|
-
|
|
14132
|
-
_executeHooks(hooks.beforeSanitizeElements, currentNode, null);
|
|
14133
|
-
if (_isClobbered(currentNode)) {
|
|
14134
|
-
_forceRemove(currentNode);
|
|
14135
|
-
return true;
|
|
14136
|
-
}
|
|
14137
|
-
const tagName = transformCaseFunc(currentNode.nodeName);
|
|
14138
|
-
_executeHooks(hooks.uponSanitizeElement, currentNode, {
|
|
14139
|
-
tagName,
|
|
14140
|
-
allowedTags: ALLOWED_TAGS
|
|
14141
|
-
});
|
|
14142
|
-
if (SAFE_FOR_XML && currentNode.hasChildNodes() && !_isNode(currentNode.firstElementChild) && regExpTest(/<[/\w!]/g, currentNode.innerHTML) && regExpTest(/<[/\w!]/g, currentNode.textContent)) {
|
|
14143
|
-
_forceRemove(currentNode);
|
|
14412
|
+
const _isUnsafeNode = function _isUnsafeNode2(currentNode, tagName) {
|
|
14413
|
+
if (SAFE_FOR_XML && currentNode.hasChildNodes() && !_isNode(currentNode.firstElementChild) && regExpTest(ELEMENT_MARKUP_PROBE, currentNode.textContent) && regExpTest(ELEMENT_MARKUP_PROBE, currentNode.innerHTML)) {
|
|
14144
14414
|
return true;
|
|
14145
14415
|
}
|
|
14146
14416
|
if (SAFE_FOR_XML && currentNode.namespaceURI === HTML_NAMESPACE && tagName === "style" && _isNode(currentNode.firstElementChild)) {
|
|
14147
|
-
_forceRemove(currentNode);
|
|
14148
14417
|
return true;
|
|
14149
14418
|
}
|
|
14150
|
-
if (currentNode.nodeType === NODE_TYPE.
|
|
14151
|
-
_forceRemove(currentNode);
|
|
14419
|
+
if (currentNode.nodeType === NODE_TYPE.processingInstruction) {
|
|
14152
14420
|
return true;
|
|
14153
14421
|
}
|
|
14154
|
-
if (SAFE_FOR_XML && currentNode.nodeType === NODE_TYPE.comment && regExpTest(
|
|
14155
|
-
_forceRemove(currentNode);
|
|
14422
|
+
if (SAFE_FOR_XML && currentNode.nodeType === NODE_TYPE.comment && regExpTest(COMMENT_MARKUP_PROBE, currentNode.data)) {
|
|
14156
14423
|
return true;
|
|
14157
14424
|
}
|
|
14158
|
-
|
|
14159
|
-
|
|
14160
|
-
|
|
14161
|
-
|
|
14162
|
-
|
|
14163
|
-
|
|
14164
|
-
return false;
|
|
14165
|
-
}
|
|
14425
|
+
return false;
|
|
14426
|
+
};
|
|
14427
|
+
const _sanitizeDisallowedNode = function _sanitizeDisallowedNode2(currentNode, tagName) {
|
|
14428
|
+
if (!FORBID_TAGS[tagName] && _isBasicCustomElement(tagName)) {
|
|
14429
|
+
if (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.tagNameCheck, tagName)) {
|
|
14430
|
+
return false;
|
|
14166
14431
|
}
|
|
14167
|
-
if (
|
|
14168
|
-
|
|
14169
|
-
|
|
14170
|
-
|
|
14171
|
-
|
|
14172
|
-
|
|
14173
|
-
|
|
14174
|
-
|
|
14175
|
-
|
|
14432
|
+
if (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof Function && CUSTOM_ELEMENT_HANDLING.tagNameCheck(tagName)) {
|
|
14433
|
+
return false;
|
|
14434
|
+
}
|
|
14435
|
+
}
|
|
14436
|
+
if (KEEP_CONTENT && !FORBID_CONTENTS[tagName]) {
|
|
14437
|
+
const parentNode = getParentNode2(currentNode);
|
|
14438
|
+
const childNodes = getChildNodes(currentNode);
|
|
14439
|
+
if (childNodes && parentNode) {
|
|
14440
|
+
const childCount = childNodes.length;
|
|
14441
|
+
for (let i4 = childCount - 1; i4 >= 0; --i4) {
|
|
14442
|
+
const hoisted = IN_PLACE ? childNodes[i4] : cloneNode(childNodes[i4], true);
|
|
14443
|
+
parentNode.insertBefore(hoisted, getNextSibling(currentNode));
|
|
14176
14444
|
}
|
|
14177
14445
|
}
|
|
14446
|
+
}
|
|
14447
|
+
_forceRemove(currentNode);
|
|
14448
|
+
return true;
|
|
14449
|
+
};
|
|
14450
|
+
const _sanitizeElements = function _sanitizeElements2(currentNode) {
|
|
14451
|
+
_executeHooks(hooks.beforeSanitizeElements, currentNode, null);
|
|
14452
|
+
if (_isClobbered(currentNode)) {
|
|
14453
|
+
_forceRemove(currentNode);
|
|
14454
|
+
return true;
|
|
14455
|
+
}
|
|
14456
|
+
const tagName = transformCaseFunc(getNodeName2 ? getNodeName2(currentNode) : currentNode.nodeName);
|
|
14457
|
+
_executeHooks(hooks.uponSanitizeElement, currentNode, {
|
|
14458
|
+
tagName,
|
|
14459
|
+
allowedTags: ALLOWED_TAGS
|
|
14460
|
+
});
|
|
14461
|
+
if (_isUnsafeNode(currentNode, tagName)) {
|
|
14178
14462
|
_forceRemove(currentNode);
|
|
14179
14463
|
return true;
|
|
14180
14464
|
}
|
|
14181
|
-
if (
|
|
14465
|
+
if (FORBID_TAGS[tagName] || !(EXTRA_ELEMENT_HANDLING.tagCheck instanceof Function && EXTRA_ELEMENT_HANDLING.tagCheck(tagName)) && !ALLOWED_TAGS[tagName]) {
|
|
14466
|
+
return _sanitizeDisallowedNode(currentNode, tagName);
|
|
14467
|
+
}
|
|
14468
|
+
const nt = getNodeType ? getNodeType(currentNode) : currentNode.nodeType;
|
|
14469
|
+
if (nt === NODE_TYPE.element && !_checkValidNamespace(currentNode)) {
|
|
14182
14470
|
_forceRemove(currentNode);
|
|
14183
14471
|
return true;
|
|
14184
14472
|
}
|
|
14185
|
-
if ((tagName === "noscript" || tagName === "noembed" || tagName === "noframes") && regExpTest(
|
|
14473
|
+
if ((tagName === "noscript" || tagName === "noembed" || tagName === "noframes") && regExpTest(FALLBACK_TAG_CLOSE, currentNode.innerHTML)) {
|
|
14186
14474
|
_forceRemove(currentNode);
|
|
14187
14475
|
return true;
|
|
14188
14476
|
}
|
|
14189
14477
|
if (SAFE_FOR_TEMPLATES && currentNode.nodeType === NODE_TYPE.text) {
|
|
14190
|
-
content = currentNode.textContent;
|
|
14191
|
-
arrayForEach([MUSTACHE_EXPR$1, ERB_EXPR$1, TMPLIT_EXPR$1], (expr) => {
|
|
14192
|
-
content = stringReplace(content, expr, " ");
|
|
14193
|
-
});
|
|
14478
|
+
const content = _stripTemplateExpressions(currentNode.textContent);
|
|
14194
14479
|
if (currentNode.textContent !== content) {
|
|
14195
14480
|
arrayPush(DOMPurify.removed, {
|
|
14196
14481
|
element: currentNode.cloneNode()
|
|
@@ -14209,9 +14494,9 @@
|
|
|
14209
14494
|
return false;
|
|
14210
14495
|
}
|
|
14211
14496
|
const nameIsPermitted = ALLOWED_ATTR[lcName] || EXTRA_ELEMENT_HANDLING.attributeCheck instanceof Function && EXTRA_ELEMENT_HANDLING.attributeCheck(lcName, lcTag);
|
|
14212
|
-
if (ALLOW_DATA_ATTR &&
|
|
14497
|
+
if (ALLOW_DATA_ATTR && regExpTest(DATA_ATTR$1, lcName)) ;
|
|
14213
14498
|
else if (ALLOW_ARIA_ATTR && regExpTest(ARIA_ATTR$1, lcName)) ;
|
|
14214
|
-
else if (!nameIsPermitted
|
|
14499
|
+
else if (!nameIsPermitted) {
|
|
14215
14500
|
if (
|
|
14216
14501
|
// First condition does a very basic check if a) it's basically a valid custom element tagname AND
|
|
14217
14502
|
// b) if the tagName passes whatever the user has configured for CUSTOM_ELEMENT_HANDLING.tagNameCheck
|
|
@@ -14236,6 +14521,35 @@
|
|
|
14236
14521
|
const _isBasicCustomElement = function _isBasicCustomElement2(tagName) {
|
|
14237
14522
|
return !RESERVED_CUSTOM_ELEMENT_NAMES[stringToLowerCase(tagName)] && regExpTest(CUSTOM_ELEMENT$1, tagName);
|
|
14238
14523
|
};
|
|
14524
|
+
const _applyTrustedTypesToAttribute = function _applyTrustedTypesToAttribute2(lcTag, lcName, namespaceURI, value) {
|
|
14525
|
+
if (trustedTypesPolicy && typeof trustedTypes === "object" && typeof trustedTypes.getAttributeType === "function" && !namespaceURI) {
|
|
14526
|
+
switch (trustedTypes.getAttributeType(lcTag, lcName)) {
|
|
14527
|
+
case "TrustedHTML": {
|
|
14528
|
+
return _createTrustedHTML(value);
|
|
14529
|
+
}
|
|
14530
|
+
case "TrustedScriptURL": {
|
|
14531
|
+
return _createTrustedScriptURL(value);
|
|
14532
|
+
}
|
|
14533
|
+
}
|
|
14534
|
+
}
|
|
14535
|
+
return value;
|
|
14536
|
+
};
|
|
14537
|
+
const _setAttributeValue = function _setAttributeValue2(currentNode, name, namespaceURI, value) {
|
|
14538
|
+
try {
|
|
14539
|
+
if (namespaceURI) {
|
|
14540
|
+
currentNode.setAttributeNS(namespaceURI, name, value);
|
|
14541
|
+
} else {
|
|
14542
|
+
currentNode.setAttribute(name, value);
|
|
14543
|
+
}
|
|
14544
|
+
if (_isClobbered(currentNode)) {
|
|
14545
|
+
_forceRemove(currentNode);
|
|
14546
|
+
} else {
|
|
14547
|
+
arrayPop(DOMPurify.removed);
|
|
14548
|
+
}
|
|
14549
|
+
} catch (_4) {
|
|
14550
|
+
_removeAttribute(name, currentNode);
|
|
14551
|
+
}
|
|
14552
|
+
};
|
|
14239
14553
|
const _sanitizeAttributes = function _sanitizeAttributes2(currentNode) {
|
|
14240
14554
|
_executeHooks(hooks.beforeSanitizeAttributes, currentNode, null);
|
|
14241
14555
|
const attributes = currentNode.attributes;
|
|
@@ -14250,6 +14564,7 @@
|
|
|
14250
14564
|
forceKeepAttr: void 0
|
|
14251
14565
|
};
|
|
14252
14566
|
let l4 = attributes.length;
|
|
14567
|
+
const lcTag = transformCaseFunc(currentNode.nodeName);
|
|
14253
14568
|
while (l4--) {
|
|
14254
14569
|
const attr = attributes[l4];
|
|
14255
14570
|
const name = attr.name, namespaceURI = attr.namespaceURI, attrValue = attr.value;
|
|
@@ -14281,50 +14596,20 @@
|
|
|
14281
14596
|
_removeAttribute(name, currentNode);
|
|
14282
14597
|
continue;
|
|
14283
14598
|
}
|
|
14284
|
-
if (!ALLOW_SELF_CLOSE_IN_ATTR && regExpTest(
|
|
14599
|
+
if (!ALLOW_SELF_CLOSE_IN_ATTR && regExpTest(SELF_CLOSING_TAG, value)) {
|
|
14285
14600
|
_removeAttribute(name, currentNode);
|
|
14286
14601
|
continue;
|
|
14287
14602
|
}
|
|
14288
14603
|
if (SAFE_FOR_TEMPLATES) {
|
|
14289
|
-
|
|
14290
|
-
value = stringReplace(value, expr, " ");
|
|
14291
|
-
});
|
|
14604
|
+
value = _stripTemplateExpressions(value);
|
|
14292
14605
|
}
|
|
14293
|
-
const lcTag = transformCaseFunc(currentNode.nodeName);
|
|
14294
14606
|
if (!_isValidAttribute(lcTag, lcName, value)) {
|
|
14295
14607
|
_removeAttribute(name, currentNode);
|
|
14296
14608
|
continue;
|
|
14297
14609
|
}
|
|
14298
|
-
|
|
14299
|
-
if (namespaceURI) ;
|
|
14300
|
-
else {
|
|
14301
|
-
switch (trustedTypes.getAttributeType(lcTag, lcName)) {
|
|
14302
|
-
case "TrustedHTML": {
|
|
14303
|
-
value = trustedTypesPolicy.createHTML(value);
|
|
14304
|
-
break;
|
|
14305
|
-
}
|
|
14306
|
-
case "TrustedScriptURL": {
|
|
14307
|
-
value = trustedTypesPolicy.createScriptURL(value);
|
|
14308
|
-
break;
|
|
14309
|
-
}
|
|
14310
|
-
}
|
|
14311
|
-
}
|
|
14312
|
-
}
|
|
14610
|
+
value = _applyTrustedTypesToAttribute(lcTag, lcName, namespaceURI, value);
|
|
14313
14611
|
if (value !== initValue) {
|
|
14314
|
-
|
|
14315
|
-
if (namespaceURI) {
|
|
14316
|
-
currentNode.setAttributeNS(namespaceURI, name, value);
|
|
14317
|
-
} else {
|
|
14318
|
-
currentNode.setAttribute(name, value);
|
|
14319
|
-
}
|
|
14320
|
-
if (_isClobbered(currentNode)) {
|
|
14321
|
-
_forceRemove(currentNode);
|
|
14322
|
-
} else {
|
|
14323
|
-
arrayPop(DOMPurify.removed);
|
|
14324
|
-
}
|
|
14325
|
-
} catch (_4) {
|
|
14326
|
-
_removeAttribute(name, currentNode);
|
|
14327
|
-
}
|
|
14612
|
+
_setAttributeValue(currentNode, name, namespaceURI, value);
|
|
14328
14613
|
}
|
|
14329
14614
|
}
|
|
14330
14615
|
_executeHooks(hooks.afterSanitizeAttributes, currentNode, null);
|
|
@@ -14337,28 +14622,67 @@
|
|
|
14337
14622
|
_executeHooks(hooks.uponSanitizeShadowNode, shadowNode, null);
|
|
14338
14623
|
_sanitizeElements(shadowNode);
|
|
14339
14624
|
_sanitizeAttributes(shadowNode);
|
|
14340
|
-
if (shadowNode.content
|
|
14625
|
+
if (_isDocumentFragment(shadowNode.content)) {
|
|
14341
14626
|
_sanitizeShadowDOM2(shadowNode.content);
|
|
14342
14627
|
}
|
|
14628
|
+
const shadowNodeType = getNodeType ? getNodeType(shadowNode) : shadowNode.nodeType;
|
|
14629
|
+
if (shadowNodeType === NODE_TYPE.element) {
|
|
14630
|
+
const innerSr = getShadowRoot(shadowNode);
|
|
14631
|
+
if (_isDocumentFragment(innerSr)) {
|
|
14632
|
+
_sanitizeAttachedShadowRoots(innerSr);
|
|
14633
|
+
_sanitizeShadowDOM2(innerSr);
|
|
14634
|
+
}
|
|
14635
|
+
}
|
|
14343
14636
|
}
|
|
14344
14637
|
_executeHooks(hooks.afterSanitizeShadowDOM, fragment, null);
|
|
14345
14638
|
};
|
|
14346
|
-
const
|
|
14347
|
-
|
|
14348
|
-
|
|
14349
|
-
|
|
14350
|
-
|
|
14351
|
-
|
|
14352
|
-
|
|
14353
|
-
|
|
14354
|
-
|
|
14355
|
-
|
|
14356
|
-
|
|
14357
|
-
|
|
14358
|
-
|
|
14359
|
-
|
|
14360
|
-
|
|
14361
|
-
|
|
14639
|
+
const _sanitizeAttachedShadowRoots = function _sanitizeAttachedShadowRoots2(root) {
|
|
14640
|
+
const stack = [{
|
|
14641
|
+
node: root,
|
|
14642
|
+
shadow: null
|
|
14643
|
+
}];
|
|
14644
|
+
while (stack.length > 0) {
|
|
14645
|
+
const item = stack.pop();
|
|
14646
|
+
if (item.shadow) {
|
|
14647
|
+
_sanitizeShadowDOM2(item.shadow);
|
|
14648
|
+
continue;
|
|
14649
|
+
}
|
|
14650
|
+
const node = item.node;
|
|
14651
|
+
const nodeType = getNodeType ? getNodeType(node) : node.nodeType;
|
|
14652
|
+
const isElement2 = nodeType === NODE_TYPE.element;
|
|
14653
|
+
const childNodes = getChildNodes(node);
|
|
14654
|
+
if (childNodes) {
|
|
14655
|
+
for (let i4 = childNodes.length - 1; i4 >= 0; --i4) {
|
|
14656
|
+
stack.push({
|
|
14657
|
+
node: childNodes[i4],
|
|
14658
|
+
shadow: null
|
|
14659
|
+
});
|
|
14660
|
+
}
|
|
14661
|
+
}
|
|
14662
|
+
if (isElement2) {
|
|
14663
|
+
const rootName = getNodeName2 ? getNodeName2(node) : null;
|
|
14664
|
+
if (typeof rootName === "string" && transformCaseFunc(rootName) === "template") {
|
|
14665
|
+
const content = node.content;
|
|
14666
|
+
if (_isDocumentFragment(content)) {
|
|
14667
|
+
stack.push({
|
|
14668
|
+
node: content,
|
|
14669
|
+
shadow: null
|
|
14670
|
+
});
|
|
14671
|
+
}
|
|
14672
|
+
}
|
|
14673
|
+
}
|
|
14674
|
+
if (isElement2) {
|
|
14675
|
+
const sr = getShadowRoot(node);
|
|
14676
|
+
if (_isDocumentFragment(sr)) {
|
|
14677
|
+
stack.push({
|
|
14678
|
+
node: null,
|
|
14679
|
+
shadow: sr
|
|
14680
|
+
}, {
|
|
14681
|
+
node: sr,
|
|
14682
|
+
shadow: null
|
|
14683
|
+
});
|
|
14684
|
+
}
|
|
14685
|
+
}
|
|
14362
14686
|
}
|
|
14363
14687
|
};
|
|
14364
14688
|
DOMPurify.sanitize = function(dirty) {
|
|
@@ -14380,23 +14704,38 @@
|
|
|
14380
14704
|
if (!DOMPurify.isSupported) {
|
|
14381
14705
|
return dirty;
|
|
14382
14706
|
}
|
|
14383
|
-
if (
|
|
14707
|
+
if (SET_CONFIG) {
|
|
14708
|
+
ALLOWED_TAGS = SET_CONFIG_ALLOWED_TAGS;
|
|
14709
|
+
ALLOWED_ATTR = SET_CONFIG_ALLOWED_ATTR;
|
|
14710
|
+
} else {
|
|
14384
14711
|
_parseConfig(cfg);
|
|
14385
14712
|
}
|
|
14386
|
-
|
|
14387
|
-
|
|
14388
|
-
|
|
14713
|
+
if (hooks.uponSanitizeElement.length > 0 || hooks.uponSanitizeAttribute.length > 0) {
|
|
14714
|
+
ALLOWED_TAGS = clone(ALLOWED_TAGS);
|
|
14715
|
+
}
|
|
14716
|
+
if (hooks.uponSanitizeAttribute.length > 0) {
|
|
14717
|
+
ALLOWED_ATTR = clone(ALLOWED_ATTR);
|
|
14389
14718
|
}
|
|
14390
|
-
|
|
14391
|
-
|
|
14719
|
+
DOMPurify.removed = [];
|
|
14720
|
+
const inPlace = IN_PLACE && typeof dirty !== "string" && _isNode(dirty);
|
|
14721
|
+
if (inPlace) {
|
|
14722
|
+
const nn2 = getNodeName2 ? getNodeName2(dirty) : dirty.nodeName;
|
|
14392
14723
|
if (typeof nn2 === "string") {
|
|
14393
14724
|
const tagName = transformCaseFunc(nn2);
|
|
14394
14725
|
if (!ALLOWED_TAGS[tagName] || FORBID_TAGS[tagName]) {
|
|
14395
14726
|
throw typeErrorCreate("root node is forbidden and cannot be sanitized in-place");
|
|
14396
14727
|
}
|
|
14397
14728
|
}
|
|
14398
|
-
|
|
14399
|
-
|
|
14729
|
+
if (_isClobbered(dirty)) {
|
|
14730
|
+
throw typeErrorCreate("root node is clobbered and cannot be sanitized in-place");
|
|
14731
|
+
}
|
|
14732
|
+
try {
|
|
14733
|
+
_sanitizeAttachedShadowRoots(dirty);
|
|
14734
|
+
} catch (error2) {
|
|
14735
|
+
_neutralizeRoot(dirty);
|
|
14736
|
+
throw error2;
|
|
14737
|
+
}
|
|
14738
|
+
} else if (_isNode(dirty)) {
|
|
14400
14739
|
body = _initDocument("<!---->");
|
|
14401
14740
|
importedNode = body.ownerDocument.importNode(dirty, true);
|
|
14402
14741
|
if (importedNode.nodeType === NODE_TYPE.element && importedNode.nodeName === "BODY") {
|
|
@@ -14406,11 +14745,11 @@
|
|
|
14406
14745
|
} else {
|
|
14407
14746
|
body.appendChild(importedNode);
|
|
14408
14747
|
}
|
|
14409
|
-
|
|
14748
|
+
_sanitizeAttachedShadowRoots(importedNode);
|
|
14410
14749
|
} else {
|
|
14411
14750
|
if (!RETURN_DOM && !SAFE_FOR_TEMPLATES && !WHOLE_DOCUMENT && // eslint-disable-next-line unicorn/prefer-includes
|
|
14412
14751
|
dirty.indexOf("<") === -1) {
|
|
14413
|
-
return trustedTypesPolicy && RETURN_TRUSTED_TYPE ?
|
|
14752
|
+
return trustedTypesPolicy && RETURN_TRUSTED_TYPE ? _createTrustedHTML(dirty) : dirty;
|
|
14414
14753
|
}
|
|
14415
14754
|
body = _initDocument(dirty);
|
|
14416
14755
|
if (!body) {
|
|
@@ -14420,25 +14759,35 @@
|
|
|
14420
14759
|
if (body && FORCE_BODY) {
|
|
14421
14760
|
_forceRemove(body.firstChild);
|
|
14422
14761
|
}
|
|
14423
|
-
const nodeIterator = _createNodeIterator(
|
|
14424
|
-
|
|
14425
|
-
|
|
14426
|
-
|
|
14427
|
-
|
|
14428
|
-
|
|
14762
|
+
const nodeIterator = _createNodeIterator(inPlace ? dirty : body);
|
|
14763
|
+
try {
|
|
14764
|
+
while (currentNode = nodeIterator.nextNode()) {
|
|
14765
|
+
_sanitizeElements(currentNode);
|
|
14766
|
+
_sanitizeAttributes(currentNode);
|
|
14767
|
+
if (_isDocumentFragment(currentNode.content)) {
|
|
14768
|
+
_sanitizeShadowDOM2(currentNode.content);
|
|
14769
|
+
}
|
|
14429
14770
|
}
|
|
14771
|
+
} catch (error2) {
|
|
14772
|
+
if (inPlace) {
|
|
14773
|
+
_neutralizeRoot(dirty);
|
|
14774
|
+
}
|
|
14775
|
+
throw error2;
|
|
14430
14776
|
}
|
|
14431
|
-
if (
|
|
14777
|
+
if (inPlace) {
|
|
14778
|
+
arrayForEach(DOMPurify.removed, (entry) => {
|
|
14779
|
+
if (entry.element) {
|
|
14780
|
+
_neutralizeSubtree(entry.element);
|
|
14781
|
+
}
|
|
14782
|
+
});
|
|
14783
|
+
if (SAFE_FOR_TEMPLATES) {
|
|
14784
|
+
_scrubTemplateExpressions2(dirty);
|
|
14785
|
+
}
|
|
14432
14786
|
return dirty;
|
|
14433
14787
|
}
|
|
14434
14788
|
if (RETURN_DOM) {
|
|
14435
14789
|
if (SAFE_FOR_TEMPLATES) {
|
|
14436
|
-
body
|
|
14437
|
-
let html3 = body.innerHTML;
|
|
14438
|
-
arrayForEach([MUSTACHE_EXPR$1, ERB_EXPR$1, TMPLIT_EXPR$1], (expr) => {
|
|
14439
|
-
html3 = stringReplace(html3, expr, " ");
|
|
14440
|
-
});
|
|
14441
|
-
body.innerHTML = html3;
|
|
14790
|
+
_scrubTemplateExpressions2(body);
|
|
14442
14791
|
}
|
|
14443
14792
|
if (RETURN_DOM_FRAGMENT) {
|
|
14444
14793
|
returnNode = createDocumentFragment2.call(body.ownerDocument);
|
|
@@ -14458,20 +14807,24 @@
|
|
|
14458
14807
|
serializedHTML = "<!DOCTYPE " + body.ownerDocument.doctype.name + ">\n" + serializedHTML;
|
|
14459
14808
|
}
|
|
14460
14809
|
if (SAFE_FOR_TEMPLATES) {
|
|
14461
|
-
|
|
14462
|
-
serializedHTML = stringReplace(serializedHTML, expr, " ");
|
|
14463
|
-
});
|
|
14810
|
+
serializedHTML = _stripTemplateExpressions(serializedHTML);
|
|
14464
14811
|
}
|
|
14465
|
-
return trustedTypesPolicy && RETURN_TRUSTED_TYPE ?
|
|
14812
|
+
return trustedTypesPolicy && RETURN_TRUSTED_TYPE ? _createTrustedHTML(serializedHTML) : serializedHTML;
|
|
14466
14813
|
};
|
|
14467
14814
|
DOMPurify.setConfig = function() {
|
|
14468
14815
|
let cfg = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : {};
|
|
14469
14816
|
_parseConfig(cfg);
|
|
14470
14817
|
SET_CONFIG = true;
|
|
14818
|
+
SET_CONFIG_ALLOWED_TAGS = ALLOWED_TAGS;
|
|
14819
|
+
SET_CONFIG_ALLOWED_ATTR = ALLOWED_ATTR;
|
|
14471
14820
|
};
|
|
14472
14821
|
DOMPurify.clearConfig = function() {
|
|
14473
14822
|
CONFIG = null;
|
|
14474
14823
|
SET_CONFIG = false;
|
|
14824
|
+
SET_CONFIG_ALLOWED_TAGS = null;
|
|
14825
|
+
SET_CONFIG_ALLOWED_ATTR = null;
|
|
14826
|
+
trustedTypesPolicy = defaultTrustedTypesPolicy;
|
|
14827
|
+
emptyHTML = "";
|
|
14475
14828
|
};
|
|
14476
14829
|
DOMPurify.isValidAttribute = function(tag2, attr, value) {
|
|
14477
14830
|
if (!CONFIG) {
|
|
@@ -14485,9 +14838,15 @@
|
|
|
14485
14838
|
if (typeof hookFunction !== "function") {
|
|
14486
14839
|
return;
|
|
14487
14840
|
}
|
|
14841
|
+
if (!objectHasOwnProperty(hooks, entryPoint)) {
|
|
14842
|
+
return;
|
|
14843
|
+
}
|
|
14488
14844
|
arrayPush(hooks[entryPoint], hookFunction);
|
|
14489
14845
|
};
|
|
14490
14846
|
DOMPurify.removeHook = function(entryPoint, hookFunction) {
|
|
14847
|
+
if (!objectHasOwnProperty(hooks, entryPoint)) {
|
|
14848
|
+
return void 0;
|
|
14849
|
+
}
|
|
14491
14850
|
if (hookFunction !== void 0) {
|
|
14492
14851
|
const index = arrayLastIndexOf(hooks[entryPoint], hookFunction);
|
|
14493
14852
|
return index === -1 ? void 0 : arraySplice(hooks[entryPoint], index, 1)[0];
|
|
@@ -14495,6 +14854,9 @@
|
|
|
14495
14854
|
return arrayPop(hooks[entryPoint]);
|
|
14496
14855
|
};
|
|
14497
14856
|
DOMPurify.removeHooks = function(entryPoint) {
|
|
14857
|
+
if (!objectHasOwnProperty(hooks, entryPoint)) {
|
|
14858
|
+
return;
|
|
14859
|
+
}
|
|
14498
14860
|
hooks[entryPoint] = [];
|
|
14499
14861
|
};
|
|
14500
14862
|
DOMPurify.removeAllHooks = function() {
|
|
@@ -27293,6 +27655,9 @@ this.ifd0Offset: ${this.ifd0Offset}, file.byteLength: ${e4.byteLength}`), e4.tif
|
|
|
27293
27655
|
}
|
|
27294
27656
|
//======= Config
|
|
27295
27657
|
configureUppy() {
|
|
27658
|
+
const dashboardOptions = { inline: false, closeAfterFinish: true };
|
|
27659
|
+
const dialog2 = this.element.closest("dialog");
|
|
27660
|
+
if (dialog2) dashboardOptions.target = dialog2;
|
|
27296
27661
|
this.uppy = new Uppy_default({
|
|
27297
27662
|
restrictions: {
|
|
27298
27663
|
maxFileSize: this.maxFileSizeValue,
|
|
@@ -27303,7 +27668,7 @@ this.ifd0Offset: ${this.ifd0Offset}, file.byteLength: ${e4.byteLength}`), e4.tif
|
|
|
27303
27668
|
allowedFileTypes: this.allowedFileTypesValue,
|
|
27304
27669
|
requiredMetaFields: this.requiredMetaFieldsValue
|
|
27305
27670
|
}
|
|
27306
|
-
}).use(Dashboard2,
|
|
27671
|
+
}).use(Dashboard2, dashboardOptions).use(ImageEditor, { target: Dashboard2 });
|
|
27307
27672
|
this.#configureUploader();
|
|
27308
27673
|
this.#configureEventHandlers();
|
|
27309
27674
|
}
|
|
@@ -27638,6 +28003,28 @@ this.ifd0Offset: ${this.ifd0Offset}, file.byteLength: ${e4.byteLength}`), e4.tif
|
|
|
27638
28003
|
}
|
|
27639
28004
|
};
|
|
27640
28005
|
|
|
28006
|
+
// src/js/controllers/password_sentinel_controller.js
|
|
28007
|
+
var password_sentinel_controller_default = class extends Controller {
|
|
28008
|
+
static values = { sentinel: String };
|
|
28009
|
+
connect() {
|
|
28010
|
+
this.armed = this.element.value === this.sentinelValue;
|
|
28011
|
+
}
|
|
28012
|
+
beforeinput(event) {
|
|
28013
|
+
if (!this.armed) return;
|
|
28014
|
+
event.preventDefault();
|
|
28015
|
+
this.armed = false;
|
|
28016
|
+
let next = "";
|
|
28017
|
+
if (event.inputType === "insertText" && event.data != null) {
|
|
28018
|
+
next = event.data;
|
|
28019
|
+
} else if (event.inputType === "insertFromPaste" && event.dataTransfer) {
|
|
28020
|
+
next = event.dataTransfer.getData("text");
|
|
28021
|
+
}
|
|
28022
|
+
this.element.value = next;
|
|
28023
|
+
this.element.setSelectionRange(next.length, next.length);
|
|
28024
|
+
this.element.dispatchEvent(new Event("input", { bubbles: true }));
|
|
28025
|
+
}
|
|
28026
|
+
};
|
|
28027
|
+
|
|
27641
28028
|
// src/js/controllers/remote_modal_controller.js
|
|
27642
28029
|
var remote_modal_controller_default = class extends Controller {
|
|
27643
28030
|
connect() {
|
|
@@ -27680,6 +28067,7 @@ this.ifd0Offset: ${this.ifd0Offset}, file.byteLength: ${e4.byteLength}`), e4.tif
|
|
|
27680
28067
|
async #animateClose() {
|
|
27681
28068
|
if (this._closing) return;
|
|
27682
28069
|
this._closing = true;
|
|
28070
|
+
this.element.getAnimations().forEach((animation) => animation.finish());
|
|
27683
28071
|
this.element.removeAttribute("data-open");
|
|
27684
28072
|
const animations = this.element.getAnimations({ subtree: true });
|
|
27685
28073
|
await Promise.allSettled(animations.map((a4) => a4.finished));
|
|
@@ -28201,7 +28589,13 @@ this.ifd0Offset: ${this.ifd0Offset}, file.byteLength: ${e4.byteLength}`), e4.tif
|
|
|
28201
28589
|
if (event.target.closest("a, button, input, label, select, textarea, [data-row-click-ignore]")) {
|
|
28202
28590
|
return;
|
|
28203
28591
|
}
|
|
28204
|
-
this.element.querySelector('[data-row-click-target="show"]')
|
|
28592
|
+
const show = this.element.querySelector('[data-row-click-target="show"]');
|
|
28593
|
+
if (!show) return;
|
|
28594
|
+
if (event.metaKey || event.ctrlKey || event.button === 1) {
|
|
28595
|
+
window.open(show.href, "_blank", "noopener");
|
|
28596
|
+
return;
|
|
28597
|
+
}
|
|
28598
|
+
show.click();
|
|
28205
28599
|
}
|
|
28206
28600
|
};
|
|
28207
28601
|
|
|
@@ -28256,40 +28650,52 @@ this.ifd0Offset: ${this.ifd0Offset}, file.byteLength: ${e4.byteLength}`), e4.tif
|
|
|
28256
28650
|
]);
|
|
28257
28651
|
connect() {
|
|
28258
28652
|
this.dialog = this.element.closest("dialog");
|
|
28259
|
-
if (!this.dialog) return;
|
|
28260
28653
|
this.baseline = null;
|
|
28261
28654
|
this.forceClose = false;
|
|
28262
28655
|
this.submitting = false;
|
|
28263
28656
|
this.onFirstIntent = this.#onFirstIntent.bind(this);
|
|
28264
|
-
this.onCancel = this.#onCancel.bind(this);
|
|
28265
28657
|
this.onSubmit = this.#onSubmit.bind(this);
|
|
28266
|
-
this.
|
|
28267
|
-
this.
|
|
28268
|
-
this.onKeydown = this.#onKeydown.bind(this);
|
|
28658
|
+
this.onLeaveClick = this.#onLeaveClick.bind(this);
|
|
28659
|
+
this.onSettled = this.#onSettled.bind(this);
|
|
28269
28660
|
this.element.addEventListener("pointerdown", this.onFirstIntent, true);
|
|
28270
28661
|
this.element.addEventListener("keydown", this.onFirstIntent, true);
|
|
28271
|
-
document.addEventListener("keydown", this.onKeydown, true);
|
|
28272
|
-
this.dialog.addEventListener("cancel", this.onCancel, true);
|
|
28273
28662
|
this.element.addEventListener("submit", this.onSubmit);
|
|
28274
|
-
this
|
|
28275
|
-
|
|
28276
|
-
|
|
28277
|
-
|
|
28278
|
-
|
|
28663
|
+
this.element.addEventListener("turbo:submit-end", this.onSettled);
|
|
28664
|
+
if (!this.dialog) {
|
|
28665
|
+
document.addEventListener("click", this.onLeaveClick, true);
|
|
28666
|
+
}
|
|
28667
|
+
if (this.dialog) {
|
|
28668
|
+
this.onCancel = this.#onCancel.bind(this);
|
|
28669
|
+
this.onCloseButtonClick = this.#onCloseButtonClick.bind(this);
|
|
28670
|
+
this.onConfirmCancel = this.#onConfirmCancel.bind(this);
|
|
28671
|
+
this.onKeydown = this.#onKeydown.bind(this);
|
|
28672
|
+
document.addEventListener("keydown", this.onKeydown, true);
|
|
28673
|
+
this.dialog.addEventListener("cancel", this.onCancel, true);
|
|
28674
|
+
this.#closeButtons().forEach(
|
|
28675
|
+
(btn) => btn.addEventListener("click", this.onCloseButtonClick, true)
|
|
28676
|
+
);
|
|
28677
|
+
if (this.hasConfirmDialogTarget) {
|
|
28678
|
+
this.confirmDialogTarget.addEventListener("cancel", this.onConfirmCancel);
|
|
28679
|
+
}
|
|
28279
28680
|
}
|
|
28280
28681
|
}
|
|
28281
28682
|
disconnect() {
|
|
28282
|
-
if (!this.dialog) return;
|
|
28283
28683
|
this.element.removeEventListener("pointerdown", this.onFirstIntent, true);
|
|
28284
28684
|
this.element.removeEventListener("keydown", this.onFirstIntent, true);
|
|
28285
|
-
document.removeEventListener("keydown", this.onKeydown, true);
|
|
28286
|
-
this.dialog.removeEventListener("cancel", this.onCancel, true);
|
|
28287
28685
|
this.element.removeEventListener("submit", this.onSubmit);
|
|
28288
|
-
this
|
|
28289
|
-
|
|
28290
|
-
|
|
28291
|
-
|
|
28292
|
-
|
|
28686
|
+
this.element.removeEventListener("turbo:submit-end", this.onSettled);
|
|
28687
|
+
if (!this.dialog) {
|
|
28688
|
+
document.removeEventListener("click", this.onLeaveClick, true);
|
|
28689
|
+
}
|
|
28690
|
+
if (this.dialog) {
|
|
28691
|
+
document.removeEventListener("keydown", this.onKeydown, true);
|
|
28692
|
+
this.dialog.removeEventListener("cancel", this.onCancel, true);
|
|
28693
|
+
this.#closeButtons().forEach(
|
|
28694
|
+
(btn) => btn.removeEventListener("click", this.onCloseButtonClick, true)
|
|
28695
|
+
);
|
|
28696
|
+
if (this.hasConfirmDialogTarget) {
|
|
28697
|
+
this.confirmDialogTarget.removeEventListener("cancel", this.onConfirmCancel);
|
|
28698
|
+
}
|
|
28293
28699
|
}
|
|
28294
28700
|
}
|
|
28295
28701
|
discard() {
|
|
@@ -28331,6 +28737,79 @@ this.ifd0Offset: ${this.ifd0Offset}, file.byteLength: ${e4.byteLength}`), e4.tif
|
|
|
28331
28737
|
#onSubmit() {
|
|
28332
28738
|
this.submitting = true;
|
|
28333
28739
|
}
|
|
28740
|
+
// A submission settled. Reset the transient guards so a same-URL Turbo-morph
|
|
28741
|
+
// re-render (which keeps this element and does NOT reconnect the controller)
|
|
28742
|
+
// doesn't leave the guard permanently disabled. Re-baseline on next interaction.
|
|
28743
|
+
#onSettled() {
|
|
28744
|
+
this.submitting = false;
|
|
28745
|
+
this.forceClose = false;
|
|
28746
|
+
this.baseline = null;
|
|
28747
|
+
}
|
|
28748
|
+
// Full-page leave guard. A control marked `data-dirty-form-guard-leave` posts
|
|
28749
|
+
// without this form's fields, so its unsaved edits would be lost. If the form
|
|
28750
|
+
// is dirty, confirm first through the app's themed dialog; the attribute's
|
|
28751
|
+
// value is the prompt. We always intercept the click (the themed confirm is
|
|
28752
|
+
// async), then re-submit the trigger's form if the user confirms.
|
|
28753
|
+
async #onLeaveClick(event) {
|
|
28754
|
+
const trigger = event.target.closest("[data-dirty-form-guard-leave]");
|
|
28755
|
+
if (!trigger) return;
|
|
28756
|
+
if (this.#guardedFormFor(trigger) !== this.element) return;
|
|
28757
|
+
if (this.forceClose || this.submitting) return;
|
|
28758
|
+
if (!this.#isDirty()) return;
|
|
28759
|
+
event.preventDefault();
|
|
28760
|
+
event.stopPropagation();
|
|
28761
|
+
const message = trigger.getAttribute("data-dirty-form-guard-leave") || "You have unsaved changes that will be lost. Continue?";
|
|
28762
|
+
const confirmed = await this.#confirm(message);
|
|
28763
|
+
if (!confirmed) return;
|
|
28764
|
+
this.forceClose = true;
|
|
28765
|
+
const form = trigger.closest("form");
|
|
28766
|
+
if (form) {
|
|
28767
|
+
const submitter2 = trigger.matches("button, input[type=submit], input[type=image]") ? trigger : null;
|
|
28768
|
+
form.requestSubmit(submitter2);
|
|
28769
|
+
}
|
|
28770
|
+
}
|
|
28771
|
+
// The CSS selector for a guarded form. The guard is attached as a Stimulus
|
|
28772
|
+
// controller (`data-controller="… dirty-form-guard"`), NOT as a CSS class — so
|
|
28773
|
+
// match on the controller token, not `form.dirty-form-guard` (which never
|
|
28774
|
+
// matches the framework's forms, leaving the leave guard silently dormant).
|
|
28775
|
+
static GUARDED_FORM_SELECTOR = "form[data-controller~='dirty-form-guard']";
|
|
28776
|
+
// The single guarded form a leave control discards: the one containing it, or —
|
|
28777
|
+
// for a control outside any form (a wizard's sibling nav strip) — the closest
|
|
28778
|
+
// guarded form, i.e. the one sharing the deepest common ancestor with the
|
|
28779
|
+
// trigger. Returns the only guarded form on simple pages.
|
|
28780
|
+
#guardedFormFor(trigger) {
|
|
28781
|
+
const selector = this.constructor.GUARDED_FORM_SELECTOR;
|
|
28782
|
+
const inside = trigger.closest(selector);
|
|
28783
|
+
if (inside) return inside;
|
|
28784
|
+
let best = null;
|
|
28785
|
+
let bestDepth = -1;
|
|
28786
|
+
document.querySelectorAll(selector).forEach((form) => {
|
|
28787
|
+
let ancestor = form;
|
|
28788
|
+
while (ancestor && !ancestor.contains(trigger)) ancestor = ancestor.parentElement;
|
|
28789
|
+
if (!ancestor) return;
|
|
28790
|
+
const depth = this.#depthOf(ancestor);
|
|
28791
|
+
if (depth > bestDepth) {
|
|
28792
|
+
bestDepth = depth;
|
|
28793
|
+
best = form;
|
|
28794
|
+
}
|
|
28795
|
+
});
|
|
28796
|
+
return best;
|
|
28797
|
+
}
|
|
28798
|
+
#depthOf(node) {
|
|
28799
|
+
let depth = 0;
|
|
28800
|
+
while (node = node.parentElement) depth++;
|
|
28801
|
+
return depth;
|
|
28802
|
+
}
|
|
28803
|
+
// Defer to the themed Turbo confirm dialog the app installs as the global
|
|
28804
|
+
// confirm method (a styled <dialog>, not the native chrome bar); fall back to
|
|
28805
|
+
// window.confirm only if it isn't available. Returns a Promise<boolean>.
|
|
28806
|
+
#confirm(message) {
|
|
28807
|
+
const turboConfirm = window.Turbo?.config?.forms?.confirm;
|
|
28808
|
+
if (typeof turboConfirm === "function") {
|
|
28809
|
+
return Promise.resolve(turboConfirm(message));
|
|
28810
|
+
}
|
|
28811
|
+
return Promise.resolve(window.confirm(message));
|
|
28812
|
+
}
|
|
28334
28813
|
#confirmIsOpen() {
|
|
28335
28814
|
return this.hasConfirmDialogTarget && this.confirmDialogTarget.open;
|
|
28336
28815
|
}
|
|
@@ -28400,9 +28879,230 @@ this.ifd0Offset: ${this.ifd0Offset}, file.byteLength: ${e4.byteLength}`), e4.tif
|
|
|
28400
28879
|
}
|
|
28401
28880
|
};
|
|
28402
28881
|
|
|
28882
|
+
// src/js/controllers/wizard_controller.js
|
|
28883
|
+
var wizard_controller_default = class extends Controller {
|
|
28884
|
+
static targets = ["direction"];
|
|
28885
|
+
connect() {
|
|
28886
|
+
this.submitting = false;
|
|
28887
|
+
this.element.addEventListener("submit", this.onSubmit);
|
|
28888
|
+
this.element.addEventListener("turbo:submit-end", this.onSettled);
|
|
28889
|
+
document.addEventListener("turbo:load", this.onSettled);
|
|
28890
|
+
window.addEventListener("pageshow", this.onSettled);
|
|
28891
|
+
}
|
|
28892
|
+
disconnect() {
|
|
28893
|
+
this.element.removeEventListener("submit", this.onSubmit);
|
|
28894
|
+
this.element.removeEventListener("turbo:submit-end", this.onSettled);
|
|
28895
|
+
document.removeEventListener("turbo:load", this.onSettled);
|
|
28896
|
+
window.removeEventListener("pageshow", this.onSettled);
|
|
28897
|
+
}
|
|
28898
|
+
// Set the hidden `_direction` value programmatically (optional helper).
|
|
28899
|
+
setDirection(value) {
|
|
28900
|
+
if (this.hasDirectionTarget) this.directionTarget.value = value;
|
|
28901
|
+
}
|
|
28902
|
+
onSubmit = (event) => {
|
|
28903
|
+
if (this.submitting) {
|
|
28904
|
+
event.preventDefault();
|
|
28905
|
+
return;
|
|
28906
|
+
}
|
|
28907
|
+
this.submitting = true;
|
|
28908
|
+
};
|
|
28909
|
+
onSettled = () => {
|
|
28910
|
+
this.submitting = false;
|
|
28911
|
+
};
|
|
28912
|
+
};
|
|
28913
|
+
|
|
28914
|
+
// src/js/controllers/kanban_controller.js
|
|
28915
|
+
var kanban_controller_default = class extends Controller {
|
|
28916
|
+
static values = { moveUrlTemplate: String };
|
|
28917
|
+
static targets = ["column"];
|
|
28918
|
+
connect() {
|
|
28919
|
+
this.draggedCard = null;
|
|
28920
|
+
this.onDragStart = this.#onDragStart.bind(this);
|
|
28921
|
+
this.onDragOver = this.#onDragOver.bind(this);
|
|
28922
|
+
this.onDragLeave = this.#onDragLeave.bind(this);
|
|
28923
|
+
this.onDrop = this.#onDrop.bind(this);
|
|
28924
|
+
this.onDragEnd = this.#onDragEnd.bind(this);
|
|
28925
|
+
this.element.addEventListener("dragstart", this.onDragStart);
|
|
28926
|
+
this.element.addEventListener("dragover", this.onDragOver);
|
|
28927
|
+
this.element.addEventListener("dragleave", this.onDragLeave);
|
|
28928
|
+
this.element.addEventListener("drop", this.onDrop);
|
|
28929
|
+
this.element.addEventListener("dragend", this.onDragEnd);
|
|
28930
|
+
this.#applyPersistedCollapseStates();
|
|
28931
|
+
}
|
|
28932
|
+
disconnect() {
|
|
28933
|
+
this.element.removeEventListener("dragstart", this.onDragStart);
|
|
28934
|
+
this.element.removeEventListener("dragover", this.onDragOver);
|
|
28935
|
+
this.element.removeEventListener("dragleave", this.onDragLeave);
|
|
28936
|
+
this.element.removeEventListener("drop", this.onDrop);
|
|
28937
|
+
this.element.removeEventListener("dragend", this.onDragEnd);
|
|
28938
|
+
}
|
|
28939
|
+
// ─── Collapse toggle ─────────────────────────────────────────────────────────
|
|
28940
|
+
// Stimulus action: data-action="click->kanban#toggleColumn"
|
|
28941
|
+
// Expected on the expand button in the collapsed strip and the collapse
|
|
28942
|
+
// button in the expanded header. data-kanban-column-key on the button
|
|
28943
|
+
// identifies which column to toggle.
|
|
28944
|
+
toggleColumn(event) {
|
|
28945
|
+
const key = event.currentTarget.dataset.kanbanColumnKey;
|
|
28946
|
+
if (!key) return;
|
|
28947
|
+
const wrapper = this.element.querySelector(`[data-kanban-col="${key}"]`);
|
|
28948
|
+
if (!wrapper) return;
|
|
28949
|
+
const strip = wrapper.querySelector("[data-kanban-role='strip']");
|
|
28950
|
+
const body = wrapper.querySelector("[data-kanban-role='body']");
|
|
28951
|
+
if (!strip || !body) return;
|
|
28952
|
+
const isCollapsed = wrapper.classList.contains("pu-kanban-column-collapsed");
|
|
28953
|
+
if (isCollapsed) {
|
|
28954
|
+
wrapper.classList.remove("pu-kanban-column-collapsed");
|
|
28955
|
+
this.#saveCollapseState(key, false);
|
|
28956
|
+
} else {
|
|
28957
|
+
wrapper.classList.add("pu-kanban-column-collapsed");
|
|
28958
|
+
this.#saveCollapseState(key, true);
|
|
28959
|
+
}
|
|
28960
|
+
}
|
|
28961
|
+
// ─── drag lifecycle ──────────────────────────────────────────────────────────
|
|
28962
|
+
#onDragStart(event) {
|
|
28963
|
+
const card = event.target.closest("[data-kanban-record-id]");
|
|
28964
|
+
if (!card) return;
|
|
28965
|
+
this.draggedCard = card;
|
|
28966
|
+
event.dataTransfer.effectAllowed = "move";
|
|
28967
|
+
event.dataTransfer.setData("text/plain", card.dataset.kanbanRecordId);
|
|
28968
|
+
requestAnimationFrame(() => card.classList.add("pu-kanban-dragging"));
|
|
28969
|
+
this.#applyDropHints(card.dataset.kanbanColumnKey);
|
|
28970
|
+
}
|
|
28971
|
+
#onDragOver(event) {
|
|
28972
|
+
const column = event.target.closest("[data-kanban-target='column']");
|
|
28973
|
+
if (!column) return;
|
|
28974
|
+
const wrapper = event.target.closest("[data-kanban-col]");
|
|
28975
|
+
if (wrapper?.classList.contains("pu-kanban-no-drop")) return;
|
|
28976
|
+
event.preventDefault();
|
|
28977
|
+
event.dataTransfer.dropEffect = "move";
|
|
28978
|
+
this.#highlightColumn(column);
|
|
28979
|
+
}
|
|
28980
|
+
#onDragLeave(event) {
|
|
28981
|
+
if (!this.element.contains(event.relatedTarget)) {
|
|
28982
|
+
this.#clearHighlights();
|
|
28983
|
+
}
|
|
28984
|
+
}
|
|
28985
|
+
async #onDrop(event) {
|
|
28986
|
+
event.preventDefault();
|
|
28987
|
+
this.#clearHighlights();
|
|
28988
|
+
if (!this.draggedCard) return;
|
|
28989
|
+
const wrapper = event.target.closest("[data-kanban-col]");
|
|
28990
|
+
if (wrapper?.classList.contains("pu-kanban-no-drop")) return;
|
|
28991
|
+
const column = event.target.closest("[data-kanban-target='column']");
|
|
28992
|
+
if (!column) return;
|
|
28993
|
+
const recordId = this.draggedCard.dataset.kanbanRecordId;
|
|
28994
|
+
const fromColumn = this.draggedCard.dataset.kanbanColumnKey;
|
|
28995
|
+
const toColumn = column.dataset.kanbanColumnKeyValue;
|
|
28996
|
+
const existingCards = [...column.querySelectorAll("[data-kanban-record-id]")].filter((c4) => c4 !== this.draggedCard);
|
|
28997
|
+
const toIndex = this.#computeDropIndex(event.clientY, existingCards);
|
|
28998
|
+
const url = this.moveUrlTemplateValue.replace("__ID__", recordId);
|
|
28999
|
+
const csrfToken = document.querySelector('meta[name="csrf-token"]')?.content ?? "";
|
|
29000
|
+
try {
|
|
29001
|
+
const response = await fetch(url, {
|
|
29002
|
+
method: "POST",
|
|
29003
|
+
headers: {
|
|
29004
|
+
"Accept": "text/vnd.turbo-stream.html",
|
|
29005
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
29006
|
+
"X-CSRF-Token": csrfToken
|
|
29007
|
+
},
|
|
29008
|
+
body: new URLSearchParams({
|
|
29009
|
+
from_column: fromColumn,
|
|
29010
|
+
to_column: toColumn,
|
|
29011
|
+
to_index: toIndex
|
|
29012
|
+
}),
|
|
29013
|
+
credentials: "same-origin"
|
|
29014
|
+
});
|
|
29015
|
+
const body = await response.text();
|
|
29016
|
+
if (window.Turbo) {
|
|
29017
|
+
Turbo.renderStreamMessage(body);
|
|
29018
|
+
}
|
|
29019
|
+
} catch (error2) {
|
|
29020
|
+
console.error("[kanban] move request failed:", error2);
|
|
29021
|
+
}
|
|
29022
|
+
}
|
|
29023
|
+
#onDragEnd(_event) {
|
|
29024
|
+
this.#clearHighlights();
|
|
29025
|
+
this.#clearDropHints();
|
|
29026
|
+
if (this.draggedCard) {
|
|
29027
|
+
this.draggedCard.classList.remove("pu-kanban-dragging");
|
|
29028
|
+
this.draggedCard = null;
|
|
29029
|
+
}
|
|
29030
|
+
}
|
|
29031
|
+
// ─── drop hints ──────────────────────────────────────────────────────────────
|
|
29032
|
+
// Marks each column wrapper with `pu-kanban-no-drop` when it would refuse
|
|
29033
|
+
// a card dragged from sourceKey. The server remains the authority; this
|
|
29034
|
+
// is a display-only hint to give the user immediate visual feedback.
|
|
29035
|
+
#applyDropHints(sourceKey) {
|
|
29036
|
+
const sourceWrapper = this.element.querySelector(`[data-kanban-col="${sourceKey}"]`);
|
|
29037
|
+
const sourceLocked = sourceWrapper?.dataset.kanbanLocked === "true";
|
|
29038
|
+
this.element.querySelectorAll("[data-kanban-col]").forEach((wrapper) => {
|
|
29039
|
+
const noDrop = sourceLocked || !this.#columnAccepts(wrapper.dataset.kanbanAccepts, sourceKey);
|
|
29040
|
+
wrapper.classList.toggle("pu-kanban-no-drop", noDrop);
|
|
29041
|
+
});
|
|
29042
|
+
}
|
|
29043
|
+
#clearDropHints() {
|
|
29044
|
+
this.element.querySelectorAll("[data-kanban-col]").forEach((w4) => w4.classList.remove("pu-kanban-no-drop"));
|
|
29045
|
+
}
|
|
29046
|
+
// Returns true if the column described by `accepts` (the serialised form
|
|
29047
|
+
// from data-kanban-accepts) would accept a card from `sourceKey`.
|
|
29048
|
+
#columnAccepts(accepts, sourceKey) {
|
|
29049
|
+
if (!accepts || accepts === "all") return true;
|
|
29050
|
+
if (accepts === "none") return false;
|
|
29051
|
+
return accepts.split(",").map((k4) => k4.trim()).includes(sourceKey);
|
|
29052
|
+
}
|
|
29053
|
+
// ─── collapse persistence ─────────────────────────────────────────────────────
|
|
29054
|
+
// Applies localStorage collapse states to all column wrappers currently in
|
|
29055
|
+
// the DOM. Called on connect() and implicitly after Turbo frame swaps
|
|
29056
|
+
// because Stimulus re-connects the controller when the frame content changes.
|
|
29057
|
+
#applyPersistedCollapseStates() {
|
|
29058
|
+
this.element.querySelectorAll("[data-kanban-col]").forEach((wrapper) => {
|
|
29059
|
+
const key = wrapper.dataset.kanbanCol;
|
|
29060
|
+
const stored = localStorage.getItem(this.#storageKey(key));
|
|
29061
|
+
if (stored === null) return;
|
|
29062
|
+
const collapsed = stored === "1";
|
|
29063
|
+
wrapper.classList.toggle("pu-kanban-column-collapsed", collapsed);
|
|
29064
|
+
});
|
|
29065
|
+
}
|
|
29066
|
+
#saveCollapseState(key, collapsed) {
|
|
29067
|
+
try {
|
|
29068
|
+
localStorage.setItem(this.#storageKey(key), collapsed ? "1" : "0");
|
|
29069
|
+
} catch {
|
|
29070
|
+
}
|
|
29071
|
+
}
|
|
29072
|
+
// Derives a unique localStorage key from the resource collection path so
|
|
29073
|
+
// different boards (different resources / tenants) don't share state.
|
|
29074
|
+
// The move URL template is "/path/__ID__/kanban_move"; strip the suffix to
|
|
29075
|
+
// recover the collection path.
|
|
29076
|
+
#storageKey(key) {
|
|
29077
|
+
const path = this.moveUrlTemplateValue.replace("/__ID__/kanban_move", "");
|
|
29078
|
+
return `pu-kanban:${path}:${key}:collapsed`;
|
|
29079
|
+
}
|
|
29080
|
+
// ─── helpers ─────────────────────────────────────────────────────────────────
|
|
29081
|
+
// Returns the 0-based insertion index within the destination column by
|
|
29082
|
+
// comparing the cursor y-position against each card's vertical midpoint.
|
|
29083
|
+
// The card is inserted before the first card whose midpoint is below the
|
|
29084
|
+
// cursor, or appended after all cards if the cursor is below every midpoint.
|
|
29085
|
+
#computeDropIndex(clientY, cards) {
|
|
29086
|
+
for (let i4 = 0; i4 < cards.length; i4++) {
|
|
29087
|
+
const rect = cards[i4].getBoundingClientRect();
|
|
29088
|
+
if (clientY < rect.top + rect.height / 2) return i4;
|
|
29089
|
+
}
|
|
29090
|
+
return cards.length;
|
|
29091
|
+
}
|
|
29092
|
+
#highlightColumn(column) {
|
|
29093
|
+
this.columnTargets.forEach((c4) => {
|
|
29094
|
+
c4.classList.toggle("pu-kanban-drop-target", c4 === column);
|
|
29095
|
+
});
|
|
29096
|
+
}
|
|
29097
|
+
#clearHighlights() {
|
|
29098
|
+
this.columnTargets.forEach((c4) => c4.classList.remove("pu-kanban-drop-target"));
|
|
29099
|
+
}
|
|
29100
|
+
};
|
|
29101
|
+
|
|
28403
29102
|
// src/js/controllers/register_controllers.js
|
|
28404
29103
|
function register_controllers_default(application2) {
|
|
28405
29104
|
application2.register("password-visibility", password_visibility_controller_default);
|
|
29105
|
+
application2.register("password-sentinel", password_sentinel_controller_default);
|
|
28406
29106
|
application2.register("sidebar", sidebar_controller_default);
|
|
28407
29107
|
application2.register("resource-header", resource_header_controller_default);
|
|
28408
29108
|
application2.register("nested-resource-form-fields", nested_resource_form_fields_controller_default);
|
|
@@ -28437,6 +29137,8 @@ this.ifd0Offset: ${this.ifd0Offset}, file.byteLength: ${e4.byteLength}`), e4.tif
|
|
|
28437
29137
|
application2.register("view-switcher", view_switcher_controller_default);
|
|
28438
29138
|
application2.register("autosubmit", autosubmit_controller_default);
|
|
28439
29139
|
application2.register("dirty-form-guard", dirty_form_guard_controller_default);
|
|
29140
|
+
application2.register("wizard", wizard_controller_default);
|
|
29141
|
+
application2.register("kanban", kanban_controller_default);
|
|
28440
29142
|
}
|
|
28441
29143
|
|
|
28442
29144
|
// src/js/turbo/turbo_actions.js
|
|
@@ -28489,7 +29191,7 @@ this.ifd0Offset: ${this.ifd0Offset}, file.byteLength: ${e4.byteLength}`), e4.tif
|
|
|
28489
29191
|
"scale-95",
|
|
28490
29192
|
"data-[open]:opacity-100",
|
|
28491
29193
|
"data-[open]:scale-100",
|
|
28492
|
-
"transition-[opacity,
|
|
29194
|
+
"transition-[opacity,scale]",
|
|
28493
29195
|
"duration-200",
|
|
28494
29196
|
"ease-out"
|
|
28495
29197
|
].join(" ");
|
|
@@ -28594,7 +29296,7 @@ cropperjs/dist/cropper.js:
|
|
|
28594
29296
|
*)
|
|
28595
29297
|
|
|
28596
29298
|
dompurify/dist/purify.es.mjs:
|
|
28597
|
-
(*! @license DOMPurify 3.4.
|
|
29299
|
+
(*! @license DOMPurify 3.4.11 | (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.11/LICENSE *)
|
|
28598
29300
|
|
|
28599
29301
|
@uppy/utils/lib/Translator.js:
|
|
28600
29302
|
(**
|