{"version":3,"file":"trix.umd.min.js","sources":["../src/trix/config/attachments.js","../src/trix/config/block_attributes.js","../src/trix/config/browser.js","../src/trix/config/lang.js","../src/trix/config/file_size_formatting.js","../src/trix/constants.js","../src/trix/core/helpers/extend.js","../src/trix/core/helpers/dom.js","../src/trix/config/input.js","../src/trix/config/key_names.js","../src/trix/config/parser.js","../src/trix/config/text_attributes.js","../src/trix/config/toolbar.js","../src/trix/config/undo.js","../src/trix/config/css.js","../src/trix/core/basic_object.js","../src/trix/core/utilities/utf16_string.js","../src/trix/core/object.js","../src/trix/core/helpers/arrays.js","../src/trix/core/helpers/bidi.js","../src/trix/core/helpers/config.js","../src/trix/core/helpers/custom_elements.js","../src/trix/core/helpers/events.js","../src/trix/core/helpers/functions.js","../src/trix/core/helpers/objects.js","../src/trix/core/helpers/ranges.js","../src/trix/observers/selection_change_observer.js","../src/trix/core/helpers/strings.js","../src/trix/core/collections/hash.js","../src/trix/core/collections/object_group.js","../src/trix/core/collections/object_map.js","../src/trix/core/collections/element_store.js","../src/trix/core/utilities/operation.js","../src/trix/views/object_view.js","../src/trix/models/html_sanitizer.js","../src/trix/views/attachment_view.js","../src/trix/views/previewable_attachment_view.js","../src/trix/views/piece_view.js","../src/trix/views/text_view.js","../src/trix/views/block_view.js","../src/trix/views/document_view.js","../src/trix/models/piece.js","../src/trix/operations/image_preload_operation.js","../src/trix/models/attachment.js","../src/trix/models/attachment_piece.js","../src/trix/models/string_piece.js","../src/trix/models/splittable_list.js","../src/trix/models/text.js","../src/trix/models/block.js","../src/trix/models/document.js","../src/trix/models/html_parser.js","../src/trix/core/serialization.js","../src/trix/models/managed_attachment.js","../src/trix/models/attachment_manager.js","../src/trix/models/line_break_insertion.js","../src/trix/models/composition.js","../src/trix/models/undo_manager.js","../src/trix/filters/filter.js","../src/trix/filters/attachment_gallery_filter.js","../src/trix/models/editor.js","../src/trix/models/location_mapper.js","../src/trix/models/point_mapper.js","../src/trix/models/selection_manager.js","../src/trix/controllers/attachment_editor_controller.js","../src/trix/controllers/composition_controller.js","../src/trix/controllers/controller.js","../src/trix/observers/mutation_observer.js","../src/trix/operations/file_verification_operation.js","../src/trix/models/flaky_android_keyboard_detector.js","../src/trix/controllers/input_controller.js","../src/trix/controllers/level_0_input_controller.js","../src/trix/controllers/level_2_input_controller.js","../src/trix/controllers/toolbar_controller.js","../src/trix/controllers/editor_controller.js","../src/trix/elements/trix_toolbar_element.js","../src/trix/elements/trix_editor_element.js","../src/trix/trix.js"],"sourcesContent":["export const attachmentSelector = \"[data-trix-attachment]\"\n\nconst attachments = {\n preview: {\n presentation: \"gallery\",\n caption: {\n name: true,\n size: true,\n },\n },\n file: {\n caption: {\n size: true,\n },\n },\n}\nexport default attachments\n","const attributes = {\n default: {\n tagName: \"div\",\n parse: false,\n },\n quote: {\n tagName: \"blockquote\",\n nestable: true,\n },\n heading1: {\n tagName: \"h1\",\n terminal: true,\n breakOnReturn: true,\n group: false,\n },\n code: {\n tagName: \"pre\",\n terminal: true,\n htmlAttributes: [ \"language\" ],\n text: {\n plaintext: true,\n },\n },\n bulletList: {\n tagName: \"ul\",\n parse: false,\n },\n bullet: {\n tagName: \"li\",\n listAttribute: \"bulletList\",\n group: false,\n nestable: true,\n test(element) {\n return tagName(element.parentNode) === attributes[this.listAttribute].tagName\n },\n },\n numberList: {\n tagName: \"ol\",\n parse: false,\n },\n number: {\n tagName: \"li\",\n listAttribute: \"numberList\",\n group: false,\n nestable: true,\n test(element) {\n return tagName(element.parentNode) === attributes[this.listAttribute].tagName\n },\n },\n attachmentGallery: {\n tagName: \"div\",\n exclusive: true,\n terminal: true,\n parse: false,\n group: false,\n },\n}\n\nconst tagName = (element) => element?.tagName?.toLowerCase()\n\nexport default attributes\n","const androidVersionMatch = navigator.userAgent.match(/android\\s([0-9]+.*Chrome)/i)\nconst androidVersion = androidVersionMatch && parseInt(androidVersionMatch[1])\n\nexport default {\n // Android emits composition events when moving the cursor through existing text\n // Introduced in Chrome 65: https://bugs.chromium.org/p/chromium/issues/detail?id=764439#c9\n composesExistingText: /Android.*Chrome/.test(navigator.userAgent),\n\n // Android 13, especially on Samsung keyboards, emits extra compositionend and beforeinput events\n // that can make the input handler lose the current selection or enter an infinite input -> render -> input\n // loop.\n recentAndroid: androidVersion && androidVersion > 12,\n samsungAndroid: androidVersion && navigator.userAgent.match(/Android.*SM-/),\n\n // IE 11 activates resizing handles on editable elements that have \"layout\"\n forcesObjectResizing: /Trident.*rv:11/.test(navigator.userAgent),\n // https://www.w3.org/TR/input-events-1/ + https://www.w3.org/TR/input-events-2/\n supportsInputEvents: typeof InputEvent !== \"undefined\" &&\n [ \"data\", \"getTargetRanges\", \"inputType\" ].every(prop => prop in InputEvent.prototype),\n}\n","export default {\n attachFiles: \"Attach Files\",\n bold: \"Bold\",\n bullets: \"Bullets\",\n byte: \"Byte\",\n bytes: \"Bytes\",\n captionPlaceholder: \"Add a caption…\",\n code: \"Code\",\n heading1: \"Heading\",\n indent: \"Increase Level\",\n italic: \"Italic\",\n link: \"Link\",\n numbers: \"Numbers\",\n outdent: \"Decrease Level\",\n quote: \"Quote\",\n redo: \"Redo\",\n remove: \"Remove\",\n strike: \"Strikethrough\",\n undo: \"Undo\",\n unlink: \"Unlink\",\n url: \"URL\",\n urlPlaceholder: \"Enter a URL…\",\n GB: \"GB\",\n KB: \"KB\",\n MB: \"MB\",\n PB: \"PB\",\n TB: \"TB\",\n}\n","/* eslint-disable\n no-case-declarations,\n*/\nimport lang from \"trix/config/lang\"\n\nconst sizes = [ lang.bytes, lang.KB, lang.MB, lang.GB, lang.TB, lang.PB ]\n\nexport default {\n prefix: \"IEC\",\n precision: 2,\n\n formatter(number) {\n switch (number) {\n case 0:\n return `0 ${lang.bytes}`\n case 1:\n return `1 ${lang.byte}`\n default:\n let base\n\n if (this.prefix === \"SI\") {\n base = 1000\n } else if (this.prefix === \"IEC\") {\n base = 1024\n }\n\n const exp = Math.floor(Math.log(number) / Math.log(base))\n const humanSize = number / Math.pow(base, exp)\n const string = humanSize.toFixed(this.precision)\n const withoutInsignificantZeros = string.replace(/0*$/, \"\").replace(/\\.$/, \"\")\n return `${withoutInsignificantZeros} ${sizes[exp]}`\n }\n },\n}\n","export const ZERO_WIDTH_SPACE = \"\\uFEFF\"\nexport const NON_BREAKING_SPACE = \"\\u00A0\"\nexport const OBJECT_REPLACEMENT_CHARACTER = \"\\uFFFC\"\n","export const extend = function(properties) {\n for (const key in properties) {\n const value = properties[key]\n this[key] = value\n }\n return this\n}\n","import blockAttributes from \"trix/config/block_attributes\"\nimport { ZERO_WIDTH_SPACE } from \"trix/constants\"\nimport { extend } from \"./extend\"\nimport { attachmentSelector } from \"trix/config/attachments\"\n\nconst html = document.documentElement\nconst match = html.matches\n\nexport const handleEvent = function(eventName, { onElement, matchingSelector, withCallback, inPhase, preventDefault, times } = {}) {\n const element = onElement ? onElement : html\n const selector = matchingSelector\n const useCapture = inPhase === \"capturing\"\n\n const handler = function(event) {\n if (times != null && --times === 0) {\n handler.destroy()\n }\n const target = findClosestElementFromNode(event.target, { matchingSelector: selector })\n if (target != null) {\n withCallback?.call(target, event, target)\n if (preventDefault) {\n event.preventDefault()\n }\n }\n }\n\n handler.destroy = () => element.removeEventListener(eventName, handler, useCapture)\n\n element.addEventListener(eventName, handler, useCapture)\n return handler\n}\n\nexport const handleEventOnce = function(eventName, options = {}) {\n options.times = 1\n return handleEvent(eventName, options)\n}\n\nexport const triggerEvent = function(eventName, { onElement, bubbles, cancelable, attributes } = {}) {\n const element = onElement != null ? onElement : html\n bubbles = bubbles !== false\n cancelable = cancelable !== false\n\n const event = document.createEvent(\"Events\")\n event.initEvent(eventName, bubbles, cancelable)\n if (attributes != null) {\n extend.call(event, attributes)\n }\n return element.dispatchEvent(event)\n}\n\nexport const elementMatchesSelector = function(element, selector) {\n if (element?.nodeType === 1) {\n return match.call(element, selector)\n }\n}\n\nexport const findClosestElementFromNode = function(node, { matchingSelector, untilNode } = {}) {\n while (node && node.nodeType !== Node.ELEMENT_NODE) {\n node = node.parentNode\n }\n if (node == null) {\n return\n }\n\n if (matchingSelector != null) {\n if (node.closest && untilNode == null) {\n return node.closest(matchingSelector)\n } else {\n while (node && node !== untilNode) {\n if (elementMatchesSelector(node, matchingSelector)) {\n return node\n }\n node = node.parentNode\n }\n }\n } else {\n return node\n }\n}\n\nexport const findInnerElement = function(element) {\n while (element?.firstElementChild) {\n element = element.firstElementChild\n }\n return element\n}\n\nexport const innerElementIsActive = (element) =>\n document.activeElement !== element && elementContainsNode(element, document.activeElement)\n\nexport const elementContainsNode = function(element, node) {\n if (!element || !node) {\n return\n }\n while (node) {\n if (node === element) {\n return true\n }\n node = node.parentNode\n }\n}\n\nexport const findNodeFromContainerAndOffset = function(container, offset) {\n if (!container) {\n return\n }\n if (container.nodeType === Node.TEXT_NODE) {\n return container\n } else if (offset === 0) {\n return container.firstChild != null ? container.firstChild : container\n } else {\n return container.childNodes.item(offset - 1)\n }\n}\n\nexport const findElementFromContainerAndOffset = function(container, offset) {\n const node = findNodeFromContainerAndOffset(container, offset)\n return findClosestElementFromNode(node)\n}\n\nexport const findChildIndexOfNode = function(node) {\n if (!node?.parentNode) {\n return\n }\n let childIndex = 0\n node = node.previousSibling\n while (node) {\n childIndex++\n node = node.previousSibling\n }\n return childIndex\n}\n\nexport const removeNode = (node) => node?.parentNode?.removeChild(node)\n\nexport const walkTree = function(tree, { onlyNodesOfType, usingFilter, expandEntityReferences } = {}) {\n const whatToShow = (() => {\n switch (onlyNodesOfType) {\n case \"element\":\n return NodeFilter.SHOW_ELEMENT\n case \"text\":\n return NodeFilter.SHOW_TEXT\n case \"comment\":\n return NodeFilter.SHOW_COMMENT\n default:\n return NodeFilter.SHOW_ALL\n }\n })()\n\n return document.createTreeWalker(\n tree,\n whatToShow,\n usingFilter != null ? usingFilter : null,\n expandEntityReferences === true\n )\n}\n\nexport const tagName = (element) => element?.tagName?.toLowerCase()\n\nexport const makeElement = function(tag, options = {}) {\n let key, value\n if (typeof tag === \"object\") {\n options = tag\n tag = options.tagName\n } else {\n options = { attributes: options }\n }\n\n const element = document.createElement(tag)\n\n if (options.editable != null) {\n if (options.attributes == null) {\n options.attributes = {}\n }\n options.attributes.contenteditable = options.editable\n }\n\n if (options.attributes) {\n for (key in options.attributes) {\n value = options.attributes[key]\n element.setAttribute(key, value)\n }\n }\n\n if (options.style) {\n for (key in options.style) {\n value = options.style[key]\n element.style[key] = value\n }\n }\n\n if (options.data) {\n for (key in options.data) {\n value = options.data[key]\n element.dataset[key] = value\n }\n }\n\n if (options.className) {\n options.className.split(\" \").forEach((className) => {\n element.classList.add(className)\n })\n }\n\n if (options.textContent) {\n element.textContent = options.textContent\n }\n\n if (options.childNodes) {\n [].concat(options.childNodes).forEach((childNode) => {\n element.appendChild(childNode)\n })\n }\n\n return element\n}\n\nlet blockTagNames = undefined\n\nexport const getBlockTagNames = function() {\n if (blockTagNames != null) {\n return blockTagNames\n }\n\n blockTagNames = []\n for (const key in blockAttributes) {\n const attributes = blockAttributes[key]\n if (attributes.tagName) {\n blockTagNames.push(attributes.tagName)\n }\n }\n\n return blockTagNames\n}\n\nexport const nodeIsBlockContainer = (node) => nodeIsBlockStartComment(node?.firstChild)\n\nexport const nodeProbablyIsBlockContainer = function(node) {\n return getBlockTagNames().includes(tagName(node)) && !getBlockTagNames().includes(tagName(node.firstChild))\n}\n\nexport const nodeIsBlockStart = function(node, { strict } = { strict: true }) {\n if (strict) {\n return nodeIsBlockStartComment(node)\n } else {\n return (\n nodeIsBlockStartComment(node) || !nodeIsBlockStartComment(node.firstChild) && nodeProbablyIsBlockContainer(node)\n )\n }\n}\n\nexport const nodeIsBlockStartComment = (node) => nodeIsCommentNode(node) && node?.data === \"block\"\n\nexport const nodeIsCommentNode = (node) => node?.nodeType === Node.COMMENT_NODE\n\nexport const nodeIsCursorTarget = function(node, { name } = {}) {\n if (!node) {\n return\n }\n if (nodeIsTextNode(node)) {\n if (node.data === ZERO_WIDTH_SPACE) {\n if (name) {\n return node.parentNode.dataset.trixCursorTarget === name\n } else {\n return true\n }\n }\n } else {\n return nodeIsCursorTarget(node.firstChild)\n }\n}\n\nexport const nodeIsAttachmentElement = (node) => elementMatchesSelector(node, attachmentSelector)\n\nexport const nodeIsEmptyTextNode = (node) => nodeIsTextNode(node) && node?.data === \"\"\n\nexport const nodeIsTextNode = (node) => node?.nodeType === Node.TEXT_NODE\n","import browser from \"trix/config/browser\"\nimport { makeElement, removeNode } from \"trix/core/helpers/dom\"\n\nconst input = {\n level2Enabled: true,\n\n getLevel() {\n if (this.level2Enabled && browser.supportsInputEvents) {\n return 2\n } else {\n return 0\n }\n },\n pickFiles(callback) {\n const input = makeElement(\"input\", { type: \"file\", multiple: true, hidden: true, id: this.fileInputId })\n\n input.addEventListener(\"change\", () => {\n callback(input.files)\n removeNode(input)\n })\n\n removeNode(document.getElementById(this.fileInputId))\n document.body.appendChild(input)\n input.click()\n }\n}\n\nexport default input\n","export default {\n 8: \"backspace\",\n 9: \"tab\",\n 13: \"return\",\n 27: \"escape\",\n 37: \"left\",\n 39: \"right\",\n 46: \"delete\",\n 68: \"d\",\n 72: \"h\",\n 79: \"o\",\n}\n","export default {\n removeBlankTableCells: false,\n tableCellSeparator: \" | \",\n tableRowSeparator: \"\\n\",\n}\n","import { attachmentSelector } from \"trix/config/attachments\"\n\nexport default {\n bold: {\n tagName: \"strong\",\n inheritable: true,\n parser(element) {\n const style = window.getComputedStyle(element)\n return style.fontWeight === \"bold\" || style.fontWeight >= 600\n },\n },\n italic: {\n tagName: \"em\",\n inheritable: true,\n parser(element) {\n const style = window.getComputedStyle(element)\n return style.fontStyle === \"italic\"\n },\n },\n href: {\n groupTagName: \"a\",\n parser(element) {\n const matchingSelector = `a:not(${attachmentSelector})`\n const link = element.closest(matchingSelector)\n if (link) {\n return link.getAttribute(\"href\")\n }\n },\n },\n strike: {\n tagName: \"del\",\n inheritable: true,\n },\n frozen: {\n style: { backgroundColor: \"highlight\" },\n },\n}\n","import lang from \"trix/config/lang\"\n\nexport default {\n getDefaultHTML() {\n return `
\n\n