/g, '
');\n\n/* src/core/ui/components/ContentEditable.svelte generated by Svelte v3.52.0 */\n\nfunction create_if_block$6(ctx) {\n\tlet pre;\n\tlet styleable_action;\n\tlet mounted;\n\tlet dispose;\n\n\treturn {\n\t\tc() {\n\t\t\tpre = element(\"pre\");\n\t\t\tattr(pre, \"class\", \"PinturaContentEditable\");\n\t\t},\n\t\tm(target, anchor) {\n\t\t\tinsert(target, pre, anchor);\n\t\t\tpre.innerHTML = /*innerHTML*/ ctx[0];\n\n\t\t\tif (!mounted) {\n\t\t\t\tdispose = action_destroyer(styleable_action = styleable.call(null, pre, `${/*contentStyle*/ ctx[7]};position:absolute;z-index:-1;pointer-events:none;`));\n\t\t\t\tmounted = true;\n\t\t\t}\n\t\t},\n\t\tp(ctx, dirty) {\n\t\t\tif (dirty[0] & /*innerHTML*/ 1) pre.innerHTML = /*innerHTML*/ ctx[0];\t\t\tif (styleable_action && is_function(styleable_action.update) && dirty[0] & /*contentStyle*/ 128) styleable_action.update.call(null, `${/*contentStyle*/ ctx[7]};position:absolute;z-index:-1;pointer-events:none;`);\n\t\t},\n\t\td(detaching) {\n\t\t\tif (detaching) detach(pre);\n\t\t\tmounted = false;\n\t\t\tdispose();\n\t\t}\n\t};\n}\n\nfunction create_fragment$g(ctx) {\n\tlet t_1;\n\tlet pre;\n\tlet pre_data_wrap_content_value;\n\tlet styleable_action;\n\tlet mounted;\n\tlet dispose;\n\tlet if_block = /*shouldSimulateTextEffects*/ ctx[8] && create_if_block$6(ctx);\n\n\treturn {\n\t\tc() {\n\t\t\tif (if_block) if_block.c();\n\t\t\tt_1 = space();\n\t\t\tpre = element(\"pre\");\n\t\t\tattr(pre, \"class\", \"PinturaContentEditable\");\n\n\t\t\tattr(pre, \"data-wrap-content\", pre_data_wrap_content_value = !/*wrapLines*/ ctx[4] && !/*allowNewline*/ ctx[5]\n\t\t\t? 'nowrap'\n\t\t\t: 'wrap');\n\n\t\t\tattr(pre, \"contenteditable\", \"\");\n\t\t\tattr(pre, \"spellcheck\", /*spellcheck*/ ctx[1]);\n\t\t\tattr(pre, \"autocorrect\", /*autocorrect*/ ctx[2]);\n\t\t\tattr(pre, \"autocapitalize\", /*autocapitalize*/ ctx[3]);\n\t\t},\n\t\tm(target, anchor) {\n\t\t\tif (if_block) if_block.m(target, anchor);\n\t\t\tinsert(target, t_1, anchor);\n\t\t\tinsert(target, pre, anchor);\n\t\t\t/*pre_binding*/ ctx[26](pre);\n\n\t\t\tif (!mounted) {\n\t\t\t\tdispose = [\n\t\t\t\t\taction_destroyer(styleable_action = styleable.call(null, pre, /*shouldSimulateTextEffects*/ ctx[8]\n\t\t\t\t\t? /*removeTextEffects*/ ctx[15](/*contentStyle*/ ctx[7])\n\t\t\t\t\t: /*contentStyle*/ ctx[7])),\n\t\t\t\t\tlisten(pre, \"input\", /*handleInput*/ ctx[13]),\n\t\t\t\t\tlisten(pre, \"compositionend\", /*handleCompositionEnd*/ ctx[12]),\n\t\t\t\t\tlisten(pre, \"paste\", /*handlePaste*/ ctx[14]),\n\t\t\t\t\tlisten(pre, \"keydown\", /*handleKeydown*/ ctx[10]),\n\t\t\t\t\tlisten(pre, \"keyup\", /*handleKeyup*/ ctx[11]),\n\t\t\t\t\tlisten(pre, \"blur\", /*handleBlur*/ ctx[9])\n\t\t\t\t];\n\n\t\t\t\tmounted = true;\n\t\t\t}\n\t\t},\n\t\tp(ctx, dirty) {\n\t\t\tif (/*shouldSimulateTextEffects*/ ctx[8]) {\n\t\t\t\tif (if_block) {\n\t\t\t\t\tif_block.p(ctx, dirty);\n\t\t\t\t} else {\n\t\t\t\t\tif_block = create_if_block$6(ctx);\n\t\t\t\t\tif_block.c();\n\t\t\t\t\tif_block.m(t_1.parentNode, t_1);\n\t\t\t\t}\n\t\t\t} else if (if_block) {\n\t\t\t\tif_block.d(1);\n\t\t\t\tif_block = null;\n\t\t\t}\n\n\t\t\tif (dirty[0] & /*wrapLines, allowNewline*/ 48 && pre_data_wrap_content_value !== (pre_data_wrap_content_value = !/*wrapLines*/ ctx[4] && !/*allowNewline*/ ctx[5]\n\t\t\t? 'nowrap'\n\t\t\t: 'wrap')) {\n\t\t\t\tattr(pre, \"data-wrap-content\", pre_data_wrap_content_value);\n\t\t\t}\n\n\t\t\tif (dirty[0] & /*spellcheck*/ 2) {\n\t\t\t\tattr(pre, \"spellcheck\", /*spellcheck*/ ctx[1]);\n\t\t\t}\n\n\t\t\tif (dirty[0] & /*autocorrect*/ 4) {\n\t\t\t\tattr(pre, \"autocorrect\", /*autocorrect*/ ctx[2]);\n\t\t\t}\n\n\t\t\tif (dirty[0] & /*autocapitalize*/ 8) {\n\t\t\t\tattr(pre, \"autocapitalize\", /*autocapitalize*/ ctx[3]);\n\t\t\t}\n\n\t\t\tif (styleable_action && is_function(styleable_action.update) && dirty[0] & /*shouldSimulateTextEffects, contentStyle*/ 384) styleable_action.update.call(null, /*shouldSimulateTextEffects*/ ctx[8]\n\t\t\t? /*removeTextEffects*/ ctx[15](/*contentStyle*/ ctx[7])\n\t\t\t: /*contentStyle*/ ctx[7]);\n\t\t},\n\t\ti: noop,\n\t\to: noop,\n\t\td(detaching) {\n\t\t\tif (if_block) if_block.d(detaching);\n\t\t\tif (detaching) detach(t_1);\n\t\t\tif (detaching) detach(pre);\n\t\t\t/*pre_binding*/ ctx[26](null);\n\t\t\tmounted = false;\n\t\t\trun_all(dispose);\n\t\t}\n\t};\n}\n\nfunction instance$g($$self, $$props, $$invalidate) {\n\tlet isReady;\n\tlet shouldSimulateTextEffects;\n\tlet { spellcheck = 'false' } = $$props;\n\tlet { autocorrect = 'off' } = $$props;\n\tlet { autocapitalize = 'off' } = $$props;\n\tlet { wrapLines = true } = $$props;\n\tlet { allowNewline = true } = $$props;\n\tlet { textFormat = 'text' } = $$props;\n\tlet { formatInput = passthrough } = $$props;\n\tlet { formatPaste = passthrough } = $$props;\n\tlet { styles = undefined } = $$props;\n\tlet { innerHTML = undefined } = $$props;\n\tlet { oninput = noop$1 } = $$props;\n\tlet { enableTextStyleShortcuts = false } = $$props;\n\tconst confirm = () => confirmInputState();\n\tconst focus = () => element && element.focus();\n\n\tconst select = () => {\n\t\tif (!element) return;\n\t\tconst range = document.createRange();\n\t\trange.selectNodeContents(element);\n\t\tconst selection = getSelectionSafe();\n\t\tselection.removeAllRanges();\n\t\tselection.addRange(range);\n\t};\n\n\t// events\n\tconst dispatchEvent = createEventDispatcher();\n\n\t// reference to contenteditable element\n\tlet element;\n\n\t// use
for line breaks\n\tdocument.execCommand('defaultParagraphSeparator', false, 'br');\n\n\tconst setInnerHTML = html => {\n\t\tif (html === element.innerHTML) return;\n\t\t$$invalidate(6, element.innerHTML = html, element);\n\t\tif (element === document.activeElement) select();\n\t};\n\n\tconst removeTextStyles = html => {\n\t\treturn html.replace(/<\\/?(?:i|b|em|u|s|strike|strong|font)>/, '').replace(/style=\".*?\"/g, '').replace(/\\n/gim, '
');\n\t};\n\n\tconst updateInnerHTML = () => {\n\t\t// sync property\n\t\t$$invalidate(0, innerHTML = element.innerHTML);\n\n\t\t// let others know\n\t\tconst currentValue = replaceDivWithBr(innerHTML);\n\n\t\tdispatchEvent('input', currentValue);\n\t\toninput(currentValue);\n\n\t\t// always scroll to top\n\t\trequestAnimationFrame(() => element && element.scrollTo(0, 0));\n\t};\n\n\tconst confirmInputState = () => {\n\t\t// bookmark caret positions\n\t\tinsertBookmarks(element);\n\n\t\t// remove styles\n\t\tconst html = textFormat === 'html'\n\t\t? element.innerHTML\n\t\t: removeTextStyles(element.innerHTML);\n\n\t\t// update HTML (don't remove caret positions)\n\t\t$$invalidate(6, element.innerHTML = formatInput(html), element);\n\n\t\t// select bookmarks and remove from DOM\n\t\tselectBookmarks(element);\n\n\t\t// content updated\n\t\tupdateInnerHTML();\n\t};\n\n\tconst handleBlur = () => {\n\t\tdispatchEvent('blur');\n\t};\n\n\tconst handleConfirm = () => {\n\t\tdispatchEvent('confirm');\n\t};\n\n\tconst handleCancel = () => {\n\t\tdispatchEvent('cancel');\n\t};\n\n\tconst styleKeys = {\n\t\tb: 'bold',\n\t\ti: 'italic',\n\t\tu: 'underline',\n\t\ts: 'strikethrough'\n\t};\n\n\tconst handleStyleShortcut = key => {\n\t\tconst cmd = styleKeys[key];\n\t\tcmd && document.execCommand(cmd);\n\t};\n\n\tconst handleKeydown = e => {\n\t\t// is escape key, cancel\n\t\tif ((/escape/i).test(e.code)) {\n\t\t\te.stopPropagation();\n\t\t\treturn handleCancel();\n\t\t}\n\n\t\tconst cmdPressed = e.ctrlKey || e.metaKey;\n\n\t\t// prevent text style keys\n\t\tif (textFormat === 'html' && cmdPressed) {\n\t\t\tif ((/b|i|u|s/).test(e.key)) {\n\t\t\t\tenableTextStyleShortcuts && handleStyleShortcut(e.key);\n\t\t\t\te.preventDefault();\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t\t// stop here\n\t\tif (!(/enter/i).test(e.code)) return;\n\n\t\t// don't let others handle return while in text area\n\t\te.stopPropagation();\n\n\t\t// is enter but is composing (confirm predictive selection)\n\t\tif (e.isComposing) return;\n\n\t\t// is cmd + enter or ctrl + enter\n\t\tif (cmdPressed) return handleConfirm();\n\n\t\t// block return when newlines not allowed\n\t\tif (!allowNewline) e.preventDefault();\n\n\t\tdocument.execCommand('insertLineBreak');\n\t\te.preventDefault();\n\t};\n\n\tconst handleKeyup = () => {\n\t\t\n\t}; // intentionally blank for now\n\n\tconst handleCompositionEnd = e => {\n\t\tif (e.data === '') return;\n\t\tconfirmInputState();\n\t};\n\n\tconst handleInput = e => {\n\t\tconst { inputType } = e;\n\n\t\t// more info on when inputType is which value\n\t\t// https://rawgit.com/w3c/input-events/v1/index.html#interface-InputEvent-Attributes\n\t\tif (inputType === 'insertCompositionText' || inputType === 'deleteCompositionText') {\n\t\t\t// we're composing (to for example write é, or Japanese characters),\n\t\t\t// if so, wait for composition end before formatting content\n\t\t\treturn;\n\t\t}\n\n\t\tconfirmInputState();\n\t};\n\n\tconst handlePaste = e => {\n\t\te.preventDefault();\n\n\t\t// remove styles\n\t\tconst plainText = e.clipboardData.getData('text/plain');\n\n\t\tconst html = textFormat === 'html'\n\t\t? plainText\n\t\t: removeTextStyles(plainText);\n\n\t\t// format the pasted text before adding\n\t\tconst text = formatPaste(html);\n\n\t\tif (!text.length) return;\n\n\t\t// get range for current selection and replace with text\n\t\tconst range = getSelectionSafe().getRangeAt(0);\n\n\t\trange.deleteContents();\n\t\trange.insertNode(document.createTextNode(text));\n\n\t\t// content updated\n\t\tupdateInnerHTML();\n\t};\n\n\t//#region bookmarks\n\tconst createBookmark = name => {\n\t\tconst bookmarkNode = h('span');\n\t\tbookmarkNode.dataset.bookmark = name;\n\t\treturn bookmarkNode;\n\t};\n\n\tconst insertBookmark = (node, offset, name) => {\n\t\t// create the bookmark element\n\t\tconst bookmarkNode = createBookmark(name);\n\n\t\t// split up in two text nodes and insert\n\t\tif (node.nodeType === Node.TEXT_NODE) {\n\t\t\tconst text = node.textContent;\n\n\t\t\tif (name === 'start') {\n\t\t\t\tconst before = t(text.substring(0, offset));\n\t\t\t\tconst after = t(text.substring(offset));\n\t\t\t\tnode.replaceWith(before, bookmarkNode, after);\n\t\t\t} else {\n\t\t\t\tconst before = t(text.substring(0, offset));\n\t\t\t\tconst after = t(text.substring(offset));\n\t\t\t\tnode.replaceWith(before, bookmarkNode, after);\n\t\t\t}\n\t\t} else // is node\n\t\tif (node.nodeType === Node.ELEMENT_NODE) {\n\t\t\tnode.insertBefore(bookmarkNode, node.childNodes[offset]);\n\t\t}\n\t};\n\n\tconst getSelectionSafe = () => {\n\t\tconst root = element.getRootNode();\n\n\t\t// chrome needs to select from shadowRoot if in Shadow DOM\n\t\t// firefox can select from document and doesn't have getSelection support on shadowRoot\n\t\t// safari cannot deal with text selection in shadowRoot at this time (MacOS Safari 16)\n\t\treturn 'getSelection' in root\n\t\t? root.getSelection()\n\t\t: document.getSelection();\n\t};\n\n\tconst insertBookmarks = element => {\n\t\tconst selection = getSelectionSafe();\n\t\tif (!(selection.getRangeAt && selection.rangeCount)) return;\n\t\tconst range = selection.getRangeAt(0);\n\t\tconst { startOffset, endOffset, startContainer, endContainer } = range;\n\n\t\t// range needs to be in element\n\t\tif (!element.contains(range.startContainer) || !element.contains(range.endContainer)) return;\n\n\t\t// should break up one text container\n\t\tif (startContainer.nodeType === Node.TEXT_NODE && startContainer === endContainer) {\n\t\t\tconst text = startContainer.textContent;\n\t\t\tconst before = text.substring(0, startOffset);\n\t\t\tconst bookmarkStart = createBookmark('start');\n\n\t\t\tconst selected = endOffset - startOffset > 0\n\t\t\t? text.substring(startOffset, endOffset)\n\t\t\t: '';\n\n\t\t\tconst bookmarkEnd = createBookmark('end');\n\t\t\tconst after = text.substring(endOffset);\n\t\t\tstartContainer.replaceWith(before, bookmarkStart, selected, bookmarkEnd, after);\n\t\t} else // should add to separate containers\n\t\t{\n\t\t\tinsertBookmark(startContainer, startOffset, 'start');\n\n\t\t\tinsertBookmark(\n\t\t\t\tendContainer,\n\t\t\t\t// if is same container need to move to next index as we've just added start bookmark\n\t\t\t\tendOffset + (startContainer === endContainer ? 1 : 0),\n\t\t\t\t'end'\n\t\t\t);\n\t\t}\n\t};\n\n\tconst selectBookmarks = element => {\n\t\t// get locations of bookmarks\n\t\tconst bookmarkStart = findBookmark(element, 'start');\n\n\t\tconst bookmarkEnd = findBookmark(element, 'end');\n\n\t\t// remove bookmarks (if found)\n\t\tif (!bookmarkStart || !bookmarkEnd) return;\n\n\t\t// set caret positions\n\t\tconst range = document.createRange();\n\n\t\trange.setStart(bookmarkStart, 0);\n\t\trange.setEnd(bookmarkEnd, 0);\n\t\tconst selection = getSelectionSafe();\n\t\tselection.removeAllRanges();\n\t\tselection.addRange(range);\n\n\t\t// remove bookmarks\n\t\tbookmarkStart.remove();\n\n\t\tbookmarkEnd.remove();\n\t};\n\n\tconst findBookmark = (element, name) => {\n\t\tconst children = element.children;\n\n\t\tfor (let i = 0; i < children.length; i++) {\n\t\t\tconst node = children[i];\n\t\t\tif (node.dataset.bookmark === name) return node;\n\n\t\t\tif (node.children.length) {\n\t\t\t\tconst bookmarkNode = findBookmark(node, name);\n\t\t\t\tif (bookmarkNode) return bookmarkNode;\n\t\t\t}\n\t\t}\n\n\t\treturn;\n\t};\n\n\t// add extra space on left or right side of content input so special symbols\n\t// are rendered on correct line while waiting for completion\n\tlet contentStyle;\n\n\t// we \"disable\" shadow and text styles because both are drawn in the stroke layer\n\tconst removeTextEffects = str => str.replace('-webkit-text-stroke', '--text-stroke').replace('text-shadow', '--text-shadow');\n\n\tfunction pre_binding($$value) {\n\t\tbinding_callbacks[$$value ? 'unshift' : 'push'](() => {\n\t\t\telement = $$value;\n\t\t\t$$invalidate(6, element);\n\t\t});\n\t}\n\n\t$$self.$$set = $$props => {\n\t\tif ('spellcheck' in $$props) $$invalidate(1, spellcheck = $$props.spellcheck);\n\t\tif ('autocorrect' in $$props) $$invalidate(2, autocorrect = $$props.autocorrect);\n\t\tif ('autocapitalize' in $$props) $$invalidate(3, autocapitalize = $$props.autocapitalize);\n\t\tif ('wrapLines' in $$props) $$invalidate(4, wrapLines = $$props.wrapLines);\n\t\tif ('allowNewline' in $$props) $$invalidate(5, allowNewline = $$props.allowNewline);\n\t\tif ('textFormat' in $$props) $$invalidate(16, textFormat = $$props.textFormat);\n\t\tif ('formatInput' in $$props) $$invalidate(17, formatInput = $$props.formatInput);\n\t\tif ('formatPaste' in $$props) $$invalidate(18, formatPaste = $$props.formatPaste);\n\t\tif ('styles' in $$props) $$invalidate(19, styles = $$props.styles);\n\t\tif ('innerHTML' in $$props) $$invalidate(0, innerHTML = $$props.innerHTML);\n\t\tif ('oninput' in $$props) $$invalidate(20, oninput = $$props.oninput);\n\t\tif ('enableTextStyleShortcuts' in $$props) $$invalidate(21, enableTextStyleShortcuts = $$props.enableTextStyleShortcuts);\n\t};\n\n\t$$self.$$.update = () => {\n\t\tif ($$self.$$.dirty[0] & /*element*/ 64) {\n\t\t\t// set value when html property is set and element reference is bound\n\t\t\t$$invalidate(25, isReady = !!element);\n\t\t}\n\n\t\tif ($$self.$$.dirty[0] & /*isReady, innerHTML*/ 33554433) {\n\t\t\tif (isReady && innerHTML) setInnerHTML(innerHTML);\n\t\t}\n\n\t\tif ($$self.$$.dirty[0] & /*wrapLines, allowNewline, styles*/ 524336) {\n\t\t\tif (!wrapLines && allowNewline) {\n\t\t\t\t// TODO: no fix for center text yet\n\t\t\t\tconst contentStyleAdditional = styles.includes(':right')\n\t\t\t\t? 'text-indent:-100px!important'\n\t\t\t\t: !styles.includes(':center')\n\t\t\t\t\t? 'min-width:calc(100% + 100px)!important'\n\t\t\t\t\t: '';\n\n\t\t\t\t$$invalidate(7, contentStyle = styles + ';overflow:visible;' + contentStyleAdditional);\n\t\t\t} else {\n\t\t\t\t$$invalidate(7, contentStyle = styles);\n\t\t\t}\n\t\t}\n\n\t\tif ($$self.$$.dirty[0] & /*styles*/ 524288) {\n\t\t\t//#endregion\n\t\t\t// if text-stroke is set we need to render an extra text layer to simulate an outside stroke effect\n\t\t\t$$invalidate(8, shouldSimulateTextEffects = styles.includes('text-stroke'));\n\t\t}\n\t};\n\n\treturn [\n\t\tinnerHTML,\n\t\tspellcheck,\n\t\tautocorrect,\n\t\tautocapitalize,\n\t\twrapLines,\n\t\tallowNewline,\n\t\telement,\n\t\tcontentStyle,\n\t\tshouldSimulateTextEffects,\n\t\thandleBlur,\n\t\thandleKeydown,\n\t\thandleKeyup,\n\t\thandleCompositionEnd,\n\t\thandleInput,\n\t\thandlePaste,\n\t\tremoveTextEffects,\n\t\ttextFormat,\n\t\tformatInput,\n\t\tformatPaste,\n\t\tstyles,\n\t\toninput,\n\t\tenableTextStyleShortcuts,\n\t\tconfirm,\n\t\tfocus,\n\t\tselect,\n\t\tisReady,\n\t\tpre_binding\n\t];\n}\n\nclass ContentEditable extends SvelteComponent {\n\tconstructor(options) {\n\t\tsuper();\n\n\t\tinit(\n\t\t\tthis,\n\t\t\toptions,\n\t\t\tinstance$g,\n\t\t\tcreate_fragment$g,\n\t\t\tsafe_not_equal,\n\t\t\t{\n\t\t\t\tspellcheck: 1,\n\t\t\t\tautocorrect: 2,\n\t\t\t\tautocapitalize: 3,\n\t\t\t\twrapLines: 4,\n\t\t\t\tallowNewline: 5,\n\t\t\t\ttextFormat: 16,\n\t\t\t\tformatInput: 17,\n\t\t\t\tformatPaste: 18,\n\t\t\t\tstyles: 19,\n\t\t\t\tinnerHTML: 0,\n\t\t\t\toninput: 20,\n\t\t\t\tenableTextStyleShortcuts: 21,\n\t\t\t\tconfirm: 22,\n\t\t\t\tfocus: 23,\n\t\t\t\tselect: 24\n\t\t\t},\n\t\t\tnull,\n\t\t\t[-1, -1]\n\t\t);\n\t}\n\n\tget spellcheck() {\n\t\treturn this.$$.ctx[1];\n\t}\n\n\tset spellcheck(spellcheck) {\n\t\tthis.$$set({ spellcheck });\n\t\tflush();\n\t}\n\n\tget autocorrect() {\n\t\treturn this.$$.ctx[2];\n\t}\n\n\tset autocorrect(autocorrect) {\n\t\tthis.$$set({ autocorrect });\n\t\tflush();\n\t}\n\n\tget autocapitalize() {\n\t\treturn this.$$.ctx[3];\n\t}\n\n\tset autocapitalize(autocapitalize) {\n\t\tthis.$$set({ autocapitalize });\n\t\tflush();\n\t}\n\n\tget wrapLines() {\n\t\treturn this.$$.ctx[4];\n\t}\n\n\tset wrapLines(wrapLines) {\n\t\tthis.$$set({ wrapLines });\n\t\tflush();\n\t}\n\n\tget allowNewline() {\n\t\treturn this.$$.ctx[5];\n\t}\n\n\tset allowNewline(allowNewline) {\n\t\tthis.$$set({ allowNewline });\n\t\tflush();\n\t}\n\n\tget textFormat() {\n\t\treturn this.$$.ctx[16];\n\t}\n\n\tset textFormat(textFormat) {\n\t\tthis.$$set({ textFormat });\n\t\tflush();\n\t}\n\n\tget formatInput() {\n\t\treturn this.$$.ctx[17];\n\t}\n\n\tset formatInput(formatInput) {\n\t\tthis.$$set({ formatInput });\n\t\tflush();\n\t}\n\n\tget formatPaste() {\n\t\treturn this.$$.ctx[18];\n\t}\n\n\tset formatPaste(formatPaste) {\n\t\tthis.$$set({ formatPaste });\n\t\tflush();\n\t}\n\n\tget styles() {\n\t\treturn this.$$.ctx[19];\n\t}\n\n\tset styles(styles) {\n\t\tthis.$$set({ styles });\n\t\tflush();\n\t}\n\n\tget innerHTML() {\n\t\treturn this.$$.ctx[0];\n\t}\n\n\tset innerHTML(innerHTML) {\n\t\tthis.$$set({ innerHTML });\n\t\tflush();\n\t}\n\n\tget oninput() {\n\t\treturn this.$$.ctx[20];\n\t}\n\n\tset oninput(oninput) {\n\t\tthis.$$set({ oninput });\n\t\tflush();\n\t}\n\n\tget enableTextStyleShortcuts() {\n\t\treturn this.$$.ctx[21];\n\t}\n\n\tset enableTextStyleShortcuts(enableTextStyleShortcuts) {\n\t\tthis.$$set({ enableTextStyleShortcuts });\n\t\tflush();\n\t}\n\n\tget confirm() {\n\t\treturn this.$$.ctx[22];\n\t}\n\n\tget focus() {\n\t\treturn this.$$.ctx[23];\n\t}\n\n\tget select() {\n\t\treturn this.$$.ctx[24];\n\t}\n}\n\nconst DefaultFormattingTags = ['i', 'b', 'u', 'strike'].map((tag) => ({\n tag,\n tagOpen: new RegExp(`<${tag}>`, 'g'),\n tagClose: new RegExp(`<\\/${tag}>`, 'g'),\n placeholderOpen: new RegExp(`___${tag}O___`, 'g'),\n placeholderClose: new RegExp(`___${tag}C___`, 'g'),\n}));\nconst replaceFormattingTags = (str, tags = DefaultFormattingTags) => {\n tags.forEach(({ tag, tagOpen, tagClose }) => {\n str = str.replace(tagOpen, `___${tag}O___`).replace(tagClose, `___${tag}C___`);\n });\n return str;\n};\nconst restoreFormattingTags = (str, tags = DefaultFormattingTags) => {\n tags.forEach(({ tag, placeholderOpen, placeholderClose }) => {\n str = str.replace(placeholderOpen, `<${tag}>`).replace(placeholderClose, `${tag}>`);\n });\n return str;\n};\nvar textToHTML = (text) => {\n // temporary replace bold italic\n text = replaceFormattingTags(text);\n // now encode\n text = text\n // collapse space sequences\n .replace(/ {2,}/g, ' ')\n // handle non breaking space characters\n .replace(/\\u00a0/g, ' ')\n // lines\n .split('\\n')\n // to br's\n .join('
');\n // put back bold and italic\n text = restoreFormattingTags(text);\n return text;\n};\n\nvar htmlToText = (html) => {\n const lines = replaceDivWithBr(html).split(/
|
/g);\n return lines\n .join('\\n')\n .replace(/ /g, String.fromCharCode(160))\n .replace(/&/g, '&');\n};\n\nvar fromPercentage = (value, context) => isString(value) ? (parseFloat(value) / 100) * context : value;\n\nvar toPercentagePoint = (point, context) => ({\n x: toPercentage(point.x, context.width),\n y: toPercentage(point.y, context.height),\n});\n\n/* src/core/ui/components/ShapeLayoutEditor.svelte generated by Svelte v3.52.0 */\n\nfunction get_each_context$1(ctx, list, i) {\n\tconst child_ctx = ctx.slice();\n\tchild_ctx[318] = list[i];\n\tchild_ctx[320] = i;\n\treturn child_ctx;\n}\n\n// (5124:12) {#each shapeNavList as item, index (item.id)}\nfunction create_each_block$1(key_1, ctx) {\n\tlet li;\n\tlet button;\n\tlet colorpreview;\n\tlet t0;\n\tlet span;\n\tlet t1_value = /*item*/ ctx[318].name + \"\";\n\tlet t1;\n\tlet button_aria_label_value;\n\tlet t2;\n\tlet current;\n\tlet mounted;\n\tlet dispose;\n\n\tcolorpreview = new ColorPreview({\n\t\t\tprops: { color: /*item*/ ctx[318].color }\n\t\t});\n\n\tfunction click_handler() {\n\t\treturn /*click_handler*/ ctx[198](/*index*/ ctx[320]);\n\t}\n\n\treturn {\n\t\tkey: key_1,\n\t\tfirst: null,\n\t\tc() {\n\t\t\tli = element(\"li\");\n\t\t\tbutton = element(\"button\");\n\t\t\tcreate_component(colorpreview.$$.fragment);\n\t\t\tt0 = space();\n\t\t\tspan = element(\"span\");\n\t\t\tt1 = text(t1_value);\n\t\t\tt2 = space();\n\t\t\tattr(button, \"class\", \"PinturaShapeListItem\");\n\t\t\tattr(button, \"type\", \"button\");\n\t\t\tattr(button, \"aria-label\", button_aria_label_value = \"Select shape \" + /*item*/ ctx[318].name);\n\t\t\tthis.first = li;\n\t\t},\n\t\tm(target, anchor) {\n\t\t\tinsert(target, li, anchor);\n\t\t\tappend(li, button);\n\t\t\tmount_component(colorpreview, button, null);\n\t\t\tappend(button, t0);\n\t\t\tappend(button, span);\n\t\t\tappend(span, t1);\n\t\t\tappend(li, t2);\n\t\t\tcurrent = true;\n\n\t\t\tif (!mounted) {\n\t\t\t\tdispose = listen(button, \"click\", click_handler);\n\t\t\t\tmounted = true;\n\t\t\t}\n\t\t},\n\t\tp(new_ctx, dirty) {\n\t\t\tctx = new_ctx;\n\t\t\tconst colorpreview_changes = {};\n\t\t\tif (dirty[0] & /*shapeNavList*/ 8388608) colorpreview_changes.color = /*item*/ ctx[318].color;\n\t\t\tcolorpreview.$set(colorpreview_changes);\n\t\t\tif ((!current || dirty[0] & /*shapeNavList*/ 8388608) && t1_value !== (t1_value = /*item*/ ctx[318].name + \"\")) set_data(t1, t1_value);\n\n\t\t\tif (!current || dirty[0] & /*shapeNavList*/ 8388608 && button_aria_label_value !== (button_aria_label_value = \"Select shape \" + /*item*/ ctx[318].name)) {\n\t\t\t\tattr(button, \"aria-label\", button_aria_label_value);\n\t\t\t}\n\t\t},\n\t\ti(local) {\n\t\t\tif (current) return;\n\t\t\ttransition_in(colorpreview.$$.fragment, local);\n\t\t\tcurrent = true;\n\t\t},\n\t\to(local) {\n\t\t\ttransition_out(colorpreview.$$.fragment, local);\n\t\t\tcurrent = false;\n\t\t},\n\t\td(detaching) {\n\t\t\tif (detaching) detach(li);\n\t\t\tdestroy_component(colorpreview);\n\t\t\tmounted = false;\n\t\t\tdispose();\n\t\t}\n\t};\n}\n\n// (5140:4) {#if shouldRenderShapeManipulator}\nfunction create_if_block_4$2(ctx) {\n\tlet shapemanipulator;\n\tlet current;\n\n\tshapemanipulator = new ShapeManipulator({\n\t\t\tprops: {\n\t\t\t\tvisible: true,\n\t\t\t\tpoints: /*shapeManipulatorPoints*/ ctx[14],\n\t\t\t\tbounds: /*utilRect*/ ctx[4],\n\t\t\t\trotatorPoint: /*shapeManipulatorRotationPointPosition*/ ctx[31],\n\t\t\t\tscalarPoint: /*shapeManipulatorScalarPointPosition*/ ctx[30],\n\t\t\t\tselectedPoint: /*shapeSelectedPointIndex*/ ctx[19],\n\t\t\t\tenableDragEdges: /*allowEdgeControls*/ ctx[20],\n\t\t\t\tenableDragPoints: /*allowCornerControls*/ ctx[33],\n\t\t\t\tenableRotatePoints: /*allowRotateControls*/ ctx[18],\n\t\t\t\tenableScalePoints: /*allowScaleControls*/ ctx[16]\n\t\t\t}\n\t\t});\n\n\tshapemanipulator.$on(\"resizestart\", /*handleManipulatorResizeGrab*/ ctx[42]);\n\tshapemanipulator.$on(\"resizemove\", /*handleManipulatorResizeDrag*/ ctx[43]);\n\tshapemanipulator.$on(\"resizeend\", /*handleManipulatorResizeEnd*/ ctx[44]);\n\tshapemanipulator.$on(\"rotatestart\", /*handleManipulatorRotateGrab*/ ctx[45]);\n\tshapemanipulator.$on(\"rotatemove\", /*handleManipulatorRotateDrag*/ ctx[46]);\n\tshapemanipulator.$on(\"rotateend\", /*handleManipulatorRotateEnd*/ ctx[47]);\n\tshapemanipulator.$on(\"scalestart\", /*handleManipulatorScaleGrab*/ ctx[48]);\n\tshapemanipulator.$on(\"scalemove\", /*handleManipulatorScaleDrag*/ ctx[49]);\n\tshapemanipulator.$on(\"scaleend\", /*handleManipulatorScaleEnd*/ ctx[50]);\n\n\treturn {\n\t\tc() {\n\t\t\tcreate_component(shapemanipulator.$$.fragment);\n\t\t},\n\t\tm(target, anchor) {\n\t\t\tmount_component(shapemanipulator, target, anchor);\n\t\t\tcurrent = true;\n\t\t},\n\t\tp(ctx, dirty) {\n\t\t\tconst shapemanipulator_changes = {};\n\t\t\tif (dirty[0] & /*shapeManipulatorPoints*/ 16384) shapemanipulator_changes.points = /*shapeManipulatorPoints*/ ctx[14];\n\t\t\tif (dirty[0] & /*utilRect*/ 16) shapemanipulator_changes.bounds = /*utilRect*/ ctx[4];\n\t\t\tif (dirty[1] & /*shapeManipulatorRotationPointPosition*/ 1) shapemanipulator_changes.rotatorPoint = /*shapeManipulatorRotationPointPosition*/ ctx[31];\n\t\t\tif (dirty[0] & /*shapeManipulatorScalarPointPosition*/ 1073741824) shapemanipulator_changes.scalarPoint = /*shapeManipulatorScalarPointPosition*/ ctx[30];\n\t\t\tif (dirty[0] & /*shapeSelectedPointIndex*/ 524288) shapemanipulator_changes.selectedPoint = /*shapeSelectedPointIndex*/ ctx[19];\n\t\t\tif (dirty[0] & /*allowEdgeControls*/ 1048576) shapemanipulator_changes.enableDragEdges = /*allowEdgeControls*/ ctx[20];\n\t\t\tif (dirty[1] & /*allowCornerControls*/ 4) shapemanipulator_changes.enableDragPoints = /*allowCornerControls*/ ctx[33];\n\t\t\tif (dirty[0] & /*allowRotateControls*/ 262144) shapemanipulator_changes.enableRotatePoints = /*allowRotateControls*/ ctx[18];\n\t\t\tif (dirty[0] & /*allowScaleControls*/ 65536) shapemanipulator_changes.enableScalePoints = /*allowScaleControls*/ ctx[16];\n\t\t\tshapemanipulator.$set(shapemanipulator_changes);\n\t\t},\n\t\ti(local) {\n\t\t\tif (current) return;\n\t\t\ttransition_in(shapemanipulator.$$.fragment, local);\n\t\t\tcurrent = true;\n\t\t},\n\t\to(local) {\n\t\t\ttransition_out(shapemanipulator.$$.fragment, local);\n\t\t\tcurrent = false;\n\t\t},\n\t\td(detaching) {\n\t\t\tdestroy_component(shapemanipulator, detaching);\n\t\t}\n\t};\n}\n\n// (5164:4) {#if shouldRenderTextInput}\nfunction create_if_block_1$3(ctx) {\n\tlet current_block_type_index;\n\tlet if_block;\n\tlet if_block_anchor;\n\tlet current;\n\tconst if_block_creators = [create_if_block_2$3, create_if_block_3$2];\n\tconst if_blocks = [];\n\n\tfunction select_block_type(ctx, dirty) {\n\t\tif (/*textInputMode*/ ctx[5] === 'modal') return 0;\n\t\tif (/*textInputMode*/ ctx[5] === 'inline') return 1;\n\t\treturn -1;\n\t}\n\n\tif (~(current_block_type_index = select_block_type(ctx))) {\n\t\tif_block = if_blocks[current_block_type_index] = if_block_creators[current_block_type_index](ctx);\n\t}\n\n\treturn {\n\t\tc() {\n\t\t\tif (if_block) if_block.c();\n\t\t\tif_block_anchor = empty();\n\t\t},\n\t\tm(target, anchor) {\n\t\t\tif (~current_block_type_index) {\n\t\t\t\tif_blocks[current_block_type_index].m(target, anchor);\n\t\t\t}\n\n\t\t\tinsert(target, if_block_anchor, anchor);\n\t\t\tcurrent = true;\n\t\t},\n\t\tp(ctx, dirty) {\n\t\t\tlet previous_block_index = current_block_type_index;\n\t\t\tcurrent_block_type_index = select_block_type(ctx);\n\n\t\t\tif (current_block_type_index === previous_block_index) {\n\t\t\t\tif (~current_block_type_index) {\n\t\t\t\t\tif_blocks[current_block_type_index].p(ctx, dirty);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tif (if_block) {\n\t\t\t\t\tgroup_outros();\n\n\t\t\t\t\ttransition_out(if_blocks[previous_block_index], 1, 1, () => {\n\t\t\t\t\t\tif_blocks[previous_block_index] = null;\n\t\t\t\t\t});\n\n\t\t\t\t\tcheck_outros();\n\t\t\t\t}\n\n\t\t\t\tif (~current_block_type_index) {\n\t\t\t\t\tif_block = if_blocks[current_block_type_index];\n\n\t\t\t\t\tif (!if_block) {\n\t\t\t\t\t\tif_block = if_blocks[current_block_type_index] = if_block_creators[current_block_type_index](ctx);\n\t\t\t\t\t\tif_block.c();\n\t\t\t\t\t} else {\n\t\t\t\t\t\tif_block.p(ctx, dirty);\n\t\t\t\t\t}\n\n\t\t\t\t\ttransition_in(if_block, 1);\n\t\t\t\t\tif_block.m(if_block_anchor.parentNode, if_block_anchor);\n\t\t\t\t} else {\n\t\t\t\t\tif_block = null;\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\ti(local) {\n\t\t\tif (current) return;\n\t\t\ttransition_in(if_block);\n\t\t\tcurrent = true;\n\t\t},\n\t\to(local) {\n\t\t\ttransition_out(if_block);\n\t\t\tcurrent = false;\n\t\t},\n\t\td(detaching) {\n\t\t\tif (~current_block_type_index) {\n\t\t\t\tif_blocks[current_block_type_index].d(detaching);\n\t\t\t}\n\n\t\t\tif (detaching) detach(if_block_anchor);\n\t\t}\n\t};\n}\n\n// (5208:45) \nfunction create_if_block_3$2(ctx) {\n\tlet div;\n\tlet contenteditable;\n\tlet styleable_action;\n\tlet current;\n\tlet mounted;\n\tlet dispose;\n\n\tlet contenteditable_props = {\n\t\tformatInput: /*formatContentEditable*/ ctx[52],\n\t\twrapLines: !!/*activeMarkupComputed*/ ctx[15].width,\n\t\ttextFormat: /*activeMarkupComputed*/ ctx[15].format,\n\t\tenableTextStyleShortcuts: /*enableTextStyleControls*/ ctx[6],\n\t\tallowNewline: /*shapeAllowNewline*/ ctx[32],\n\t\tstyles: /*textInputTextStyles*/ ctx[29]\n\t};\n\n\tcontenteditable = new ContentEditable({ props: contenteditable_props });\n\t/*contenteditable_binding*/ ctx[202](contenteditable);\n\tcontenteditable.$on(\"input\", /*handleTextInput*/ ctx[53]);\n\tcontenteditable.$on(\"keyup\", /*handleTextInputKeyUp*/ ctx[57]);\n\tcontenteditable.$on(\"cancel\", /*handleTextCancel*/ ctx[59]);\n\tcontenteditable.$on(\"confirm\", /*handleTextConfirm*/ ctx[58]);\n\n\treturn {\n\t\tc() {\n\t\t\tdiv = element(\"div\");\n\t\t\tcreate_component(contenteditable.$$.fragment);\n\t\t\tattr(div, \"class\", \"PinturaInlineInput\");\n\t\t},\n\t\tm(target, anchor) {\n\t\t\tinsert(target, div, anchor);\n\t\t\tmount_component(contenteditable, div, null);\n\t\t\tcurrent = true;\n\n\t\t\tif (!mounted) {\n\t\t\t\tdispose = [\n\t\t\t\t\taction_destroyer(styleable_action = styleable.call(null, div, /*textInputPositionStyles*/ ctx[28])),\n\t\t\t\t\tlisten(div, \"focusout\", /*focusout_handler*/ ctx[203])\n\t\t\t\t];\n\n\t\t\t\tmounted = true;\n\t\t\t}\n\t\t},\n\t\tp(ctx, dirty) {\n\t\t\tconst contenteditable_changes = {};\n\t\t\tif (dirty[0] & /*activeMarkupComputed*/ 32768) contenteditable_changes.wrapLines = !!/*activeMarkupComputed*/ ctx[15].width;\n\t\t\tif (dirty[0] & /*activeMarkupComputed*/ 32768) contenteditable_changes.textFormat = /*activeMarkupComputed*/ ctx[15].format;\n\t\t\tif (dirty[0] & /*enableTextStyleControls*/ 64) contenteditable_changes.enableTextStyleShortcuts = /*enableTextStyleControls*/ ctx[6];\n\t\t\tif (dirty[1] & /*shapeAllowNewline*/ 2) contenteditable_changes.allowNewline = /*shapeAllowNewline*/ ctx[32];\n\t\t\tif (dirty[0] & /*textInputTextStyles*/ 536870912) contenteditable_changes.styles = /*textInputTextStyles*/ ctx[29];\n\t\t\tcontenteditable.$set(contenteditable_changes);\n\t\t\tif (styleable_action && is_function(styleable_action.update) && dirty[0] & /*textInputPositionStyles*/ 268435456) styleable_action.update.call(null, /*textInputPositionStyles*/ ctx[28]);\n\t\t},\n\t\ti(local) {\n\t\t\tif (current) return;\n\t\t\ttransition_in(contenteditable.$$.fragment, local);\n\t\t\tcurrent = true;\n\t\t},\n\t\to(local) {\n\t\t\ttransition_out(contenteditable.$$.fragment, local);\n\t\t\tcurrent = false;\n\t\t},\n\t\td(detaching) {\n\t\t\tif (detaching) detach(div);\n\t\t\t/*contenteditable_binding*/ ctx[202](null);\n\t\t\tdestroy_component(contenteditable);\n\t\t\tmounted = false;\n\t\t\trun_all(dispose);\n\t\t}\n\t};\n}\n\n// (5165:8) {#if textInputMode === 'modal'}\nfunction create_if_block_2$3(ctx) {\n\tlet inputform;\n\tlet current;\n\n\tinputform = new InputForm({\n\t\t\tprops: {\n\t\t\t\tpanelOffset: /*offset*/ ctx[2],\n\t\t\t\tonconfirm: /*handleTextConfirm*/ ctx[58],\n\t\t\t\toncancel: /*handleTextCancel*/ ctx[59],\n\t\t\t\tbuttonCancel: {\n\t\t\t\t\ticon: /*locale*/ ctx[7].shapeIconInputCancel,\n\t\t\t\t\tlabel: /*locale*/ ctx[7].shapeLabelInputCancel\n\t\t\t\t},\n\t\t\t\tbuttonConfirm: {\n\t\t\t\t\ticon: /*locale*/ ctx[7].shapeIconInputConfirm,\n\t\t\t\t\tlabel: /*locale*/ ctx[7].shapeLabelInputConfirm,\n\t\t\t\t\thideLabel: true\n\t\t\t\t},\n\t\t\t\twillHandleKeydown: /*func*/ ctx[201],\n\t\t\t\t$$slots: { default: [create_default_slot$7] },\n\t\t\t\t$$scope: { ctx }\n\t\t\t}\n\t\t});\n\n\treturn {\n\t\tc() {\n\t\t\tcreate_component(inputform.$$.fragment);\n\t\t},\n\t\tm(target, anchor) {\n\t\t\tmount_component(inputform, target, anchor);\n\t\t\tcurrent = true;\n\t\t},\n\t\tp(ctx, dirty) {\n\t\t\tconst inputform_changes = {};\n\t\t\tif (dirty[0] & /*offset*/ 4) inputform_changes.panelOffset = /*offset*/ ctx[2];\n\n\t\t\tif (dirty[0] & /*locale*/ 128) inputform_changes.buttonCancel = {\n\t\t\t\ticon: /*locale*/ ctx[7].shapeIconInputCancel,\n\t\t\t\tlabel: /*locale*/ ctx[7].shapeLabelInputCancel\n\t\t\t};\n\n\t\t\tif (dirty[0] & /*locale*/ 128) inputform_changes.buttonConfirm = {\n\t\t\t\ticon: /*locale*/ ctx[7].shapeIconInputConfirm,\n\t\t\t\tlabel: /*locale*/ ctx[7].shapeLabelInputConfirm,\n\t\t\t\thideLabel: true\n\t\t\t};\n\n\t\t\tif (dirty[0] & /*activeMarkup*/ 4096) inputform_changes.willHandleKeydown = /*func*/ ctx[201];\n\n\t\t\tif (dirty[0] & /*textInput, textInputText, textInputTextStyles*/ 671088896 | dirty[10] & /*$$scope*/ 2048) {\n\t\t\t\tinputform_changes.$$scope = { dirty, ctx };\n\t\t\t}\n\n\t\t\tinputform.$set(inputform_changes);\n\t\t},\n\t\ti(local) {\n\t\t\tif (current) return;\n\t\t\ttransition_in(inputform.$$.fragment, local);\n\t\t\tcurrent = true;\n\t\t},\n\t\to(local) {\n\t\t\ttransition_out(inputform.$$.fragment, local);\n\t\t\tcurrent = false;\n\t\t},\n\t\td(detaching) {\n\t\t\tdestroy_component(inputform, detaching);\n\t\t}\n\t};\n}\n\n// (5166:12)
{ const { key } = e; if (key === 'Enter') { // if is combo, always confirm if (isTextConfirmKeyCombination(e)) { return true; } // if can enter newlines a normal return doesn't confirm the text input if (activeMarkup.disableNewline === false) { return false; } } return true; }} >\nfunction create_default_slot$7(ctx) {\n\tlet textarea;\n\tlet styleable_action;\n\tlet mounted;\n\tlet dispose;\n\n\treturn {\n\t\tc() {\n\t\t\ttextarea = element(\"textarea\");\n\t\t\tattr(textarea, \"spellcheck\", \"false\");\n\t\t\tattr(textarea, \"autocorrect\", \"off\");\n\t\t\tattr(textarea, \"autocapitalize\", \"off\");\n\t\t},\n\t\tm(target, anchor) {\n\t\t\tinsert(target, textarea, anchor);\n\t\t\t/*textarea_binding*/ ctx[199](textarea);\n\t\t\tset_input_value(textarea, /*textInputText*/ ctx[27]);\n\n\t\t\tif (!mounted) {\n\t\t\t\tdispose = [\n\t\t\t\t\tlisten(textarea, \"keydown\", /*handleTextInputKeyDown*/ ctx[55]),\n\t\t\t\t\tlisten(textarea, \"keypress\", /*handleTextInputAttempt*/ ctx[54]),\n\t\t\t\t\tlisten(textarea, \"keyup\", /*handleTextInputKeyUp*/ ctx[57]),\n\t\t\t\t\tlisten(textarea, \"input\", /*handleTextInput*/ ctx[53]),\n\t\t\t\t\tlisten(textarea, \"input\", /*textarea_input_handler*/ ctx[200]),\n\t\t\t\t\taction_destroyer(styleable_action = styleable.call(null, textarea, /*textInputTextStyles*/ ctx[29]))\n\t\t\t\t];\n\n\t\t\t\tmounted = true;\n\t\t\t}\n\t\t},\n\t\tp(ctx, dirty) {\n\t\t\tif (dirty[0] & /*textInputText*/ 134217728) {\n\t\t\t\tset_input_value(textarea, /*textInputText*/ ctx[27]);\n\t\t\t}\n\n\t\t\tif (styleable_action && is_function(styleable_action.update) && dirty[0] & /*textInputTextStyles*/ 536870912) styleable_action.update.call(null, /*textInputTextStyles*/ ctx[29]);\n\t\t},\n\t\td(detaching) {\n\t\t\tif (detaching) detach(textarea);\n\t\t\t/*textarea_binding*/ ctx[199](null);\n\t\t\tmounted = false;\n\t\t\trun_all(dispose);\n\t\t}\n\t};\n}\n\n// (5234:4) {#if $markupControlsOpacity > 0}\nfunction create_if_block$5(ctx) {\n\tlet div;\n\tlet dynamiccomponenttree;\n\tlet current;\n\tlet mounted;\n\tlet dispose;\n\n\tdynamiccomponenttree = new DynamicComponentTree_1({\n\t\t\tprops: {\n\t\t\t\titems: /*shapeControls*/ ctx[24],\n\t\t\t\tkey: /*activeShapeId*/ ctx[11]\n\t\t\t}\n\t\t});\n\n\treturn {\n\t\tc() {\n\t\t\tdiv = element(\"div\");\n\t\t\tcreate_component(dynamiccomponenttree.$$.fragment);\n\t\t\tattr(div, \"class\", \"PinturaShapeControls\");\n\t\t\tset_style(div, \"transform\", /*styleMarkupControlTransform*/ ctx[26]);\n\t\t\tset_style(div, \"opacity\", /*styleMarkupControlOpacity*/ ctx[25]);\n\t\t},\n\t\tm(target, anchor) {\n\t\t\tinsert(target, div, anchor);\n\t\t\tmount_component(dynamiccomponenttree, div, null);\n\t\t\tcurrent = true;\n\n\t\t\tif (!mounted) {\n\t\t\t\tdispose = [\n\t\t\t\t\tlisten(div, \"measure\", /*measure_handler_1*/ ctx[204]),\n\t\t\t\t\taction_destroyer(measurable.call(null, div))\n\t\t\t\t];\n\n\t\t\t\tmounted = true;\n\t\t\t}\n\t\t},\n\t\tp(ctx, dirty) {\n\t\t\tconst dynamiccomponenttree_changes = {};\n\t\t\tif (dirty[0] & /*shapeControls*/ 16777216) dynamiccomponenttree_changes.items = /*shapeControls*/ ctx[24];\n\t\t\tif (dirty[0] & /*activeShapeId*/ 2048) dynamiccomponenttree_changes.key = /*activeShapeId*/ ctx[11];\n\t\t\tdynamiccomponenttree.$set(dynamiccomponenttree_changes);\n\n\t\t\tif (dirty[0] & /*styleMarkupControlTransform*/ 67108864) {\n\t\t\t\tset_style(div, \"transform\", /*styleMarkupControlTransform*/ ctx[26]);\n\t\t\t}\n\n\t\t\tif (dirty[0] & /*styleMarkupControlOpacity*/ 33554432) {\n\t\t\t\tset_style(div, \"opacity\", /*styleMarkupControlOpacity*/ ctx[25]);\n\t\t\t}\n\t\t},\n\t\ti(local) {\n\t\t\tif (current) return;\n\t\t\ttransition_in(dynamiccomponenttree.$$.fragment, local);\n\t\t\tcurrent = true;\n\t\t},\n\t\to(local) {\n\t\t\ttransition_out(dynamiccomponenttree.$$.fragment, local);\n\t\t\tcurrent = false;\n\t\t},\n\t\td(detaching) {\n\t\t\tif (detaching) detach(div);\n\t\t\tdestroy_component(dynamiccomponenttree);\n\t\t\tmounted = false;\n\t\t\trun_all(dispose);\n\t\t}\n\t};\n}\n\nfunction create_fragment$f(ctx) {\n\tlet div;\n\tlet nav;\n\tlet ul;\n\tlet each_blocks = [];\n\tlet each_1_lookup = new Map();\n\tlet t0;\n\tlet t1;\n\tlet t2;\n\tlet interactable_action;\n\tlet current;\n\tlet mounted;\n\tlet dispose;\n\tlet each_value = /*shapeNavList*/ ctx[23];\n\tconst get_key = ctx => /*item*/ ctx[318].id;\n\n\tfor (let i = 0; i < each_value.length; i += 1) {\n\t\tlet child_ctx = get_each_context$1(ctx, each_value, i);\n\t\tlet key = get_key(child_ctx);\n\t\teach_1_lookup.set(key, each_blocks[i] = create_each_block$1(key, child_ctx));\n\t}\n\n\tlet if_block0 = /*shouldRenderShapeManipulator*/ ctx[17] && create_if_block_4$2(ctx);\n\tlet if_block1 = /*shouldRenderTextInput*/ ctx[13] && create_if_block_1$3(ctx);\n\tlet if_block2 = /*$markupControlsOpacity*/ ctx[21] > 0 && create_if_block$5(ctx);\n\n\treturn {\n\t\tc() {\n\t\t\tdiv = element(\"div\");\n\t\t\tnav = element(\"nav\");\n\t\t\tul = element(\"ul\");\n\n\t\t\tfor (let i = 0; i < each_blocks.length; i += 1) {\n\t\t\t\teach_blocks[i].c();\n\t\t\t}\n\n\t\t\tt0 = space();\n\t\t\tif (if_block0) if_block0.c();\n\t\t\tt1 = space();\n\t\t\tif (if_block1) if_block1.c();\n\t\t\tt2 = space();\n\t\t\tif (if_block2) if_block2.c();\n\t\t\tattr(nav, \"class\", \"PinturaShapeList\");\n\t\t\tattr(nav, \"data-visible\", /*showShapeList*/ ctx[22]);\n\t\t\tattr(div, \"class\", \"PinturaShapeEditor\");\n\t\t\tattr(div, \"tabindex\", \"0\");\n\t\t},\n\t\tm(target, anchor) {\n\t\t\tinsert(target, div, anchor);\n\t\t\tappend(div, nav);\n\t\t\tappend(nav, ul);\n\n\t\t\tfor (let i = 0; i < each_blocks.length; i += 1) {\n\t\t\t\teach_blocks[i].m(ul, null);\n\t\t\t}\n\n\t\t\tappend(div, t0);\n\t\t\tif (if_block0) if_block0.m(div, null);\n\t\t\tappend(div, t1);\n\t\t\tif (if_block1) if_block1.m(div, null);\n\t\t\tappend(div, t2);\n\t\t\tif (if_block2) if_block2.m(div, null);\n\t\t\t/*div_binding*/ ctx[205](div);\n\t\t\tcurrent = true;\n\n\t\t\tif (!mounted) {\n\t\t\t\tdispose = [\n\t\t\t\t\tlisten(nav, \"focusin\", /*handleFocusIn*/ ctx[62]),\n\t\t\t\t\tlisten(nav, \"focusout\", /*handleFocusOut*/ ctx[63]),\n\t\t\t\t\tlisten(div, \"keydown\", function () {\n\t\t\t\t\t\tif (is_function(/*disabled*/ ctx[0] ? noop$1 : /*handleKeyDown*/ ctx[51])) (/*disabled*/ ctx[0] ? noop$1 : /*handleKeyDown*/ ctx[51]).apply(this, arguments);\n\t\t\t\t\t}),\n\t\t\t\t\tlisten(div, \"nudge\", function () {\n\t\t\t\t\t\tif (is_function(/*disabled*/ ctx[0] ? noop$1 : /*handleNudge*/ ctx[61])) (/*disabled*/ ctx[0] ? noop$1 : /*handleNudge*/ ctx[61]).apply(this, arguments);\n\t\t\t\t\t}),\n\t\t\t\t\tlisten(div, \"measure\", /*measure_handler*/ ctx[197]),\n\t\t\t\t\tlisten(div, \"pointermove\", function () {\n\t\t\t\t\t\tif (is_function(/*disabled*/ ctx[0]\n\t\t\t\t\t\t? noop$1\n\t\t\t\t\t\t: /*handlePointerMove*/ ctx[64])) (/*disabled*/ ctx[0]\n\t\t\t\t\t\t? noop$1\n\t\t\t\t\t\t: /*handlePointerMove*/ ctx[64]).apply(this, arguments);\n\t\t\t\t\t}),\n\t\t\t\t\tlisten(div, \"pointerleave\", function () {\n\t\t\t\t\t\tif (is_function(/*disabled*/ ctx[0]\n\t\t\t\t\t\t? noop$1\n\t\t\t\t\t\t: /*handlePointerLeave*/ ctx[65])) (/*disabled*/ ctx[0]\n\t\t\t\t\t\t? noop$1\n\t\t\t\t\t\t: /*handlePointerLeave*/ ctx[65]).apply(this, arguments);\n\t\t\t\t\t}),\n\t\t\t\t\tlisten(div, \"interactionstart\", function () {\n\t\t\t\t\t\tif (is_function(/*disabled*/ ctx[0]\n\t\t\t\t\t\t? noop$1\n\t\t\t\t\t\t: /*handleInteractionStart*/ ctx[36])) (/*disabled*/ ctx[0]\n\t\t\t\t\t\t? noop$1\n\t\t\t\t\t\t: /*handleInteractionStart*/ ctx[36]).apply(this, arguments);\n\t\t\t\t\t}),\n\t\t\t\t\tlisten(div, \"interactionupdate\", function () {\n\t\t\t\t\t\tif (is_function(/*disabled*/ ctx[0]\n\t\t\t\t\t\t? noop$1\n\t\t\t\t\t\t: /*handleInteractionUpdate*/ ctx[38])) (/*disabled*/ ctx[0]\n\t\t\t\t\t\t? noop$1\n\t\t\t\t\t\t: /*handleInteractionUpdate*/ ctx[38]).apply(this, arguments);\n\t\t\t\t\t}),\n\t\t\t\t\tlisten(div, \"interactioncancel\", function () {\n\t\t\t\t\t\tif (is_function(/*disabled*/ ctx[0]\n\t\t\t\t\t\t? noop$1\n\t\t\t\t\t\t: /*handleInteractionCancel*/ ctx[37])) (/*disabled*/ ctx[0]\n\t\t\t\t\t\t? noop$1\n\t\t\t\t\t\t: /*handleInteractionCancel*/ ctx[37]).apply(this, arguments);\n\t\t\t\t\t}),\n\t\t\t\t\tlisten(div, \"interactionrelease\", function () {\n\t\t\t\t\t\tif (is_function(/*disabled*/ ctx[0]\n\t\t\t\t\t\t? noop$1\n\t\t\t\t\t\t: /*handleInteractionRelease*/ ctx[39])) (/*disabled*/ ctx[0]\n\t\t\t\t\t\t? noop$1\n\t\t\t\t\t\t: /*handleInteractionRelease*/ ctx[39]).apply(this, arguments);\n\t\t\t\t\t}),\n\t\t\t\t\tlisten(div, \"interactionend\", function () {\n\t\t\t\t\t\tif (is_function(/*disabled*/ ctx[0]\n\t\t\t\t\t\t? noop$1\n\t\t\t\t\t\t: /*handleInteractionEnd*/ ctx[40])) (/*disabled*/ ctx[0]\n\t\t\t\t\t\t? noop$1\n\t\t\t\t\t\t: /*handleInteractionEnd*/ ctx[40]).apply(this, arguments);\n\t\t\t\t\t}),\n\t\t\t\t\taction_destroyer(measurable.call(null, div)),\n\t\t\t\t\taction_destroyer(nudgeable.call(null, div)),\n\t\t\t\t\taction_destroyer(interactable_action = interactable.call(null, div, {\n\t\t\t\t\t\tdrag: true,\n\t\t\t\t\t\tinertia: true,\n\t\t\t\t\t\tmultiTouch: false,\n\t\t\t\t\t\tobserveKeys: true,\n\t\t\t\t\t\tshouldStartInteraction: interactable_function,\n\t\t\t\t\t\tgetEventPosition: /*interactable_function_1*/ ctx[206]\n\t\t\t\t\t}))\n\t\t\t\t];\n\n\t\t\t\tmounted = true;\n\t\t\t}\n\t\t},\n\t\tp(new_ctx, dirty) {\n\t\t\tctx = new_ctx;\n\n\t\t\tif (dirty[0] & /*shapeNavList*/ 8388608 | dirty[2] & /*selectShapeByIndex*/ 16) {\n\t\t\t\teach_value = /*shapeNavList*/ ctx[23];\n\t\t\t\tgroup_outros();\n\t\t\t\teach_blocks = update_keyed_each(each_blocks, dirty, get_key, 1, ctx, each_value, each_1_lookup, ul, outro_and_destroy_block, create_each_block$1, null, get_each_context$1);\n\t\t\t\tcheck_outros();\n\t\t\t}\n\n\t\t\tif (!current || dirty[0] & /*showShapeList*/ 4194304) {\n\t\t\t\tattr(nav, \"data-visible\", /*showShapeList*/ ctx[22]);\n\t\t\t}\n\n\t\t\tif (/*shouldRenderShapeManipulator*/ ctx[17]) {\n\t\t\t\tif (if_block0) {\n\t\t\t\t\tif_block0.p(ctx, dirty);\n\n\t\t\t\t\tif (dirty[0] & /*shouldRenderShapeManipulator*/ 131072) {\n\t\t\t\t\t\ttransition_in(if_block0, 1);\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tif_block0 = create_if_block_4$2(ctx);\n\t\t\t\t\tif_block0.c();\n\t\t\t\t\ttransition_in(if_block0, 1);\n\t\t\t\t\tif_block0.m(div, t1);\n\t\t\t\t}\n\t\t\t} else if (if_block0) {\n\t\t\t\tgroup_outros();\n\n\t\t\t\ttransition_out(if_block0, 1, 1, () => {\n\t\t\t\t\tif_block0 = null;\n\t\t\t\t});\n\n\t\t\t\tcheck_outros();\n\t\t\t}\n\n\t\t\tif (/*shouldRenderTextInput*/ ctx[13]) {\n\t\t\t\tif (if_block1) {\n\t\t\t\t\tif_block1.p(ctx, dirty);\n\n\t\t\t\t\tif (dirty[0] & /*shouldRenderTextInput*/ 8192) {\n\t\t\t\t\t\ttransition_in(if_block1, 1);\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tif_block1 = create_if_block_1$3(ctx);\n\t\t\t\t\tif_block1.c();\n\t\t\t\t\ttransition_in(if_block1, 1);\n\t\t\t\t\tif_block1.m(div, t2);\n\t\t\t\t}\n\t\t\t} else if (if_block1) {\n\t\t\t\tgroup_outros();\n\n\t\t\t\ttransition_out(if_block1, 1, 1, () => {\n\t\t\t\t\tif_block1 = null;\n\t\t\t\t});\n\n\t\t\t\tcheck_outros();\n\t\t\t}\n\n\t\t\tif (/*$markupControlsOpacity*/ ctx[21] > 0) {\n\t\t\t\tif (if_block2) {\n\t\t\t\t\tif_block2.p(ctx, dirty);\n\n\t\t\t\t\tif (dirty[0] & /*$markupControlsOpacity*/ 2097152) {\n\t\t\t\t\t\ttransition_in(if_block2, 1);\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tif_block2 = create_if_block$5(ctx);\n\t\t\t\t\tif_block2.c();\n\t\t\t\t\ttransition_in(if_block2, 1);\n\t\t\t\t\tif_block2.m(div, null);\n\t\t\t\t}\n\t\t\t} else if (if_block2) {\n\t\t\t\tgroup_outros();\n\n\t\t\t\ttransition_out(if_block2, 1, 1, () => {\n\t\t\t\t\tif_block2 = null;\n\t\t\t\t});\n\n\t\t\t\tcheck_outros();\n\t\t\t}\n\n\t\t\tif (interactable_action && is_function(interactable_action.update) && dirty[0] & /*rootRect*/ 8) interactable_action.update.call(null, {\n\t\t\t\tdrag: true,\n\t\t\t\tinertia: true,\n\t\t\t\tmultiTouch: false,\n\t\t\t\tobserveKeys: true,\n\t\t\t\tshouldStartInteraction: interactable_function,\n\t\t\t\tgetEventPosition: /*interactable_function_1*/ ctx[206]\n\t\t\t});\n\t\t},\n\t\ti(local) {\n\t\t\tif (current) return;\n\n\t\t\tfor (let i = 0; i < each_value.length; i += 1) {\n\t\t\t\ttransition_in(each_blocks[i]);\n\t\t\t}\n\n\t\t\ttransition_in(if_block0);\n\t\t\ttransition_in(if_block1);\n\t\t\ttransition_in(if_block2);\n\t\t\tcurrent = true;\n\t\t},\n\t\to(local) {\n\t\t\tfor (let i = 0; i < each_blocks.length; i += 1) {\n\t\t\t\ttransition_out(each_blocks[i]);\n\t\t\t}\n\n\t\t\ttransition_out(if_block0);\n\t\t\ttransition_out(if_block1);\n\t\t\ttransition_out(if_block2);\n\t\t\tcurrent = false;\n\t\t},\n\t\td(detaching) {\n\t\t\tif (detaching) detach(div);\n\n\t\t\tfor (let i = 0; i < each_blocks.length; i += 1) {\n\t\t\t\teach_blocks[i].d();\n\t\t\t}\n\n\t\t\tif (if_block0) if_block0.d();\n\t\t\tif (if_block1) if_block1.d();\n\t\t\tif (if_block2) if_block2.d();\n\t\t\t/*div_binding*/ ctx[205](null);\n\t\t\tmounted = false;\n\t\t\trun_all(dispose);\n\t\t}\n\t};\n}\n\nconst POLYGON_END_PATH_DISTANCE_SQUARED = 256;\nconst POLYGON_VERTEX_MIN_DISTANCE_SQUARED = 100;\nconst SHAPE_CONTROL_OFFSET = 20;\nconst MIN_TEXT_MARKUP_WIDTH = 10;\n\n// UI hover shapes\nconst HoverShapeId = 'markup-hover';\n\nconst interactable_function = (e, element) => {\n\treturn e.target === element || e.target.className === 'PinturaContentEditable';\n};\n\nfunction instance$f($$self, $$props, $$invalidate) {\n\tlet gridRect;\n\tlet activeMarkup;\n\tlet multiSelection;\n\tlet isMultiSelection;\n\tlet multiSelectionComputed;\n\tlet activeMarkupSelected;\n\tlet activeShapeIsDraft;\n\tlet activeShapeId;\n\tlet activeMarkupComputed;\n\tlet activeMarkupItemIsDraft;\n\tlet shapeActivePoints;\n\tlet multiSelectionShapePoints;\n\tlet allowResizeControls;\n\tlet allowRotateControls;\n\tlet allowScaleControls;\n\tlet allowEdgeControls;\n\tlet allowCornerControls;\n\tlet multiSelectionPoints;\n\tlet shouldRenderShapeManipulator;\n\tlet selectionScreenPoints;\n\tlet shapeManipulatorPoints;\n\tlet shapeSelectedPointIndex;\n\tlet hasSelectedShapePoint;\n\tlet shapeSelectedManipulatorPoint;\n\tlet shapeAllowNewline;\n\tlet shapeManipulatorRotationPoint;\n\tlet shapeManipulatorRotationPointPosition;\n\tlet shapeManipulatorScalarPoint;\n\tlet shapeManipulatorScalarPointPosition;\n\tlet shouldSnapPointer;\n\tlet isHoveringShape;\n\tlet isManagingShape;\n\tlet disableSnapping;\n\tlet isTextMarkupSelected;\n\tlet shouldRenderTextInput;\n\tlet textShapeOrigin;\n\tlet textShapeDisplayOrigin;\n\tlet textSizeDisplayOrigin;\n\tlet textRectDisplayOrigin;\n\tlet textInputText;\n\tlet activeTextShapeComputed;\n\tlet textInputTextStyles;\n\tlet textInputPositionStyles;\n\tlet caretColorRGBA;\n\tlet controlledMarkupItem;\n\tlet allowShapeFlip;\n\tlet allowShapeChangeTextLayout;\n\tlet allowShapeDuplicate;\n\tlet allowShapeRemove;\n\tlet allowShapeReorder;\n\tlet allowShapeInput;\n\tlet allowShapeAdjustOpacity;\n\tlet allowShapeStyleText;\n\tlet shouldShowMarkupControls;\n\tlet markupControlsAnchorPosition;\n\tlet shapeControlsPosition;\n\tlet styleMarkupControlTransform;\n\tlet styleMarkupControlOpacity;\n\tlet shapeControlsMode;\n\tlet shapeControls;\n\tlet shapeNavList;\n\n\tlet $shapes,\n\t\t$$unsubscribe_shapes = noop,\n\t\t$$subscribe_shapes = () => ($$unsubscribe_shapes(), $$unsubscribe_shapes = subscribe(shapes, $$value => $$invalidate(193, $shapes = $$value)), shapes);\n\n\tlet $markupControlsOpacity;\n\tlet $isAnimated;\n\tlet $keysPressedStored;\n\tlet $gridOpacity;\n\t$$self.$$.on_destroy.push(() => $$unsubscribe_shapes());\n\tlet { uid = getUniqueId() } = $$props;\n\tlet { ui } = $$props;\n\tlet { disabled = false } = $$props;\n\tlet { shapes } = $$props;\n\t$$subscribe_shapes();\n\tlet { selection } = $$props;\n\tlet { offset } = $$props;\n\tlet { contextRotation = 0 } = $$props;\n\tlet { contextFlipX = false } = $$props;\n\tlet { contextFlipY = false } = $$props;\n\tlet { contextZoom = 1 } = $$props;\n\tlet { active = false } = $$props;\n\tlet { opacity = 1 } = $$props;\n\tlet { parentRect } = $$props;\n\tlet { rootRect } = $$props;\n\tlet { utilRect } = $$props;\n\tlet { hoverColor } = $$props;\n\tlet { caretColor } = $$props;\n\tlet { gridColor } = $$props;\n\tlet { snapColor } = $$props;\n\tlet { textInputMode = 'inline' } = $$props;\n\tlet { oninteractionstart = noop$1 } = $$props;\n\tlet { oninteractionupdate = noop$1 } = $$props;\n\tlet { oninteractionrelease = noop$1 } = $$props;\n\tlet { oninteractionend = noop$1 } = $$props;\n\tlet { oninteractioncancel = noop$1 } = $$props;\n\tlet { onaddshape = noop$1 } = $$props;\n\tlet { onupdateshape = noop$1 } = $$props;\n\tlet { onselectshape = noop$1 } = $$props;\n\tlet { onblurshape = noop$1 } = $$props;\n\tlet { onremoveshape = noop$1 } = $$props;\n\tlet { ontapshape = noop$1 } = $$props;\n\tlet { onhovershape = noop$1 } = $$props;\n\tlet { ontriggerhistorywrite = noop$1 } = $$props;\n\tlet { onhovercanvas = noop$1 } = $$props;\n\tlet { ontapcanvas = noop$1 } = $$props;\n\tlet { onleavecanvas = noop$1 } = $$props;\n\tlet { beforeSelectShape = () => true } = $$props;\n\tlet { beforeDeselectShape = () => true } = $$props;\n\tlet { beforeRemoveShape = () => true } = $$props;\n\tlet { beforeUpdateShape = (shape, props, rect) => props } = $$props;\n\tlet { willRenderShapeControls = passthrough } = $$props;\n\tlet { willRenderShapeTextControls = passthrough } = $$props;\n\tlet { willStartInteraction = (position, rect) => true } = $$props;\n\tlet { mapEditorPointToImagePoint } = $$props;\n\tlet { mapImagePointToEditorPoint } = $$props;\n\tlet { eraseRadius = undefined } = $$props;\n\tlet { selectRadius = undefined } = $$props;\n\tlet { enableButtonFlipVertical = false } = $$props;\n\tlet { enableTapToAddText = true } = $$props;\n\tlet { enableMultiSelect = false } = $$props;\n\tlet { enableTextStyleControls = true } = $$props;\n\tlet { locale } = $$props;\n\tlet { snapThreshold = 0 } = $$props;\n\tlet { snapPointer = true } = $$props;\n\tlet { enableSnapToContext = true } = $$props;\n\tlet { gridSize = 0 } = $$props;\n\n\t// returns currently relevant snap targets, excludes \"excludedShape\" from targets\n\tconst getSnapTargets = excludedShape => {\n\t\t// no targets if not snapping\n\t\tif (snapThreshold === 0) return [];\n\n\t\t// create context\n\t\tlet contextShape;\n\n\t\tif (enableSnapToContext) {\n\t\t\tcontextShape = { ...parentRect, x: 0, y: 0 };\n\t\t}\n\n\t\treturn [\n\t\t\t// exclude interaction shape\n\t\t\t...$shapes.// don't include self\n\t\t\tfilter(shape => shape !== excludedShape).// don't include drafts\n\t\t\tfilter(shape => !shapeIsDraft(shape)).// exclude shapes that are not snap targets\n\t\t\tfilter(shape => shapeCanAcceptSnap(shape)).// only useful when we have computed shapes\n\t\t\tmap(shape => shapeComputeDisplay({ ...shape }, parentRect)),\n\t\t\t// also snap to context edges and center\n\t\t\tcontextShape\n\t\t].filter(Boolean);\n\t};\n\n\t// helper function to get snap options when resizing or translating shapes\n\tconst getSnapOptions = (snapThreshold, gridSize, gridRect, excludedShape) => {\n\t\t// no snapping\n\t\tif (snapThreshold === 0) return { snapTargets: [] };\n\n\t\t// let's define snap options\n\t\treturn {\n\t\t\tsnapThreshold,\n\t\t\tgridSize,\n\t\t\tgridRect,\n\t\t\tsnapTargets: getSnapTargets(excludedShape)\n\t\t};\n\t};\n\n\tconst isAnimated = getContext('isAnimated');\n\tcomponent_subscribe($$self, isAnimated, value => $$invalidate(194, $isAnimated = value));\n\tconst softKeyboardState = getContext('softKeyboardState');\n\tconst syncShapeCollection = collection => collection.set(get_store_value(collection));\n\tconst getShapeDraft = collection => get_store_value(collection).find(shapeIsDraft);\n\n\tconst addShapeDraft = (shape, collection) => {\n\t\t// collection already contains a shape, exit\n\t\tif (getShapeDraft(collection)) return;\n\n\t\t// turn shape intro draft\n\t\tshapeMakeDraft(shape);\n\n\t\t// add to collection\n\t\treturn addShape(shape, collection);\n\t};\n\n\tconst discardShapeDraft = collection => {\n\t\t// don't trigger update if no draft in array\n\t\tif (!getShapeDraft(collection)) return;\n\n\t\t// update!\n\t\tcollection.update(items => items.filter(shape => !shapeIsDraft(shape)));\n\t};\n\n\tconst confirmShapeDraft = collection => {\n\t\t// don't trigger update if no draft in array\n\t\tconst draft = getShapeDraft(collection);\n\n\t\tif (!draft) return;\n\t\tshapeMakeFinal(draft);\n\t\tsyncShapeCollection(collection);\n\t\treturn draft;\n\t};\n\n\tconst updateShapeDraft = (props, collection, update = (item, props) => ({ ...item, ...props })) => {\n\t\t// don't trigger update if no draft in array\n\t\tconst draft = getShapeDraft(collection);\n\n\t\tif (!draft) return;\n\n\t\tcollection.update(items => items.map(item => {\n\t\t\tif (!shapeIsDraft(item)) return item;\n\t\t\treturn update(item, props);\n\t\t}));\n\t};\n\n\tconst addShape = (shape, collection = shapes) => {\n\t\tcollection.update(items => [...items, shape]);\n\t\treturn shape;\n\t};\n\n\t// helpers\n\tconst updateShape = (shape, props, parentRect) => {\n\t\tconst propsToUpdate = beforeUpdateShape({ ...shape }, props, { ...parentRect });\n\t\tshapeUpdateProps(shape, propsToUpdate, parentRect);\n\t\treturn shape;\n\t};\n\n\tconst positionRelativeToAnchor = (anchor, target) => {\n\t\tconst length = vectorDistance(anchor, target);\n\t\tconst angle = vectorAngleBetween(anchor, target);\n\t\tconst angleSnapInterval = Math.PI / 8;\n\t\tconst angleSnapped = angleSnapInterval * Math.round(angle / angleSnapInterval) - contextRotation % angleSnapInterval;\n\t\ttarget.x = anchor.x + length * Math.cos(angleSnapped);\n\t\ttarget.y = anchor.y + length * Math.sin(angleSnapped);\n\t};\n\n\tconst shapeToPoly = (computedShape, resolution = 12) => {\n\t\t// rect to poly\n\t\tif (shapeIsRect(computedShape)) {\n\t\t\treturn rectRotate(computedShape, computedShape.rotation, rectCenter(computedShape));\n\t\t} else // text to poly\n\t\tif (shapeIsText(computedShape)) {\n\t\t\tconst computedRect = getMarkupShapeRect(computedShape);\n\t\t\treturn rectRotate(computedRect, computedShape.rotation, rectCenter(computedRect));\n\t\t} else // ellipse\n\t\tif (shapeIsEllipse(computedShape)) {\n\t\t\treturn ellipseToPolygon(vectorCreate(computedShape.x, computedShape.y), computedShape.rx, computedShape.ry, computedShape.rotation, computedShape.flipX, computedShape.flipY, resolution);\n\t\t}\n\n\t\treturn [];\n\t};\n\n\t//#region createShape eraseShape\n\tconst keysPressedStored = getContext('keysPressed');\n\n\tcomponent_subscribe($$self, keysPressedStored, value => $$invalidate(195, $keysPressedStored = value));\n\n\t// draw shape manipulator edges\n\tconst getShapeUpdateRotation = (rotation, flipX, flipY) => {\n\t\tif (rotation === 0) return rotation;\n\t\tif (flipX && flipY) return rotation;\n\t\tif (flipX) return -rotation;\n\t\tif (flipY) return -rotation;\n\t\treturn rotation;\n\t};\n\n\tconst setInteractionSnapLines = snapLines => {\n\t\tif (snapLines.x === null && snapLines.y === null) return clearInteractionSnapLines();\n\t\tif (snapLines.x) $$invalidate(149, interactionSnapLineX = mapEditorPointToImagePoint({ x: snapLines.x, y: 0 }).x);\n\t\tif (snapLines.y) $$invalidate(150, interactionSnapLineY = mapEditorPointToImagePoint({ x: 0, y: snapLines.y }).y);\n\t};\n\n\tconst clearInteractionSnapLines = () => {\n\t\t$$invalidate(149, interactionSnapLineX = null);\n\t\t$$invalidate(150, interactionSnapLineY = null);\n\t};\n\n\tlet interactionSnapLineX = null;\n\tlet interactionSnapLineY = null;\n\n\tconst createShape = (shapeDefault, options = {}) => {\n\n\t\tlet interactionOrigin;\n\t\tlet interactionRadius;\n\t\tlet interactionPosition;\n\t\tlet interactionPositions = [];\n\t\tlet isEllipse = shapeIsEllipse(shapeDefault);\n\t\tlet isText = shapeIsText(shapeDefault);\n\t\tlet isRelative = options.position === 'relative';\n\t\tconst shouldInterpolate = options.interpolateInput;\n\n\t\t// get snap targets\n\t\tconst snapTargets = getSnapTargets();\n\n\t\t// snap point to targets\n\t\tconst snapPoint = point => {\n\t\t\t// do nothing if doens't need to snap\n\t\t\tif (!snapThreshold) return point;\n\n\t\t\t// snap point to shapes/grid\n\t\t\tconst imagePoint = mapEditorPointToImagePoint(point);\n\n\t\t\tconst { snapTranslation, snapLines } = snapLinesToTargets(getShapeSnapLines(imagePoint), snapTargets, snapThreshold, gridSize, gridRect);\n\t\t\tupdateWithSnapTranslation(point, snapTranslation);\n\t\t\tsetInteractionSnapLines(snapLines);\n\t\t\treturn point;\n\t\t};\n\n\t\t// a shape is selected\n\t\tconst hasSelectedShape = !!getSelectedShape();\n\n\t\t// target shapes list or collection\n\t\tconst collection = options.isSelection ? selection : shapes;\n\n\t\tconst isSteppedInputMode = options.inputMode === 'step';\n\n\t\tif (shapeIsPath(shapeDefault)) {\n\t\t\t// stepped input mode\n\t\t\tif (isSteppedInputMode) {\n\t\t\t\tlet isFirstPoint = false;\n\n\t\t\t\tconst getPoint = (previousPoint, pointerPosition) => {\n\t\t\t\t\t// has started drawing? only handle end\n\t\t\t\t\tconst draft = getShapeDraft(collection);\n\n\t\t\t\t\t// if has previous point\n\t\t\t\t\tconst previousPointComputed = previousPoint && vectorCreate(fromPercentage(previousPoint.x, parentRect.width), fromPercentage(previousPoint.y, parentRect.height));\n\n\t\t\t\t\t// point in editor view\n\t\t\t\t\tconst previousPointInEditor = previousPointComputed && mapImagePointToEditorPoint(previousPointComputed);\n\n\t\t\t\t\t// if shift is held snap to last point\n\t\t\t\t\tif ($keysPressedStored.includes(16) && previousPointInEditor) positionRelativeToAnchor(previousPointInEditor, pointerPosition);\n\n\t\t\t\t\tconst currentPoint = mapEditorPointToImagePoint(pointerPosition);\n\n\t\t\t\t\t// get first point as absolute\n\t\t\t\t\tconst originPoint = draft.points.length ? draft.points[0] : currentPoint;\n\n\t\t\t\t\tconst originPointComputed = vectorCreate(fromPercentage(originPoint.x, parentRect.width), fromPercentage(originPoint.y, parentRect.height));\n\n\t\t\t\t\t// point in editor view\n\t\t\t\t\tconst originPointInEditor = mapImagePointToEditorPoint(originPointComputed);\n\n\t\t\t\t\t// distance between origin and new point\n\t\t\t\t\tconst distFromOriginSquared = vectorDistanceSquared(originPointInEditor, pointerPosition);\n\n\t\t\t\t\t// clicked second point at same location, discard draft\n\t\t\t\t\tif (!isFirstPoint && draft.points.length === 1 && distFromOriginSquared < 36) {\n\t\t\t\t\t\treturn discardShapeDraft(collection);\n\t\t\t\t\t}\n\n\t\t\t\t\t// minimum of three points to create a polygon\n\t\t\t\t\tif (draft.points.length > 2 && distFromOriginSquared <= POLYGON_END_PATH_DISTANCE_SQUARED) {\n\t\t\t\t\t\tdraft.pathClose = true;\n\n\t\t\t\t\t\t// select the draft\n\t\t\t\t\t\tshapeCanSelect(draft) && selectShape(draft);\n\n\t\t\t\t\t\tconst shape = confirmShapeDraft(collection);\n\t\t\t\t\t\tonaddshape(shape);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\t// clicked at same location as previous point, confirm as line\n\t\t\t\t\tconst hasVisibleStroke = draft.strokeWidth && draft.strokeColor && (draft.strokeColor[3] === undefined || draft.strokeColor[3] > 0);\n\n\t\t\t\t\tconst distFromPreviousSquared = vectorDistanceSquared(previousPointInEditor, pointerPosition);\n\n\t\t\t\t\tif (hasVisibleStroke && draft.points.length > 1 && distFromPreviousSquared < POLYGON_END_PATH_DISTANCE_SQUARED) {\n\t\t\t\t\t\t// open path\n\t\t\t\t\t\tdraft.pathClose = false;\n\n\t\t\t\t\t\tdraft.backgroundColor = [0, 0, 0, 0];\n\n\t\t\t\t\t\t// select the draft\n\t\t\t\t\t\tshapeCanSelect(draft) && selectShape(draft);\n\n\t\t\t\t\t\tconst shape = confirmShapeDraft(collection);\n\t\t\t\t\t\tonaddshape(shape);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\t// distance between previous and new point needs to be sufficient\n\t\t\t\t\tif (draft.points.length > 0) {\n\t\t\t\t\t\tif (distFromPreviousSquared < POLYGON_VERTEX_MIN_DISTANCE_SQUARED) return;\n\t\t\t\t\t}\n\n\t\t\t\t\t// return new point!\n\t\t\t\t\treturn isRelative\n\t\t\t\t\t? toPercentagePoint(currentPoint, parentRect)\n\t\t\t\t\t: currentPoint;\n\t\t\t\t};\n\n\t\t\t\treturn {\n\t\t\t\t\tstart: e => {\n\t\t\t\t\t\tconst draft = getShapeDraft(collection);\n\t\t\t\t\t\tconst { origin } = e.detail;\n\t\t\t\t\t\tinteractionRadius = 4;\n\t\t\t\t\t\tinteractionOrigin = snapPoint(vectorClone(origin));\n\t\t\t\t\t\tinteractionPosition = vectorClone(interactionOrigin);\n\n\t\t\t\t\t\t// has started drawing? only handle end\n\t\t\t\t\t\tif (draft) {\n\t\t\t\t\t\t\t// add next point\n\t\t\t\t\t\t\tconst previousPoint = draft.points[draft.points.length - 1];\n\n\t\t\t\t\t\t\tconst computedPoint = getPoint(previousPoint, interactionOrigin);\n\t\t\t\t\t\t\tif (computedPoint) draft.points = [...draft.points, computedPoint];\n\t\t\t\t\t\t\tupdateShapeDraft({ points: draft.points }, collection);\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// first point\n\t\t\t\t\t\tisFirstPoint = true;\n\t\t\t\t\t},\n\t\t\t\t\tupdate: e => {\n\t\t\t\t\t\t// this only runs for first point so we can drag to start AND tap to start a path\n\t\t\t\t\t\tif (!isFirstPoint) return;\n\n\t\t\t\t\t\t// get draft ref\n\t\t\t\t\t\tconst draft = getShapeDraft(collection);\n\n\t\t\t\t\t\tif (!draft) return;\n\n\t\t\t\t\t\t// get next point\n\t\t\t\t\t\tconst previousPoint = draft.points[0];\n\n\t\t\t\t\t\tconst computedPoint = getPoint(previousPoint, vectorCreate(interactionOrigin.x + e.detail.translation.x, interactionOrigin.y + e.detail.translation.y));\n\t\t\t\t\t\tif (!computedPoint) return;\n\n\t\t\t\t\t\t// first point is pointerdown, keep overwriting next point\n\t\t\t\t\t\tdraft.points[1] = computedPoint;\n\n\t\t\t\t\t\tupdateShapeDraft({ points: draft.points }, collection);\n\t\t\t\t\t},\n\t\t\t\t\trelease: e => e.detail.preventInertia(),\n\t\t\t\t\tcancel: () => {\n\t\t\t\t\t\tdiscardShapeDraft(collection);\n\t\t\t\t\t},\n\t\t\t\t\tend: e => {\n\t\t\t\t\t\tif (!isFirstPoint) return;\n\n\t\t\t\t\t\t// hasn't started drawing yet, let's add origin\n\t\t\t\t\t\tconst originPoint = mapEditorPointToImagePoint(interactionOrigin);\n\n\t\t\t\t\t\tconst computedPoint = isRelative\n\t\t\t\t\t\t? toPercentagePoint(originPoint, parentRect)\n\t\t\t\t\t\t: originPoint;\n\n\t\t\t\t\t\t// if is tap\n\t\t\t\t\t\tconst { isTap } = e.detail;\n\n\t\t\t\t\t\tif (isTap) {\n\t\t\t\t\t\t\t// if any shape below pointer select the shape and discard the path\n\t\t\t\t\t\t\tconst foundShapes = getShapesNearPosition($shapes, originPoint, 0, shapeCanSelect);\n\n\t\t\t\t\t\t\tif (foundShapes.length) return discardShapeDraft(collection);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// add to markup so it's drawn\n\t\t\t\t\t\taddShapeDraft({ ...shapeDefault, points: [computedPoint] }, collection);\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t\t}\n\n\t\t\t// drag input mode\n\t\t\treturn {\n\t\t\t\tstart: e => {\n\t\t\t\t\tconst { origin } = e.detail;\n\t\t\t\t\tinteractionRadius = 4;\n\t\t\t\t\tinteractionOrigin = vectorClone(origin);\n\t\t\t\t\tinteractionPosition = vectorClone(interactionOrigin);\n\n\t\t\t\t\t// add first point\n\t\t\t\t\tinteractionPositions.push({ ...interactionPosition });\n\n\t\t\t\t\t// to shape point\n\t\t\t\t\tconst originPoint = mapEditorPointToImagePoint(interactionOrigin);\n\n\t\t\t\t\tconst computedPoint = isRelative\n\t\t\t\t\t? toPercentagePoint(originPoint, parentRect)\n\t\t\t\t\t: originPoint;\n\n\t\t\t\t\t// add to markup so it's drawn\n\t\t\t\t\taddShapeDraft({ ...shapeDefault, points: [computedPoint] }, collection);\n\t\t\t\t},\n\t\t\t\tupdate: e => {\n\t\t\t\t\tconst draft = getShapeDraft(collection);\n\t\t\t\t\tif (!draft) return;\n\t\t\t\t\tconst { translation } = e.detail;\n\n\t\t\t\t\t// position\n\t\t\t\t\tconst pointerPosition = vectorCreate(interactionOrigin.x + translation.x, interactionOrigin.y + translation.y);\n\n\t\t\t\t\t// distance between interaction and pointer if it's too close, we don't draw this point\n\t\t\t\t\tconst dist = vectorDistance(interactionPosition, pointerPosition);\n\n\t\t\t\t\tif (fixPrecision(dist, 5) <= interactionRadius) return;\n\n\t\t\t\t\t// final points to add to shape\n\t\t\t\t\tconst pointsToAdd = [];\n\n\t\t\t\t\t// create smoothed target position\n\t\t\t\t\tconst angle = vectorAngleBetween(pointerPosition, interactionPosition);\n\n\t\t\t\t\tconst moveDist = interactionRadius - dist;\n\n\t\t\t\t\tconst targetPosition = {\n\t\t\t\t\t\tx: interactionPosition.x + moveDist * Math.cos(angle),\n\t\t\t\t\t\ty: interactionPosition.y + moveDist * Math.sin(angle)\n\t\t\t\t\t};\n\n\t\t\t\t\t// remember\n\t\t\t\t\tinteractionPositions.push({ ...targetPosition });\n\n\t\t\t\t\t// need more than two positions to start smoothing process\n\t\t\t\t\tif (shouldInterpolate && draft.points.length > 1) {\n\t\t\t\t\t\tconst totalPositions = interactionPositions.length;\n\t\t\t\t\t\tconst steps = Math.ceil(dist / 10);\n\t\t\t\t\t\tlet scale = dist / 30;\n\t\t\t\t\t\tconst a = interactionPositions[totalPositions - 3];\n\t\t\t\t\t\tconst b = interactionPositions[totalPositions - 2];\n\t\t\t\t\t\tconst c = interactionPositions[totalPositions - 1];\n\n\t\t\t\t\t\t// scale first segment, as it doesn't have smoothed points it's relatively long\n\t\t\t\t\t\tif (!interactionPositions[totalPositions - 4]) {\n\t\t\t\t\t\t\tscale *= 0.1;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tpointsToAdd.push(...pointsInterpolate(a, b, c, steps, scale).slice(1, -1));\n\t\t\t\t\t\tinteractionPositions.splice(totalPositions - 1, 0, ...pointsToAdd);\n\t\t\t\t\t}\n\n\t\t\t\t\t// add target\n\t\t\t\t\tpointsToAdd.push(targetPosition);\n\n\t\t\t\t\t// remember so we can determine how far we moved from previous interaction\n\t\t\t\t\tinteractionPosition.x = targetPosition.x;\n\n\t\t\t\t\tinteractionPosition.y = targetPosition.y;\n\n\t\t\t\t\tupdateShapeDraft(\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tpoints: draft.points.concat(pointsToAdd.map(pointToAdd => {\n\t\t\t\t\t\t\t\tconst point = mapEditorPointToImagePoint(pointToAdd);\n\n\t\t\t\t\t\t\t\tconst computedPoint = isRelative\n\t\t\t\t\t\t\t\t? toPercentagePoint(point, parentRect)\n\t\t\t\t\t\t\t\t: point;\n\n\t\t\t\t\t\t\t\treturn computedPoint;\n\t\t\t\t\t\t\t}))\n\t\t\t\t\t\t},\n\t\t\t\t\t\tcollection\n\t\t\t\t\t);\n\t\t\t\t},\n\t\t\t\trelease: e => e.detail.preventInertia(),\n\t\t\t\tcancel: () => {\n\t\t\t\t\tdiscardShapeDraft(collection);\n\t\t\t\t},\n\t\t\t\tend: e => {\n\t\t\t\t\tconst draft = getShapeDraft(collection);\n\t\t\t\t\tif (!draft) return;\n\n\t\t\t\t\t// if is bitmap we've just drawn a dot\n\t\t\t\t\tif (e.detail.isTap) {\n\t\t\t\t\t\tlet shouldDiscard = false;\n\n\t\t\t\t\t\t// if is bitmap can draw dots\n\t\t\t\t\t\tif (draft.bitmap) {\n\t\t\t\t\t\t\tif (interactionTarget) shouldDiscard = true;\n\t\t\t\t\t\t\tif (hasSelectedShape) shouldDiscard = true;\n\t\t\t\t\t\t} else // non-bitmap cannot create 1 point lines\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tshouldDiscard = true;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (shouldDiscard) return discardShapeDraft(collection);\n\t\t\t\t\t}\n\n\t\t\t\t\tconst shape = confirmShapeDraft(collection);\n\t\t\t\t\tonaddshape(shape);\n\t\t\t\t}\n\t\t\t};\n\t\t} else if (isEllipse || isText || shapeIsRect(shapeDefault)) {\n\t\t\treturn {\n\t\t\t\tstart: e => {\n\t\t\t\t\tconst { origin } = e.detail;\n\n\t\t\t\t\t// need to remember origin so we can correctly apply interaction translation\n\t\t\t\t\tinteractionOrigin = snapPoint(vectorClone(origin));\n\n\t\t\t\t\tconst mappedOriginPosition = mapEditorPointToImagePoint(interactionOrigin);\n\n\t\t\t\t\tconst computedOriginPosition = isRelative\n\t\t\t\t\t? toPercentagePoint(mappedOriginPosition, parentRect)\n\t\t\t\t\t: mappedOriginPosition;\n\n\t\t\t\t\tconst rotation = -1 * getShapeUpdateRotation(contextRotation, contextFlipX, contextFlipY);\n\n\t\t\t\t\t// add to markup so it's drawn\n\t\t\t\t\tconst draft = {\n\t\t\t\t\t\t...shapeDefault,\n\t\t\t\t\t\trotation,\n\t\t\t\t\t\t...computedOriginPosition\n\t\t\t\t\t};\n\n\t\t\t\t\t// de-flip shape\n\t\t\t\t\tdraft.flipX = contextFlipX;\n\n\t\t\t\t\tdraft.flipY = contextFlipY;\n\n\t\t\t\t\t// remove position\n\t\t\t\t\tdelete draft.position;\n\n\t\t\t\t\t// hide initially\n\t\t\t\t\tdraft.opacity = 0;\n\n\t\t\t\t\t// set base position props\n\t\t\t\t\tconst initProps = isEllipse ? ['rx', 'ry'] : ['width', 'height'];\n\n\t\t\t\t\tinitProps.forEach(prop => {\n\t\t\t\t\t\t// set base props\n\t\t\t\t\t\tdraft[prop] = isRelative ? '0%' : 0;\n\t\t\t\t\t});\n\n\t\t\t\t\taddShapeDraft(draft, collection);\n\t\t\t\t},\n\t\t\t\tupdate: e => {\n\t\t\t\t\tconst draft = getShapeDraft(collection);\n\t\t\t\t\tif (!draft) return;\n\t\t\t\t\tdraft.opacity = 1;\n\t\t\t\t\tlet { aspectRatio } = draft;\n\t\t\t\t\tlet { translation, shiftKey, ctrlKey } = e.detail;\n\n\t\t\t\t\t// always draw squares when holding shift\n\t\t\t\t\tif (!aspectRatio && shiftKey) aspectRatio = 1;\n\n\t\t\t\t\t// position\n\t\t\t\t\tconst pointerPosition = snapPoint(vectorCreate(interactionOrigin.x + translation.x, interactionOrigin.y + translation.y));\n\n\t\t\t\t\tconst mappedOriginPosition = mapEditorPointToImagePoint(interactionOrigin);\n\t\t\t\t\tconst mappedPointerPosition = mapEditorPointToImagePoint(pointerPosition);\n\n\t\t\t\t\t// force pointer position to fixed aspect ratio\n\t\t\t\t\tif (aspectRatio) {\n\t\t\t\t\t\tconst pointerArea = rectCreateFromPoints([mappedOriginPosition, mappedPointerPosition]);\n\t\t\t\t\t\tconst pointerAreaSnapped = rectContainRect(pointerArea, aspectRatio);\n\n\t\t\t\t\t\tconst mx = mappedPointerPosition.x < mappedOriginPosition.x\n\t\t\t\t\t\t? -1\n\t\t\t\t\t\t: 1;\n\n\t\t\t\t\t\tconst my = mappedPointerPosition.y < mappedOriginPosition.y\n\t\t\t\t\t\t? -1\n\t\t\t\t\t\t: 1;\n\n\t\t\t\t\t\tmappedPointerPosition.x = mappedOriginPosition.x + pointerAreaSnapped.width * mx;\n\t\t\t\t\t\tmappedPointerPosition.y = mappedOriginPosition.y + pointerAreaSnapped.height * my;\n\t\t\t\t\t}\n\n\t\t\t\t\tconst pivot = {\n\t\t\t\t\t\tx: mappedOriginPosition.x + (mappedPointerPosition.x - mappedOriginPosition.x) * 0.5,\n\t\t\t\t\t\ty: mappedOriginPosition.y + (mappedPointerPosition.y - mappedOriginPosition.y) * 0.5\n\t\t\t\t\t};\n\n\t\t\t\t\tconst rotation = getShapeUpdateRotation(contextRotation, contextFlipX, contextFlipY);\n\t\t\t\t\tvectorRotate(mappedOriginPosition, rotation, pivot);\n\t\t\t\t\tvectorRotate(mappedPointerPosition, rotation, pivot);\n\t\t\t\t\tconst l = Math.min(mappedOriginPosition.x, mappedPointerPosition.x);\n\t\t\t\t\tconst t = Math.min(mappedOriginPosition.y, mappedPointerPosition.y);\n\t\t\t\t\tconst r = Math.max(mappedOriginPosition.x, mappedPointerPosition.x);\n\t\t\t\t\tconst b = Math.max(mappedOriginPosition.y, mappedPointerPosition.y);\n\t\t\t\t\tlet width = r - l;\n\t\t\t\t\tlet height = b - t;\n\t\t\t\t\tlet props = {};\n\n\t\t\t\t\tif (isEllipse) {\n\t\t\t\t\t\tif (ctrlKey) {\n\t\t\t\t\t\t\tprops.x = mappedOriginPosition.x;\n\t\t\t\t\t\t\tprops.y = mappedOriginPosition.y;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tprops.x = l + width * 0.5;\n\t\t\t\t\t\t\tprops.y = t + height * 0.5;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tprops.rx = width * 0.5;\n\t\t\t\t\t\tprops.ry = height * 0.5;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tif (ctrlKey) {\n\t\t\t\t\t\t\tprops.x = mappedOriginPosition.x - width * 0.5;\n\t\t\t\t\t\t\tprops.y = mappedOriginPosition.y - height * 0.5;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tprops.x = l;\n\t\t\t\t\t\t\tprops.y = t;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tprops.width = width;\n\t\t\t\t\t\tprops.height = height;\n\t\t\t\t\t}\n\n\t\t\t\t\tupdateShapeDraft(props, collection, (item, props) => {\n\t\t\t\t\t\treturn updateShape(item, props, parentRect);\n\t\t\t\t\t});\n\t\t\t\t},\n\t\t\t\trelease: e => {\n\t\t\t\t\te.detail.preventInertia();\n\t\t\t\t},\n\t\t\t\tcancel: () => {\n\t\t\t\t\tdiscardShapeDraft(collection);\n\t\t\t\t},\n\t\t\t\tend: e => {\n\t\t\t\t\tconst draft = getShapeDraft(collection);\n\t\t\t\t\tif (!draft) return;\n\n\t\t\t\t\tif (e.detail.isTap) {\n\t\t\t\t\t\t// will discard a new text shape when created by tap and a previous text shape was just confirmed\n\t\t\t\t\t\tif (shapeIsText(draft) && enableTapToAddText && interactionShapeWasConfirmed) {\n\t\t\t\t\t\t\treturn discardShapeDraft(collection);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// will cancel if is not text shape\n\t\t\t\t\t\tif (!shapeIsText(draft) || !enableTapToAddText || interactionTarget) {\n\t\t\t\t\t\t\treturn discardShapeDraft(collection);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// will add 'auto' text shape if not targetting other shape\n\t\t\t\t\t\tdelete draft.width;\n\n\t\t\t\t\t\tdelete draft.height;\n\t\t\t\t\t\tif (draft.disableNewline !== false) delete draft.textAlign;\n\n\t\t\t\t\t\t// position the shape correctly\n\t\t\t\t\t\tconst draftComputedShape = shapeComputeDisplay({ ...draft }, parentRect);\n\n\t\t\t\t\t\tconst size = textToSize(draft.text, draftComputedShape);\n\n\t\t\t\t\t\tconst mappedOriginPosition = mapEditorPointToImagePoint({\n\t\t\t\t\t\t\tx: interactionOrigin.x,\n\t\t\t\t\t\t\ty: interactionOrigin.y - size.height * 0.5\n\t\t\t\t\t\t});\n\n\t\t\t\t\t\tconst mappedPointerPosition = mapEditorPointToImagePoint({\n\t\t\t\t\t\t\tx: interactionOrigin.x + size.width,\n\t\t\t\t\t\t\ty: interactionOrigin.y + size.height * 0.5\n\t\t\t\t\t\t});\n\n\t\t\t\t\t\tconst pivot = {\n\t\t\t\t\t\t\tx: mappedOriginPosition.x + (mappedPointerPosition.x - mappedOriginPosition.x) * 0.5,\n\t\t\t\t\t\t\ty: mappedOriginPosition.y + (mappedPointerPosition.y - mappedOriginPosition.y) * 0.5\n\t\t\t\t\t\t};\n\n\t\t\t\t\t\tconst rotation = getShapeUpdateRotation(contextRotation, contextFlipX, contextFlipY);\n\t\t\t\t\t\tvectorRotate(mappedOriginPosition, rotation, pivot);\n\t\t\t\t\t\tvectorRotate(mappedPointerPosition, rotation, pivot);\n\t\t\t\t\t\tlet l = Math.min(mappedOriginPosition.x, mappedPointerPosition.x);\n\t\t\t\t\t\tlet t = Math.min(mappedOriginPosition.y, mappedPointerPosition.y);\n\t\t\t\t\t\tif (l < 0) l = 0;\n\t\t\t\t\t\tif (t < 0) t = 0;\n\t\t\t\t\t\tif (l + size.width > parentRect.width) l = parentRect.width - size.width;\n\t\t\t\t\t\tif (t + size.height > parentRect.height) t = parentRect.height - size.height;\n\n\t\t\t\t\t\tdraft.x = isString(draft.x)\n\t\t\t\t\t\t? toPercentage(l, parentRect.width)\n\t\t\t\t\t\t: l;\n\n\t\t\t\t\t\tdraft.y = isString(draft.y)\n\t\t\t\t\t\t? toPercentage(t, parentRect.height)\n\t\t\t\t\t\t: t;\n\t\t\t\t\t}\n\n\t\t\t\t\t// reveal\n\t\t\t\t\tdraft.opacity = 1;\n\n\t\t\t\t\t// finish markup\n\t\t\t\t\tif (!shapeIsText(draft)) {\n\t\t\t\t\t\tconst shape = confirmShapeDraft(collection);\n\t\t\t\t\t\tonaddshape(shape);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tonaddshape(draft);\n\t\t\t\t\t}\n\n\t\t\t\t\t// select the draft\n\t\t\t\t\tshapeCanSelect(draft) && selectShape(draft);\n\n\t\t\t\t\t// also enable editing the text\n\t\t\t\t\tif (shapeIsText(draft)) editMarkupItem(draft);\n\t\t\t\t}\n\t\t\t};\n\t\t} else if (shapeIsLine(shapeDefault)) {\n\t\t\treturn {\n\t\t\t\tstart: e => {\n\t\t\t\t\tconst { origin } = e.detail;\n\t\t\t\t\tconst originMapped = mapEditorPointToImagePoint(snapPoint(origin));\n\t\t\t\t\tconst originSnapped = vectorApply(originMapped, snapToPixel);\n\t\t\t\t\tinteractionOrigin = vectorClone(origin);\n\n\t\t\t\t\t// add to markup so it's drawn\n\t\t\t\t\taddShapeDraft(\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t...shapeDefault,\n\t\t\t\t\t\t\tx1: isRelative\n\t\t\t\t\t\t\t? toPercentage(originSnapped.x, parentRect.width)\n\t\t\t\t\t\t\t: originSnapped.x,\n\t\t\t\t\t\t\ty1: isRelative\n\t\t\t\t\t\t\t? toPercentage(originSnapped.y, parentRect.height)\n\t\t\t\t\t\t\t: originSnapped.y,\n\t\t\t\t\t\t\tx2: isRelative\n\t\t\t\t\t\t\t? toPercentage(originSnapped.x, parentRect.width)\n\t\t\t\t\t\t\t: originSnapped.x,\n\t\t\t\t\t\t\ty2: isRelative\n\t\t\t\t\t\t\t? toPercentage(originSnapped.y, parentRect.height)\n\t\t\t\t\t\t\t: originSnapped.y,\n\t\t\t\t\t\t\topacity: 0\n\t\t\t\t\t\t},\n\t\t\t\t\t\tcollection\n\t\t\t\t\t);\n\t\t\t\t},\n\t\t\t\tupdate: e => {\n\t\t\t\t\t// we only get the draft to test if it's still there (might be removed by undo function)\n\t\t\t\t\tconst draft = getShapeDraft(collection);\n\n\t\t\t\t\tif (!draft) return;\n\t\t\t\t\tconst { translation } = e.detail;\n\t\t\t\t\tconst interactionTarget = snapPoint(vectorAdd(vectorClone(interactionOrigin), translation));\n\n\t\t\t\t\t// shift pressed\n\t\t\t\t\tif ($keysPressedStored.includes(16)) {\n\t\t\t\t\t\tconst length = vectorDistance(interactionOrigin, interactionTarget);\n\t\t\t\t\t\tconst angle = vectorAngleBetween(interactionOrigin, interactionTarget);\n\t\t\t\t\t\tconst angleSnapInterval = Math.PI / 4;\n\t\t\t\t\t\tconst angleSnapped = angleSnapInterval * Math.round(angle / angleSnapInterval);\n\t\t\t\t\t\tinteractionTarget.x = interactionOrigin.x + length * Math.cos(angleSnapped);\n\t\t\t\t\t\tinteractionTarget.y = interactionOrigin.y + length * Math.sin(angleSnapped);\n\t\t\t\t\t}\n\n\t\t\t\t\t// update line end position\n\t\t\t\t\tconst point = mapEditorPointToImagePoint(interactionTarget);\n\n\t\t\t\t\tupdateShapeDraft(\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tx2: isRelative\n\t\t\t\t\t\t\t? toPercentage(point.x, parentRect.width)\n\t\t\t\t\t\t\t: point.x,\n\t\t\t\t\t\t\ty2: isRelative\n\t\t\t\t\t\t\t? toPercentage(point.y, parentRect.height)\n\t\t\t\t\t\t\t: point.y,\n\t\t\t\t\t\t\topacity: 1\n\t\t\t\t\t\t},\n\t\t\t\t\t\tcollection\n\t\t\t\t\t);\n\t\t\t\t},\n\t\t\t\trelease: e => e.detail.preventInertia(),\n\t\t\t\tcancel: () => {\n\t\t\t\t\tdiscardShapeDraft(collection);\n\t\t\t\t},\n\t\t\t\tend: e => {\n\t\t\t\t\tconst draft = getShapeDraft(collection);\n\t\t\t\t\tif (!draft) return;\n\t\t\t\t\tif (e.detail.isTap) return discardShapeDraft(collection);\n\t\t\t\t\tdraft.opacity = 1;\n\n\t\t\t\t\t// finish markup\n\t\t\t\t\tconst shape = confirmShapeDraft(collection);\n\n\t\t\t\t\tonaddshape(shape);\n\n\t\t\t\t\t// select the draft\n\t\t\t\t\tshapeCanSelect(draft) && selectShape(shape);\n\t\t\t\t}\n\t\t\t};\n\t\t}\n\t};\n\n\tconst eraseShape = () => {\n\t\tlet origin;\n\t\tlet positionPrevious;\n\t\tconst eraseRadiusSquared = eraseRadius * eraseRadius;\n\n\t\tconst erase = (positionA, positionB, forceErase = false) => {\n\t\t\tconst d2 = vectorDistanceSquared(positionA, positionB);\n\n\t\t\t// skip, too close to previous point\n\t\t\tif (!forceErase && d2 < 2) return false;\n\n\t\t\tconst shapesThatCanBeErased = $shapes.filter(shape => !shape.disableErase);\n\n\t\t\t// if line is shorter than radius do simple erase operation, find all points within range from current position\n\t\t\tlet shapesFound;\n\n\t\t\tif (d2 < eraseRadiusSquared) {\n\t\t\t\tshapesFound = getShapesNearPosition($shapes, mapEditorPointToImagePoint(positionB), eraseRadius);\n\t\t\t} else {\n\t\t\t\tshapesFound = getShapesBetweenPoints(shapesThatCanBeErased, mapEditorPointToImagePoint(positionA), mapEditorPointToImagePoint(positionB), eraseRadius);\n\t\t\t}\n\n\t\t\tconst shapesRemoved = removeMarkupItems(shapesFound);\n\t\t\tshapesRemoved.forEach(onremoveshape);\n\t\t\treturn true;\n\t\t};\n\n\t\treturn {\n\t\t\tstart: e => {\n\t\t\t\torigin = vectorCreate(Math.round(e.detail.origin.x), Math.round(e.detail.origin.y));\n\t\t\t\terase(origin, origin, true);\n\t\t\t\tpositionPrevious = origin;\n\t\t\t},\n\t\t\tupdate: e => {\n\t\t\t\tconst { translation } = e.detail;\n\t\t\t\tconst positionCurrent = vectorCreate(Math.round(origin.x + translation.x), Math.round(origin.y + translation.y));\n\n\t\t\t\tif (erase(positionPrevious, positionCurrent)) {\n\t\t\t\t\tpositionPrevious = vectorClone(positionCurrent);\n\t\t\t\t}\n\t\t\t},\n\t\t\trelease: e => e.detail.preventInertia(),\n\t\t\tcancel: () => {\n\t\t\t\tdiscardShapeDraft(collection);\n\t\t\t},\n\t\t\tend: () => {\n\t\t\t\t\n\t\t\t}\n\t\t};\n\t};\n\n\t//#endregion\n\t//\n\t// Mapping coordinates\n\t//\n\tconst getImagePointWithScreenTranslation = (point, translation) => {\n\t\tconst pointInScreenSpace = mapImagePointToEditorPoint(point);\n\t\treturn mapEditorPointToImagePoint(vectorAdd(pointInScreenSpace, translation));\n\t};\n\n\tconst getNumberSequence = (interval, min, max) => {\n\t\tconst length = (max - min) / interval + 1;\n\t\treturn Array.from({ length }, (_, i) => min + i * interval);\n\t};\n\n\tconst getShapeSnapLines = shape => {\n\t\tlet linesX = [];\n\t\tlet linesY = [];\n\n\t\t// Handle line as points\n\t\tlet points = shape.points;\n\n\t\tif (shape.x1 !== undefined) {\n\t\t\tpoints = [{ x: shape.x1, y: shape.y1 }, { x: shape.x2, y: shape.y2 }];\n\t\t}\n\n\t\t// Path → start & end + bounds + center\n\t\tif (points) {\n\t\t\tconst mappedPoints = points.map(mapImagePointToEditorPoint);\n\t\t\tconst rect = rectCreateFromPoints(mappedPoints);\n\t\t\tconst firstPoint = mappedPoints[0];\n\t\t\tconst lastPoint = mappedPoints[mappedPoints.length - 1];\n\n\t\t\tlinesX = [\n\t\t\t\trect.x,\n\t\t\t\trect.x + rect.width * 0.5,\n\t\t\t\trect.x + rect.width,\n\t\t\t\tfirstPoint.x,\n\t\t\t\tlastPoint.x\n\t\t\t];\n\n\t\t\tlinesY = [\n\t\t\t\trect.y,\n\t\t\t\trect.y + rect.height * 0.5,\n\t\t\t\trect.y + rect.height,\n\t\t\t\tfirstPoint.y,\n\t\t\t\tlastPoint.y\n\t\t\t];\n\t\t} else // Rect → bounds + center\n\t\tif (shape.text || shape.width !== undefined || shape.rx !== undefined) {\n\t\t\t// make sure is rect\n\t\t\tlet rect;\n\n\t\t\tif (shape.text && shape.height === undefined) {\n\t\t\t\t// calculate rect\n\t\t\t\tconst size = textToSize(shape.text, shape);\n\n\t\t\t\trect = rectCreate(shape.x, shape.y, size.width, size.height);\n\t\t\t} else {\n\t\t\t\t// ellipse and rectangle\n\t\t\t\trect = shape.width !== undefined\n\t\t\t\t? rectCreateFromAny(shape)\n\t\t\t\t: rectCreateFromAny({\n\t\t\t\t\t\tx: shape.x - shape.rx,\n\t\t\t\t\t\ty: shape.y - shape.ry,\n\t\t\t\t\t\twidth: shape.rx * 2,\n\t\t\t\t\t\theight: shape.ry * 2\n\t\t\t\t\t});\n\t\t\t}\n\n\t\t\tconst rectAlignedCorners = rectGetCorners(rect).map(mapImagePointToEditorPoint);\n\n\t\t\tconst rectRotatedCorners = Math.abs(shape.rotation) > 0\n\t\t\t? rectRotate(rect, shape.rotation).map(mapImagePointToEditorPoint)\n\t\t\t: rectAlignedCorners;\n\n\t\t\tconst rectBounds = rectCreateFromPoints(rectRotatedCorners);\n\t\t\tconst center = rectCenter(rectBounds);\n\t\t\tconst width = rectBounds.width;\n\t\t\tconst height = rectBounds.height;\n\t\t\tconst cw = width * 0.5;\n\t\t\tconst ch = height * 0.5;\n\t\t\tlinesX = [center.x - cw, center.x, center.x + cw];\n\t\t\tlinesY = [center.y - ch, center.y, center.y + ch];\n\t\t} else // Just a point\n\t\tif (shape.x !== undefined) {\n\t\t\tconst point = mapImagePointToEditorPoint(shape);\n\t\t\tlinesX = [point.x];\n\t\t\tlinesY = [point.y];\n\t\t}\n\n\t\treturn { x: linesX, y: linesY };\n\t};\n\n\tconst getSnapTarget = (originLines, targetLines, snapThreshold) => {\n\t\tlet dist;\n\t\tlet previousSnapDist = Number.Infinity;\n\t\tlet origin = null;\n\t\tlet target = null;\n\t\tlet a, b;\n\n\t\tfor (let i = 0; i < originLines.length; i++) {\n\t\t\ta = originLines[i];\n\n\t\t\tfor (let j = 0; j < targetLines.length; j++) {\n\t\t\t\tb = targetLines[j];\n\t\t\t\tdist = Math.abs(a - b);\n\n\t\t\t\tif (dist < snapThreshold && (target === null || dist < previousSnapDist)) {\n\t\t\t\t\tpreviousSnapDist = dist;\n\t\t\t\t\torigin = a;\n\t\t\t\t\ttarget = b;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn { origin, target };\n\t};\n\n\tconst snapToShape = (originSnapLines, targetSnapLines, snapThreshold) => {\n\t\tconst snapLine = { x: null, y: null };\n\t\tconst snapTranslation = { x: null, y: null };\n\t\tconst snapX = getSnapTarget(originSnapLines.x, targetSnapLines.x, snapThreshold);\n\n\t\tif (snapX.target !== null) {\n\t\t\tsnapLine.x = snapX.target;\n\t\t\tsnapTranslation.x = snapX.target - snapX.origin;\n\t\t}\n\n\t\tconst snapY = getSnapTarget(originSnapLines.y, targetSnapLines.y, snapThreshold);\n\n\t\tif (snapY.target !== null) {\n\t\t\tsnapLine.y = snapY.target;\n\t\t\tsnapTranslation.y = snapY.target - snapY.origin;\n\t\t}\n\n\t\treturn { snapTranslation, snapLine };\n\t};\n\n\tconst snapLinesToTargets = (snapLines, snapTargets, snapThreshold, gridSize, gridRect) => {\n\t\tconst lines = { x: null, y: null };\n\t\tconst translation = { x: null, y: null };\n\n\t\t// create snap lines\n\t\tconst targetSnapLines = snapTargets.map(getShapeSnapLines);\n\n\t\t// add grid snapping\n\t\tif (gridSize > 0) {\n\t\t\tconst editorPointA = mapImagePointToEditorPoint({ x: 0, y: 0 });\n\t\t\tconst editorPointB = mapImagePointToEditorPoint({ x: gridSize, y: gridSize });\n\t\t\tconst editorPointC = mapImagePointToEditorPoint({ x: gridRect.width, y: gridRect.height });\n\t\t\tconst projectedSize = editorPointB.x - editorPointA.x;\n\t\t\tconst linesX = getNumberSequence(projectedSize, editorPointA.x, editorPointC.x);\n\t\t\tconst linesY = getNumberSequence(projectedSize, editorPointA.y, editorPointC.y);\n\t\t\ttargetSnapLines.push({ x: linesX, y: linesY });\n\t\t}\n\n\t\t// snap to targets\n\t\ttargetSnapLines.forEach(targetSnapLines => {\n\t\t\t// get snap results\n\t\t\tconst { snapTranslation, snapLine } = snapToShape(snapLines, targetSnapLines, snapThreshold);\n\n\t\t\t// determine if results are better than previous snap results\n\t\t\tif (snapTranslation.x !== null && (translation.x === null || Math.abs(snapTranslation.x) < Math.abs(translation.x))) {\n\t\t\t\ttranslation.x = snapTranslation.x || 0;\n\t\t\t\tlines.x = snapLine.x;\n\t\t\t}\n\n\t\t\tif (snapTranslation.y !== null && (translation.y === null || Math.abs(snapTranslation.y) < Math.abs(translation.y))) {\n\t\t\t\ttranslation.y = snapTranslation.y || 0;\n\t\t\t\tlines.y = snapLine.y;\n\t\t\t}\n\t\t});\n\n\t\treturn {\n\t\t\tsnapTranslation: translation,\n\t\t\tsnapLines: lines\n\t\t};\n\t};\n\n\tconst updateWithSnapTranslation = (vector, translation) => {\n\t\tif (translation.x !== null) vector.x += translation.x;\n\t\tif (translation.y !== null) vector.y += translation.y;\n\t\treturn vector;\n\t};\n\n\t//\n\t// Interaction\n\t//\n\t//#region translateShape rotateShape resizeShape\n\tconst translateShape = (shapeCurrent, shapeOriginComputed, translation, options) => {\n\t\t// get snap info\n\t\tconst { snapThreshold = 0, gridSize = 0, gridRect, snapTargets = [] } = options || {};\n\n\t\t// will hold props we will update in target shape\n\t\tlet propsToUpdate = null;\n\n\t\t// if is line\n\t\tif (shapeIsLine(shapeCurrent)) {\n\t\t\tif (snapThreshold) {\n\t\t\t\tconst shapeComputed = shapeComputeDisplay({ ...shapeCurrent }, parentRect);\n\t\t\t\tconst beginImageOffset = getImagePointWithScreenTranslation(shapeLineGetStartPoint(shapeOriginComputed), translation);\n\t\t\t\tconst endImageOffset = getImagePointWithScreenTranslation(shapeLineGetEndPoint(shapeOriginComputed), translation);\n\n\t\t\t\tconst shapeSnapLines = getShapeSnapLines({\n\t\t\t\t\t...shapeComputed,\n\t\t\t\t\tx1: beginImageOffset.x,\n\t\t\t\t\ty1: beginImageOffset.y,\n\t\t\t\t\tx2: endImageOffset.x,\n\t\t\t\t\ty2: endImageOffset.y\n\t\t\t\t});\n\n\t\t\t\tconst { snapTranslation, snapLines } = snapLinesToTargets(shapeSnapLines, snapTargets, snapThreshold, gridSize, gridRect);\n\t\t\t\tupdateWithSnapTranslation(translation, snapTranslation);\n\t\t\t\tsetInteractionSnapLines(snapLines);\n\t\t\t}\n\n\t\t\tconst beginImageOffset = getImagePointWithScreenTranslation(shapeLineGetStartPoint(shapeOriginComputed), translation);\n\t\t\tconst endImageOffset = getImagePointWithScreenTranslation(shapeLineGetEndPoint(shapeOriginComputed), translation);\n\n\t\t\t// move shape\n\t\t\tpropsToUpdate = {\n\t\t\t\tx1: beginImageOffset.x,\n\t\t\t\ty1: beginImageOffset.y,\n\t\t\t\tx2: endImageOffset.x,\n\t\t\t\ty2: endImageOffset.y\n\t\t\t};\n\t\t} else if (shapeCurrent.points) {\n\t\t\t// deselect on move\n\t\t\tif (shapeCanSelectPoint(interactionShape)) {\n\t\t\t\tupdateMarkupShape(interactionShape, { selectedPoint: undefined });\n\t\t\t}\n\n\t\t\t// snap it!\n\t\t\tif (snapThreshold) {\n\t\t\t\tconst shapeComputed = shapeComputeDisplay({ ...shapeCurrent }, parentRect);\n\n\t\t\t\tconst shapeSnapLines = getShapeSnapLines({\n\t\t\t\t\t...shapeComputed,\n\t\t\t\t\tpoints: shapeOriginComputed.points.map(point => getImagePointWithScreenTranslation(point, translation))\n\t\t\t\t});\n\n\t\t\t\tconst { snapTranslation, snapLines } = snapLinesToTargets(shapeSnapLines, snapTargets, snapThreshold, gridSize, gridRect);\n\t\t\t\tupdateWithSnapTranslation(translation, snapTranslation);\n\t\t\t\tsetInteractionSnapLines(snapLines);\n\t\t\t}\n\n\t\t\tpropsToUpdate = {\n\t\t\t\tpoints: shapeOriginComputed.points.map(point => getImagePointWithScreenTranslation(point, translation))\n\t\t\t};\n\t\t} else if (shapeIsRect(shapeCurrent) || shapeIsText(shapeCurrent) || shapeIsEllipse(shapeCurrent)) {\n\t\t\tif (snapThreshold) {\n\t\t\t\tconst shapeComputed = shapeComputeDisplay({ ...shapeCurrent }, parentRect);\n\t\t\t\tconst shapeTranslatedPosition = getImagePointWithScreenTranslation(shapeOriginComputed, translation);\n\n\t\t\t\tconst shapeSnapLines = getShapeSnapLines({\n\t\t\t\t\t...shapeComputed,\n\t\t\t\t\t...shapeTranslatedPosition\n\t\t\t\t});\n\n\t\t\t\tconst { snapTranslation, snapLines } = snapLinesToTargets(shapeSnapLines, snapTargets, snapThreshold, gridSize, gridRect);\n\t\t\t\tupdateWithSnapTranslation(translation, snapTranslation);\n\t\t\t\tsetInteractionSnapLines(snapLines);\n\t\t\t}\n\n\t\t\tpropsToUpdate = getImagePointWithScreenTranslation(shapeOriginComputed, translation);\n\t\t}\n\n\t\tif (!propsToUpdate) return;\n\t\tupdateShape(shapeCurrent, propsToUpdate, parentRect);\n\t\tsyncShapes();\n\t};\n\n\tconst IndexFlipXMap = { 0: 1, 1: 0, 2: 3, 3: 2 };\n\tconst IndexFlipYMap = { 0: 3, 1: 2, 2: 1, 3: 0 };\n\n\tconst resizeShape = (shapeCurrent, shapeOriginComputed, indexes, translation, options) => {\n\t\tconst { shiftKey, snapThreshold = 0, gridSize = 0, gridRect, snapTargets = [], aspectRatio = undefined, beforeResizeShape = noop$1 } = options || {};\n\n\t\t// snap point\n\t\tconst snapPoint = snapThreshold\n\t\t? (point, translation, getPoint) => {\n\t\t\t\tconst { snapTranslation, snapLines } = snapLinesToTargets(getShapeSnapLines(point), snapTargets, snapThreshold, gridSize, gridRect);\n\t\t\t\tupdateWithSnapTranslation(translation, snapTranslation);\n\t\t\t\tsetInteractionSnapLines(snapLines);\n\t\t\t\tconst snappedPoint = getImagePointWithScreenTranslation(getPoint(), translation);\n\t\t\t\tpoint.x = snappedPoint.x;\n\t\t\t\tpoint.y = snappedPoint.y;\n\t\t\t}\n\t\t: noop$1;\n\n\t\t// is a line of points\n\t\tif (shapeIsLine(shapeCurrent) || shapeIsPath(shapeCurrent) && hasProp(shapeCurrent, 'pathClose')) {\n\t\t\tconst [targetIndex] = indexes;\n\n\t\t\t// shift pressed\n\t\t\tconst anchor = shiftKey\n\t\t\t? positionRelativeToAnchor\n\t\t\t: (_, target) => target;\n\n\t\t\tlet getPoint;\n\t\t\tlet anchorPoint;\n\t\t\tlet setPoint;\n\n\t\t\tif (shapeIsLine(shapeCurrent)) {\n\t\t\t\t// start\n\t\t\t\tif (targetIndex === 0) {\n\t\t\t\t\tgetPoint = () => shapeLineGetStartPoint(shapeOriginComputed);\n\n\t\t\t\t\t// snap angle relative to opposite point\n\t\t\t\t\tanchorPoint = point => anchor(shapeLineGetEndPoint(shapeOriginComputed), point);\n\n\t\t\t\t\tsetPoint = (shape, point) => {\n\t\t\t\t\t\tshape.x1 = point.x;\n\t\t\t\t\t\tshape.y1 = point.y;\n\t\t\t\t\t};\n\t\t\t\t} else // end\n\t\t\t\tif (targetIndex === 1) {\n\t\t\t\t\tgetPoint = () => shapeLineGetEndPoint(shapeOriginComputed);\n\n\t\t\t\t\t// snap angle relative to opposite point\n\t\t\t\t\tanchorPoint = point => anchor(shapeLineGetStartPoint(shapeOriginComputed), point);\n\n\t\t\t\t\tsetPoint = (shape, point) => {\n\t\t\t\t\t\tshape.x2 = point.x;\n\t\t\t\t\t\tshape.y2 = point.y;\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tconst computedPoints = shapeOriginComputed.points;\n\t\t\t\tgetPoint = () => ({ ...computedPoints[targetIndex] });\n\n\t\t\t\tanchorPoint = point => {\n\t\t\t\t\tconst pointBefore = computedPoints[arrayIndexWrap(targetIndex - 1, computedPoints)];\n\t\t\t\t\tconst pointAfter = computedPoints[arrayIndexWrap(targetIndex + 1, computedPoints)];\n\t\t\t\t\tanchor(pointBefore, point);\n\t\t\t\t\tanchor(pointAfter, point);\n\t\t\t\t};\n\n\t\t\t\tsetPoint = (shape, point) => shape.points = shape.points.map((p, index) => {\n\t\t\t\t\tif (targetIndex === index) return point;\n\t\t\t\t\treturn p;\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tconst point = getImagePointWithScreenTranslation(getPoint(), translation);\n\t\t\tsnapPoint(point, translation, getPoint);\n\t\t\tanchorPoint(point);\n\t\t\tsetPoint(shapeCurrent, point);\n\t\t} else // is a box\n\t\tif (shapeHasSize(shapeCurrent) || shapeIsEllipse(shapeCurrent) || // shapeIsTextBox(shapeCurrent) ||\n\t\tshapeIsText(shapeCurrent)) {\n\t\t\tlet hasAutoHeight = false;\n\t\t\tlet hasAutoWidthAndHeight = false;\n\t\t\tlet shapeOriginRect;\n\n\t\t\tif (shapeIsEllipse(shapeOriginComputed)) {\n\t\t\t\tshapeOriginRect = rectCreateFromEllipse(shapeOriginComputed);\n\t\t\t} else if (shapeHasSize(shapeOriginComputed)) {\n\t\t\t\tshapeOriginRect = rectCreateFromAny(shapeOriginComputed);\n\t\t\t} else if (hasProp(shapeOriginComputed, 'width')) {\n\t\t\t\t// is text with 'width' only\n\t\t\t\thasAutoHeight = true;\n\n\t\t\t\tshapeOriginRect = rectCreateFromAny(shapeOriginComputed);\n\t\t\t\tconst size = textToSize(shapeOriginComputed.text, shapeOriginComputed);\n\t\t\t\tshapeOriginRect.height = size.height;\n\t\t\t} else {\n\t\t\t\t// is text with no width and height set\n\t\t\t\thasAutoWidthAndHeight = true;\n\n\t\t\t\tshapeOriginRect = rectCreateFromAny(shapeOriginComputed);\n\t\t\t\tconst size = textToSize(shapeOriginComputed.text, shapeOriginComputed);\n\t\t\t\tshapeOriginRect.width = size.width;\n\t\t\t\tshapeOriginRect.height = size.height;\n\t\t\t}\n\n\t\t\tlet shapeAspectRatio = aspectRatio;\n\n\t\t\tif (shapeCurrent.aspectRatio) {\n\t\t\t\tshapeAspectRatio = shapeCurrent.aspectRatio;\n\t\t\t} else if (options.shiftKey && !(hasAutoHeight || hasAutoWidthAndHeight)) {\n\t\t\t\tshapeAspectRatio = shapeOriginRect.width / shapeOriginRect.height;\n\t\t\t}\n\n\t\t\t// current shape\n\t\t\tconst rectAligned = rectCreateFromAny(shapeOriginRect);\n\n\t\t\tconst rectAlignedCenterPosition = rectCenter(rectAligned);\n\t\t\tconst rectRotation = shapeCurrent.rotation;\n\t\t\tconst rectAlignedCorners = rectGetCorners(rectAligned);\n\t\t\tconst rectCorners = rectRotate(rectAligned, rectRotation);\n\n\t\t\t// is translating one corner\n\t\t\tif (indexes.length === 1) {\n\t\t\t\t// corner\n\t\t\t\tlet cornerIndex = indexes[0];\n\n\t\t\t\tif (shapeCurrent.flipX) cornerIndex = IndexFlipXMap[cornerIndex];\n\t\t\t\tif (shapeCurrent.flipY) cornerIndex = IndexFlipYMap[cornerIndex];\n\t\t\t\tconst [tl, tr, br, bl] = rectAlignedCorners;\n\t\t\t\tconst screenCornerPosition = mapImagePointToEditorPoint(rectCorners[cornerIndex]);\n\t\t\t\tconst screenTargetPosition = vectorAdd({ ...screenCornerPosition }, translation);\n\t\t\t\tconst imageTargetPosition = mapEditorPointToImagePoint(screenTargetPosition);\n\t\t\t\tsnapPoint(imageTargetPosition, translation, () => mapEditorPointToImagePoint(screenCornerPosition));\n\t\t\t\tconst imageTargetTranslation = vectorCreate(imageTargetPosition.x - rectCorners[cornerIndex].x, imageTargetPosition.y - rectCorners[cornerIndex].y);\n\t\t\t\tconst imageTargetTranslationAligned = vectorRotate(vectorClone(imageTargetTranslation), -rectRotation);\n\t\t\t\tconst imageTargetPositionAligned = vectorCreate(rectAlignedCorners[cornerIndex].x + imageTargetTranslationAligned.x, rectAlignedCorners[cornerIndex].y + imageTargetTranslationAligned.y);\n\t\t\t\tlet anchor;\n\t\t\t\tif (cornerIndex === 0) anchor = br;\n\t\t\t\tif (cornerIndex === 1) anchor = bl;\n\t\t\t\tif (cornerIndex === 2) anchor = tl;\n\t\t\t\tif (cornerIndex === 3) anchor = tr;\n\n\t\t\t\t// create an aligned and updated rectangle\n\t\t\t\tconst rectAlignedResized = rectCreateFromPoints([anchor, imageTargetPositionAligned]);\n\n\t\t\t\t// limit rect\n\t\t\t\tif (shapeAspectRatio) {\n\t\t\t\t\t// get size that adheres to aspect ratio but still fits current rectangle\n\t\t\t\t\tconst { width, height } = rectContainRect(rectAlignedResized, shapeAspectRatio);\n\n\t\t\t\t\tconst [t, r, b, l] = rectToBounds(rectAlignedResized);\n\n\t\t\t\t\t// update size\n\t\t\t\t\trectAlignedResized.width = width;\n\n\t\t\t\t\trectAlignedResized.height = height;\n\n\t\t\t\t\t// align to anchor\n\t\t\t\t\tif (imageTargetPositionAligned.y < anchor.y) {\n\t\t\t\t\t\trectAlignedResized.y = b - height;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (imageTargetPositionAligned.x < anchor.x) {\n\t\t\t\t\t\trectAlignedResized.x = r - width;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// rotate the aligned rectangle around it's original pivot point\n\t\t\t\tconst rr = rectRotate(rectAlignedResized, rectRotation, rectAlignedCenterPosition);\n\n\t\t\t\t// now calculate the new center and then rotate the tl and br corners around that center to find their new positions\n\t\t\t\tconst rrc = vectorCenter(rr);\n\n\t\t\t\tconst p1 = vectorRotate(rr[0], -rectRotation, rrc);\n\t\t\t\tconst p2 = vectorRotate(rr[2], -rectRotation, rrc);\n\t\t\t\tconst rectUpdated = rectCreateFromPoints([p1, p2]);\n\t\t\t\tbeforeResizeShape(rectUpdated);\n\n\t\t\t\tupdateShape(\n\t\t\t\t\tshapeCurrent,\n\t\t\t\t\tshapeIsEllipse(shapeCurrent)\n\t\t\t\t\t? ellipseCreateFromRect(rectUpdated)\n\t\t\t\t\t: rectUpdated,\n\t\t\t\t\tparentRect\n\t\t\t\t);\n\t\t\t} else // is translating two corner points\n\t\t\t{\n\t\t\t\t// 0-1 -> 2-3\n\t\t\t\t// 1-2 -> 3-0\n\t\t\t\t// 2-3 -> 0-1\n\t\t\t\t// 3-0 -> 1-2\n\t\t\t\tindexes = indexes.map(index => {\n\t\t\t\t\tif (shapeCurrent.flipX) index = IndexFlipXMap[index];\n\t\t\t\t\tif (shapeCurrent.flipY) index = IndexFlipYMap[index];\n\t\t\t\t\treturn index;\n\t\t\t\t});\n\n\t\t\t\tconst [cornerA, cornerB] = indexes.map(index => rectCorners[index]);\n\n\t\t\t\tconst mid = {\n\t\t\t\t\tx: cornerA.x + (cornerB.x - cornerA.x) * 0.5,\n\t\t\t\t\ty: cornerA.y + (cornerB.y - cornerA.y) * 0.5\n\t\t\t\t};\n\n\t\t\t\tconst [cornerAlignedA, cornerAlignedB] = indexes.map(index => rectAlignedCorners[index]);\n\n\t\t\t\tconst [cornerAlignedC, cornerAlignedD] = indexes.map(index => {\n\t\t\t\t\tconst mappedIndex = index + 2;\n\t\t\t\t\tif (mappedIndex < 4) return rectAlignedCorners[mappedIndex];\n\t\t\t\t\treturn rectAlignedCorners[mappedIndex - 4];\n\t\t\t\t});\n\n\t\t\t\t// const midAligned = {\n\t\t\t\t// x: cornerAlignedA.x + (cornerAlignedB.x - cornerAlignedA.x) * .5,\n\t\t\t\t// y: cornerAlignedA.y + (cornerAlignedB.y - cornerAlignedA.y) * .5\n\t\t\t\t// };\n\t\t\t\tconst midAnchorAligned = {\n\t\t\t\t\tx: cornerAlignedC.x + (cornerAlignedD.x - cornerAlignedC.x) * 0.5,\n\t\t\t\t\ty: cornerAlignedC.y + (cornerAlignedD.y - cornerAlignedC.y) * 0.5\n\t\t\t\t};\n\n\t\t\t\tconst screenMidPosition = mapImagePointToEditorPoint(mid);\n\t\t\t\tconst screenTargetPosition = vectorAdd({ ...screenMidPosition }, translation);\n\t\t\t\tconst imageTargetPosition = mapEditorPointToImagePoint(screenTargetPosition);\n\t\t\t\tsnapPoint(imageTargetPosition, translation, () => mapEditorPointToImagePoint(screenMidPosition));\n\t\t\t\tconst imageTargetTranslation = vectorCreate(imageTargetPosition.x - mid.x, imageTargetPosition.y - mid.y);\n\t\t\t\tconst imageTargetTranslationAligned = vectorRotate(vectorClone(imageTargetTranslation), -rectRotation);\n\t\t\t\tconst d = vectorSubtract(vectorClone(cornerAlignedA), cornerAlignedB);\n\t\t\t\tconst f = vectorApply(d, v => 1 - Math.abs(Math.sign(v)));\n\t\t\t\tconst t = vectorCreate(imageTargetTranslationAligned.x * f.x, imageTargetTranslationAligned.y * f.y);\n\t\t\t\tvectorAdd(cornerAlignedA, t);\n\t\t\t\tvectorAdd(cornerAlignedB, t);\n\t\t\t\tconst rectAlignedResized = rectCreateFromPoints(rectAlignedCorners);\n\n\t\t\t\t// limit rect\n\t\t\t\tif (shapeAspectRatio) {\n\t\t\t\t\tlet width = rectAlignedResized.width;\n\t\t\t\t\tlet height = rectAlignedResized.height;\n\n\t\t\t\t\tif (f.y === 0) {\n\t\t\t\t\t\theight = width / shapeAspectRatio;\n\t\t\t\t\t} else {\n\t\t\t\t\t\twidth = height * shapeAspectRatio;\n\t\t\t\t\t}\n\n\t\t\t\t\t// update size\n\t\t\t\t\trectAlignedResized.width = width;\n\n\t\t\t\t\trectAlignedResized.height = height;\n\n\t\t\t\t\t// align to anchor\n\t\t\t\t\tif (f.y === 0) {\n\t\t\t\t\t\trectAlignedResized.y = midAnchorAligned.y - height * 0.5;\n\t\t\t\t\t} else {\n\t\t\t\t\t\trectAlignedResized.x = midAnchorAligned.x - width * 0.5;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// rotate the aligned rectangle around it's original pivot point\n\t\t\t\tconst rr = rectRotate(rectAlignedResized, rectRotation, rectAlignedCenterPosition);\n\n\t\t\t\t// now calculate the new center and then rotate the tl and br corners around that center to find their new positions\n\t\t\t\tconst rrc = vectorCenter(rr);\n\n\t\t\t\tconst p1 = vectorRotate(rr[0], -rectRotation, rrc);\n\t\t\t\tconst p2 = vectorRotate(rr[2], -rectRotation, rrc);\n\t\t\t\tconst rectUpdated = rectCreateFromPoints([p1, p2]);\n\t\t\t\tlet props;\n\n\t\t\t\tif (shapeIsEllipse(shapeCurrent)) {\n\t\t\t\t\tprops = ellipseCreateFromRect(rectUpdated);\n\t\t\t\t} else if (shapeHasSize(shapeCurrent)) {\n\t\t\t\t\tprops = rectUpdated;\n\t\t\t\t} else if (hasAutoHeight) {\n\t\t\t\t\tprops = {\n\t\t\t\t\t\tx: rectUpdated.x,\n\t\t\t\t\t\ty: rectUpdated.y,\n\t\t\t\t\t\twidth: rectUpdated.width\n\t\t\t\t\t};\n\t\t\t\t}\n\n\t\t\t\tupdateShape(shapeCurrent, props, parentRect);\n\t\t\t}\n\t\t}\n\n\t\t// redraw\n\t\tsyncShapes();\n\t};\n\n\tlet rotatorInitialPosition;\n\n\tconst rotateShape = (shapeCurrent, shapeOriginComputed, translation, options) => {\n\t\t// calculate angle between translated rotation control and origin\n\t\tconst shapeRect = getMarkupShapeRect(shapeComputeDisplay(shapeDeepCopy(shapeCurrent), parentRect));\n\n\t\tconst shapeCenter = rectCenter(shapeRect);\n\t\tconst p = getImagePointWithScreenTranslation(rotatorInitialPosition, translation);\n\t\tlet shapeRotation = vectorAngleBetween(p, shapeCenter) + Math.PI / 2;\n\n\t\tif (options.shiftKey) {\n\t\t\tconst rotatorSnapInterval = Math.PI / 16;\n\t\t\tshapeRotation = rotatorSnapInterval * Math.round(shapeRotation / rotatorSnapInterval) - contextRotation % rotatorSnapInterval;\n\t\t}\n\n\t\tupdateShape(shapeCurrent, { rotation: shapeRotation }, parentRect);\n\t\tsyncShapes();\n\t};\n\n\tconst scaleShape = (shapeCurrent, shapeOriginComputed, translation, options) => {\n\t\t// calculate current size\n\t\tconst shapeOriginRect = rectCreateFromAny(shapeOriginComputed);\n\n\t\tconst textSize = textToSize(shapeOriginComputed.text, shapeOriginComputed);\n\t\tif (!shapeOriginRect.width) shapeOriginRect.width = textSize.width;\n\t\tif (!shapeOriginRect.height) shapeOriginRect.height = textSize.height;\n\n\t\t// get corner index\n\t\tlet index = 2;\n\n\t\tif (shapeCurrent.flipX && shapeCurrent.flipY) {\n\t\t\tindex = 0;\n\t\t} else if (shapeCurrent.flipX) {\n\t\t\tindex = 3;\n\t\t} else if (shapeCurrent.flipY) {\n\t\t\tindex = 1;\n\t\t}\n\n\t\t// resize from bottom right corner\n\t\tresizeShape(shapeCurrent, shapeOriginComputed, [index], translation, {\n\t\t\t// pass base options\n\t\t\t...options,\n\t\t\t// we fix the aspect ratio\n\t\t\taspectRatio: shapeOriginRect.width / shapeOriginRect.height,\n\t\t\t// before we update the shape we scale the text\n\t\t\tbeforeResizeShape: shape => {\n\t\t\t\tconst scalar = shape.width / shapeOriginRect.width;\n\t\t\t\tshape.fontSize = shapeOriginComputed.fontSize * scalar;\n\t\t\t\tif (!shapeOriginComputed.width) delete shape.width;\n\t\t\t\tif (!shapeOriginComputed.height) delete shape.height;\n\t\t\t}\n\t\t});\n\t};\n\n\t//#endregion\n\tconst addPointToPath = (shape, point) => {\n\t\t// poly to edges\n\t\tconst points = shape.points;\n\n\t\tconst edges = [];\n\n\t\tfor (let i = 0; i < points.length - 1; i++) {\n\t\t\tedges.push([points[i], points[i + 1]]);\n\t\t}\n\n\t\tedges.push([points[points.length - 1], points[0]]);\n\n\t\t// find edge closest to position\n\t\tconst closestEdgeIndex = edges.findIndex(([from, to]) => {\n\t\t\treturn circleOverlapsWithLine(point, 6, from, to);\n\t\t});\n\n\t\t// insert point\n\t\tlet selectedPoint;\n\n\t\tconst newPoints = [];\n\n\t\tfor (let i = 0; i < edges.length; i++) {\n\t\t\tnewPoints.push(edges[i][0]);\n\n\t\t\tif (i === closestEdgeIndex) {\n\t\t\t\tselectedPoint = i + 1;\n\t\t\t\tnewPoints.push(point);\n\t\t\t}\n\t\t}\n\n\t\t// redraw shape\n\t\tupdateMarkupShape(shape, { points: newPoints, selectedPoint });\n\t};\n\n\tconst removePointFromPath = (shape, pointIndex) => {\n\t\tconst newPoints = shape.points.filter((shape, index) => index !== pointIndex);\n\n\t\t// redraw shape\n\t\tupdateMarkupShape(shape, {\n\t\t\tpoints: newPoints,\n\t\t\tselectedPoint: undefined\n\t\t});\n\t};\n\n\tconst getMarkupItemDraft = () => {\n\t\tif (!$shapes.length) return;\n\t\treturn $shapes.find(shapeIsDraft);\n\t};\n\n\tconst getMarkupItemDraftIndex = () => {\n\t\tif (!$shapes.length) return;\n\t\treturn $shapes.findIndex(shapeIsDraft);\n\t};\n\n\tconst addMarkupItemDraft = shape => {\n\t\tif (getMarkupItemDraft()) return;\n\t\tshapeMakeDraft(shape);\n\t\treturn addShape(shape);\n\t};\n\n\tconst confirmMarkupItemDraft = () => {\n\t\tconst markupItem = getMarkupItemDraft();\n\t\tif (!markupItem) return;\n\t\tshapeMakeFinal(markupItem);\n\t\tsyncShapes();\n\t\treturn markupItem;\n\t};\n\n\tconst discardMarkupItemDraft = () => {\n\t\tif (!getMarkupItemDraft()) return;\n\t\tconst arrayClone = [...$shapes];\n\t\tarrayClone.splice(getMarkupItemDraftIndex(), 1);\n\t\tshapes.set(arrayClone);\n\t};\n\n\tconst createMarkupItem = (props = {}) => ({ id: getUniqueId(), ...props });\n\n\tconst syncShapes = () => {\n\t\tshapes.set($shapes);\n\t};\n\n\tconst removeMarkupShapeProps = (shape, props = [], sync = true) => {\n\t\tprops.filter(Boolean).forEach(prop => delete shape[prop]);\n\n\t\t// sync markup array\n\t\tif (!sync) return;\n\n\t\tsyncShapes();\n\t};\n\n\tconst updateMarkupShape = (shape, props, sync = true) => {\n\t\t// update markup\n\t\tshape = Object.assign(shape, props);\n\n\t\t// sync markup array\n\t\tif (!sync) return;\n\n\t\tsyncShapes();\n\t};\n\n\tconst updateMarkupShapeProperty = (shape, name, value, sync = true) => {\n\t\tshape[name] = value;\n\n\t\t// sync markup array\n\t\tif (!sync) return;\n\n\t\tsyncShapes();\n\t};\n\n\tconst updateMarkupItemsShapeProperty = (name, value, sync = true) => {\n\t\t$shapes.forEach(shape => updateMarkupShapeProperty(shape, name, value, false));\n\n\t\t// sync markup array\n\t\tif (!sync) return;\n\n\t\tsyncShapes();\n\t};\n\n\tconst updateMarkupShapeItems = (props, sync = true) => {\n\t\t$shapes.forEach(markupItem => updateMarkupShape(markupItem, props, false));\n\n\t\t// sync markup array\n\t\tif (!sync) return;\n\n\t\tsyncShapes();\n\t};\n\n\tconst getActiveMarkupItems = () => [...$shapes].reverse().filter(shapeIsSelected);\n\tconst getActiveMarkupItem = () => [...$shapes].reverse().find(shapeIsSelected);\n\tconst hasActiveMarkupItem = () => !!getActiveMarkupItem();\n\n\tconst removeShape = (shapeToRemove, silent = false) => {\n\t\tif (!beforeRemoveShape(shapeToRemove)) return false;\n\t\tshapes.set($shapes.filter(shape => shape !== shapeToRemove));\n\t\tif (!silent) onremoveshape(shapeToRemove);\n\t};\n\n\tconst removeMarkupItem = (shapeToRemove, autoSelectNextShape = true, silent = false) => {\n\t\t// get removable markup items,\n\t\tconst removableShapes = $shapes.filter(shape => shapeCanRemove(shape) && shapeCanSelect(shape));\n\n\t\t// find index of active shape so we can select the next active shape on removal\n\t\tconst index = removableShapes.findIndex(shape => shape === shapeToRemove);\n\n\t\t// remove the active ship from the shape array\n\t\tconst didRemove = removeShape(shapeToRemove, silent);\n\n\t\tif (didRemove === false) return;\n\n\t\t// remember selection\n\t\tpreviousSelectedShape = shapeToRemove;\n\n\t\t// if no more markup items, release active shape\n\t\tif (removableShapes.length - 1 <= 0) return blurShapes();\n\n\t\t// if we shouldn't select the next available shape, bail\n\t\tif (!autoSelectNextShape) return true;\n\n\t\t// select next active markup item\n\t\tconst indexNext = index - 1 < 0 ? removableShapes.length - 1 : index - 1;\n\n\t\tselectShape(removableShapes[indexNext]);\n\n\t\t// did remove shape\n\t\treturn true;\n\t};\n\n\tconst removeActiveMarkupItem = () => {\n\t\t// multiple shapes selected\n\t\tif (isMultiSelection) {\n\t\t\tconst removedShapes = [];\n\n\t\t\tmultiSelection.forEach((shape, index) => {\n\t\t\t\tif (!shapeCanRemove(shape)) return;\n\t\t\t\tconst removed = removeMarkupItem(shape, index === multiSelection.length - 1, true);\n\t\t\t\tif (removed) removedShapes.push(shape);\n\t\t\t});\n\n\t\t\tif (removedShapes.length) ontriggerhistorywrite();\n\t\t\treturn;\n\t\t}\n\n\t\t// single shape selected\n\t\tconst activeShape = getActiveMarkupItem();\n\n\t\tif (!activeShape) return;\n\t\tremoveMarkupItem(activeShape);\n\t};\n\n\tlet previousSelectedShape = undefined;\n\n\tconst blurShapes = (options = {}) => {\n\t\tconst { storePrevious = true } = options;\n\n\t\t// clear text state cache, when a text shape is blurred the changes are confirmed\n\t\tObject.keys(ShapeTextStateCache).forEach(key => ShapeTextStateCache[key] = {});\n\n\t\t// get selected shape\n\t\tconst selectedShape = getSelectedShape();\n\n\t\t// remember shape\n\t\tif (storePrevious) previousSelectedShape = selectedShape;\n\n\t\tupdateMarkupShapeItems({\n\t\t\tisSelected: false,\n\t\t\tisEditing: false,\n\t\t\t_prerender: false\n\t\t});\n\n\t\tselectedShape && onblurshape(selectedShape);\n\t};\n\n\tconst getSelectedShape = () => $shapes.find(shapeIsSelected);\n\n\tconst selectShape = (shape, sync = true, multi = false) => {\n\t\t// can't select draft\n\t\tif (shapeIsDraft(shape)) return;\n\n\t\t// get currently selected shape\n\t\tconst selectedShapeCurrent = getSelectedShape();\n\n\t\tconst selectedShape = selectedShapeCurrent || previousSelectedShape;\n\n\t\t// was already selected\n\t\tconst wasSelectedShape = shapeIsSelected(shape);\n\n\t\t// reset previous selected shape\n\t\tpreviousSelectedShape = undefined;\n\n\t\t// if is returned false will cancel select operation\n\t\tif (!beforeSelectShape(selectedShape, shape)) return;\n\n\t\t// remove selected state from other markup\n\t\tif (!multi) blurShapes();\n\n\t\t// select\n\t\tshapeSelect(shape);\n\n\t\t// selected this shape\n\t\t!wasSelectedShape && onselectshape(shape);\n\n\t\t// sync\n\t\tif (!sync) return;\n\n\t\tsyncShapes();\n\t};\n\n\tconst deselectMarkupItem = markupItem => {\n\t\t// we're blurring an active text field, should confirm state if inline text editing\n\t\tif (textInput && textInput.confirm && markupItem.isEditing) textInput.confirm();\n\n\t\tconst newProps = {\n\t\t\tisSelected: false,\n\t\t\tisEditing: false,\n\t\t\t_prerender: false\n\t\t};\n\n\t\t// clear any selected points\n\t\tif (markupItem.selectedPoint > -1) {\n\t\t\tnewProps.selectedPoint = undefined;\n\t\t}\n\n\t\tupdateMarkupShape(markupItem, newProps);\n\t\tonblurshape(markupItem);\n\t};\n\n\tconst editMarkupItem = markupItem => {\n\t\tupdateMarkupShape(markupItem, {\n\t\t\tisSelected: true,\n\t\t\tisEditing: true,\n\t\t\t_prerender: textInputMode === 'inline'\n\t\t});\n\t};\n\n\tconst finishEditMarkupItem = markupItem => {\n\t\tupdateMarkupShape(markupItem, {\n\t\t\tisSelected: true,\n\t\t\tisEditing: false,\n\t\t\t_prerender: false\n\t\t});\n\t};\n\n\tconst removeMarkupItems = shapesToRemove => {\n\t\tif (!shapesToRemove.length) return [];\n\t\tconst shapesToRemoveFiltered = shapesToRemove.filter(beforeRemoveShape);\n\t\tshapes.set($shapes.filter(shape => !shapesToRemoveFiltered.includes(shape)));\n\t\treturn shapesToRemoveFiltered;\n\t};\n\n\tconst getTextShapeRect = shape => {\n\t\tconst size = textToSize(shape.text, shape);\n\n\t\treturn rectCreate(\n\t\t\tshape.x,\n\t\t\tshape.y,\n\t\t\tshape.width\n\t\t\t? Math.min(shape.width, size.width)\n\t\t\t: size.width,\n\t\t\tshape.height\n\t\t\t? Math.min(shape.height, size.height)\n\t\t\t: size.height\n\t\t);\n\t};\n\n\tconst getMarkupShapeRect = shape => {\n\t\t// has own rect\n\t\tif (shapeHasSize(shape)) return rectCreateFromAny(shape);\n\n\t\tif (shapeIsEllipse(shape)) return rectCreateFromEllipse(shape);\n\n\t\t// calculate the size on canvas\n\t\tconst rect = getTextShapeRect(shape);\n\n\t\trect.width = Math.max(MIN_TEXT_MARKUP_WIDTH, shape.width || rect.width);\n\t\treturn rect;\n\t};\n\n\tconst getShapesNearPosition = (shapes, position, range = 0, shapeFilter = () => true) => [...shapes].// reverse the array, want to select from top to bottom\n\treverse().// filter out shapes we're not interested in\n\tfilter(shapeFilter).// need priority indicator\n\tmap(shape => ({ shape, priority: 1 })).// find markup near pointer\n\tfilter(result => {\n\t\t// get shape\n\t\tconst { shape } = result;\n\n\t\t// we need to know where the shape will end up\n\t\tconst computedShape = shapeComputeDisplay(shapeDeepCopy(shape), parentRect);\n\n\t\t// add stroke width to range\n\t\tconst r = range + (computedShape.strokeWidth || 0) * 0.5;\n\n\t\t// test if clicked in rect\n\t\tif (shapeIsRect(computedShape)) {\n\t\t\treturn circleOverlapsWithRect(position, r, computedShape, shape.rotation);\n\t\t}\n\n\t\t// test if clicked on text\n\t\tif (shapeIsText(computedShape)) {\n\t\t\tconst shapeRect = getMarkupShapeRect(computedShape);\n\t\t\tconst isPositionInBounds = circleOverlapsWithRect(position, r, shapeRect, shape.rotation);\n\t\t\tlet isPositionInVisual = false;\n\n\t\t\tif (isPositionInBounds && !shapeIsSelected(shape)) {\n\t\t\t\tconst visualRect = getTextShapeRect(computedShape);\n\n\t\t\t\tif (shape.textAlign === 'right' && !shape.flipX) {\n\t\t\t\t\tvisualRect.x = shapeRect.x + shapeRect.width - visualRect.width;\n\t\t\t\t}\n\n\t\t\t\tif (shape.textAlign === 'center') {\n\t\t\t\t\tvisualRect.x = shapeRect.x + shapeRect.width * 0.5 - visualRect.width * 0.5;\n\t\t\t\t}\n\n\t\t\t\tisPositionInVisual = circleOverlapsWithRect(position, r, visualRect, shape.rotation, rectCenter(shapeRect));\n\t\t\t\tif (!isPositionInVisual) result.priority = -1;\n\t\t\t}\n\n\t\t\treturn isPositionInBounds;\n\t\t}\n\n\t\t// test if click on ellipse\n\t\tif (shapeIsEllipse(computedShape)) {\n\t\t\treturn circleOverlapsWithEllipse(position, r, computedShape, shape.rotation, shape.flipX, shape.flipY);\n\t\t}\n\n\t\t// test if clicked on line\n\t\tif (shapeIsLine(computedShape)) {\n\t\t\t// radius around click position, test if line intersects\n\t\t\treturn circleOverlapsWithLine(position, Math.max(16, r), shapeLineGetStartPoint(computedShape), shapeLineGetEndPoint(computedShape)); // always increase range when selecting lines\n\t\t}\n\n\t\t// test if clicked inside polygon\n\t\tif (shapeIsPath(computedShape) && computedShape.pathClose) {\n\t\t\treturn circleOverlapsWithPolygon(position, r, computedShape.points);\n\t\t}\n\n\t\t// test if clicked on path\n\t\tif (shapeIsPath(computedShape)) {\n\t\t\t// is a dot\n\t\t\tif (computedShape.points.length === 1) {\n\t\t\t\treturn vectorDistanceSquared(position, computedShape.points[0]) < r * r;\n\t\t\t}\n\n\t\t\t// radius around click position, test if line intersects\n\t\t\treturn circleOverlapsWithPath(position, Math.max(16, r), computedShape.points); // always increase range when selecting lines\n\t\t}\n\n\t\treturn false;\n\t}).sort((a, b) => {\n\t\tif (a.priority < b.priority) return 1;\n\t\tif (a.priority > b.priority) return -1;\n\t\treturn 0;\n\t}).map(item => item.shape);\n\n\tconst getShapesBetweenPoints = (shapes, a, b, range = 0) => {\n\t\t// make sure range is not negative\n\t\tconst r = Math.abs(range);\n\n\t\t// create line\n\t\tconst line = lineCreate(a, b);\n\n\t\t// eraseLine should be in image space\n\t\tconst eraseLine = lineExtend(line, r);\n\n\t\t// create line polygon\n\t\tconst erasePoly = lineExtrude(eraseLine, r);\n\n\t\t// loop over shapes and find intersecting shapes\n\t\tconst res = shapes.filter(shape => {\n\t\t\tconst computedShape = shapeComputeDisplay(shapeDeepCopy(shape), parentRect);\n\n\t\t\t// if shape is line or path\n\t\t\tif (shapeIsLine(computedShape) || shapeIsPath(computedShape)) {\n\t\t\t\tconst points = computedShape.points\n\t\t\t\t? [...computedShape.points]\n\t\t\t\t: [\n\t\t\t\t\t\tshapeLineGetStartPoint(computedShape),\n\t\t\t\t\t\tshapeLineGetEndPoint(computedShape)\n\t\t\t\t\t];\n\n\t\t\t\treturn !!linePointsIntersection(eraseLine, points);\n\t\t\t}\n\n\t\t\t// else turn into polygon and compare polygons\n\t\t\treturn polyIntersectsWithPoly(erasePoly, shapeToPoly(computedShape));\n\t\t});\n\n\t\treturn res;\n\t};\n\n\t//#region shape base interaction\n\tlet interactionPoint = undefined;\n\n\tlet interactionTimer = undefined;\n\tlet interactionTarget = undefined;\n\tlet interactionShape = undefined;\n\tlet interactionShapeOrigin = undefined;\n\tlet interactionShapeOriginComputed = undefined;\n\tlet interactionShapeWasSelected = false;\n\tlet interactionShapeWasConfirmed = false;\n\tlet interactionShapeTextDrag = false;\n\tlet isInteracting = false;\n\tlet multiSelectionInteraction = undefined;\n\n\tconst initMultiSelectionInteraction = shapes => {\n\t\tmultiSelectionInteraction = shapes.map(shape => {\n\t\t\treturn {\n\t\t\t\tshape,\n\t\t\t\tshapeOrigin: shapeDeepCopy(shape),\n\t\t\t\tshapeOriginComputed: shapeComputeDisplay(shapeDeepCopy(shape), parentRect)\n\t\t\t};\n\t\t});\n\t};\n\n\tconst handleInteractionStart = e => {\n\t\tconst { origin } = e.detail;\n\t\tconst isMultiSelecting = enableMultiSelect && $keysPressedStored.includes(16);\n\t\tmultiSelectionInteraction = undefined;\n\t\t$$invalidate(151, interactionShape = undefined);\n\t\tinteractionShapeOrigin = undefined;\n\t\tinteractionShapeOriginComputed = undefined;\n\t\tinteractionShapeWasSelected = false;\n\t\tinteractionTarget = undefined;\n\t\tinteractionShapeWasConfirmed = false;\n\t\tinteractionShapeTextDrag = false;\n\t\tclearInteractionSnapLines();\n\t\tclearTimeout(interactionTimer);\n\t\tinteractionTimer = setTimeout(() => $$invalidate(152, isInteracting = true), 250);\n\n\t\t// if is editing text\n\t\tconst draft = getMarkupItemDraft();\n\n\t\tif (draft && !(shapeIsPath(draft) && hasProp(draft, 'pathClose'))) {\n\t\t\tconfirmMarkupItemDraft();\n\t\t}\n\n\t\t// test if target is a shape, if not, run interaction handler\n\t\tinteractionPoint = mapEditorPointToImagePoint(vectorClone(origin));\n\n\t\t// if is shape path, we need to first finish path before we can do other actions\n\t\tif (draft && shapeIsPath(draft) && hasProp(draft, 'pathClose')) {\n\t\t\treturn oninteractionstart(e);\n\t\t}\n\n\t\tconst foundShapes = getShapesNearPosition($shapes, interactionPoint, selectRadius, shape => shapeCanSelect(shape));\n\t\tconst targettedMarkupItem = foundShapes.length && foundShapes.shift();\n\n\t\t// confirm text when moving between shapes\n\t\tif (activeMarkup && shapeIsTextEditing(activeMarkup)) {\n\t\t\tif (targettedMarkupItem !== activeMarkup) {\n\t\t\t\thandleTextConfirm();\n\t\t\t} else {\n\t\t\t\tconst isFixedBox = hasProp(activeMarkup, 'height');\n\t\t\t\tconst isAutoHeightBox = !isFixedBox && hasProp(activeMarkup, 'width');\n\n\t\t\t\t// auto-width cannot be dragged\n\t\t\t\tif (isFixedBox || isAutoHeightBox) {\n\t\t\t\t\t// test if is trying to drag fixed with shape?\n\t\t\t\t\tconst textTester = shapeDeepCopy(activeMarkup);\n\n\t\t\t\t\tconst textTesterComputed = shapeDeepCopy(textTester);\n\t\t\t\t\tshapeComputeDisplay(textTesterComputed, parentRect);\n\n\t\t\t\t\t// need current size\n\t\t\t\t\tconst textCurrentShapeSize = sizeApply(textToSize(textTester.text, textTesterComputed), v => Math.ceil(v));\n\n\t\t\t\t\tconst sizeFrom = sizeCreate(textTesterComputed.width, textTesterComputed.height || textCurrentShapeSize.height);\n\t\t\t\t\ttoAutoWidth(sizeFrom, textTester, textTesterComputed);\n\n\t\t\t\t\t// uncomment to see the auto-width shape (for debugging purposes)\n\t\t\t\t\t// addShape({\n\t\t\t\t\t// ...textTester,\n\t\t\t\t\t// disableSelect: true,\n\t\t\t\t\t// backgroundColor: [1, 0, 0, 0.25],\n\t\t\t\t\t// isSelected: false,\n\t\t\t\t\t// isEditing: false,\n\t\t\t\t\t// });\n\t\t\t\t\tconst shapesUnderCursor = getShapesNearPosition([{ ...textTester, strokeWidth: '5%' }], interactionPoint, 0);\n\n\t\t\t\t\tinteractionShapeTextDrag = shapesUnderCursor.length === 0;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// don't add new text shape when just confirmed another inline text shape\n\t\tif (activeMarkup && enableTapToAddText) {\n\t\t\tinteractionShapeWasConfirmed = true;\n\t\t}\n\n\t\t// deselect\n\t\tif (!targettedMarkupItem && activeMarkup && shapeIsTextEditing(activeMarkup)) {\n\t\t\tdeselectMarkupItem(activeMarkup);\n\t\t}\n\n\t\t// if is clicking outside of canvas, don't create new shape\n\t\tif (!willStartInteraction(origin)) return;\n\n\t\t// check if markup was targetted\n\t\tif (targettedMarkupItem && shapeIsSelected(targettedMarkupItem)) {\n\t\t\tinteractionShapeWasSelected = true;\n\t\t\t$$invalidate(151, interactionShape = targettedMarkupItem);\n\n\t\t\t// there's a multiselection if is holding shift we need to deselect this shape\n\t\t\tif (isMultiSelection) {\n\t\t\t\t// is holding shift\n\t\t\t\tif (isMultiSelecting) deselectMarkupItem(targettedMarkupItem);\n\n\t\t\t\t// reset multiselection\n\t\t\t\tinitMultiSelectionInteraction(multiSelection);\n\n\t\t\t\t// exit\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// create a deep copy so we can check on end interaction if the shape was changed\n\t\t\tinteractionShapeOrigin = shapeDeepCopy(interactionShape);\n\n\t\t\t// clone shape we're interacting with so we can update it properly\n\t\t\tinteractionShapeOriginComputed = shapeComputeDisplay(shapeDeepCopy(interactionShape), parentRect);\n\n\t\t\treturn;\n\t\t}\n\n\t\t// set target\n\t\tinteractionTarget = targettedMarkupItem || undefined;\n\n\t\t// started interacting\n\t\tconst didStartInteraction = oninteractionstart(e);\n\n\t\t// drag current targetted it as fallback interaction,\n\t\t// helps make sticker interaction easier\n\t\tif (!didStartInteraction && targettedMarkupItem) {\n\t\t\t// select\n\t\t\tselectShape(targettedMarkupItem, true, isMultiSelecting);\n\n\t\t\t$$invalidate(151, interactionShape = targettedMarkupItem);\n\n\t\t\t// handle multiselection\n\t\t\tif (isMultiSelection) return initMultiSelectionInteraction(multiSelection);\n\n\t\t\t// create a deep copy so we can check on end interaction if the shape was changed\n\t\t\tinteractionShapeOrigin = shapeDeepCopy(interactionShape);\n\n\t\t\t// clone shape we're interacting with so we can update it properly\n\t\t\tinteractionShapeOriginComputed = shapeComputeDisplay(shapeDeepCopy(interactionShape), parentRect);\n\t\t}\n\t};\n\n\tconst resetInteraction = () => {\n\t\tclearTimeout(interactionTimer);\n\t\tinteractionTimer = undefined;\n\t\t$$invalidate(152, isInteracting = false);\n\t\tclearInteractionSnapLines();\n\t};\n\n\tconst handleInteractionCancel = e => {\n\t\tresetInteraction();\n\t\toninteractioncancel(e);\n\t};\n\n\tlet axisOverride = null;\n\n\tconst handleInteractionUpdate = e => {\n\t\tconst { translation, ctrlKey, metaKey, shiftKey, position } = e.detail;\n\n\t\t// is interacting with shape\n\t\tif (interactionShape) {\n\t\t\t// prevent moving if not allowed\n\t\t\tif (!shapeCanMove(interactionShape)) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// prevent moving if is editing text\n\t\t\tif (shapeIsTextEditing(interactionShape)) {\n\t\t\t\tif (!interactionShapeTextDrag) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\thandleTextConfirm();\n\t\t\t}\n\n\t\t\t// if shift pressed, we pick biggest axis translation and use that\n\t\t\tif (shiftKey) {\n\t\t\t\tlet tx = Math.abs(translation.x);\n\t\t\t\tlet ty = Math.abs(translation.y);\n\t\t\t\tif (axisOverride === 'x') ty -= 64;\n\t\t\t\tif (axisOverride === 'y') tx -= 64;\n\n\t\t\t\tif (tx > ty) {\n\t\t\t\t\ttranslation.y = 0;\n\t\t\t\t\taxisOverride = 'x';\n\t\t\t\t} else if (ty > tx) {\n\t\t\t\t\ttranslation.x = 0;\n\t\t\t\t\taxisOverride = 'y';\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\taxisOverride = null;\n\t\t\t}\n\n\t\t\t// if control or cmd pressed, prevent snapping\n\t\t\tconst preventSnap = ctrlKey || metaKey;\n\n\t\t\t// is interacting with multiple shapes\n\t\t\tif (multiSelectionInteraction) {\n\t\t\t\t// cannot move all shapes in multi selection\n\t\t\t\tif (!multiSelection.every(shapeCanMove)) return;\n\n\t\t\t\t// let's move shapes\n\t\t\t\tmultiSelectionInteraction.forEach(({ shape, shapeOriginComputed }) => {\n\t\t\t\t\ttranslateShape(shape, shapeOriginComputed, translation, {\n\t\t\t\t\t\t...getSnapOptions(0, gridSize, gridRect, shape), // (TODO: implement snapping for multi selection) preventSnap ? 0 : snapThreshold,\n\t\t\t\t\t\t\n\t\t\t\t\t});\n\t\t\t\t});\n\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\treturn translateShape(interactionShape, interactionShapeOriginComputed, translation, {\n\t\t\t\t...getSnapOptions(preventSnap ? 0 : snapThreshold, gridSize, gridRect, interactionShape)\n\t\t\t});\n\t\t}\n\n\t\toninteractionupdate(e);\n\t};\n\n\tconst handleInteractionRelease = e => {\n\t\tresetInteraction();\n\n\t\tif (!interactionShape) {\n\t\t\toninteractionrelease(e);\n\t\t\treturn;\n\t\t}\n\n\t\t// test if is text and if we double tapped, if so, switch to text edit mode\n\t\t// don't run these interactions if we've selected multiple items\n\t\tif (isMultiSelection) return;\n\n\t\t// tapped on path shape\n\t\tif (e.detail.isTap && shapeIsPath(interactionShape) && shapeCanAddPoint(interactionShape)) {\n\t\t\tconst { position } = e.detail;\n\t\t\tconst imagePoint = mapEditorPointToImagePoint(vectorClone(position));\n\t\t\taddPointToPath(interactionShape, imagePoint);\n\t\t\treturn;\n\t\t} else // if tapped shape, and shape is text, and shape was selected\n\t\tif (e.detail.isTap && interactionShapeWasSelected && shapeIsText(interactionShape) && shapeCanInput(interactionShape) !== false) {\n\t\t\teditMarkupItem(interactionShape);\n\t\t}\n\t};\n\n\tconst handleInteractionEnd = e => {\n\t\tconst isSelectInteraction = interactionTarget && e.detail.isTap;\n\t\tconst isMultiSelecting = enableMultiSelect && $keysPressedStored.includes(16);\n\n\t\tif (e.detail.isTap) {\n\t\t\tsetTimeout(() => {\n\t\t\t\tontapcanvas(\n\t\t\t\t\t{\n\t\t\t\t\t\ttarget: interactionShape || interactionTarget,\n\t\t\t\t\t\tposition: interactionPoint\n\t\t\t\t\t},\n\t\t\t\t\t0\n\t\t\t\t);\n\t\t\t});\n\t\t}\n\n\t\t// shape remains active so user can resize, rotate\n\t\tif (interactionShape) {\n\t\t\tontapshape(interactionShape);\n\n\t\t\tif (!shapeEqual(interactionShape, interactionShapeOrigin)) {\n\t\t\t\tonupdateshape(interactionShape);\n\t\t\t}\n\n\t\t\t$$invalidate(151, interactionShape = undefined);\n\t\t\treturn;\n\t\t}\n\n\t\t// if we're not multiselecting we have to deselect currently selected shape\n\t\tlet allowDeselectShape = true;\n\n\t\tif (!isMultiSelecting) {\n\t\t\t// can we deselect the current shape?\n\t\t\tconst currentSelectedShape = getSelectedShape();\n\n\t\t\tallowDeselectShape = currentSelectedShape\n\t\t\t? beforeDeselectShape(currentSelectedShape, interactionTarget || undefined)\n\t\t\t: true;\n\n\t\t\tif (allowDeselectShape) {\n\t\t\t\tblurShapes({ storePrevious: false });\n\t\t\t}\n\t\t}\n\n\t\toninteractionend(e);\n\n\t\tif (allowDeselectShape && isSelectInteraction) {\n\t\t\tselectShape(interactionTarget, true, isMultiSelecting);\n\t\t}\n\t};\n\n\t//#endregion\n\tconst getMultiSelectionPoints = shapes => {\n\t\tconst shapePoints = shapes.map(shape => getMarkupShapePoints(shapeComputeDisplay(shape, parentRect))).flat();\n\t\tconst selectionRect = rectCreateFromPoints(shapePoints);\n\t\treturn rectGetCorners(selectionRect);\n\t};\n\n\t//\n\t// Shape manipulation\n\t//\n\tconst getMarkupShapePoints = shape => {\n\t\tconst hasRotation = isNumber(shape.rotation) && shape.rotation !== 0;\n\n\t\tif (shapeIsRect(shape)) {\n\t\t\t// too small to draw\n\t\t\tif (shape.width < 1 && shape.height < 1) return;\n\n\t\t\tconst center = rectCenter(shape);\n\t\t\tconst points = rectGetCorners(shape);\n\n\t\t\tif (shape.flipX || shape.flipY) {\n\t\t\t\tvectorsFlip(points, shape.flipX, shape.flipY, center.x, center.y);\n\t\t\t}\n\n\t\t\treturn hasRotation\n\t\t\t? vectorsRotate(points, shape.rotation, center.x, center.y)\n\t\t\t: points;\n\t\t}\n\n\t\tif (shapeIsEllipse(shape)) {\n\t\t\tif (shape.rx < 1 && shape.ry < 1) return;\n\t\t\tconst center = shape;\n\t\t\tconst points = rectGetCorners(rectCreateFromEllipse(shape));\n\n\t\t\tif (shape.flipX || shape.flipY) {\n\t\t\t\tvectorsFlip(points, shape.flipX, shape.flipY, center.x, center.y);\n\t\t\t}\n\n\t\t\treturn hasRotation\n\t\t\t? vectorsRotate(points, shape.rotation, center.x, center.y)\n\t\t\t: points;\n\t\t}\n\n\t\tif (shapeIsLine(shape)) {\n\t\t\treturn [shapeLineGetStartPoint(shape), shapeLineGetEndPoint(shape)];\n\t\t}\n\n\t\tif (shapeIsPath(shape)) {\n\t\t\tif (shape.bitmap && !shape.pathClose) {\n\t\t\t\tconst rect = rectCreateFromPoints(shape.points);\n\t\t\t\trect.x -= shape.strokeWidth * 0.5;\n\t\t\t\trect.y -= shape.strokeWidth * 0.5;\n\t\t\t\trect.width += shape.strokeWidth;\n\t\t\t\trect.height += shape.strokeWidth;\n\t\t\t\tconst points = getMarkupShapePoints({ ...shape, ...rect });\n\t\t\t\treturn points;\n\t\t\t}\n\n\t\t\treturn [...shape.points];\n\t\t}\n\n\t\tif (shapeIsText(shape)) {\n\t\t\t// too small to draw\n\t\t\tif (shape.width < 5 && shape.height < 5) return;\n\n\t\t\tconst rect = getMarkupShapeRect(shape);\n\t\t\trect.width = Math.max(MIN_TEXT_MARKUP_WIDTH, rect.width);\n\t\t\tconst center = rectCenter(rect);\n\t\t\tconst points = rectGetCorners(rect);\n\n\t\t\tif (shape.flipX || shape.flipY) {\n\t\t\t\tvectorsFlip(points, shape.flipX, shape.flipY, center.x, center.y);\n\t\t\t}\n\n\t\t\treturn hasRotation\n\t\t\t? vectorsRotate(points, shape.rotation, center.x, center.y)\n\t\t\t: points;\n\t\t}\n\n\t\treturn [];\n\t};\n\n\tconst getShapeRotationPoint = shape => {\n\t\tconst points = getMarkupShapePoints(shape);\n\t\tlet origin;\n\t\tlet dir;\n\n\t\tif (shape.flipY) {\n\t\t\torigin = vectorCenter([points[0], points[1]]);\n\t\t\tdir = vectorNormalize(vectorCreate(points[1].x - points[2].x, points[1].y - points[2].y));\n\t\t} else {\n\t\t\torigin = vectorCenter([points[2], points[3]]);\n\t\t\tdir = vectorNormalize(vectorCreate(points[2].x - points[1].x, points[2].y - points[1].y));\n\t\t}\n\n\t\tvectorMultiply(dir, SHAPE_CONTROL_OFFSET / contextZoom);\n\t\treturn { origin, dir };\n\t};\n\n\t// const getShapeScalarPoint = (shape) => {\n\t// const points = getMarkupShapePoints(shape);\n\t// return { ...points[1] };\n\t// /*\n\t// let origin;\n\t// let dir;\n\t// if (shape.flipY) {\n\t// origin = { ...points[1] };\n\t// dir = vectorNormalize(\n\t// vectorCreate(points[1].x - points[2].x, points[1].y - points[2].y)\n\t// );\n\t// }\n\t// // normal\n\t// else {\n\t// origin = { ...points[2] };\n\t// dir = vectorNormalize(\n\t// vectorCreate(points[2].x - points[1].x, points[2].y - points[1].y)\n\t// );\n\t// }\n\t// vectorRotate(dir, -Math.PI / 4);\n\t// vectorMultiply(dir, SHAPE_CONTROL_OFFSET / contextZoom);\n\t// */\n\t// // return {\n\t// // origin,\n\t// // dir,\n\t// // };\n\t// };\n\tconst getShapeRotationPointOnScreen = shape => {\n\t\tconst markupPoint = getShapeRotationPoint(shape);\n\n\t\tconst screenPosition = mapImagePointToEditorPoint({\n\t\t\tx: markupPoint.origin.x + markupPoint.dir.x,\n\t\t\ty: markupPoint.origin.y + markupPoint.dir.y\n\t\t});\n\n\t\tconst screenOrigin = mapImagePointToEditorPoint(markupPoint.origin);\n\n\t\treturn {\n\t\t\torigin: screenOrigin,\n\t\t\tposition: screenPosition\n\t\t};\n\t};\n\n\tconst getShapeScalarPointOnScreen = shape => {\n\t\tconst points = getMarkupShapePoints(shape);\n\t\tlet index = 2;\n\n\t\tif (shape.flipX && shape.flipY) {\n\t\t\tindex = 0;\n\t\t} else if (shape.flipX) {\n\t\t\tindex = 3;\n\t\t} else if (shape.flipY) {\n\t\t\tindex = 1;\n\t\t}\n\n\t\tconst markupPoint = { ...points[index] };\n\t\tconst screenPosition = mapImagePointToEditorPoint({ x: markupPoint.x, y: markupPoint.y });\n\t\treturn screenPosition;\n\t};\n\n\tfunction getAllowEdgeControls(activeMarkup) {\n\t\tif (!activeMarkup) return false;\n\n\t\t// if is non width & height text, we render controls on edges\n\t\tif (shapeIsText(activeMarkup) && !activeMarkup.height) {\n\t\t\t// for auto-height\n\t\t\treturn [[1, 2], [3, 0]];\n\t\t}\n\n\t\t// never on lines and paths\n\t\tif (shapeIsLine(activeMarkup) || shapeIsPath(activeMarkup)) return false;\n\n\t\t// allowed\n\t\treturn true;\n\t}\n\n\t// UI manipulator functions\n\tconst createIsNotComparison = fn => shape => !fn(shape);\n\n\tconst createShapeComparison = id => shape => shape.id === id;\n\n\tconst createShapeFilter = id => {\n\t\tconst isShapeMatchingId = createShapeComparison(id);\n\t\treturn shapes => shapes.filter(createIsNotComparison(isShapeMatchingId));\n\t};\n\n\tconst createShapeRemover = id => {\n\t\tconst isShapeMatchingId = createShapeComparison(id);\n\t\tconst shapeFilter = createShapeFilter(id);\n\n\t\treturn () => ui.update(shapes => {\n\t\t\tif (!shapes.some(isShapeMatchingId)) return shapes;\n\t\t\treturn shapeFilter(shapes);\n\t\t});\n\t};\n\n\tconst createShapePusher = id => {\n\t\tconst shapeFilter = createShapeFilter(id);\n\n\t\treturn (...newShapes) => {\n\t\t\tui.update(shapes => [...shapeFilter(shapes), ...newShapes.filter(Boolean)]);\n\t\t};\n\t};\n\n\tconst popHoverShapes = createShapeRemover(HoverShapeId);\n\tconst pushHoverShapes = createShapePusher(HoverShapeId);\n\n\tconst redrawHoverShapes = shape => {\n\t\tconst shapePoints = getMarkupShapePoints(shapeComputeDisplay(shapeDeepCopy(shape), parentRect));\n\t\tif (!shapePoints) return;\n\t\tconst screenPoints = shapePoints.map(mapImagePointToEditorPoint);\n\n\t\tconst pathClose = shape.pathClose\n\t\t? true\n\t\t: !shape.bitmap && (shapeIsPath(shape) || shapeIsLine(shape))\n\t\t\t? false\n\t\t\t: true;\n\n\t\tconst isHookStyle = shape.selectionStyle === 'hook';\n\n\t\tconst outlineShadow = isHookStyle\n\t\t? screenPoints.map((p, index, arr) => ({\n\t\t\t\tid: HoverShapeId,\n\t\t\t\tpoints: getManipulatorSelectionHookShape(arr, index),\n\t\t\t\tstrokeColor: [0, 0, 0, 0.1],\n\t\t\t\tstrokeWidth: 2\n\t\t\t}))\n\t\t: [\n\t\t\t\t{\n\t\t\t\t\tid: HoverShapeId,\n\t\t\t\t\tpoints: screenPoints.map(p => vectorCreate(p.x + 1, p.y + 1)),\n\t\t\t\t\tstrokeColor: [0, 0, 0, 0.1],\n\t\t\t\t\tstrokeWidth: 2,\n\t\t\t\t\tpathClose\n\t\t\t\t}\n\t\t\t];\n\n\t\tconst outline = isHookStyle\n\t\t? screenPoints.map((p, index, arr) => ({\n\t\t\t\tid: HoverShapeId,\n\t\t\t\tpoints: getManipulatorSelectionHookShape(arr, index),\n\t\t\t\tstrokeColor: hoverColor,\n\t\t\t\tstrokeWidth: 2\n\t\t\t}))\n\t\t: [\n\t\t\t\t{\n\t\t\t\t\tid: HoverShapeId,\n\t\t\t\t\tpoints: screenPoints,\n\t\t\t\t\tstrokeColor: hoverColor,\n\t\t\t\t\tstrokeWidth: 2,\n\t\t\t\t\tpathClose\n\t\t\t\t}\n\t\t\t];\n\n\t\tpushHoverShapes(...outlineShadow, ...outline);\n\t};\n\n\tlet hoverShape;\n\n\t// draw shape manipulator edges\n\tconst ManipulatorShapeId = `markup-manipulator-segment-` + uid;\n\n\tconst popManipulatorShapes = createShapeRemover(ManipulatorShapeId);\n\tconst pushManipulatorShapes = createShapePusher(ManipulatorShapeId);\n\n\tconst getManipulatorSelectionHookShape = (points, index) => {\n\t\tconst currentPoint = points[index];\n\n\t\tconst previousPoint = index - 1 < 0\n\t\t? points[points.length - 1]\n\t\t: points[index - 1];\n\n\t\tconst nextPoint = index + 1 < points.length\n\t\t? points[index + 1]\n\t\t: points[0];\n\n\t\t// line from currentPoint to previous point\n\t\tconst cp = vectorSubtract(vectorClone(previousPoint), currentPoint);\n\n\t\tvectorNormalize(cp);\n\t\tconst start = vectorAdd(vectorMultiply(cp, 10), currentPoint);\n\n\t\t// line from currentPoint to next point\n\t\tconst cn = vectorSubtract(vectorClone(nextPoint), currentPoint);\n\n\t\tvectorNormalize(cn);\n\t\tconst end = vectorAdd(vectorMultiply(cn, 10), currentPoint);\n\t\treturn [start, currentPoint, end];\n\t};\n\n\tconst redrawManipulatorShapes = (opacity, shape, pointerPosition) => {\n\t\tif (!shapeCanSelect(shape)) return;\n\t\tconst isPath = shapeIsPath(shape);\n\t\tconst isPolygon = isPath && hasProp(shape, 'pathClose');\n\t\tconst hasStroke = isPolygon && shapeHasStroke(shape);\n\t\tconst isDraft = shapeIsDraft(shape);\n\t\tconst isTextEditing = shapeIsTextEditing(shape);\n\t\tconst isHookStyle = shape.selectionStyle === 'hook';\n\t\tconst shouldDrawPoints = shape.disableShowPoints !== true;\n\t\tconst { selectionOpacity = 1 } = shape;\n\n\t\t// don't draw line manipulator on sharpie draft\n\t\tif (isDraft && isPath && !isPolygon) return;\n\n\t\t// create manipulator outline segments\n\t\tconst segments = [];\n\n\t\tconst screenPoints = [...selectionScreenPoints];\n\t\tconst screenPointsLast = screenPoints[screenPoints.length - 1];\n\t\tconst screenPointsFirst = screenPoints[0];\n\t\tconst shadowOpacity = (isTextEditing ? 0.05 : 0.1) * opacity * selectionOpacity;\n\t\tconst shadowVisible = shadowOpacity > 0;\n\t\tconst lineOpacity = (isTextEditing ? 0.5 : 1) * opacity * selectionOpacity;\n\t\tconst lineVisible = lineOpacity > 0;\n\t\tconst strokeColorShadow = [0, 0, 0];\n\t\tconst strokeColor = [1, 1, 1];\n\t\tconst strokeWidth = isTextEditing ? 1 : 1.5;\n\n\t\t// if shift is held snap to last point\n\t\tif ($keysPressedStored.includes(16) && screenPointsLast && pointerPosition) positionRelativeToAnchor(screenPointsLast, pointerPosition);\n\n\t\tconst distToOriginSquared = screenPoints.length && isPolygon && isDraft && pointerPosition\n\t\t? vectorDistanceSquared(screenPointsFirst, pointerPosition)\n\t\t: Infinity;\n\n\t\tconst distToLastSquared = screenPoints.length && isPolygon && isDraft && pointerPosition\n\t\t? vectorDistanceSquared(screenPointsLast, pointerPosition)\n\t\t: Infinity;\n\n\t\tconst canClosePath = distToOriginSquared <= POLYGON_END_PATH_DISTANCE_SQUARED;\n\t\tconst canEndPath = distToLastSquared <= POLYGON_END_PATH_DISTANCE_SQUARED;\n\n\t\t// if we can't close the path we draw a line from the last point to the pointer position\n\t\tif (!canClosePath && pointerPosition) screenPoints.push(pointerPosition);\n\n\t\tconst canDrawLine = screenPoints.length > 1;\n\n\t\tconst closeSelectionPath = shape.pathClose\n\t\t? true\n\t\t: !shape.bitmap && (shapeIsPath(shape) || shapeIsLine(shape))\n\t\t\t? screenPoints.length > 2 && canClosePath\n\t\t\t: true;\n\n\t\t// shadows\n\t\tif (canDrawLine && shadowVisible) {\n\t\t\t// draw corner hooks\n\t\t\tisHookStyle\n\t\t\t? segments.push(...screenPoints.map((p, index, arr) => ({\n\t\t\t\t\tid: ManipulatorShapeId,\n\t\t\t\t\tpoints: getManipulatorSelectionHookShape(arr, index),\n\t\t\t\t\tstrokeColor: strokeColorShadow,\n\t\t\t\t\topacity: shadowOpacity\n\t\t\t\t})))\n\t\t\t: // draw rectangle\n\t\t\t\tsegments.push({\n\t\t\t\t\tid: ManipulatorShapeId,\n\t\t\t\t\tpoints: screenPoints.map(p => vectorCreate(p.x + 1, p.y + 1)),\n\t\t\t\t\tpathClose: closeSelectionPath,\n\t\t\t\t\tstrokeColor: strokeColorShadow,\n\t\t\t\t\tstrokeWidth: 2,\n\t\t\t\t\topacity: shadowOpacity\n\t\t\t\t});\n\t\t}\n\n\t\tisPolygon && shadowVisible && shouldDrawPoints && segments.push(...screenPoints.map(point => {\n\t\t\treturn {\n\t\t\t\tid: ManipulatorShapeId,\n\t\t\t\t...point,\n\t\t\t\trx: strokeWidth * 2,\n\t\t\t\try: strokeWidth * 2,\n\t\t\t\tbackgroundColor: strokeColorShadow,\n\t\t\t\topacity: shadowOpacity\n\t\t\t};\n\t\t}));\n\n\t\tif (shapeManipulatorRotationPoint && shadowVisible) {\n\t\t\t// add rotator line shadow\n\t\t\tsegments.push({\n\t\t\t\tid: ManipulatorShapeId,\n\t\t\t\tpoints: [\n\t\t\t\t\tvectorCreate(shapeManipulatorRotationPoint.origin.x + 1, shapeManipulatorRotationPoint.origin.y + 1),\n\t\t\t\t\tvectorCreate(shapeManipulatorRotationPoint.position.x + 1, shapeManipulatorRotationPoint.position.y + 1)\n\t\t\t\t],\n\t\t\t\tstrokeColor: strokeColorShadow,\n\t\t\t\tstrokeWidth: 2,\n\t\t\t\topacity: shadowOpacity\n\t\t\t});\n\t\t}\n\n\t\t// lines\n\t\tif (canDrawLine && lineVisible) {\n\t\t\t// draw corner hooks\n\t\t\tisHookStyle\n\t\t\t? segments.push(...screenPoints.map((p, index, arr) => ({\n\t\t\t\t\tid: ManipulatorShapeId,\n\t\t\t\t\tpoints: getManipulatorSelectionHookShape(arr, index),\n\t\t\t\t\tstrokeColor,\n\t\t\t\t\tstrokeWidth,\n\t\t\t\t\topacity: lineOpacity\n\t\t\t\t})))\n\t\t\t: // draw rectangle\n\t\t\t\tsegments.push({\n\t\t\t\t\tid: ManipulatorShapeId,\n\t\t\t\t\tpoints: screenPoints,\n\t\t\t\t\tpathClose: closeSelectionPath,\n\t\t\t\t\tstrokeColor,\n\t\t\t\t\tstrokeWidth,\n\t\t\t\t\tbackgroundColor: isDraft && isPolygon && shape.backgroundColor && shape.backgroundColor[3] > 0\n\t\t\t\t\t? shape.backgroundColor\n\t\t\t\t\t: [0, 0, 0, 0],\n\t\t\t\t\topacity: lineOpacity\n\t\t\t\t});\n\t\t}\n\n\t\tisPolygon && shouldDrawPoints && segments.push(...screenPoints.map(point => {\n\t\t\treturn {\n\t\t\t\tid: ManipulatorShapeId,\n\t\t\t\t...point,\n\t\t\t\trx: strokeWidth * 3,\n\t\t\t\try: strokeWidth * 3,\n\t\t\t\tbackgroundColor: [0.5, 0.5, 0.5],\n\t\t\t\tstrokeWidth,\n\t\t\t\tstrokeColor\n\t\t\t};\n\t\t}));\n\n\t\t// draft mode\n\t\tisPolygon && isDraft && shouldDrawPoints && selectionScreenPoints.length >= 3 && segments.push({\n\t\t\t...screenPointsFirst,\n\t\t\tid: ManipulatorShapeId,\n\t\t\trx: strokeWidth * (canClosePath ? 5 : 4),\n\t\t\try: strokeWidth * (canClosePath ? 5 : 4),\n\t\t\tbackgroundColor: canClosePath ? hoverColor : [0.5, 0.5, 0.5],\n\t\t\tstrokeWidth,\n\t\t\tstrokeColor\n\t\t});\n\n\t\tisPolygon && isDraft && hasStroke && shouldDrawPoints && !canClosePath && shapeActivePoints.length >= 2 && segments.push({\n\t\t\t...screenPointsLast,\n\t\t\tid: ManipulatorShapeId,\n\t\t\trx: strokeWidth * (canEndPath ? 5 : 4),\n\t\t\try: strokeWidth * (canEndPath ? 5 : 4),\n\t\t\tbackgroundColor: canEndPath ? hoverColor : [0.5, 0.5, 0.5],\n\t\t\tstrokeWidth,\n\t\t\tstrokeColor\n\t\t});\n\n\t\tif (shapeManipulatorRotationPoint && lineVisible && !isHookStyle) {\n\t\t\t// add rotator line\n\t\t\tsegments.push({\n\t\t\t\tid: ManipulatorShapeId,\n\t\t\t\tpoints: [\n\t\t\t\t\t{\n\t\t\t\t\t\tx: shapeManipulatorRotationPoint.origin.x,\n\t\t\t\t\t\ty: shapeManipulatorRotationPoint.origin.y\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tx: shapeManipulatorRotationPoint.position.x,\n\t\t\t\t\t\ty: shapeManipulatorRotationPoint.position.y\n\t\t\t\t\t}\n\t\t\t\t],\n\t\t\t\tstrokeColor,\n\t\t\t\tstrokeWidth,\n\t\t\t\topacity: lineOpacity\n\t\t\t});\n\t\t}\n\n\t\t// replace existing segments\n\t\tpushManipulatorShapes(...segments);\n\t};\n\n\t// add contextPresentationRect so we redraw active markup each frame\n\tconst clearHoverShapeIfActive = () => {\n\t\tif (activeMarkup !== hoverShape) return;\n\t\tclearHoverShape();\n\t};\n\n\tconst clearHoverShape = () => {\n\t\t$$invalidate(153, hoverShape = undefined);\n\t\tpopHoverShapes();\n\t};\n\n\t// multi selection shapes (drawn at shapes edges when multiselecting)\n\tconst MultiSelectionShapeId = `markup-multi-selection-segment-` + uid;\n\n\tconst popMultiSelectionShapes = createShapeRemover(MultiSelectionShapeId);\n\tconst pushMultiSelectionShapes = createShapePusher(MultiSelectionShapeId);\n\n\t// snap pointer so we can show where initial point is going to be\n\tconst snapPointerPosition = (pointerPosition, disableSnapping) => {\n\t\tif (snapThreshold <= 0) return pointerPosition;\n\n\t\tif (disableSnapping) {\n\t\t\tclearInteractionSnapLines();\n\t\t\treturn pointerPosition;\n\t\t}\n\n\t\tconst imagePoint = mapEditorPointToImagePoint(pointerPosition);\n\t\tconst snapTargets = getSnapTargets();\n\t\tconst { snapTranslation, snapLines } = snapLinesToTargets(getShapeSnapLines(imagePoint), snapTargets, snapThreshold, gridSize, gridRect);\n\t\tsetInteractionSnapLines(snapLines);\n\t\tupdateWithSnapTranslation(pointerPosition, snapTranslation);\n\t\treturn pointerPosition;\n\t};\n\n\t// draw grid lines\n\tconst gridOpacity = spring(0);\n\n\tcomponent_subscribe($$self, gridOpacity, value => $$invalidate(196, $gridOpacity = value));\n\tconst GridShapeId = `markup-grid-line-${uid}`;\n\tconst popGridShapes = createShapeRemover(GridShapeId);\n\tconst pushGridShapes = createShapePusher(GridShapeId);\n\n\tconst redrawGridShapes = (cellSize, gridRect, strokeColor, opacity) => {\n\t\tconst editorPointA = mapImagePointToEditorPoint({ x: 0, y: 0 });\n\t\tconst editorPointB = mapImagePointToEditorPoint({ x: cellSize, y: cellSize });\n\t\tconst editorPointC = mapImagePointToEditorPoint({ x: gridRect.width, y: gridRect.height });\n\t\tconst projectedCellSize = editorPointB.x - editorPointA.x;\n\n\t\t// animate grid opacity\n\t\tgridOpacity.set(projectedCellSize > 6 ? 1 : 0);\n\n\t\t// no need tor ender\n\t\tif (opacity <= 0) return popGridShapes();\n\n\t\tconst linesVertical = getNumberSequence(projectedCellSize, editorPointA.x, editorPointC.x).map(line => ({\n\t\t\tid: GridShapeId,\n\t\t\topacity,\n\t\t\tpoints: [{ x: line, y: editorPointA.y }, { x: line, y: editorPointC.y }],\n\t\t\tstrokeWidth: 1,\n\t\t\tstrokeColor\n\t\t}));\n\n\t\tconst linesHorizontal = getNumberSequence(projectedCellSize, editorPointA.y, editorPointC.y).map(line => ({\n\t\t\tid: GridShapeId,\n\t\t\topacity,\n\t\t\tpoints: [{ x: editorPointA.x, y: line }, { x: editorPointC.x, y: line }],\n\t\t\tstrokeWidth: 1,\n\t\t\tstrokeColor\n\t\t}));\n\n\t\t// update UI\n\t\tpushGridShapes(...linesHorizontal, ...linesVertical);\n\t}; // ui.update((shapes) => [...filterGridLines(shapes), ...linesHorizontal, ...linesVertical]);\n\n\t//#region snap lines\n\tconst SnapShapeId = `markup-snap-line`;\n\n\tconst popSnapShapes = createShapeRemover(SnapShapeId);\n\tconst pushSnapShapes = createShapePusher(SnapShapeId);\n\n\tconst redrawSnapShapes = (linesX, linesY, strokeColor) => {\n\t\tif (linesX === null && linesY === null) {\n\t\t\treturn popSnapShapes();\n\t\t}\n\n\t\tlet xLine;\n\t\tlet yLine;\n\n\t\tif (linesX !== null) {\n\t\t\txLine = {\n\t\t\t\tid: SnapShapeId,\n\t\t\t\tstrokeColor,\n\t\t\t\tstrokeWidth: 1.5,\n\t\t\t\tpoints: [\n\t\t\t\t\tmapImagePointToEditorPoint({ x: linesX, y: 0 }),\n\t\t\t\t\tmapImagePointToEditorPoint({ x: linesX, y: parentRect.height })\n\t\t\t\t]\n\t\t\t};\n\t\t}\n\n\t\tif (linesY !== null) {\n\t\t\tyLine = {\n\t\t\t\tid: SnapShapeId,\n\t\t\t\tstrokeColor,\n\t\t\t\tstrokeWidth: 1.5,\n\t\t\t\tpoints: [\n\t\t\t\t\tmapImagePointToEditorPoint({ x: 0, y: linesY }),\n\t\t\t\t\tmapImagePointToEditorPoint({ x: parentRect.width, y: linesY })\n\t\t\t\t]\n\t\t\t};\n\t\t}\n\n\t\tpushSnapShapes(xLine, yLine);\n\t};\n\n\t//#endregion\n\tconst togglePrerender = isActive => {\n\t\tif (!isActive) return updateMarkupShapeItems({ _prerender: false });\n\t\tconst shape = $shapes.find(markupItem => markupItem.isEditing);\n\t\tif (!shape) return;\n\t\tupdateMarkupShape(shape, { _prerender: textInputMode === 'inline' });\n\t};\n\n\tconst handleManipulatorResizeGrab = e => {\n\t\t$$invalidate(152, isInteracting = true);\n\t\t$$invalidate(151, interactionShape = activeMarkup);\n\t\tinteractionShapeOriginComputed = activeMarkupComputed;\n\t};\n\n\tconst handleManipulatorResizeDrag = e => {\n\t\t// is possible when using multiple fingers (scale canvas + drag / resize)\n\t\tif (!interactionShape) {\n\t\t\t$$invalidate(152, isInteracting = false);\n\t\t\treturn;\n\t\t}\n\n\t\tconst { translation, indexes, shiftKey, ctrlKey, metaKey, isNudge } = e.detail;\n\n\t\t// if control or cmd pressed, prevent snapping\n\t\tconst preventSnap = ctrlKey || metaKey;\n\n\t\tresizeShape(interactionShape, interactionShapeOriginComputed, indexes, translation, {\n\t\t\t// anchor to opposite point\n\t\t\tshiftKey: isNudge ? false : shiftKey,\n\t\t\t// snap\n\t\t\t...getSnapOptions(preventSnap ? 0 : snapThreshold, gridSize, gridRect, interactionShape)\n\t\t});\n\n\t\t// deselect on move\n\t\tif (shapeCanSelectPoint(interactionShape) && vectorDistanceSquared(translation) > 16) {\n\t\t\tupdateMarkupShape(interactionShape, { selectedPoint: undefined });\n\t\t}\n\t};\n\n\tconst handleManipulatorResizeEnd = e => {\n\t\t// is possible when using multiple fingers (scale canvas + drag / resize)\n\t\tif (!interactionShape) {\n\t\t\t$$invalidate(152, isInteracting = false);\n\t\t\treturn;\n\t\t}\n\n\t\tselectShape(interactionShape);\n\t\tconst { isTap, translation, indexes } = e.detail;\n\n\t\tif (isTap) {\n\t\t\t// explicitely (de)select corner\n\t\t\tif (shapeCanSelectPoint(interactionShape)) {\n\t\t\t\tupdateMarkupShape(interactionShape, {\n\t\t\t\t\tselectedPoint: interactionShape.selectedPoint === indexes[0]\n\t\t\t\t\t? undefined\n\t\t\t\t\t: indexes[0]\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t// tapped!\n\t\t\tontapshape(interactionShape);\n\t\t}\n\n\t\t$$invalidate(151, interactionShape = undefined);\n\t\t$$invalidate(152, isInteracting = false);\n\t\tonupdateshape(activeMarkup);\n\t};\n\n\t// rotate\n\tconst handleManipulatorRotateGrab = e => {\n\t\trotatorInitialPosition = getShapeRotationPoint(activeMarkupComputed).origin;\n\t\t$$invalidate(152, isInteracting = true);\n\t\t$$invalidate(151, interactionShape = activeMarkup);\n\t\tinteractionShapeOriginComputed = activeMarkupComputed;\n\t};\n\n\tconst handleManipulatorRotateDrag = e => {\n\t\t// is possible when using multiple fingers (scale canvas + drag / resize)\n\t\tif (!interactionShape) {\n\t\t\t$$invalidate(152, isInteracting = false);\n\t\t\treturn;\n\t\t}\n\n\t\tconst { translation, shiftKey } = e.detail;\n\t\trotateShape(interactionShape, interactionShapeOriginComputed, translation, { shiftKey });\n\t};\n\n\tconst handleManipulatorRotateEnd = () => {\n\t\t// is possible when using multiple fingers (scale canvas + drag / resize)\n\t\tif (!interactionShape) {\n\t\t\t$$invalidate(152, isInteracting = false);\n\t\t\treturn;\n\t\t}\n\n\t\tselectShape(interactionShape);\n\t\t$$invalidate(151, interactionShape = undefined);\n\t\t$$invalidate(152, isInteracting = false);\n\t\tonupdateshape(activeMarkup);\n\t};\n\n\t// scale\n\tconst handleManipulatorScaleGrab = e => {\n\t\t$$invalidate(152, isInteracting = true);\n\t\t$$invalidate(151, interactionShape = activeMarkup);\n\t\tinteractionShapeOriginComputed = activeMarkupComputed;\n\t};\n\n\tconst handleManipulatorScaleDrag = e => {\n\t\t// is possible when using multiple fingers (scale canvas + drag / resize)\n\t\tif (!interactionShape) {\n\t\t\t$$invalidate(152, isInteracting = false);\n\t\t\treturn;\n\t\t}\n\n\t\tconst { translation, ctrlKey, metaKey } = e.detail;\n\n\t\t// if control or cmd pressed, prevent snapping\n\t\tconst preventSnap = ctrlKey || metaKey;\n\n\t\tscaleShape(interactionShape, interactionShapeOriginComputed, translation, {\n\t\t\t// snap\n\t\t\t...getSnapOptions(preventSnap ? 0 : snapThreshold, gridSize, gridRect, interactionShape)\n\t\t});\n\t};\n\n\tconst handleManipulatorScaleEnd = () => {\n\t\t// is possible when using multiple fingers (scale canvas + drag / resize)\n\t\tif (!interactionShape) {\n\t\t\t$$invalidate(152, isInteracting = false);\n\t\t\treturn;\n\t\t}\n\n\t\tselectShape(interactionShape);\n\t\t$$invalidate(151, interactionShape = undefined);\n\t\t$$invalidate(152, isInteracting = false);\n\t\tonupdateshape(activeMarkup);\n\t};\n\n\tconst handleKeyPressedStore = () => {\n\t\tconst [keyCode] = $keysPressedStored;\n\n\t\t// is not backspace or delete\n\t\tif (keyCode !== 8 && keyCode !== 46) return;\n\n\t\t// only handle key input if a shape has been selected\n\t\tif (!hasActiveMarkupItem()) return;\n\n\t\t// dont remove if is editing\n\t\tif (activeMarkup.isEditing) return;\n\n\t\t// if is path and has selected point\n\t\tif (shapeIsPath(activeMarkup) && isNumber(activeMarkup.selectedPoint)) {\n\t\t\tsetTimeout(() => removePointFromPath(activeMarkup, activeMarkup.selectedPoint), 0);\n\t\t\treturn;\n\t\t}\n\n\t\t// remove item\n\t\tsetTimeout(() => removeActiveMarkupItem(), 0);\n\t};\n\n\tconst handleKeyDown = e => {\n\t\t// get key type\n\t\tconst { key } = e;\n\n\t\tconst isEscape = (/escape/i).test(key);\n\n\t\t// only handle key input if a shape has been selected\n\t\tif (!hasActiveMarkupItem()) return;\n\n\t\t// if is escape deselect active item\n\t\tif (isEscape) {\n\t\t\te.preventDefault();\n\t\t\te.stopPropagation();\n\t\t\treturn deselectMarkupItem(activeMarkup);\n\t\t}\n\n\t\t// if is duplicate active shape keyboard shortcut\n\t\tif (allowShapeDuplicate && key === 'd' && (e.metaKey || e.ctrlKey)) {\n\t\t\te.preventDefault();\n\t\t\te.stopPropagation();\n\t\t\tduplicateActiveMarkup();\n\t\t}\n\t};\n\n\t//\n\t// Text input\n\t//\n\tlet textInput;\n\n\tconst getTextShapeOriginSnapshot = () => ({ ...activeMarkup });\n\n\t// when softkeyboard closes while text editing we confirm text\n\tsoftKeyboardState.subscribe(keyboardState => {\n\t\t// need to be in inline mode\n\t\tif (textInputMode !== 'inline') return;\n\n\t\t// confirm text if switching from visible to hidden while editing text\n\t\tif (activeMarkup && activeMarkup.isEditing && keyboardState === 'hidden') {\n\t\t\thandleTextConfirm();\n\t\t}\n\t});\n\n\tconst getTextInputTextStyles = (shapeComputed, mode) => {\n\t\tconst { textAlign = 'left', fontFamily = 'sans-serif', fontWeight = 'normal', fontStyle = 'normal', letterSpacing = 'normal', textShadowX = 0, textShadowY = 0, textShadowBlur = 0, textShadowColor = undefined, textOutlineWidth = 0, textOutlineColor = undefined } = shapeComputed;\n\t\tconst imp = '!important';\n\n\t\tconst cosmetic = `caret-color:${caretColorRGBA};text-align:${textAlign}${imp};font-family:${fontFamily}${imp};font-weight:${fontWeight}${imp};font-style:${fontStyle}${imp};letter-spacing:${isNumber(letterSpacing)\n\t\t? letterSpacing + 'px'\n\t\t: 'normal'}${imp}`;\n\n\t\tif (mode === 'modal') return cosmetic;\n\n\t\tconst textOutline = textOutlineWidth\n\t\t? `;-webkit-text-stroke:${textOutlineWidth * 2}px ${colorArrayToRGBA(textOutlineColor)} ${imp}`\n\t\t: '';\n\n\t\tconst textShadow = textShadowBlur || textShadowX || textShadowY\n\t\t? `;text-shadow:${textShadowX}px ${textShadowY}px ${textShadowBlur}px ${colorArrayToRGBA(textShadowColor)} ${imp}`\n\t\t: '';\n\n\t\tlet fontSize = shapeComputed.fontSize;\n\n\t\t// this is to work around Firefox 1000px limit on font-size\n\t\tlet fontScalar = 1;\n\n\t\tlet fontScalarStyles = '';\n\n\t\tif (fontSize > 1000 && isFirefox()) {\n\t\t\tfontScalar = fontSize / 1000;\n\t\t\tfontScalarStyles = `transform-origin:0 0;transform:scale(${fontScalar})`;\n\t\t\tfontSize = 1000;\n\t\t}\n\n\t\tlet lineHeight = shapeComputed.lineHeight / fontScalar;\n\t\tconst color = colorArrayToRGBA(shapeComputed.color);\n\t\tconst initialLineOffset = Math.max(0, fontSize - lineHeight) * 0.5;\n\t\treturn `--font-scalar:${fontScalar};--bottom-inset:${initialLineOffset}px;padding:${initialLineOffset}px 0 0${imp};color:${color}${imp};font-size:${fontSize}px${imp};line-height:${lineHeight}px${imp};${cosmetic};${fontScalarStyles}${textOutline}${textShadow}`;\n\t};\n\n\tconst getTextInputPositionStyles = (shapeComputed, offset, contextZoom, contextRotation) => {\n\t\tlet center;\n\t\tlet size;\n\n\t\tif (shapeComputed.width && shapeComputed.height) {\n\t\t\t// text box\n\t\t\tcenter = rectCenter(shapeComputed);\n\n\t\t\tsize = sizeCreateFromAny(shapeComputed);\n\t\t} else {\n\t\t\t// text line\n\t\t\tsize = textToSize(activeMarkup.text, activeMarkupComputed);\n\n\t\t\t// if has fix width, we'll use that\n\t\t\tsize.width = activeMarkupComputed.width || size.width;\n\n\t\t\tcenter = vectorCreate(shapeComputed.x + size.width * 0.5, shapeComputed.y + size.height * 0.5);\n\t\t}\n\n\t\tconst lineHeight = Math.max(0, shapeComputed.fontSize - shapeComputed.lineHeight) + shapeComputed.lineHeight;\n\t\tconst position = mapImagePointToEditorPoint(center);\n\t\tlet tx = position.x - offset.x - size.width * 0.5;\n\t\tlet ty = position.y - offset.y - size.height * 0.5;\n\t\tlet flipX = shapeComputed.flipX;\n\t\tlet flipY = shapeComputed.flipY;\n\t\tlet r = shapeComputed.rotation;\n\n\t\tif (contextFlipX && contextFlipY) {\n\t\t\tflipX = !flipX;\n\t\t\tflipY = !flipY;\n\t\t} else if (contextFlipX) {\n\t\t\tflipX = !flipX;\n\t\t\tr = -r;\n\t\t} else if (contextFlipY) {\n\t\t\tflipY = !flipY;\n\t\t\tr = -r;\n\t\t}\n\n\t\tr += contextRotation;\n\t\tconst sx = contextZoom * (flipX ? -1 : 1);\n\t\tconst sy = contextZoom * (flipY ? -1 : 1);\n\t\treturn `--line-height:${lineHeight}px;width:${size.width}px;height:${size.height}px;transform:translate(${tx}px,${ty}px) rotate(${r}rad) scale(${sx}, ${sy})`;\n\t};\n\n\t// sets the contenteditable text when user clicks text shape\n\tconst syncTextInput = () => updateTextInputValue(textInputText);\n\n\tconst updateTextInputValue = text => {\n\t\t$$invalidate(8, textInput.innerHTML = textToHTML(text), textInput);\n\t};\n\n\t// cleans up single line text\n\tconst removeLineBreaks = text => {\n\t\tconst lines = text.// split on lines so we can create compressed normal line of text\n\t\tsplit(/[\\n\\r]/g);\n\n\t\tif (lines.length > 1) {\n\t\t\treturn lines.// remove spaces around lines\n\t\t\tmap(str => str.trim()).// remove empty strings\n\t\t\tfilter(str => str.length).// create new line\n\t\t\tjoin(' ');\n\t\t}\n\n\t\t// just return line\n\t\treturn lines[0];\n\t};\n\n\tconst getTextInputValue = inputElement => {\n\t\tconst elementValue = inputElement.value === undefined\n\t\t? inputElement.innerHTML\n\t\t: inputElement.value;\n\n\t\tconst elementValueClean = activeMarkup.format === 'text'\n\t\t? htmlToText(elementValue)\n\t\t: elementValue;\n\n\t\tconst value = shapeIsTextBlock(activeMarkup) && activeMarkup.disableNewline !== false\n\t\t? removeLineBreaks(elementValueClean)\n\t\t: elementValueClean;\n\n\t\treturn value;\n\t};\n\n\tconst formatContentEditable = html => {\n\t\t// check if can input\n\t\tconst canInput = shapeCanInput(activeMarkup, html);\n\n\t\treturn canInput === true ? html : canInput;\n\t};\n\n\tconst handleTextInput = () => {\n\t\tconst value = getTextInputValue(textInput);\n\t\tconst canInput = shapeCanInput(activeMarkup, value);\n\t\tconst text = canInput === true ? value : canInput;\n\t\tlet x = textShapeDisplayOrigin.x;\n\t\tlet y = textShapeDisplayOrigin.y;\n\n\t\t// if does not have height we need to adjust offset if is rotated\n\t\tif (!activeMarkup.height) {\n\t\t\t// draw origin rect\n\t\t\tconst originRotatedRect = rectRotate({ ...textRectDisplayOrigin }, activeMarkup.rotation);\n\n\t\t\t// draw current rect\n\t\t\tconst size = textToSize(text, activeMarkupComputed);\n\n\t\t\tconst currentRotatedRect = rectRotate({ x, y, ...size }, activeMarkup.rotation);\n\t\t\tconst [originTopLeft, ,originBottomRight] = originRotatedRect;\n\t\t\tconst [currentTopLeft, ,currentBottomRight] = currentRotatedRect;\n\t\t\tlet a = originTopLeft;\n\t\t\tlet b = currentTopLeft;\n\n\t\t\t// if flipped we need to 'extend' the text field in the opposite direction\n\t\t\tif (activeMarkup.flipX) {\n\t\t\t\ta = originBottomRight;\n\t\t\t\tb = currentBottomRight;\n\t\t\t}\n\n\t\t\t// move\n\t\t\tconst d = vectorSubtract(vectorClone(a), b);\n\n\t\t\tx += d.x;\n\t\t\ty += d.y;\n\t\t}\n\n\t\tupdateMarkupShape(activeMarkup, {\n\t\t\tx: isString(textShapeOrigin.x)\n\t\t\t? toPercentage(x, parentRect.width)\n\t\t\t: x,\n\t\t\ty: isString(textShapeOrigin.y)\n\t\t\t? toPercentage(y, parentRect.height)\n\t\t\t: y,\n\t\t\ttext\n\t\t});\n\t};\n\n\tconst handleTextInputAttempt = e => {\n\t\tconst { target, key } = e;\n\t\tconst currentValue = target.value || target.innerText;\n\t\tconst selectionStart = target.selectionStart || 0;\n\t\tconst selectionEnd = target.selectionEnd || currentValue.length;\n\t\tconst currentValueStart = currentValue.substring(0, selectionStart);\n\t\tconst currentValueEnd = currentValue.substring(selectionEnd);\n\t\tconst intendedValue = currentValueStart + key + currentValueEnd;\n\t\tconst filteredValue = shapeCanInput(activeMarkup, intendedValue);\n\t\tif (filteredValue !== intendedValue) return e.preventDefault();\n\t};\n\n\t// prevent moving shape when focusing text field, and close field if escape pressed\n\tconst handleTextInputKeyDown = e => {\n\t\t// block newlines for auto width text that doesn't allow newlines\n\t\tif (shapeIsTextBlock(activeMarkup) && (/enter/i).test(e.code) && activeMarkup.disableNewline !== false) {\n\t\t\treturn e.preventDefault();\n\t\t}\n\n\t\tif ((/arrow/i).test(e.code)) {\n\t\t\treturn e.stopPropagation();\n\t\t}\n\n\t\tif ((/escape/i).test(e.key)) {\n\t\t\treturn handleTextCancel();\n\t\t}\n\t};\n\n\tconst isTextConfirmKeyCombination = e => {\n\t\tconst { key, ctrlKey, metaKey, altKey } = e;\n\t\treturn key === 'Enter' && (ctrlKey || altKey || metaKey);\n\t};\n\n\t// handle alt/ctrl/cmd + return\n\tconst handleTextInputKeyUp = e => {\n\t\t// is confirm text combo\n\t\tif (!isTextConfirmKeyCombination(e)) return;\n\n\t\t// confirm multi line text elements\n\t\treturn handleTextConfirm();\n\t};\n\n\tconst handleTextConfirm = () => {\n\t\tif (activeShapeIsDraft) confirmMarkupItemDraft();\n\n\t\t// final text input check\n\t\ttextInput && textInput.confirm && textInput.confirm();\n\n\t\ttextInput && handleTextInput();\n\n\t\t// done\n\t\tfinishEditMarkupItem(activeMarkup);\n\n\t\t// if (wasDraft) onaddshape(activeMarkup);\n\t\t// else\n\t\tonupdateshape(activeMarkup);\n\t};\n\n\tconst handleTextCancel = () => {\n\t\tif (activeShapeIsDraft) {\n\t\t\tdiscardMarkupItemDraft();\n\t\t} else {\n\t\t\tupdateMarkupShape(activeMarkup, {\n\t\t\t\ttext: textShapeOrigin.text,\n\t\t\t\tx: textShapeOrigin.x,\n\t\t\t\ty: textShapeOrigin.y\n\t\t\t});\n\n\t\t\tfinishEditMarkupItem(activeMarkup);\n\t\t}\n\t};\n\n\t//#region shape popup menu actions\n\tconst getShapeSizeTranslation = (originRect, targetRect, { flipX, flipY, rotation }, anchor = 'top left') => {\n\t\tlet a, b;\n\n\t\t// draw origin rect\n\t\tconst [originTopLeft, originTopRight, originBottomRight, originBottomLeft] = rectRotate(originRect, rotation);\n\n\t\t// draw target rect\n\t\tconst [targetTopLeft, targetTopRight, targetBottomRight, targetBottomLeft] = rectRotate(targetRect, rotation);\n\n\t\tif (anchor === 'top center') {\n\t\t\t// for now doesn't matter if it's flipped horizontal\n\t\t\tconst originMid = vectorCenter(flipY\n\t\t\t? [originBottomLeft, originBottomRight]\n\t\t\t: [originTopLeft, originTopRight]);\n\n\t\t\tconst targetMid = vectorCenter(flipY\n\t\t\t? [targetBottomLeft, targetBottomRight]\n\t\t\t: [targetTopLeft, targetTopRight]);\n\n\t\t\ta = originMid;\n\t\t\tb = targetMid;\n\t\t} else if (anchor === 'top right' && !flipX || anchor === 'top left' && flipX) {\n\t\t\t// tr\n\t\t\ta = flipY ? originBottomRight : originTopRight;\n\n\t\t\tb = flipY ? targetBottomRight : targetTopRight;\n\t\t} else {\n\t\t\t// tl\n\t\t\ta = flipY ? originBottomLeft : originTopLeft;\n\n\t\t\tb = flipY ? targetBottomLeft : targetTopLeft;\n\t\t}\n\n\t\treturn vectorSubtract(vectorClone(a), b);\n\t};\n\n\tconst translateRelative = (current, position, translation) => vectorCreate(\n\t\tisString(current.x)\n\t\t? toPercentage(position.x + translation.x, parentRect.width)\n\t\t: position.x + translation.x,\n\t\tisString(current.y)\n\t\t? toPercentage(position.y + translation.y, parentRect.height)\n\t\t: position.y + translation.y\n\t);\n\n\tconst ShapeTextStateCache = {};\n\tconst handleRemovePointActiveMarkup = () => removePointFromPath(activeMarkup, activeMarkup.selectedPoint);\n\tconst handleEditTextActiveMarkup = () => editMarkupItem(activeMarkup);\n\n\tconst toAutoWidth = (sizeFrom, target, targetComputed) => {\n\t\t// remove width and height props\n\t\tconst { width, height, ...autoSizeProps } = targetComputed;\n\n\t\tconst sizeTo = textToSize(target.text, autoSizeProps);\n\n\t\t// this will change dimensions so if rotated we have to calculate new position\n\t\tconst translation = getShapeSizeTranslation(rectCreate(targetComputed.x, targetComputed.y, sizeFrom.width, sizeFrom.height), rectCreate(targetComputed.x, targetComputed.y, sizeTo.width, sizeTo.height), targetComputed, `top ${target.textAlign}`);\n\n\t\tconst shouldRemoveTextAlign = targetComputed.disableNewline !== false;\n\n\t\t// switch to auto mode\n\t\tremoveMarkupShapeProps(target, ['width', 'height', shouldRemoveTextAlign && 'textAlign']);\n\n\t\t// update position based on size translation\n\t\tupdateMarkupShape(target, {\n\t\t\t...translateRelative(target, targetComputed, translation)\n\t\t});\n\t};\n\n\tconst toAutoHeight = (sizeFrom, target, targetComputed, shapeSize, cache = {}) => {\n\t\t// -> switch to auto height mode\n\t\tconst { height, ...computedMarkup } = targetComputed;\n\n\t\tconst shapeAutoTextSize = sizeApply(textToSize(target.text, computedMarkup), v => Math.ceil(v));\n\t\tconst sizeTo = sizeCreate(cache.width || targetComputed.width || shapeSize.width, shapeAutoTextSize.height);\n\t\tconst textAlign = cache.textAlign || 'left';\n\n\t\t// this will change dimensions so if rotated we have to calculate new position\n\t\tconst translation = getShapeSizeTranslation(rectCreate(targetComputed.x, targetComputed.y, sizeFrom.width, sizeFrom.height), rectCreate(targetComputed.x, targetComputed.y, sizeTo.width, sizeTo.height), targetComputed, `top ${textAlign}`);\n\n\t\t// switch to auto-height mode\n\t\tremoveMarkupShapeProps(target, ['height']);\n\n\t\t// update position based on size translation\n\t\tupdateMarkupShape(target, {\n\t\t\t...translateRelative(target, targetComputed, translation),\n\t\t\twidth: isString(target.width)\n\t\t\t? toPercentage(sizeTo.width, parentRect.width)\n\t\t\t: sizeTo.width,\n\t\t\ttextAlign\n\t\t});\n\t};\n\n\tconst toFixedSize = (sizeFrom, target, targetComputed, shapeSize, cache = {}) => {\n\t\t// this can change dimensions so we need to recalculate position\n\t\tconst sizeTo = sizeCreate(cache.width || shapeSize.width, cache.height || shapeSize.height);\n\n\t\tconst textAlign = cache.textAlign || 'left';\n\n\t\t// this will change dimensions so if rotated we have to calculate new position\n\t\tconst translation = getShapeSizeTranslation(rectCreate(targetComputed.x, targetComputed.y, sizeFrom.width, sizeFrom.height), rectCreate(targetComputed.x, targetComputed.y, sizeTo.width, sizeTo.height), targetComputed, `top ${textAlign}`);\n\n\t\t// switch to fixed box, use stored alignment or default to left text align\n\t\tupdateMarkupShape(target, {\n\t\t\t...translateRelative(target, targetComputed, translation),\n\t\t\twidth: isString(target.width)\n\t\t\t? toPercentage(sizeTo.width, parentRect.width)\n\t\t\t: sizeTo.width,\n\t\t\theight: isString(target.width)\n\t\t\t? toPercentage(sizeTo.height, parentRect.height)\n\t\t\t: sizeTo.height,\n\t\t\ttextAlign\n\t\t});\n\t};\n\n\tconst handleTextSwitchLayout = () => {\n\t\tconst currentShapeSize = sizeApply(textToSize(activeMarkup.text, activeMarkupComputed), v => Math.ceil(v));\n\t\tconst isFixedBox = hasProp(activeMarkup, 'height');\n\t\tconst isAutoHeightBox = !isFixedBox && hasProp(activeMarkup, 'width');\n\n\t\t// set/get cache entries for this text shape\n\t\tconst key = activeMarkup.id;\n\n\t\tlet cache = ShapeTextStateCache[key];\n\n\t\tif (!cache) {\n\t\t\tShapeTextStateCache[key] = {};\n\t\t\tcache = ShapeTextStateCache[key];\n\t\t}\n\n\t\tif (isFixedBox) {\n\t\t\t// store size so we can restore it later\n\t\t\tcache.textAlign = activeMarkup.textAlign;\n\n\t\t\tcache.width = activeMarkupComputed.width;\n\t\t\tcache.height = activeMarkupComputed.height;\n\t\t\tconst sizeFrom = sizeCreate(activeMarkupComputed.width, activeMarkupComputed.height);\n\n\t\t\tif (shapeCanChangeTextLayout(activeMarkup, 'auto-height')) {\n\t\t\t\ttoAutoHeight(sizeFrom, activeMarkup, activeMarkupComputed, currentShapeSize, cache);\n\t\t\t} else if (shapeCanChangeTextLayout(activeMarkup, 'auto-width')) {\n\t\t\t\ttoAutoWidth(sizeFrom, activeMarkup, activeMarkupComputed);\n\t\t\t}\n\t\t} else if (isAutoHeightBox) {\n\t\t\t// store alignment so we can restore it later\n\t\t\tcache.textAlign = activeMarkup.textAlign;\n\n\t\t\tcache.width = activeMarkupComputed.width;\n\n\t\t\t// -> switch to auto width\n\t\t\tconst sizeFrom = sizeCreate(activeMarkupComputed.width, currentShapeSize.height);\n\n\t\t\tif (shapeCanChangeTextLayout(activeMarkup, 'auto-width')) {\n\t\t\t\ttoAutoWidth(sizeFrom, activeMarkup, activeMarkupComputed);\n\t\t\t} else if (shapeCanChangeTextLayout(activeMarkup, 'fixed-size')) {\n\t\t\t\ttoFixedSize(sizeFrom, activeMarkup, activeMarkupComputed, currentShapeSize, cache);\n\t\t\t}\n\t\t} else {\n\t\t\t// -> switch to fixed size or auto height\n\t\t\tcache.textAlign = activeMarkup.textAlign || cache.textAlign;\n\n\t\t\tconst sizeFrom = sizeCreate(Math.ceil(currentShapeSize.width), Math.ceil(currentShapeSize.height));\n\n\t\t\tif (shapeCanChangeTextLayout(activeMarkup, 'fixed-size')) {\n\t\t\t\ttoFixedSize(sizeFrom, activeMarkup, activeMarkupComputed, currentShapeSize, cache);\n\t\t\t} else if (shapeCanChangeTextLayout(activeMarkup, 'auto-height')) {\n\t\t\t\ttoAutoHeight(sizeFrom, activeMarkup, activeMarkupComputed, currentShapeSize, cache);\n\t\t\t}\n\t\t}\n\t};\n\n\tconst handleFlipX = e => {\n\t\te.stopPropagation();\n\t\tconst flipX = activeMarkup.flipX || false;\n\t\tupdateMarkupShapeProperty(activeMarkup, 'flipX', !flipX);\n\t\tonupdateshape(activeMarkup);\n\t};\n\n\tconst handleFlipY = e => {\n\t\te.stopPropagation();\n\t\tconst flipY = activeMarkup.flipY || false;\n\t\tupdateMarkupShapeProperty(activeMarkup, 'flipY', !flipY);\n\t\tonupdateshape(activeMarkup);\n\t};\n\n\tconst handleAdjustOpacity = value => {\n\t\tupdateMarkupShapeProperty(activeMarkup, 'opacity', value);\n\t};\n\n\tconst handleConfirmOpacity = value => {\n\t\thandleAdjustOpacity(value);\n\t\tonupdateshape(activeMarkup);\n\t};\n\n\tconst handleRemoveActiveMarkup = e => {\n\t\te.stopPropagation();\n\t\te.target.blur(); // cancels focus of remove button\n\t\tremoveActiveMarkupItem();\n\t};\n\n\tconst handleMoveToFrontActiveMarkup = e => {\n\t\te.stopPropagation();\n\n\t\t// test if is not already at top of stack, if so, exit\n\t\tconst index = $shapes.findIndex(shape => shape === activeMarkup);\n\n\t\tif (index === $shapes.length - 1) return;\n\n\t\t// add to last index\n\t\tshapes.set($shapes.filter(markupItem => markupItem !== activeMarkup).concat([activeMarkup]));\n\n\t\tonupdateshape(activeMarkup);\n\t};\n\n\tconst handleDuplicateActiveMarkup = e => {\n\t\te.stopPropagation();\n\t\tduplicateActiveMarkup();\n\t};\n\n\tconst duplicateActiveMarkup = () => {\n\t\tconst originals = isMultiSelection ? multiSelection : [activeMarkup];\n\n\t\tconst duplicates = originals.filter(shapeCanDuplicate).map(originalShape => {\n\t\t\t// create clone\n\t\t\tconst clone = shapeDeepCopy(originalShape);\n\n\t\t\tclone.id = getUniqueId();\n\t\t\tconst duplicationOffset = vectorCreate(50, -50);\n\n\t\t\t// offset\n\t\t\tif (hasProp(clone, 'points')) {\n\t\t\t\tconst computedProps = shapeGetPropsPixelValues(clone, ['points'], parentRect);\n\n\t\t\t\tcomputedProps.points.forEach(point => {\n\t\t\t\t\tpoint.x += duplicationOffset.x;\n\t\t\t\t\tpoint.y += duplicationOffset.y;\n\t\t\t\t});\n\n\t\t\t\tshapeUpdateProps(clone, computedProps, parentRect);\n\t\t\t} else if (shapeIsLine(clone)) {\n\t\t\t\tconst computedProps = shapeGetPropsPixelValues(clone, ['x1', 'y1', 'x2', 'y2'], parentRect);\n\t\t\t\tcomputedProps.x1 += duplicationOffset.x;\n\t\t\t\tcomputedProps.y1 += duplicationOffset.y;\n\t\t\t\tcomputedProps.x2 += duplicationOffset.x;\n\t\t\t\tcomputedProps.y2 += duplicationOffset.y;\n\t\t\t\tshapeUpdateProps(clone, computedProps, parentRect);\n\t\t\t} else {\n\t\t\t\tconst currentPosition = shapeGetPropsPixelValues(clone, ['x', 'y'], parentRect);\n\t\t\t\tcurrentPosition.x += 50;\n\t\t\t\tcurrentPosition.y -= 50;\n\t\t\t\tshapeUpdateProps(clone, currentPosition, parentRect);\n\t\t\t}\n\n\t\t\t// add clone\n\t\t\tshapes.set([...$shapes, clone]);\n\n\t\t\t// added\n\t\t\tonaddshape(clone);\n\n\t\t\treturn clone;\n\t\t});\n\n\t\tif (duplicates.length > 1) {\n\t\t\tblurShapes();\n\n\t\t\tduplicates.forEach(shape => {\n\t\t\t\tselectShape(shape, true, true);\n\t\t\t});\n\n\t\t\treturn;\n\t\t}\n\n\t\t// select clone\n\t\tselectShape(duplicates[0]);\n\t};\n\n\t//#endregion\n\t//\n\t// show & position markup controls panel\n\t//\n\tconst markupControlsOpacity = spring(0, { stiffness: 0.2, damping: 0.7 });\n\n\tcomponent_subscribe($$self, markupControlsOpacity, value => $$invalidate(21, $markupControlsOpacity = value));\n\n\tconst getMarkupControlsAnchorPosition = rect => {\n\t\treturn vectorApply(vectorCreate(rect.x + rect.width * 0.5, rect.y), snapToPixel);\n\t};\n\n\tlet shapeControlsSize;\n\n\tconst getShapeControlPositionOnCanvas = (utilRect, position, controlSize, yOffset) => {\n\t\tconst left = utilRect.x;\n\t\tconst top = utilRect.y;\n\t\tconst right = left + utilRect.width;\n\t\tlet x = Math.max(position.x - controlSize.width * 0.5, left);\n\t\tlet y = Math.max(position.y - controlSize.height + yOffset, top);\n\t\tif (x + controlSize.width > right) x = right - controlSize.width;\n\t\treturn vectorCreate(x, y);\n\t};\n\n\tconst TextLayoutChangeIcon = (locale, shape) => {\n\t\tconst { disableTextLayout = [] } = shape;\n\n\t\t// is fixed size\n\t\tif ('height' in shape) {\n\t\t\tif (disableTextLayout.includes('auto-height')) {\n\t\t\t\t// next is auto-width, no need to set as is empty\n\t\t\t\treturn locale.shapeIconButtonTextLayoutAutoWidth;\n\t\t\t} else {\n\t\t\t\t// next is auto-height\n\t\t\t\treturn locale.shapeIconButtonTextLayoutAutoHeight;\n\t\t\t}\n\t\t} else // is auto-height\n\t\tif ('width' in shape) {\n\t\t\tif (disableTextLayout.includes('auto-width')) {\n\t\t\t\t// next is fixed-size\n\t\t\t\treturn locale.shapeIconButtonTextLayoutFixedSize;\n\t\t\t} else {\n\t\t\t\t// next is auto-width, no need to set as is empty\n\t\t\t\treturn locale.shapeIconButtonTextLayoutAutoWidth;\n\t\t\t}\n\t\t} else // is auto-width\n\t\t{\n\t\t\t// next is fixed-size\n\t\t\tif (disableTextLayout.includes('fixed-size')) {\n\t\t\t\t// next is fixed-size\n\t\t\t\treturn locale.shapeIconButtonTextLayoutAutoHeight;\n\t\t\t} else {\n\t\t\t\t// next is auto-width, no need to set as is empty\n\t\t\t\treturn locale.shapeIconButtonTextLayoutFixedSize;\n\t\t\t}\n\t\t}\n\t};\n\n\tconst TextLayoutChangeLabel = (locale, shape) => {\n\t\tconst { disableTextLayout = [] } = shape;\n\n\t\t// is fixed size\n\t\tif ('height' in shape) {\n\t\t\tif (disableTextLayout.includes('auto-height')) {\n\t\t\t\t// next is auto-width, no need to set as is empty\n\t\t\t\treturn locale.shapeTitleButtonTextLayoutAutoWidth;\n\t\t\t} else {\n\t\t\t\t// next is auto-height\n\t\t\t\treturn locale.shapeTitleButtonTextLayoutAutoHeight;\n\t\t\t}\n\t\t} else // is auto-height\n\t\tif ('width' in shape) {\n\t\t\tif (disableTextLayout.includes('auto-width')) {\n\t\t\t\t// next is fixed-size\n\t\t\t\treturn locale.shapeTitleButtonTextLayoutFixedSize;\n\t\t\t} else {\n\t\t\t\t// next is auto-width, no need to set as is empty\n\t\t\t\treturn locale.shapeTitleButtonTextLayoutAutoWidth;\n\t\t\t}\n\t\t} else // is auto-width\n\t\t{\n\t\t\t// next is fixed-size\n\t\t\tif (disableTextLayout.includes('fixed-size')) {\n\t\t\t\t// next is fixed-size\n\t\t\t\treturn locale.shapeTitleButtonTextLayoutAutoHeight;\n\t\t\t} else {\n\t\t\t\t// next is auto-width, no need to set as is empty\n\t\t\t\treturn locale.shapeTitleButtonTextLayoutFixedSize;\n\t\t\t}\n\t\t}\n\t};\n\n\tconst handleNudge = e => {\n\t\tconst shapes = getActiveMarkupItems();\n\t\tif (!shapes) return;\n\t\tif (shapes.find(shapeIsTextEditing)) return;\n\t\tif (shapes.find(shape => !shapeCanMove(shape))) return;\n\n\t\tshapes.forEach(shape => {\n\t\t\t$$invalidate(151, interactionShape = shape);\n\t\t\tinteractionShapeOriginComputed = shapeComputeDisplay(shapeDeepCopy(interactionShape), parentRect);\n\t\t\ttranslateShape(interactionShape, interactionShapeOriginComputed, e.detail);\n\t\t});\n\t};\n\n\t// shape navigator\n\tlet showShapeList = false;\n\n\tconst handleFocusIn = e => {\n\t\t$$invalidate(22, showShapeList = true);\n\t};\n\n\tconst handleFocusOut = ({ relatedTarget }) => {\n\t\t// still in list\n\t\tif (relatedTarget && relatedTarget.classList.contains('shape-selector__button')) return;\n\n\t\t$$invalidate(22, showShapeList = false);\n\t};\n\n\t// change cursor style\n\tlet lastImagePosition = vectorCreateEmpty();\n\n\tlet pointerPositionInEditor = undefined;\n\n\tconst handlePointerMove = e => {\n\t\t// not handled for touch interaction\n\t\tif (e.pointerType === 'touch' || e.target.className !== 'PinturaShapeEditor') {\n\t\t\tonleavecanvas();\n\t\t\treturn;\n\t\t}\n\n\t\t// if interactionTimer is set an interaction was started\n\t\tif (isInteracting || interactionTimer) setHoverShape(undefined);\n\n\t\tconst editorPosition = getEventPositionInEditor(e, rootRect);\n\n\t\t// current position in editor\n\t\t$$invalidate(154, pointerPositionInEditor = vectorClone(editorPosition));\n\n\t\tconst imagePosition = vectorApply(mapEditorPointToImagePoint(editorPosition), v => Math.round(v));\n\t\tif (vectorEqual(imagePosition, lastImagePosition)) return;\n\t\tlastImagePosition = vectorClone(imagePosition);\n\t\tonhovercanvas(isInteracting, editorPosition, imagePosition);\n\t\tif (isInteracting || interactionTimer) return;\n\n\t\t// no hover effect when creating markup\n\t\tif (activeMarkupItemIsDraft) return;\n\n\t\tconst [shape] = getShapesNearPosition($shapes, imagePosition, 0, shapeCanSelect);\n\n\t\t// can't hover draft\n\t\tif (shape && shapeIsDraft(shape)) return;\n\n\t\tsetHoverShape(shape);\n\t};\n\n\tconst setHoverShape = shape => {\n\t\t// let others know we're hovering this shape\n\t\tonhovershape(shape);\n\n\t\t// update interface\n\t\t$$invalidate(153, hoverShape = shape);\n\t};\n\n\tconst handlePointerLeave = e => {\n\t\t// not handled for touch interaction\n\t\tif (e.pointerType === 'touch') return;\n\n\t\tonleavecanvas();\n\t};\n\n\tconst selectShapeByIndex = index => {\n\t\tselectShape($shapes[index]);\n\t};\n\n\t//\n\t// handle Apple Pencil, without this hack there's a delay between taps\n\t//\n\tlet element;\n\n\tfunction handleTouchmove(e) {\n\t\tif (isApplePencilEvent(e) && e.target === element) {\n\t\t\te.preventDefault();\n\t\t}\n\t}\n\n\t// clean up\n\tonDestroy(() => {\n\t\tpopSnapShapes();\n\t\tpopGridShapes();\n\t\tpopManipulatorShapes();\n\t\tpopHoverShapes();\n\t\telement.removeEventListener('touchmove', handleTouchmove, false);\n\t});\n\n\tfunction measure_handler(event) {\n\t\tbubble.call(this, $$self, event);\n\t}\n\n\tconst click_handler = index => selectShapeByIndex(index);\n\n\tfunction textarea_binding($$value) {\n\t\tbinding_callbacks[$$value ? 'unshift' : 'push'](() => {\n\t\t\ttextInput = $$value;\n\t\t\t$$invalidate(8, textInput);\n\t\t});\n\t}\n\n\tfunction textarea_input_handler() {\n\t\ttextInputText = this.value;\n\t\t((((($$invalidate(27, textInputText), $$invalidate(13, shouldRenderTextInput)), $$invalidate(12, activeMarkup)), $$invalidate(178, isTextMarkupSelected)), $$invalidate(0, disabled)), $$invalidate(193, $shapes));\n\t}\n\n\tconst func = e => {\n\t\tconst { key } = e;\n\n\t\tif (key === 'Enter') {\n\t\t\t// if is combo, always confirm\n\t\t\tif (isTextConfirmKeyCombination(e)) {\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\t// if can enter newlines a normal return doesn't confirm the text input\n\t\t\tif (activeMarkup.disableNewline === false) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\n\t\treturn true;\n\t};\n\n\tfunction contenteditable_binding($$value) {\n\t\tbinding_callbacks[$$value ? 'unshift' : 'push'](() => {\n\t\t\ttextInput = $$value;\n\t\t\t$$invalidate(8, textInput);\n\t\t});\n\t}\n\n\tconst focusout_handler = () => {\n\t\t// need to focus root shape editor node as otherwise next escape press closes modal when in modal\n\t\telement && element.focus();\n\t};\n\n\tconst measure_handler_1 = e => $$invalidate(9, shapeControlsSize = e.detail);\n\n\tfunction div_binding($$value) {\n\t\tbinding_callbacks[$$value ? 'unshift' : 'push'](() => {\n\t\t\telement = $$value;\n\t\t\t$$invalidate(10, element);\n\t\t});\n\t}\n\n\tconst interactable_function_1 = e => getEventPositionInEditor(e, rootRect);\n\n\t$$self.$$set = $$props => {\n\t\tif ('uid' in $$props) $$invalidate(67, uid = $$props.uid);\n\t\tif ('ui' in $$props) $$invalidate(68, ui = $$props.ui);\n\t\tif ('disabled' in $$props) $$invalidate(0, disabled = $$props.disabled);\n\t\tif ('shapes' in $$props) $$subscribe_shapes($$invalidate(1, shapes = $$props.shapes));\n\t\tif ('selection' in $$props) $$invalidate(69, selection = $$props.selection);\n\t\tif ('offset' in $$props) $$invalidate(2, offset = $$props.offset);\n\t\tif ('contextRotation' in $$props) $$invalidate(70, contextRotation = $$props.contextRotation);\n\t\tif ('contextFlipX' in $$props) $$invalidate(71, contextFlipX = $$props.contextFlipX);\n\t\tif ('contextFlipY' in $$props) $$invalidate(72, contextFlipY = $$props.contextFlipY);\n\t\tif ('contextZoom' in $$props) $$invalidate(73, contextZoom = $$props.contextZoom);\n\t\tif ('active' in $$props) $$invalidate(74, active = $$props.active);\n\t\tif ('opacity' in $$props) $$invalidate(75, opacity = $$props.opacity);\n\t\tif ('parentRect' in $$props) $$invalidate(76, parentRect = $$props.parentRect);\n\t\tif ('rootRect' in $$props) $$invalidate(3, rootRect = $$props.rootRect);\n\t\tif ('utilRect' in $$props) $$invalidate(4, utilRect = $$props.utilRect);\n\t\tif ('hoverColor' in $$props) $$invalidate(77, hoverColor = $$props.hoverColor);\n\t\tif ('caretColor' in $$props) $$invalidate(78, caretColor = $$props.caretColor);\n\t\tif ('gridColor' in $$props) $$invalidate(79, gridColor = $$props.gridColor);\n\t\tif ('snapColor' in $$props) $$invalidate(80, snapColor = $$props.snapColor);\n\t\tif ('textInputMode' in $$props) $$invalidate(5, textInputMode = $$props.textInputMode);\n\t\tif ('oninteractionstart' in $$props) $$invalidate(81, oninteractionstart = $$props.oninteractionstart);\n\t\tif ('oninteractionupdate' in $$props) $$invalidate(82, oninteractionupdate = $$props.oninteractionupdate);\n\t\tif ('oninteractionrelease' in $$props) $$invalidate(83, oninteractionrelease = $$props.oninteractionrelease);\n\t\tif ('oninteractionend' in $$props) $$invalidate(84, oninteractionend = $$props.oninteractionend);\n\t\tif ('oninteractioncancel' in $$props) $$invalidate(85, oninteractioncancel = $$props.oninteractioncancel);\n\t\tif ('onaddshape' in $$props) $$invalidate(86, onaddshape = $$props.onaddshape);\n\t\tif ('onupdateshape' in $$props) $$invalidate(87, onupdateshape = $$props.onupdateshape);\n\t\tif ('onselectshape' in $$props) $$invalidate(88, onselectshape = $$props.onselectshape);\n\t\tif ('onblurshape' in $$props) $$invalidate(89, onblurshape = $$props.onblurshape);\n\t\tif ('onremoveshape' in $$props) $$invalidate(90, onremoveshape = $$props.onremoveshape);\n\t\tif ('ontapshape' in $$props) $$invalidate(91, ontapshape = $$props.ontapshape);\n\t\tif ('onhovershape' in $$props) $$invalidate(92, onhovershape = $$props.onhovershape);\n\t\tif ('ontriggerhistorywrite' in $$props) $$invalidate(93, ontriggerhistorywrite = $$props.ontriggerhistorywrite);\n\t\tif ('onhovercanvas' in $$props) $$invalidate(94, onhovercanvas = $$props.onhovercanvas);\n\t\tif ('ontapcanvas' in $$props) $$invalidate(95, ontapcanvas = $$props.ontapcanvas);\n\t\tif ('onleavecanvas' in $$props) $$invalidate(96, onleavecanvas = $$props.onleavecanvas);\n\t\tif ('beforeSelectShape' in $$props) $$invalidate(97, beforeSelectShape = $$props.beforeSelectShape);\n\t\tif ('beforeDeselectShape' in $$props) $$invalidate(98, beforeDeselectShape = $$props.beforeDeselectShape);\n\t\tif ('beforeRemoveShape' in $$props) $$invalidate(99, beforeRemoveShape = $$props.beforeRemoveShape);\n\t\tif ('beforeUpdateShape' in $$props) $$invalidate(100, beforeUpdateShape = $$props.beforeUpdateShape);\n\t\tif ('willRenderShapeControls' in $$props) $$invalidate(101, willRenderShapeControls = $$props.willRenderShapeControls);\n\t\tif ('willRenderShapeTextControls' in $$props) $$invalidate(102, willRenderShapeTextControls = $$props.willRenderShapeTextControls);\n\t\tif ('willStartInteraction' in $$props) $$invalidate(103, willStartInteraction = $$props.willStartInteraction);\n\t\tif ('mapEditorPointToImagePoint' in $$props) $$invalidate(104, mapEditorPointToImagePoint = $$props.mapEditorPointToImagePoint);\n\t\tif ('mapImagePointToEditorPoint' in $$props) $$invalidate(105, mapImagePointToEditorPoint = $$props.mapImagePointToEditorPoint);\n\t\tif ('eraseRadius' in $$props) $$invalidate(106, eraseRadius = $$props.eraseRadius);\n\t\tif ('selectRadius' in $$props) $$invalidate(107, selectRadius = $$props.selectRadius);\n\t\tif ('enableButtonFlipVertical' in $$props) $$invalidate(108, enableButtonFlipVertical = $$props.enableButtonFlipVertical);\n\t\tif ('enableTapToAddText' in $$props) $$invalidate(109, enableTapToAddText = $$props.enableTapToAddText);\n\t\tif ('enableMultiSelect' in $$props) $$invalidate(110, enableMultiSelect = $$props.enableMultiSelect);\n\t\tif ('enableTextStyleControls' in $$props) $$invalidate(6, enableTextStyleControls = $$props.enableTextStyleControls);\n\t\tif ('locale' in $$props) $$invalidate(7, locale = $$props.locale);\n\t\tif ('snapThreshold' in $$props) $$invalidate(111, snapThreshold = $$props.snapThreshold);\n\t\tif ('snapPointer' in $$props) $$invalidate(112, snapPointer = $$props.snapPointer);\n\t\tif ('enableSnapToContext' in $$props) $$invalidate(113, enableSnapToContext = $$props.enableSnapToContext);\n\t\tif ('gridSize' in $$props) $$invalidate(114, gridSize = $$props.gridSize);\n\t};\n\n\t$$self.$$.update = () => {\n\t\tif ($$self.$$.dirty[2] & /*parentRect*/ 16384 | $$self.$$.dirty[3] & /*gridSize*/ 2097152) {\n\t\t\t// redraw grid lines\n\t\t\t$$invalidate(180, gridRect = gridSize > 0 ? { x: 0, y: 0, ...parentRect } : undefined);\n\t\t}\n\n\t\tif ($$self.$$.dirty[0] & /*disabled*/ 1 | $$self.$$.dirty[6] & /*$shapes*/ 128) {\n\t\t\t$$invalidate(12, activeMarkup = !disabled && $shapes && (getMarkupItemDraft() || getActiveMarkupItem()));\n\t\t}\n\n\t\tif ($$self.$$.dirty[3] & /*enableMultiSelect*/ 131072 | $$self.$$.dirty[6] & /*$shapes*/ 128) {\n\t\t\t$$invalidate(172, multiSelection = enableMultiSelect ? $shapes.filter(shapeIsSelected) : []);\n\t\t}\n\n\t\tif ($$self.$$.dirty[5] & /*multiSelection*/ 131072) {\n\t\t\t$$invalidate(171, isMultiSelection = multiSelection.length > 1);\n\t\t}\n\n\t\tif ($$self.$$.dirty[2] & /*parentRect*/ 16384 | $$self.$$.dirty[5] & /*isMultiSelection, multiSelection*/ 196608) {\n\t\t\t$$invalidate(192, multiSelectionComputed = isMultiSelection\n\t\t\t? multiSelection.map(shape => shapeComputeDisplay(shapeDeepCopy(shape), parentRect))\n\t\t\t: []);\n\t\t}\n\n\t\tif ($$self.$$.dirty[0] & /*activeMarkup*/ 4096) {\n\t\t\t$$invalidate(189, activeMarkupSelected = !!activeMarkup);\n\t\t}\n\n\t\tif ($$self.$$.dirty[0] & /*activeMarkup*/ 4096) {\n\t\t\t$$invalidate(174, activeShapeIsDraft = activeMarkup && shapeIsDraft(activeMarkup));\n\t\t}\n\n\t\tif ($$self.$$.dirty[0] & /*activeMarkup*/ 4096 | $$self.$$.dirty[5] & /*activeShapeIsDraft*/ 524288) {\n\t\t\t$$invalidate(11, activeShapeId = activeMarkup && !activeShapeIsDraft\n\t\t\t? activeMarkup.id\n\t\t\t: undefined);\n\t\t}\n\n\t\tif ($$self.$$.dirty[0] & /*rootRect, activeMarkup*/ 4104 | $$self.$$.dirty[2] & /*parentRect*/ 16384) {\n\t\t\t// rootRect is in there so it recomputes the shape when the editor is resized\n\t\t\t$$invalidate(15, activeMarkupComputed = rootRect && activeMarkup && shapeComputeDisplay(shapeDeepCopy(activeMarkup), parentRect));\n\t\t}\n\n\t\tif ($$self.$$.dirty[0] & /*activeMarkup*/ 4096 | $$self.$$.dirty[5] & /*activeShapeIsDraft*/ 524288) {\n\t\t\t$$invalidate(155, activeMarkupItemIsDraft = !!(activeMarkup && activeShapeIsDraft));\n\t\t}\n\n\t\tif ($$self.$$.dirty[0] & /*activeMarkup, activeMarkupComputed*/ 36864 | $$self.$$.dirty[2] & /*opacity*/ 8192) {\n\t\t\t// $: shapeProps = activeMarkup || undefined;\n\t\t\t// TODO: we use opacity to trigger a redraw of the active points, this should be changed to the image zoom factor in a future release\n\t\t\t// && !shapeIsPath(activeMarkupComputed)\n\t\t\t$$invalidate(187, shapeActivePoints = activeMarkup && opacity && getMarkupShapePoints(activeMarkupComputed) || []);\n\t\t}\n\n\t\tif ($$self.$$.dirty[5] & /*isMultiSelection*/ 65536 | $$self.$$.dirty[6] & /*multiSelectionComputed*/ 64) {\n\t\t\t$$invalidate(184, multiSelectionShapePoints = isMultiSelection\n\t\t\t? multiSelectionComputed.map(getMarkupShapePoints)\n\t\t\t: []);\n\t\t}\n\n\t\tif ($$self.$$.dirty[0] & /*activeMarkup*/ 4096 | $$self.$$.dirty[5] & /*isMultiSelection*/ 65536) {\n\t\t\t$$invalidate(191, allowResizeControls = !isMultiSelection && activeMarkup && shapeCanResize(activeMarkup) && !shapeIsTextEditing(activeMarkup));\n\t\t}\n\n\t\tif ($$self.$$.dirty[0] & /*activeMarkup*/ 4096 | $$self.$$.dirty[5] & /*isMultiSelection*/ 65536) {\n\t\t\t$$invalidate(18, allowRotateControls = !isMultiSelection && activeMarkup && shapeCanRotate(activeMarkup) && !shapeIsTextEditing(activeMarkup));\n\t\t}\n\n\t\tif ($$self.$$.dirty[0] & /*activeMarkup*/ 4096) {\n\t\t\t$$invalidate(16, allowScaleControls = activeMarkup && shapeIsText(activeMarkup) && !shapeIsTextBox(activeMarkup) && !shapeIsTextEditing(activeMarkup) && activeMarkup.disableTextScale === false);\n\t\t}\n\n\t\tif ($$self.$$.dirty[0] & /*activeMarkup*/ 4096 | $$self.$$.dirty[6] & /*allowResizeControls*/ 32) {\n\t\t\t$$invalidate(20, allowEdgeControls = allowResizeControls\n\t\t\t? getAllowEdgeControls(activeMarkup)\n\t\t\t: false);\n\t\t}\n\n\t\tif ($$self.$$.dirty[0] & /*activeMarkup, allowEdgeControls*/ 1052672 | $$self.$$.dirty[6] & /*allowResizeControls*/ 32) {\n\t\t\t$$invalidate(33, allowCornerControls = activeMarkup\n\t\t\t? Array.isArray(allowEdgeControls)\n\t\t\t\t? false\n\t\t\t\t: allowResizeControls\n\t\t\t: false);\n\t\t}\n\n\t\tif ($$self.$$.dirty[5] & /*isMultiSelection, multiSelection*/ 196608) {\n\t\t\t$$invalidate(190, multiSelectionPoints = isMultiSelection\n\t\t\t? getMultiSelectionPoints(multiSelection)\n\t\t\t: []);\n\t\t}\n\n\t\tif ($$self.$$.dirty[0] & /*activeMarkup*/ 4096 | $$self.$$.dirty[5] & /*isMultiSelection*/ 65536 | $$self.$$.dirty[6] & /*shapeActivePoints*/ 2) {\n\t\t\t$$invalidate(17, shouldRenderShapeManipulator = activeMarkup && shapeActivePoints.length > 1 || isMultiSelection);\n\t\t}\n\n\t\tif ($$self.$$.dirty[3] & /*mapImagePointToEditorPoint*/ 4096 | $$self.$$.dirty[5] & /*isMultiSelection*/ 65536 | $$self.$$.dirty[6] & /*multiSelectionPoints, shapeActivePoints*/ 18) {\n\t\t\t$$invalidate(185, selectionScreenPoints = isMultiSelection\n\t\t\t? multiSelectionPoints.map(mapImagePointToEditorPoint)\n\t\t\t: shapeActivePoints.map(mapImagePointToEditorPoint));\n\t\t}\n\n\t\tif ($$self.$$.dirty[0] & /*offset*/ 4 | $$self.$$.dirty[5] & /*selectionScreenPoints*/ 1073741824) {\n\t\t\t$$invalidate(14, shapeManipulatorPoints = selectionScreenPoints.map(point => vectorCreate(point.x - offset.x, point.y - offset.y)));\n\t\t}\n\n\t\tif ($$self.$$.dirty[0] & /*activeMarkup*/ 4096) {\n\t\t\t$$invalidate(19, shapeSelectedPointIndex = activeMarkup && activeMarkup.points && isNumber(activeMarkup.selectedPoint)\n\t\t\t? activeMarkup.selectedPoint\n\t\t\t: -1);\n\t\t}\n\n\t\tif ($$self.$$.dirty[0] & /*shapeSelectedPointIndex*/ 524288) {\n\t\t\t$$invalidate(164, hasSelectedShapePoint = shapeSelectedPointIndex >= 0);\n\t\t}\n\n\t\tif ($$self.$$.dirty[0] & /*shapeManipulatorPoints, shapeSelectedPointIndex*/ 540672 | $$self.$$.dirty[5] & /*hasSelectedShapePoint*/ 512) {\n\t\t\t$$invalidate(169, shapeSelectedManipulatorPoint = hasSelectedShapePoint\n\t\t\t? shapeManipulatorPoints[shapeSelectedPointIndex]\n\t\t\t: undefined);\n\t\t}\n\n\t\tif ($$self.$$.dirty[0] & /*activeMarkup*/ 4096 | $$self.$$.dirty[6] & /*activeMarkupSelected*/ 8) {\n\t\t\t$$invalidate(32, shapeAllowNewline = activeMarkupSelected && (shapeIsTextBox(activeMarkup)\n\t\t\t? // has selected text box, if text box has disableNewline property, follow setting, else allow new lines\n\t\t\t\thasProp(activeMarkup, 'disableNewline')\n\t\t\t\t? !activeMarkup.disableNewline\n\t\t\t\t: true\n\t\t\t: // has selected something else, only disable new lines if disableNewline is explicitly set to false\n\t\t\t\tactiveMarkup.disableNewline === false));\n\t\t}\n\n\t\tif ($$self.$$.dirty[3] & /*mapImagePointToEditorPoint*/ 4096 | $$self.$$.dirty[4] & /*hoverShape*/ 536870912) {\n\t\t\thoverShape && mapImagePointToEditorPoint && !shapeIsSelected(hoverShape) && shapeCanSelect(hoverShape)\n\t\t\t? redrawHoverShapes(hoverShape)\n\t\t\t: popHoverShapes();\n\t\t}\n\n\t\tif ($$self.$$.dirty[0] & /*shouldRenderShapeManipulator, allowRotateControls, shapeManipulatorPoints, activeMarkupComputed*/ 442368 | $$self.$$.dirty[2] & /*opacity*/ 8192) {\n\t\t\t// TODO: we use opacity to trigger a redraw of the active points,\n\t\t\t// this should be changed to the image zoom factor in a future release\n\t\t\t$$invalidate(186, shapeManipulatorRotationPoint = shouldRenderShapeManipulator && allowRotateControls && opacity && shapeManipulatorPoints\n\t\t\t? getShapeRotationPointOnScreen(activeMarkupComputed)\n\t\t\t: undefined);\n\t\t}\n\n\t\tif ($$self.$$.dirty[0] & /*offset*/ 4 | $$self.$$.dirty[6] & /*shapeManipulatorRotationPoint*/ 1) {\n\t\t\t$$invalidate(31, shapeManipulatorRotationPointPosition = shapeManipulatorRotationPoint && vectorCreate(shapeManipulatorRotationPoint.position.x - offset.x, shapeManipulatorRotationPoint.position.y - offset.y));\n\t\t}\n\n\t\tif ($$self.$$.dirty[0] & /*shouldRenderShapeManipulator, allowScaleControls, shapeManipulatorPoints, activeMarkupComputed*/ 245760 | $$self.$$.dirty[2] & /*opacity*/ 8192) {\n\t\t\t$$invalidate(188, shapeManipulatorScalarPoint = shouldRenderShapeManipulator && allowScaleControls && opacity && shapeManipulatorPoints\n\t\t\t? getShapeScalarPointOnScreen(activeMarkupComputed)\n\t\t\t: undefined);\n\t\t}\n\n\t\tif ($$self.$$.dirty[0] & /*offset*/ 4 | $$self.$$.dirty[6] & /*shapeManipulatorScalarPoint*/ 4) {\n\t\t\t$$invalidate(30, shapeManipulatorScalarPointPosition = shapeManipulatorScalarPoint && vectorCreate(shapeManipulatorScalarPoint.x - offset.x, shapeManipulatorScalarPoint.y - offset.y));\n\t\t}\n\n\t\tif ($$self.$$.dirty[6] & /*$keysPressedStored*/ 512) {\n\t\t\t// hold cmd or control to disable snapping\n\t\t\t$$invalidate(179, disableSnapping = !!($keysPressedStored || []).find(keyCode => [91, 93, 17].includes(keyCode)));\n\t\t}\n\n\t\tif ($$self.$$.dirty[3] & /*snapPointer*/ 524288 | $$self.$$.dirty[5] & /*disableSnapping*/ 16777216) {\n\t\t\t$$invalidate(181, shouldSnapPointer = !(disableSnapping || !snapPointer));\n\t\t}\n\n\t\tif ($$self.$$.dirty[0] & /*activeMarkup*/ 4096 | $$self.$$.dirty[2] & /*opacity*/ 8192 | $$self.$$.dirty[4] & /*pointerPositionInEditor*/ 1073741824 | $$self.$$.dirty[5] & /*selectionScreenPoints, activeShapeIsDraft, shouldSnapPointer*/ 1141374976) {\n\t\t\tif (activeMarkup && selectionScreenPoints && opacity > 0) {\n\t\t\t\tclearHoverShapeIfActive();\n\n\t\t\t\tredrawManipulatorShapes(opacity, activeMarkup, // use snapped pointer position when we're drawing path\n\t\t\t\tactiveShapeIsDraft && shapeIsPath(activeMarkup)\n\t\t\t\t? snapPointerPosition({ ...pointerPositionInEditor }, !shouldSnapPointer)\n\t\t\t\t: undefined);\n\t\t\t}\n\t\t}\n\n\t\tif ($$self.$$.dirty[0] & /*activeMarkup*/ 4096) {\n\t\t\tif (!activeMarkup) {\n\t\t\t\tclearHoverShape();\n\t\t\t\tpopManipulatorShapes();\n\t\t\t}\n\t\t}\n\n\t\tif ($$self.$$.dirty[3] & /*mapImagePointToEditorPoint*/ 4096 | $$self.$$.dirty[5] & /*multiSelectionShapePoints*/ 536870912) {\n\t\t\tif (multiSelectionShapePoints.length) {\n\t\t\t\tpopMultiSelectionShapes();\n\n\t\t\t\tconst multiSelectionShapes = multiSelectionShapePoints.map(points => ({\n\t\t\t\t\tid: MultiSelectionShapeId,\n\t\t\t\t\tpoints: points.map(mapImagePointToEditorPoint),\n\t\t\t\t\tstrokeWidth: 1,\n\t\t\t\t\tstrokeColor: [1, 1, 1, 0.5],\n\t\t\t\t\tpathClose: true\n\t\t\t\t}));\n\n\t\t\t\tpushMultiSelectionShapes(...multiSelectionShapes);\n\t\t\t} else {\n\t\t\t\tpopMultiSelectionShapes();\n\t\t\t}\n\t\t}\n\n\t\tif ($$self.$$.dirty[4] & /*hoverShape*/ 536870912) {\n\t\t\t$$invalidate(182, isHoveringShape = !!hoverShape);\n\t\t}\n\n\t\tif ($$self.$$.dirty[4] & /*interactionShape*/ 134217728) {\n\t\t\t$$invalidate(183, isManagingShape = !!interactionShape);\n\t\t}\n\n\t\tif ($$self.$$.dirty[4] & /*pointerPositionInEditor*/ 1073741824 | $$self.$$.dirty[5] & /*isManagingShape, isHoveringShape, shouldSnapPointer*/ 469762048) {\n\t\t\tif (// when pointer is available / updated\n\t\t\tpointerPositionInEditor && // when we're not interacting\n\t\t\t!isManagingShape && // and not hovering shape\n\t\t\t!isHoveringShape) {\n\t\t\t\tsnapPointerPosition({ ...pointerPositionInEditor }, !shouldSnapPointer);\n\t\t\t}\n\t\t}\n\n\t\tif ($$self.$$.dirty[2] & /*gridColor*/ 131072 | $$self.$$.dirty[3] & /*gridSize, mapImagePointToEditorPoint*/ 2101248 | $$self.$$.dirty[5] & /*gridRect*/ 33554432 | $$self.$$.dirty[6] & /*$gridOpacity*/ 1024) {\n\t\t\tif (gridSize > 0 && gridRect && mapImagePointToEditorPoint) {\n\t\t\t\tredrawGridShapes(gridSize, gridRect, gridColor, $gridOpacity);\n\t\t\t}\n\t\t}\n\n\t\tif ($$self.$$.dirty[2] & /*snapColor*/ 262144 | $$self.$$.dirty[3] & /*mapImagePointToEditorPoint*/ 4096 | $$self.$$.dirty[4] & /*interactionSnapLineX, interactionSnapLineY*/ 100663296 | $$self.$$.dirty[5] & /*disableSnapping*/ 16777216) {\n\t\t\tif (mapImagePointToEditorPoint) {\n\t\t\t\tif (disableSnapping) {\n\t\t\t\t\tpopSnapShapes();\n\t\t\t\t} else {\n\t\t\t\t\tredrawSnapShapes(interactionSnapLineX, interactionSnapLineY, snapColor);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif ($$self.$$.dirty[2] & /*active*/ 4096) {\n\t\t\ttogglePrerender(active);\n\t\t}\n\n\t\tif ($$self.$$.dirty[6] & /*$keysPressedStored*/ 512) {\n\t\t\t//\n\t\t\t// Keyboard\n\t\t\t//\n\t\t\tif ($keysPressedStored && $keysPressedStored.length) handleKeyPressedStore();\n\t\t}\n\n\t\tif ($$self.$$.dirty[0] & /*textInput, textInputMode*/ 288) {\n\t\t\t// auto focus text input when created and in inline mode\n\t\t\tif (textInput && textInputMode === 'inline') textInput.focus();\n\t\t}\n\n\t\tif ($$self.$$.dirty[0] & /*activeMarkup*/ 4096) {\n\t\t\t$$invalidate(178, isTextMarkupSelected = activeMarkup && shapeIsText(activeMarkup));\n\t\t}\n\n\t\tif ($$self.$$.dirty[0] & /*activeMarkup*/ 4096 | $$self.$$.dirty[5] & /*isTextMarkupSelected*/ 8388608) {\n\t\t\t$$invalidate(13, shouldRenderTextInput = isTextMarkupSelected && shapeCanInput(activeMarkup) !== false && shapeIsTextEditing(activeMarkup));\n\t\t}\n\n\t\tif ($$self.$$.dirty[0] & /*shouldRenderTextInput*/ 8192) {\n\t\t\t$$invalidate(173, textShapeOrigin = shouldRenderTextInput\n\t\t\t? getTextShapeOriginSnapshot()\n\t\t\t: undefined);\n\t\t}\n\n\t\tif ($$self.$$.dirty[2] & /*parentRect*/ 16384 | $$self.$$.dirty[5] & /*textShapeOrigin*/ 262144) {\n\t\t\t$$invalidate(175, textShapeDisplayOrigin = textShapeOrigin && shapeComputeDisplay({ ...textShapeOrigin }, parentRect));\n\t\t}\n\n\t\tif ($$self.$$.dirty[5] & /*textShapeDisplayOrigin*/ 1048576) {\n\t\t\t$$invalidate(177, textSizeDisplayOrigin = textShapeDisplayOrigin && textToSize(textShapeDisplayOrigin.text, textShapeDisplayOrigin));\n\t\t}\n\n\t\tif ($$self.$$.dirty[5] & /*textShapeDisplayOrigin, textSizeDisplayOrigin*/ 5242880) {\n\t\t\ttextRectDisplayOrigin = textShapeDisplayOrigin && rectCreate(textShapeDisplayOrigin.x, textShapeDisplayOrigin.y, textSizeDisplayOrigin.width, textSizeDisplayOrigin.height);\n\t\t}\n\n\t\tif ($$self.$$.dirty[0] & /*shouldRenderTextInput, activeMarkup*/ 12288) {\n\t\t\t$$invalidate(27, textInputText = shouldRenderTextInput ? activeMarkup.text : '');\n\t\t}\n\n\t\tif ($$self.$$.dirty[0] & /*rootRect, activeMarkup, shouldRenderTextInput*/ 12296 | $$self.$$.dirty[2] & /*parentRect*/ 16384) {\n\t\t\t$$invalidate(176, activeTextShapeComputed = rootRect && activeMarkup && shouldRenderTextInput && shapeComputeDisplay(\n\t\t\t\t{\n\t\t\t\t\t...shapeDeepCopy(activeMarkup),\n\t\t\t\t\t// this is the same as how canvas floors shape width/height\n\t\t\t\t\twidth: isNumber(activeMarkup.width)\n\t\t\t\t\t? Math.floor(activeMarkup.width)\n\t\t\t\t\t: undefined,\n\t\t\t\t\theight: isNumber(activeMarkup.height)\n\t\t\t\t\t? Math.floor(activeMarkup.height)\n\t\t\t\t\t: undefined\n\t\t\t\t},\n\t\t\t\tparentRect\n\t\t\t));\n\t\t}\n\n\t\tif ($$self.$$.dirty[0] & /*shouldRenderTextInput, textInputMode*/ 8224 | $$self.$$.dirty[5] & /*activeTextShapeComputed*/ 2097152) {\n\t\t\t$$invalidate(29, textInputTextStyles = shouldRenderTextInput && getTextInputTextStyles(activeTextShapeComputed, textInputMode));\n\t\t}\n\n\t\tif ($$self.$$.dirty[0] & /*shouldRenderTextInput, offset*/ 8196 | $$self.$$.dirty[2] & /*contextZoom, contextRotation*/ 2304 | $$self.$$.dirty[5] & /*activeTextShapeComputed*/ 2097152) {\n\t\t\t$$invalidate(28, textInputPositionStyles = shouldRenderTextInput && getTextInputPositionStyles(activeTextShapeComputed, offset, contextZoom, contextRotation));\n\t\t}\n\n\t\tif ($$self.$$.dirty[2] & /*caretColor*/ 65536) {\n\t\t\tcaretColorRGBA = caretColor ? colorArrayToRGBA(caretColor) : 'auto';\n\t\t}\n\n\t\tif ($$self.$$.dirty[0] & /*shouldRenderTextInput, textInput, textInputMode*/ 8480) {\n\t\t\tif (shouldRenderTextInput && textInput && textInputMode === 'inline') syncTextInput();\n\t\t}\n\n\t\tif ($$self.$$.dirty[0] & /*activeMarkup*/ 4096 | $$self.$$.dirty[5] & /*activeMarkupItemIsDraft, controlledMarkupItem*/ 33) {\n\t\t\t$$invalidate(160, controlledMarkupItem = activeMarkup && !activeMarkupItemIsDraft\n\t\t\t? activeMarkup\n\t\t\t: controlledMarkupItem);\n\t\t}\n\n\t\tif ($$self.$$.dirty[5] & /*controlledMarkupItem*/ 32) {\n\t\t\t$$invalidate(162, allowShapeFlip = controlledMarkupItem && shapeCanFlip(controlledMarkupItem));\n\t\t}\n\n\t\tif ($$self.$$.dirty[5] & /*controlledMarkupItem*/ 32) {\n\t\t\t$$invalidate(157, allowShapeChangeTextLayout = controlledMarkupItem && shapeCanChangeTextLayout(controlledMarkupItem));\n\t\t}\n\n\t\tif ($$self.$$.dirty[5] & /*controlledMarkupItem*/ 32) {\n\t\t\t$$invalidate(159, allowShapeDuplicate = controlledMarkupItem && shapeCanDuplicate(controlledMarkupItem));\n\t\t}\n\n\t\tif ($$self.$$.dirty[5] & /*controlledMarkupItem*/ 32) {\n\t\t\t$$invalidate(158, allowShapeRemove = controlledMarkupItem && shapeCanRemove(controlledMarkupItem));\n\t\t}\n\n\t\tif ($$self.$$.dirty[5] & /*isMultiSelection, controlledMarkupItem*/ 65568) {\n\t\t\t$$invalidate(161, allowShapeReorder = !isMultiSelection && controlledMarkupItem && shapeCanReorder(controlledMarkupItem));\n\t\t}\n\n\t\tif ($$self.$$.dirty[5] & /*controlledMarkupItem*/ 32) {\n\t\t\t$$invalidate(156, allowShapeInput = controlledMarkupItem && shapeCanInput(controlledMarkupItem) !== false);\n\t\t}\n\n\t\tif ($$self.$$.dirty[5] & /*controlledMarkupItem*/ 32) {\n\t\t\t$$invalidate(163, allowShapeAdjustOpacity = controlledMarkupItem && hasProp(controlledMarkupItem, 'backgroundImage') && shapeCanStyle(controlledMarkupItem, 'opacity'));\n\t\t}\n\n\t\tif ($$self.$$.dirty[0] & /*activeMarkup, textInputMode*/ 4128) {\n\t\t\t$$invalidate(166, allowShapeStyleText = activeMarkup && activeMarkup.format === 'html' && textInputMode === 'inline');\n\t\t}\n\n\t\tif ($$self.$$.dirty[0] & /*activeMarkup*/ 4096 | $$self.$$.dirty[5] & /*activeMarkupItemIsDraft, allowShapeStyleText*/ 2049) {\n\t\t\t$$invalidate(170, shouldShowMarkupControls = activeMarkup && (!activeMarkupItemIsDraft || allowShapeStyleText));\n\t\t}\n\n\t\tif ($$self.$$.dirty[4] & /*isInteracting*/ 268435456 | $$self.$$.dirty[5] & /*shouldShowMarkupControls*/ 32768 | $$self.$$.dirty[6] & /*$isAnimated*/ 256) {\n\t\t\t// show controls if markup is active, but not in draft mode, except for when editing text\n\t\t\tmarkupControlsOpacity.set(shouldShowMarkupControls && !isInteracting ? 1 : 0, { hard: $isAnimated === false });\n\t\t}\n\n\t\tif ($$self.$$.dirty[0] & /*shapeManipulatorPoints*/ 16384 | $$self.$$.dirty[5] & /*shouldShowMarkupControls, markupControlsAnchorPosition*/ 40960) {\n\t\t\t$$invalidate(168, markupControlsAnchorPosition = shouldShowMarkupControls && shapeManipulatorPoints.length\n\t\t\t? getMarkupControlsAnchorPosition(rectCreateFromPoints(shapeManipulatorPoints))\n\t\t\t: markupControlsAnchorPosition);\n\t\t}\n\n\t\tif ($$self.$$.dirty[0] & /*shapeControlsSize, utilRect*/ 528 | $$self.$$.dirty[5] & /*shapeSelectedManipulatorPoint, markupControlsAnchorPosition*/ 24576) {\n\t\t\t$$invalidate(167, shapeControlsPosition = (shapeSelectedManipulatorPoint || markupControlsAnchorPosition) && shapeControlsSize && utilRect && getShapeControlPositionOnCanvas(utilRect, shapeSelectedManipulatorPoint || markupControlsAnchorPosition, shapeControlsSize, -16));\n\t\t}\n\n\t\tif ($$self.$$.dirty[5] & /*shapeControlsPosition*/ 4096) {\n\t\t\t// $: markupControlsStyle =\n\t\t\t// shapeControlsPosition &&\n\t\t\t// `transform: translate(${shapeControlsPosition.x}px, ${shapeControlsPosition.y}px);opacity:${$markupControlsOpacity}`;\n\t\t\t$$invalidate(26, styleMarkupControlTransform = shapeControlsPosition\n\t\t\t? `translate(${shapeControlsPosition.x}px, ${shapeControlsPosition.y}px)`\n\t\t\t: undefined);\n\t\t}\n\n\t\tif ($$self.$$.dirty[0] & /*$markupControlsOpacity*/ 2097152 | $$self.$$.dirty[5] & /*shapeControlsPosition*/ 4096) {\n\t\t\t$$invalidate(25, styleMarkupControlOpacity = shapeControlsPosition\n\t\t\t? $markupControlsOpacity\n\t\t\t: undefined);\n\t\t}\n\n\t\tif ($$self.$$.dirty[0] & /*shouldRenderTextInput*/ 8192) {\n\t\t\t$$invalidate(165, shapeControlsMode = shouldRenderTextInput ? 'text' : 'shape');\n\t\t}\n\n\t\tif ($$self.$$.dirty[0] & /*activeShapeId, locale, activeMarkup*/ 6272 | $$self.$$.dirty[3] & /*willRenderShapeTextControls, willRenderShapeControls, enableButtonFlipVertical*/ 33536 | $$self.$$.dirty[5] & /*shapeControlsMode, allowShapeStyleText, hasSelectedShapePoint, allowShapeAdjustOpacity, allowShapeFlip, allowShapeReorder, controlledMarkupItem, allowShapeDuplicate, allowShapeRemove, allowShapeInput, allowShapeChangeTextLayout*/ 4094 | $$self.$$.dirty[6] & /*$shapes*/ 128) {\n\t\t\t$$invalidate(24, shapeControls = // render text controls\n\t\t\tshapeControlsMode === 'text' && willRenderShapeTextControls && allowShapeStyleText\n\t\t\t? runSafe(() => willRenderShapeTextControls(\n\t\t\t\t\t[\n\t\t\t\t\t\t[\n\t\t\t\t\t\t\t'div',\n\t\t\t\t\t\t\t'text-styles',\n\t\t\t\t\t\t\t{ class: 'PinturaShapeControlsGroup' },\n\t\t\t\t\t\t\t[\n\t\t\t\t\t\t\t\t...[\n\t\t\t\t\t\t\t\t\t[\n\t\t\t\t\t\t\t\t\t\t'bold',\n\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\tlabel: 'B',\n\t\t\t\t\t\t\t\t\t\t\tstyle: 'font-weight:900;',\n\t\t\t\t\t\t\t\t\t\t\ttitle: 'Bold',\n\t\t\t\t\t\t\t\t\t\t\tshortcut: ['CMD', 'B']\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\t\t[\n\t\t\t\t\t\t\t\t\t\t'italic',\n\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\tlabel: 'I',\n\t\t\t\t\t\t\t\t\t\t\tstyle: 'font-family:Times New Roman;font-style:italic;',\n\t\t\t\t\t\t\t\t\t\t\ttitle: 'Italic',\n\t\t\t\t\t\t\t\t\t\t\tshortcut: ['CMD', 'I']\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\t\t[\n\t\t\t\t\t\t\t\t\t\t'underline',\n\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\tlabel: 'U',\n\t\t\t\t\t\t\t\t\t\t\tstyle: 'text-decoration:underline;',\n\t\t\t\t\t\t\t\t\t\t\ttitle: 'Underline',\n\t\t\t\t\t\t\t\t\t\t\tshortcut: ['CMD', 'U']\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\t\t[\n\t\t\t\t\t\t\t\t\t\t'strikeThrough',\n\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\tlabel: 'S',\n\t\t\t\t\t\t\t\t\t\t\tstyle: 'text-decoration:line-through;',\n\t\t\t\t\t\t\t\t\t\t\ttitle: 'Strikethrough',\n\t\t\t\t\t\t\t\t\t\t\tshortcut: ['CMD', 'S']\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t]\n\t\t\t\t\t\t\t\t].map(([style, props]) => {\n\t\t\t\t\t\t\t\t\treturn [\n\t\t\t\t\t\t\t\t\t\t'Button',\n\t\t\t\t\t\t\t\t\t\t'style-' + style,\n\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\tonclick: () => document.execCommand(style),\n\t\t\t\t\t\t\t\t\t\t\t...props,\n\t\t\t\t\t\t\t\t\t\t\tstyle: 'font-size:1.25em;text-underline-offset:1px;text-decoration-thickness:1.5px;font-weight:400;' + props.style\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t];\n\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t]\n\t\t\t\t\t\t],\n\t\t\t\t\t\t[\n\t\t\t\t\t\t\t'div',\n\t\t\t\t\t\t\t'text-reset',\n\t\t\t\t\t\t\t{ class: 'PinturaShapeControlsGroup' },\n\t\t\t\t\t\t\t[\n\t\t\t\t\t\t\t\t[\n\t\t\t\t\t\t\t\t\t'Button',\n\t\t\t\t\t\t\t\t\t'style-reset',\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\tonclick: () => document.execCommand('removeFormat'),\n\t\t\t\t\t\t\t\t\t\tstyle: 'font-weight:400',\n\t\t\t\t\t\t\t\t\t\ttitle: 'Remove styles',\n\t\t\t\t\t\t\t\t\t\tlabel: [\n\t\t\t\t\t\t\t\t\t\t\t[\n\t\t\t\t\t\t\t\t\t\t\t\t'span',\n\t\t\t\t\t\t\t\t\t\t\t\t'T',\n\t\t\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\t\t\tstyle: 'font-size:1.25em;font-style:italic;font-family:Times New Roman;text-decoration:underline',\n\t\t\t\t\t\t\t\t\t\t\t\t\ttextContent: 'T'\n\t\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\t\t\t\t[\n\t\t\t\t\t\t\t\t\t\t\t\t'span',\n\t\t\t\t\t\t\t\t\t\t\t\t'x',\n\t\t\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\t\t\tstyle: 'position:relative;top:.175rem;',\n\t\t\t\t\t\t\t\t\t\t\t\t\tinnerHTML: '×'\n\t\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\t]\n\t\t\t\t\t\t\t\t\t\t]\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t]\n\t\t\t\t\t\t\t]\n\t\t\t\t\t\t]\n\t\t\t\t\t],\n\t\t\t\t\tactiveShapeId\n\t\t\t\t))\n\t\t\t: // render shape controls\n\t\t\t\tshapeControlsMode === 'shape' && willRenderShapeControls && activeShapeId\n\t\t\t\t? runSafe(() => willRenderShapeControls(\n\t\t\t\t\t\thasSelectedShapePoint\n\t\t\t\t\t\t? [\n\t\t\t\t\t\t\t\t[\n\t\t\t\t\t\t\t\t\t'div',\n\t\t\t\t\t\t\t\t\t'epsilon',\n\t\t\t\t\t\t\t\t\t{ class: 'PinturaShapeControlsGroup' },\n\t\t\t\t\t\t\t\t\t[\n\t\t\t\t\t\t\t\t\t\t[\n\t\t\t\t\t\t\t\t\t\t\t'Button',\n\t\t\t\t\t\t\t\t\t\t\t'remove-point',\n\t\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\t\tonclick: handleRemovePointActiveMarkup,\n\t\t\t\t\t\t\t\t\t\t\t\tlabel: locale.shapeTitleButtonRemove,\n\t\t\t\t\t\t\t\t\t\t\t\ticon: locale.shapeIconButtonRemove,\n\t\t\t\t\t\t\t\t\t\t\t\tshortcut: ['Backspace'],\n\t\t\t\t\t\t\t\t\t\t\t\thideLabel: true\n\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t]\n\t\t\t\t\t\t\t\t\t]\n\t\t\t\t\t\t\t\t]\n\t\t\t\t\t\t\t]\n\t\t\t\t\t\t: [\n\t\t\t\t\t\t\t\tallowShapeAdjustOpacity && [\n\t\t\t\t\t\t\t\t\t'div',\n\t\t\t\t\t\t\t\t\t'alpha',\n\t\t\t\t\t\t\t\t\t{ class: 'PinturaShapeControlsGroup' },\n\t\t\t\t\t\t\t\t\t[\n\t\t\t\t\t\t\t\t\t\t[\n\t\t\t\t\t\t\t\t\t\t\t'ToggleSlider',\n\t\t\t\t\t\t\t\t\t\t\t'adjust-opacity',\n\t\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\t\tonrelease: handleConfirmOpacity,\n\t\t\t\t\t\t\t\t\t\t\t\tonchange: handleAdjustOpacity,\n\t\t\t\t\t\t\t\t\t\t\t\tstep: 0.01,\n\t\t\t\t\t\t\t\t\t\t\t\tvalue: hasProp(activeMarkup, 'opacity')\n\t\t\t\t\t\t\t\t\t\t\t\t? activeMarkup.opacity\n\t\t\t\t\t\t\t\t\t\t\t\t: 1,\n\t\t\t\t\t\t\t\t\t\t\t\tlabel: (value, min, max) => `${Math.round(value / max * 100)}%`,\n\t\t\t\t\t\t\t\t\t\t\t\tmin: 0,\n\t\t\t\t\t\t\t\t\t\t\t\tmax: 1,\n\t\t\t\t\t\t\t\t\t\t\t\tdirection: 'x'\n\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t]\n\t\t\t\t\t\t\t\t\t]\n\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\t[\n\t\t\t\t\t\t\t\t\t'div',\n\t\t\t\t\t\t\t\t\t'beta',\n\t\t\t\t\t\t\t\t\t{ class: 'PinturaShapeControlsGroup' },\n\t\t\t\t\t\t\t\t\t[\n\t\t\t\t\t\t\t\t\t\tallowShapeFlip && [\n\t\t\t\t\t\t\t\t\t\t\t'Button',\n\t\t\t\t\t\t\t\t\t\t\t'flip-horizontal',\n\t\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\t\tonclick: handleFlipX,\n\t\t\t\t\t\t\t\t\t\t\t\tlabel: locale.shapeTitleButtonFlipHorizontal,\n\t\t\t\t\t\t\t\t\t\t\t\ticon: locale.shapeIconButtonFlipHorizontal,\n\t\t\t\t\t\t\t\t\t\t\t\thideLabel: true\n\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\t\t\tallowShapeFlip && enableButtonFlipVertical && [\n\t\t\t\t\t\t\t\t\t\t\t'Button',\n\t\t\t\t\t\t\t\t\t\t\t'flip-vertical',\n\t\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\t\tonclick: handleFlipY,\n\t\t\t\t\t\t\t\t\t\t\t\tlabel: locale.shapeTitleButtonFlipVertical,\n\t\t\t\t\t\t\t\t\t\t\t\ticon: locale.shapeIconButtonFlipVertical,\n\t\t\t\t\t\t\t\t\t\t\t\thideLabel: true\n\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\t\t\tallowShapeReorder && [\n\t\t\t\t\t\t\t\t\t\t\t'Button',\n\t\t\t\t\t\t\t\t\t\t\t'to-front',\n\t\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\t\tonclick: handleMoveToFrontActiveMarkup,\n\t\t\t\t\t\t\t\t\t\t\t\tlabel: locale.shapeTitleButtonMoveToFront,\n\t\t\t\t\t\t\t\t\t\t\t\ticon: locale.shapeIconButtonMoveToFront,\n\t\t\t\t\t\t\t\t\t\t\t\thideLabel: true,\n\t\t\t\t\t\t\t\t\t\t\t\tdisabled: $shapes[$shapes.length - 1] === controlledMarkupItem\n\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\t\t\tallowShapeDuplicate && [\n\t\t\t\t\t\t\t\t\t\t\t'Button',\n\t\t\t\t\t\t\t\t\t\t\t'duplicate',\n\t\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\t\tonclick: handleDuplicateActiveMarkup,\n\t\t\t\t\t\t\t\t\t\t\t\tlabel: locale.shapeTitleButtonDuplicate,\n\t\t\t\t\t\t\t\t\t\t\t\ticon: locale.shapeIconButtonDuplicate,\n\t\t\t\t\t\t\t\t\t\t\t\tshortcut: ['CMD', 'D'],\n\t\t\t\t\t\t\t\t\t\t\t\thideLabel: true\n\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\t\t\tallowShapeRemove && [\n\t\t\t\t\t\t\t\t\t\t\t'Button',\n\t\t\t\t\t\t\t\t\t\t\t'remove',\n\t\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\t\tonclick: handleRemoveActiveMarkup,\n\t\t\t\t\t\t\t\t\t\t\t\tlabel: locale.shapeTitleButtonRemove,\n\t\t\t\t\t\t\t\t\t\t\t\ticon: locale.shapeIconButtonRemove,\n\t\t\t\t\t\t\t\t\t\t\t\tshortcut: ['Backspace'],\n\t\t\t\t\t\t\t\t\t\t\t\thideLabel: true\n\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t]\n\t\t\t\t\t\t\t\t\t].filter(Boolean)\n\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\tallowShapeInput && allowShapeChangeTextLayout && [\n\t\t\t\t\t\t\t\t\t'div',\n\t\t\t\t\t\t\t\t\t'gamma',\n\t\t\t\t\t\t\t\t\t{ class: 'PinturaShapeControlsGroup' },\n\t\t\t\t\t\t\t\t\t[\n\t\t\t\t\t\t\t\t\t\t[\n\t\t\t\t\t\t\t\t\t\t\t'Button',\n\t\t\t\t\t\t\t\t\t\t\t'text-layout',\n\t\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\t\tonclick: handleTextSwitchLayout,\n\t\t\t\t\t\t\t\t\t\t\t\tlabel: localize(TextLayoutChangeLabel, locale, activeMarkup),\n\t\t\t\t\t\t\t\t\t\t\t\ticon: localize(TextLayoutChangeIcon, locale, activeMarkup),\n\t\t\t\t\t\t\t\t\t\t\t\thideLabel: true\n\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t]\n\t\t\t\t\t\t\t\t\t]\n\t\t\t\t\t\t\t\t],\n\t\t\t\t\t\t\t\tallowShapeInput && [\n\t\t\t\t\t\t\t\t\t'div',\n\t\t\t\t\t\t\t\t\t'delta',\n\t\t\t\t\t\t\t\t\t{ class: 'PinturaShapeControlsGroup' },\n\t\t\t\t\t\t\t\t\t[\n\t\t\t\t\t\t\t\t\t\t[\n\t\t\t\t\t\t\t\t\t\t\t'Button',\n\t\t\t\t\t\t\t\t\t\t\t'edit-text',\n\t\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\t\tlabel: locale.shapeLabelInputText,\n\t\t\t\t\t\t\t\t\t\t\t\tonclick: handleEditTextActiveMarkup\n\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t]\n\t\t\t\t\t\t\t\t\t]\n\t\t\t\t\t\t\t\t]\n\t\t\t\t\t\t\t].filter(Boolean),\n\t\t\t\t\t\tactiveShapeId\n\t\t\t\t\t))\n\t\t\t\t: // fallback to empty array\n\t\t\t\t\t[]);\n\t\t}\n\n\t\tif ($$self.$$.dirty[0] & /*locale*/ 128 | $$self.$$.dirty[6] & /*$shapes*/ 128) {\n\t\t\t$$invalidate(23, shapeNavList = $shapes.filter(shapeCanSelect).filter(shape => shape.id).filter(shape => !shapeIsDraft(shape)).map(shape => ({\n\t\t\t\tid: shape.id,\n\t\t\t\tcolor: shapeIsText(shape)\n\t\t\t\t? shape.color\n\t\t\t\t: shapeIsLine(shape)\n\t\t\t\t\t? shape.strokeColor\n\t\t\t\t\t: shape.backgroundColor,\n\t\t\t\tname: shape.name || locale[`shapeLabelTool${capitalizeFirstLetter(shapeGetDescription(shape))}`]\n\t\t\t})));\n\t\t}\n\n\t\tif ($$self.$$.dirty[0] & /*element*/ 1024) {\n\t\t\tif (element) {\n\t\t\t\telement.addEventListener('touchmove', handleTouchmove, false);\n\t\t\t}\n\t\t}\n\t};\n\n\treturn [\n\t\tdisabled,\n\t\tshapes,\n\t\toffset,\n\t\trootRect,\n\t\tutilRect,\n\t\ttextInputMode,\n\t\tenableTextStyleControls,\n\t\tlocale,\n\t\ttextInput,\n\t\tshapeControlsSize,\n\t\telement,\n\t\tactiveShapeId,\n\t\tactiveMarkup,\n\t\tshouldRenderTextInput,\n\t\tshapeManipulatorPoints,\n\t\tactiveMarkupComputed,\n\t\tallowScaleControls,\n\t\tshouldRenderShapeManipulator,\n\t\tallowRotateControls,\n\t\tshapeSelectedPointIndex,\n\t\tallowEdgeControls,\n\t\t$markupControlsOpacity,\n\t\tshowShapeList,\n\t\tshapeNavList,\n\t\tshapeControls,\n\t\tstyleMarkupControlOpacity,\n\t\tstyleMarkupControlTransform,\n\t\ttextInputText,\n\t\ttextInputPositionStyles,\n\t\ttextInputTextStyles,\n\t\tshapeManipulatorScalarPointPosition,\n\t\tshapeManipulatorRotationPointPosition,\n\t\tshapeAllowNewline,\n\t\tallowCornerControls,\n\t\tisAnimated,\n\t\tkeysPressedStored,\n\t\thandleInteractionStart,\n\t\thandleInteractionCancel,\n\t\thandleInteractionUpdate,\n\t\thandleInteractionRelease,\n\t\thandleInteractionEnd,\n\t\tgridOpacity,\n\t\thandleManipulatorResizeGrab,\n\t\thandleManipulatorResizeDrag,\n\t\thandleManipulatorResizeEnd,\n\t\thandleManipulatorRotateGrab,\n\t\thandleManipulatorRotateDrag,\n\t\thandleManipulatorRotateEnd,\n\t\thandleManipulatorScaleGrab,\n\t\thandleManipulatorScaleDrag,\n\t\thandleManipulatorScaleEnd,\n\t\thandleKeyDown,\n\t\tformatContentEditable,\n\t\thandleTextInput,\n\t\thandleTextInputAttempt,\n\t\thandleTextInputKeyDown,\n\t\tisTextConfirmKeyCombination,\n\t\thandleTextInputKeyUp,\n\t\thandleTextConfirm,\n\t\thandleTextCancel,\n\t\tmarkupControlsOpacity,\n\t\thandleNudge,\n\t\thandleFocusIn,\n\t\thandleFocusOut,\n\t\thandlePointerMove,\n\t\thandlePointerLeave,\n\t\tselectShapeByIndex,\n\t\tuid,\n\t\tui,\n\t\tselection,\n\t\tcontextRotation,\n\t\tcontextFlipX,\n\t\tcontextFlipY,\n\t\tcontextZoom,\n\t\tactive,\n\t\topacity,\n\t\tparentRect,\n\t\thoverColor,\n\t\tcaretColor,\n\t\tgridColor,\n\t\tsnapColor,\n\t\toninteractionstart,\n\t\toninteractionupdate,\n\t\toninteractionrelease,\n\t\toninteractionend,\n\t\toninteractioncancel,\n\t\tonaddshape,\n\t\tonupdateshape,\n\t\tonselectshape,\n\t\tonblurshape,\n\t\tonremoveshape,\n\t\tontapshape,\n\t\tonhovershape,\n\t\tontriggerhistorywrite,\n\t\tonhovercanvas,\n\t\tontapcanvas,\n\t\tonleavecanvas,\n\t\tbeforeSelectShape,\n\t\tbeforeDeselectShape,\n\t\tbeforeRemoveShape,\n\t\tbeforeUpdateShape,\n\t\twillRenderShapeControls,\n\t\twillRenderShapeTextControls,\n\t\twillStartInteraction,\n\t\tmapEditorPointToImagePoint,\n\t\tmapImagePointToEditorPoint,\n\t\teraseRadius,\n\t\tselectRadius,\n\t\tenableButtonFlipVertical,\n\t\tenableTapToAddText,\n\t\tenableMultiSelect,\n\t\tsnapThreshold,\n\t\tsnapPointer,\n\t\tenableSnapToContext,\n\t\tgridSize,\n\t\tsyncShapeCollection,\n\t\tgetShapeDraft,\n\t\taddShapeDraft,\n\t\tdiscardShapeDraft,\n\t\tconfirmShapeDraft,\n\t\tupdateShapeDraft,\n\t\taddShape,\n\t\tcreateShape,\n\t\teraseShape,\n\t\tgetMarkupItemDraft,\n\t\tgetMarkupItemDraftIndex,\n\t\taddMarkupItemDraft,\n\t\tconfirmMarkupItemDraft,\n\t\tdiscardMarkupItemDraft,\n\t\tcreateMarkupItem,\n\t\tsyncShapes,\n\t\tremoveMarkupShapeProps,\n\t\tupdateMarkupShape,\n\t\tupdateMarkupShapeProperty,\n\t\tupdateMarkupItemsShapeProperty,\n\t\tupdateMarkupShapeItems,\n\t\tgetActiveMarkupItems,\n\t\tgetActiveMarkupItem,\n\t\thasActiveMarkupItem,\n\t\tblurShapes,\n\t\tselectShape,\n\t\tdeselectMarkupItem,\n\t\teditMarkupItem,\n\t\tfinishEditMarkupItem,\n\t\tremoveMarkupItems,\n\t\tgetTextShapeRect,\n\t\tgetMarkupShapeRect,\n\t\tgetShapesNearPosition,\n\t\tgetShapesBetweenPoints,\n\t\tinteractionSnapLineX,\n\t\tinteractionSnapLineY,\n\t\tinteractionShape,\n\t\tisInteracting,\n\t\thoverShape,\n\t\tpointerPositionInEditor,\n\t\tactiveMarkupItemIsDraft,\n\t\tallowShapeInput,\n\t\tallowShapeChangeTextLayout,\n\t\tallowShapeRemove,\n\t\tallowShapeDuplicate,\n\t\tcontrolledMarkupItem,\n\t\tallowShapeReorder,\n\t\tallowShapeFlip,\n\t\tallowShapeAdjustOpacity,\n\t\thasSelectedShapePoint,\n\t\tshapeControlsMode,\n\t\tallowShapeStyleText,\n\t\tshapeControlsPosition,\n\t\tmarkupControlsAnchorPosition,\n\t\tshapeSelectedManipulatorPoint,\n\t\tshouldShowMarkupControls,\n\t\tisMultiSelection,\n\t\tmultiSelection,\n\t\ttextShapeOrigin,\n\t\tactiveShapeIsDraft,\n\t\ttextShapeDisplayOrigin,\n\t\tactiveTextShapeComputed,\n\t\ttextSizeDisplayOrigin,\n\t\tisTextMarkupSelected,\n\t\tdisableSnapping,\n\t\tgridRect,\n\t\tshouldSnapPointer,\n\t\tisHoveringShape,\n\t\tisManagingShape,\n\t\tmultiSelectionShapePoints,\n\t\tselectionScreenPoints,\n\t\tshapeManipulatorRotationPoint,\n\t\tshapeActivePoints,\n\t\tshapeManipulatorScalarPoint,\n\t\tactiveMarkupSelected,\n\t\tmultiSelectionPoints,\n\t\tallowResizeControls,\n\t\tmultiSelectionComputed,\n\t\t$shapes,\n\t\t$isAnimated,\n\t\t$keysPressedStored,\n\t\t$gridOpacity,\n\t\tmeasure_handler,\n\t\tclick_handler,\n\t\ttextarea_binding,\n\t\ttextarea_input_handler,\n\t\tfunc,\n\t\tcontenteditable_binding,\n\t\tfocusout_handler,\n\t\tmeasure_handler_1,\n\t\tdiv_binding,\n\t\tinteractable_function_1\n\t];\n}\n\nclass ShapeLayoutEditor extends SvelteComponent {\n\tconstructor(options) {\n\t\tsuper();\n\n\t\tinit(\n\t\t\tthis,\n\t\t\toptions,\n\t\t\tinstance$f,\n\t\t\tcreate_fragment$f,\n\t\t\tsafe_not_equal,\n\t\t\t{\n\t\t\t\tuid: 67,\n\t\t\t\tui: 68,\n\t\t\t\tdisabled: 0,\n\t\t\t\tshapes: 1,\n\t\t\t\tselection: 69,\n\t\t\t\toffset: 2,\n\t\t\t\tcontextRotation: 70,\n\t\t\t\tcontextFlipX: 71,\n\t\t\t\tcontextFlipY: 72,\n\t\t\t\tcontextZoom: 73,\n\t\t\t\tactive: 74,\n\t\t\t\topacity: 75,\n\t\t\t\tparentRect: 76,\n\t\t\t\trootRect: 3,\n\t\t\t\tutilRect: 4,\n\t\t\t\thoverColor: 77,\n\t\t\t\tcaretColor: 78,\n\t\t\t\tgridColor: 79,\n\t\t\t\tsnapColor: 80,\n\t\t\t\ttextInputMode: 5,\n\t\t\t\toninteractionstart: 81,\n\t\t\t\toninteractionupdate: 82,\n\t\t\t\toninteractionrelease: 83,\n\t\t\t\toninteractionend: 84,\n\t\t\t\toninteractioncancel: 85,\n\t\t\t\tonaddshape: 86,\n\t\t\t\tonupdateshape: 87,\n\t\t\t\tonselectshape: 88,\n\t\t\t\tonblurshape: 89,\n\t\t\t\tonremoveshape: 90,\n\t\t\t\tontapshape: 91,\n\t\t\t\tonhovershape: 92,\n\t\t\t\tontriggerhistorywrite: 93,\n\t\t\t\tonhovercanvas: 94,\n\t\t\t\tontapcanvas: 95,\n\t\t\t\tonleavecanvas: 96,\n\t\t\t\tbeforeSelectShape: 97,\n\t\t\t\tbeforeDeselectShape: 98,\n\t\t\t\tbeforeRemoveShape: 99,\n\t\t\t\tbeforeUpdateShape: 100,\n\t\t\t\twillRenderShapeControls: 101,\n\t\t\t\twillRenderShapeTextControls: 102,\n\t\t\t\twillStartInteraction: 103,\n\t\t\t\tmapEditorPointToImagePoint: 104,\n\t\t\t\tmapImagePointToEditorPoint: 105,\n\t\t\t\teraseRadius: 106,\n\t\t\t\tselectRadius: 107,\n\t\t\t\tenableButtonFlipVertical: 108,\n\t\t\t\tenableTapToAddText: 109,\n\t\t\t\tenableMultiSelect: 110,\n\t\t\t\tenableTextStyleControls: 6,\n\t\t\t\tlocale: 7,\n\t\t\t\tsnapThreshold: 111,\n\t\t\t\tsnapPointer: 112,\n\t\t\t\tenableSnapToContext: 113,\n\t\t\t\tgridSize: 114,\n\t\t\t\tsyncShapeCollection: 115,\n\t\t\t\tgetShapeDraft: 116,\n\t\t\t\taddShapeDraft: 117,\n\t\t\t\tdiscardShapeDraft: 118,\n\t\t\t\tconfirmShapeDraft: 119,\n\t\t\t\tupdateShapeDraft: 120,\n\t\t\t\taddShape: 121,\n\t\t\t\tcreateShape: 122,\n\t\t\t\teraseShape: 123,\n\t\t\t\tgetMarkupItemDraft: 124,\n\t\t\t\tgetMarkupItemDraftIndex: 125,\n\t\t\t\taddMarkupItemDraft: 126,\n\t\t\t\tconfirmMarkupItemDraft: 127,\n\t\t\t\tdiscardMarkupItemDraft: 128,\n\t\t\t\tcreateMarkupItem: 129,\n\t\t\t\tsyncShapes: 130,\n\t\t\t\tremoveMarkupShapeProps: 131,\n\t\t\t\tupdateMarkupShape: 132,\n\t\t\t\tupdateMarkupShapeProperty: 133,\n\t\t\t\tupdateMarkupItemsShapeProperty: 134,\n\t\t\t\tupdateMarkupShapeItems: 135,\n\t\t\t\tgetActiveMarkupItems: 136,\n\t\t\t\tgetActiveMarkupItem: 137,\n\t\t\t\thasActiveMarkupItem: 138,\n\t\t\t\tblurShapes: 139,\n\t\t\t\tselectShape: 140,\n\t\t\t\tdeselectMarkupItem: 141,\n\t\t\t\teditMarkupItem: 142,\n\t\t\t\tfinishEditMarkupItem: 143,\n\t\t\t\tremoveMarkupItems: 144,\n\t\t\t\tgetTextShapeRect: 145,\n\t\t\t\tgetMarkupShapeRect: 146,\n\t\t\t\tgetShapesNearPosition: 147,\n\t\t\t\tgetShapesBetweenPoints: 148\n\t\t\t},\n\t\t\tnull,\n\t\t\t[-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1]\n\t\t);\n\t}\n\n\tget syncShapeCollection() {\n\t\treturn this.$$.ctx[115];\n\t}\n\n\tget getShapeDraft() {\n\t\treturn this.$$.ctx[116];\n\t}\n\n\tget addShapeDraft() {\n\t\treturn this.$$.ctx[117];\n\t}\n\n\tget discardShapeDraft() {\n\t\treturn this.$$.ctx[118];\n\t}\n\n\tget confirmShapeDraft() {\n\t\treturn this.$$.ctx[119];\n\t}\n\n\tget updateShapeDraft() {\n\t\treturn this.$$.ctx[120];\n\t}\n\n\tget addShape() {\n\t\treturn this.$$.ctx[121];\n\t}\n\n\tget createShape() {\n\t\treturn this.$$.ctx[122];\n\t}\n\n\tget eraseShape() {\n\t\treturn this.$$.ctx[123];\n\t}\n\n\tget getMarkupItemDraft() {\n\t\treturn this.$$.ctx[124];\n\t}\n\n\tget getMarkupItemDraftIndex() {\n\t\treturn this.$$.ctx[125];\n\t}\n\n\tget addMarkupItemDraft() {\n\t\treturn this.$$.ctx[126];\n\t}\n\n\tget confirmMarkupItemDraft() {\n\t\treturn this.$$.ctx[127];\n\t}\n\n\tget discardMarkupItemDraft() {\n\t\treturn this.$$.ctx[128];\n\t}\n\n\tget createMarkupItem() {\n\t\treturn this.$$.ctx[129];\n\t}\n\n\tget syncShapes() {\n\t\treturn this.$$.ctx[130];\n\t}\n\n\tget removeMarkupShapeProps() {\n\t\treturn this.$$.ctx[131];\n\t}\n\n\tget updateMarkupShape() {\n\t\treturn this.$$.ctx[132];\n\t}\n\n\tget updateMarkupShapeProperty() {\n\t\treturn this.$$.ctx[133];\n\t}\n\n\tget updateMarkupItemsShapeProperty() {\n\t\treturn this.$$.ctx[134];\n\t}\n\n\tget updateMarkupShapeItems() {\n\t\treturn this.$$.ctx[135];\n\t}\n\n\tget getActiveMarkupItems() {\n\t\treturn this.$$.ctx[136];\n\t}\n\n\tget getActiveMarkupItem() {\n\t\treturn this.$$.ctx[137];\n\t}\n\n\tget hasActiveMarkupItem() {\n\t\treturn this.$$.ctx[138];\n\t}\n\n\tget blurShapes() {\n\t\treturn this.$$.ctx[139];\n\t}\n\n\tget selectShape() {\n\t\treturn this.$$.ctx[140];\n\t}\n\n\tget deselectMarkupItem() {\n\t\treturn this.$$.ctx[141];\n\t}\n\n\tget editMarkupItem() {\n\t\treturn this.$$.ctx[142];\n\t}\n\n\tget finishEditMarkupItem() {\n\t\treturn this.$$.ctx[143];\n\t}\n\n\tget removeMarkupItems() {\n\t\treturn this.$$.ctx[144];\n\t}\n\n\tget getTextShapeRect() {\n\t\treturn this.$$.ctx[145];\n\t}\n\n\tget getMarkupShapeRect() {\n\t\treturn this.$$.ctx[146];\n\t}\n\n\tget getShapesNearPosition() {\n\t\treturn this.$$.ctx[147];\n\t}\n\n\tget getShapesBetweenPoints() {\n\t\treturn this.$$.ctx[148];\n\t}\n}\n\n/* src/core/ui/components/ShapeStyleControls.svelte generated by Svelte v3.52.0 */\n\nfunction create_else_block$4(ctx) {\n\tlet ul;\n\tlet dynamiccomponenttree;\n\tlet current;\n\n\tdynamiccomponenttree = new DynamicComponentTree_1({\n\t\t\tprops: { items: /*mappedControls*/ ctx[2] }\n\t\t});\n\n\treturn {\n\t\tc() {\n\t\t\tul = element(\"ul\");\n\t\t\tcreate_component(dynamiccomponenttree.$$.fragment);\n\t\t\tattr(ul, \"class\", \"PinturaShapeStyleList\");\n\t\t},\n\t\tm(target, anchor) {\n\t\t\tinsert(target, ul, anchor);\n\t\t\tmount_component(dynamiccomponenttree, ul, null);\n\t\t\tcurrent = true;\n\t\t},\n\t\tp(ctx, dirty) {\n\t\t\tconst dynamiccomponenttree_changes = {};\n\t\t\tif (dirty & /*mappedControls*/ 4) dynamiccomponenttree_changes.items = /*mappedControls*/ ctx[2];\n\t\t\tdynamiccomponenttree.$set(dynamiccomponenttree_changes);\n\t\t},\n\t\ti(local) {\n\t\t\tif (current) return;\n\t\t\ttransition_in(dynamiccomponenttree.$$.fragment, local);\n\t\t\tcurrent = true;\n\t\t},\n\t\to(local) {\n\t\t\ttransition_out(dynamiccomponenttree.$$.fragment, local);\n\t\t\tcurrent = false;\n\t\t},\n\t\td(detaching) {\n\t\t\tif (detaching) detach(ul);\n\t\t\tdestroy_component(dynamiccomponenttree);\n\t\t}\n\t};\n}\n\n// (51:4) {#if scrollEnable}\nfunction create_if_block$4(ctx) {\n\tlet scrollable;\n\tlet current;\n\n\tscrollable = new Scrollable({\n\t\t\tprops: {\n\t\t\t\tclass: \"PinturaShapeStyles\",\n\t\t\t\telasticity: /*scrollElasticity*/ ctx[0],\n\t\t\t\t$$slots: { default: [create_default_slot$6] },\n\t\t\t\t$$scope: { ctx }\n\t\t\t}\n\t\t});\n\n\treturn {\n\t\tc() {\n\t\t\tcreate_component(scrollable.$$.fragment);\n\t\t},\n\t\tm(target, anchor) {\n\t\t\tmount_component(scrollable, target, anchor);\n\t\t\tcurrent = true;\n\t\t},\n\t\tp(ctx, dirty) {\n\t\t\tconst scrollable_changes = {};\n\t\t\tif (dirty & /*scrollElasticity*/ 1) scrollable_changes.elasticity = /*scrollElasticity*/ ctx[0];\n\n\t\t\tif (dirty & /*$$scope, mappedControls*/ 524292) {\n\t\t\t\tscrollable_changes.$$scope = { dirty, ctx };\n\t\t\t}\n\n\t\t\tscrollable.$set(scrollable_changes);\n\t\t},\n\t\ti(local) {\n\t\t\tif (current) return;\n\t\t\ttransition_in(scrollable.$$.fragment, local);\n\t\t\tcurrent = true;\n\t\t},\n\t\to(local) {\n\t\t\ttransition_out(scrollable.$$.fragment, local);\n\t\t\tcurrent = false;\n\t\t},\n\t\td(detaching) {\n\t\t\tdestroy_component(scrollable, detaching);\n\t\t}\n\t};\n}\n\n// (52:8) \nfunction create_default_slot$6(ctx) {\n\tlet ul;\n\tlet dynamiccomponenttree;\n\tlet current;\n\n\tdynamiccomponenttree = new DynamicComponentTree_1({\n\t\t\tprops: { items: /*mappedControls*/ ctx[2] }\n\t\t});\n\n\treturn {\n\t\tc() {\n\t\t\tul = element(\"ul\");\n\t\t\tcreate_component(dynamiccomponenttree.$$.fragment);\n\t\t\tattr(ul, \"class\", \"PinturaShapeStyleList\");\n\t\t},\n\t\tm(target, anchor) {\n\t\t\tinsert(target, ul, anchor);\n\t\t\tmount_component(dynamiccomponenttree, ul, null);\n\t\t\tcurrent = true;\n\t\t},\n\t\tp(ctx, dirty) {\n\t\t\tconst dynamiccomponenttree_changes = {};\n\t\t\tif (dirty & /*mappedControls*/ 4) dynamiccomponenttree_changes.items = /*mappedControls*/ ctx[2];\n\t\t\tdynamiccomponenttree.$set(dynamiccomponenttree_changes);\n\t\t},\n\t\ti(local) {\n\t\t\tif (current) return;\n\t\t\ttransition_in(dynamiccomponenttree.$$.fragment, local);\n\t\t\tcurrent = true;\n\t\t},\n\t\to(local) {\n\t\t\ttransition_out(dynamiccomponenttree.$$.fragment, local);\n\t\t\tcurrent = false;\n\t\t},\n\t\td(detaching) {\n\t\t\tif (detaching) detach(ul);\n\t\t\tdestroy_component(dynamiccomponenttree);\n\t\t}\n\t};\n}\n\nfunction create_fragment$e(ctx) {\n\tlet div;\n\tlet current_block_type_index;\n\tlet if_block;\n\tlet current;\n\tconst if_block_creators = [create_if_block$4, create_else_block$4];\n\tconst if_blocks = [];\n\n\tfunction select_block_type(ctx, dirty) {\n\t\tif (/*scrollEnable*/ ctx[1]) return 0;\n\t\treturn 1;\n\t}\n\n\tcurrent_block_type_index = select_block_type(ctx);\n\tif_block = if_blocks[current_block_type_index] = if_block_creators[current_block_type_index](ctx);\n\n\treturn {\n\t\tc() {\n\t\t\tdiv = element(\"div\");\n\t\t\tif_block.c();\n\t\t\tset_style(div, \"opacity\", /*styleOpacity*/ ctx[5]);\n\t\t\tset_style(div, \"pointer-events\", /*stylePointerEvents*/ ctx[4]);\n\t\t\tset_style(div, \"visibility\", /*styleVisibility*/ ctx[3]);\n\t\t},\n\t\tm(target, anchor) {\n\t\t\tinsert(target, div, anchor);\n\t\t\tif_blocks[current_block_type_index].m(div, null);\n\t\t\tcurrent = true;\n\t\t},\n\t\tp(ctx, [dirty]) {\n\t\t\tlet previous_block_index = current_block_type_index;\n\t\t\tcurrent_block_type_index = select_block_type(ctx);\n\n\t\t\tif (current_block_type_index === previous_block_index) {\n\t\t\t\tif_blocks[current_block_type_index].p(ctx, dirty);\n\t\t\t} else {\n\t\t\t\tgroup_outros();\n\n\t\t\t\ttransition_out(if_blocks[previous_block_index], 1, 1, () => {\n\t\t\t\t\tif_blocks[previous_block_index] = null;\n\t\t\t\t});\n\n\t\t\t\tcheck_outros();\n\t\t\t\tif_block = if_blocks[current_block_type_index];\n\n\t\t\t\tif (!if_block) {\n\t\t\t\t\tif_block = if_blocks[current_block_type_index] = if_block_creators[current_block_type_index](ctx);\n\t\t\t\t\tif_block.c();\n\t\t\t\t} else {\n\t\t\t\t\tif_block.p(ctx, dirty);\n\t\t\t\t}\n\n\t\t\t\ttransition_in(if_block, 1);\n\t\t\t\tif_block.m(div, null);\n\t\t\t}\n\n\t\t\tif (dirty & /*styleOpacity*/ 32) {\n\t\t\t\tset_style(div, \"opacity\", /*styleOpacity*/ ctx[5]);\n\t\t\t}\n\n\t\t\tif (dirty & /*stylePointerEvents*/ 16) {\n\t\t\t\tset_style(div, \"pointer-events\", /*stylePointerEvents*/ ctx[4]);\n\t\t\t}\n\n\t\t\tif (dirty & /*styleVisibility*/ 8) {\n\t\t\t\tset_style(div, \"visibility\", /*styleVisibility*/ ctx[3]);\n\t\t\t}\n\t\t},\n\t\ti(local) {\n\t\t\tif (current) return;\n\t\t\ttransition_in(if_block);\n\t\t\tcurrent = true;\n\t\t},\n\t\to(local) {\n\t\t\ttransition_out(if_block);\n\t\t\tcurrent = false;\n\t\t},\n\t\td(detaching) {\n\t\t\tif (detaching) detach(div);\n\t\t\tif_blocks[current_block_type_index].d();\n\t\t}\n\t};\n}\n\nfunction instance$e($$self, $$props, $$invalidate) {\n\tlet styleOpacity;\n\tlet stylePointerEvents;\n\tlet styleVisibility;\n\tlet mappedControls;\n\tlet $env;\n\tlet $redrawTrigger;\n\tlet $opacity;\n\tlet $isAnimated;\n\tlet { isActive = false } = $$props;\n\tlet { controls = [] } = $$props;\n\tlet { locale } = $$props;\n\tlet { scrollElasticity } = $$props;\n\tlet { scrollEnable = true } = $$props;\n\tlet { hideTitles = false } = $$props;\n\tlet { willRenderControls = passthrough } = $$props;\n\n\t// context\n\tconst redrawTrigger = getContext('redrawTrigger');\n\n\tcomponent_subscribe($$self, redrawTrigger, value => $$invalidate(16, $redrawTrigger = value));\n\tconst env = getContext('env');\n\tcomponent_subscribe($$self, env, value => $$invalidate(15, $env = value));\n\tconst isAnimated = getContext('isAnimated');\n\tcomponent_subscribe($$self, isAnimated, value => $$invalidate(18, $isAnimated = value));\n\tconst opacity = spring(0, { stiffness: 0.25, damping: 0.9 });\n\tcomponent_subscribe($$self, opacity, value => $$invalidate(17, $opacity = value));\n\n\t$$self.$$set = $$props => {\n\t\tif ('isActive' in $$props) $$invalidate(10, isActive = $$props.isActive);\n\t\tif ('controls' in $$props) $$invalidate(11, controls = $$props.controls);\n\t\tif ('locale' in $$props) $$invalidate(12, locale = $$props.locale);\n\t\tif ('scrollElasticity' in $$props) $$invalidate(0, scrollElasticity = $$props.scrollElasticity);\n\t\tif ('scrollEnable' in $$props) $$invalidate(1, scrollEnable = $$props.scrollEnable);\n\t\tif ('hideTitles' in $$props) $$invalidate(13, hideTitles = $$props.hideTitles);\n\t\tif ('willRenderControls' in $$props) $$invalidate(14, willRenderControls = $$props.willRenderControls);\n\t};\n\n\t$$self.$$.update = () => {\n\t\tif ($$self.$$.dirty & /*isActive, $isAnimated*/ 263168) {\n\t\t\topacity.set(isActive ? 1 : 0, { hard: $isAnimated === false });\n\t\t}\n\n\t\tif ($$self.$$.dirty & /*$opacity*/ 131072) {\n\t\t\t$$invalidate(5, styleOpacity = $opacity);\n\t\t}\n\n\t\tif ($$self.$$.dirty & /*isActive*/ 1024) {\n\t\t\t$$invalidate(4, stylePointerEvents = isActive ? 'auto' : 'none');\n\t\t}\n\n\t\tif ($$self.$$.dirty & /*$opacity*/ 131072) {\n\t\t\t$$invalidate(3, styleVisibility = $opacity <= 0 ? 'hidden' : 'visible');\n\t\t}\n\n\t\tif ($$self.$$.dirty & /*$redrawTrigger, willRenderControls, controls, locale, hideTitles, $env*/ 129024) {\n\t\t\t$$invalidate(2, mappedControls = $redrawTrigger && willRenderControls(\n\t\t\t\tcontrols.map(control => [\n\t\t\t\t\t'ShapeStyle',\n\t\t\t\t\tcontrol.id,\n\t\t\t\t\t{\n\t\t\t\t\t\ttitle: control.componentProps.title,\n\t\t\t\t\t\tlocale,\n\t\t\t\t\t\thideTitle: hideTitles,\n\t\t\t\t\t\titems: [[control.component, control.id, control.componentProps]]\n\t\t\t\t\t}\n\t\t\t\t]),\n\t\t\t\t$env,\n\t\t\t\t() => redrawTrigger.set({})\n\t\t\t));\n\t\t}\n\t};\n\n\treturn [\n\t\tscrollElasticity,\n\t\tscrollEnable,\n\t\tmappedControls,\n\t\tstyleVisibility,\n\t\tstylePointerEvents,\n\t\tstyleOpacity,\n\t\tredrawTrigger,\n\t\tenv,\n\t\tisAnimated,\n\t\topacity,\n\t\tisActive,\n\t\tcontrols,\n\t\tlocale,\n\t\thideTitles,\n\t\twillRenderControls,\n\t\t$env,\n\t\t$redrawTrigger,\n\t\t$opacity,\n\t\t$isAnimated\n\t];\n}\n\nclass ShapeStyleControls extends SvelteComponent {\n\tconstructor(options) {\n\t\tsuper();\n\n\t\tinit(this, options, instance$e, create_fragment$e, safe_not_equal, {\n\t\t\tisActive: 10,\n\t\t\tcontrols: 11,\n\t\t\tlocale: 12,\n\t\t\tscrollElasticity: 0,\n\t\t\tscrollEnable: 1,\n\t\t\thideTitles: 13,\n\t\t\twillRenderControls: 14\n\t\t});\n\t}\n}\n\n/* src/core/ui/components/ShapeStyleEditor.svelte generated by Svelte v3.52.0 */\n\nfunction get_each_context(ctx, list, i) {\n\tconst child_ctx = ctx.slice();\n\tchild_ctx[15] = list[i].key;\n\tchild_ctx[6] = list[i].controls;\n\tchild_ctx[16] = list[i].isActive;\n\treturn child_ctx;\n}\n\n// (180:4) {#each currentStyleControlSets as { key, controls, isActive }\nfunction create_each_block(key_1, ctx) {\n\tlet first;\n\tlet shapestylecontrols;\n\tlet current;\n\n\tshapestylecontrols = new ShapeStyleControls({\n\t\t\tprops: {\n\t\t\t\tisActive: /*isActive*/ ctx[16],\n\t\t\t\tcontrols: /*controls*/ ctx[6],\n\t\t\t\tlocale: /*locale*/ ctx[1],\n\t\t\t\tscrollElasticity: /*scrollElasticity*/ ctx[2],\n\t\t\t\tscrollEnable: /*scrollEnable*/ ctx[3],\n\t\t\t\thideTitles: /*hideTitles*/ ctx[4],\n\t\t\t\twillRenderControls: /*willRenderControls*/ ctx[5]\n\t\t\t}\n\t\t});\n\n\treturn {\n\t\tkey: key_1,\n\t\tfirst: null,\n\t\tc() {\n\t\t\tfirst = empty();\n\t\t\tcreate_component(shapestylecontrols.$$.fragment);\n\t\t\tthis.first = first;\n\t\t},\n\t\tm(target, anchor) {\n\t\t\tinsert(target, first, anchor);\n\t\t\tmount_component(shapestylecontrols, target, anchor);\n\t\t\tcurrent = true;\n\t\t},\n\t\tp(new_ctx, dirty) {\n\t\t\tctx = new_ctx;\n\t\t\tconst shapestylecontrols_changes = {};\n\t\t\tif (dirty & /*currentStyleControlSets*/ 128) shapestylecontrols_changes.isActive = /*isActive*/ ctx[16];\n\t\t\tif (dirty & /*currentStyleControlSets*/ 128) shapestylecontrols_changes.controls = /*controls*/ ctx[6];\n\t\t\tif (dirty & /*locale*/ 2) shapestylecontrols_changes.locale = /*locale*/ ctx[1];\n\t\t\tif (dirty & /*scrollElasticity*/ 4) shapestylecontrols_changes.scrollElasticity = /*scrollElasticity*/ ctx[2];\n\t\t\tif (dirty & /*scrollEnable*/ 8) shapestylecontrols_changes.scrollEnable = /*scrollEnable*/ ctx[3];\n\t\t\tif (dirty & /*hideTitles*/ 16) shapestylecontrols_changes.hideTitles = /*hideTitles*/ ctx[4];\n\t\t\tif (dirty & /*willRenderControls*/ 32) shapestylecontrols_changes.willRenderControls = /*willRenderControls*/ ctx[5];\n\t\t\tshapestylecontrols.$set(shapestylecontrols_changes);\n\t\t},\n\t\ti(local) {\n\t\t\tif (current) return;\n\t\t\ttransition_in(shapestylecontrols.$$.fragment, local);\n\t\t\tcurrent = true;\n\t\t},\n\t\to(local) {\n\t\t\ttransition_out(shapestylecontrols.$$.fragment, local);\n\t\t\tcurrent = false;\n\t\t},\n\t\td(detaching) {\n\t\t\tif (detaching) detach(first);\n\t\t\tdestroy_component(shapestylecontrols, detaching);\n\t\t}\n\t};\n}\n\nfunction create_fragment$d(ctx) {\n\tlet div;\n\tlet each_blocks = [];\n\tlet each_1_lookup = new Map();\n\tlet div_class_value;\n\tlet current;\n\tlet each_value = /*currentStyleControlSets*/ ctx[7];\n\tconst get_key = ctx => /*key*/ ctx[15];\n\n\tfor (let i = 0; i < each_value.length; i += 1) {\n\t\tlet child_ctx = get_each_context(ctx, each_value, i);\n\t\tlet key = get_key(child_ctx);\n\t\teach_1_lookup.set(key, each_blocks[i] = create_each_block(key, child_ctx));\n\t}\n\n\treturn {\n\t\tc() {\n\t\t\tdiv = element(\"div\");\n\n\t\t\tfor (let i = 0; i < each_blocks.length; i += 1) {\n\t\t\t\teach_blocks[i].c();\n\t\t\t}\n\n\t\t\tattr(div, \"class\", div_class_value = arrayJoin([\n\t\t\t\t'PinturaShapeStyleEditor',\n\t\t\t\t!/*scrollEnable*/ ctx[3] && 'PinturaShapeStyleEditorOverflow',\n\t\t\t\t/*klass*/ ctx[0]\n\t\t\t]));\n\t\t},\n\t\tm(target, anchor) {\n\t\t\tinsert(target, div, anchor);\n\n\t\t\tfor (let i = 0; i < each_blocks.length; i += 1) {\n\t\t\t\teach_blocks[i].m(div, null);\n\t\t\t}\n\n\t\t\tcurrent = true;\n\t\t},\n\t\tp(ctx, [dirty]) {\n\t\t\tif (dirty & /*currentStyleControlSets, locale, scrollElasticity, scrollEnable, hideTitles, willRenderControls*/ 190) {\n\t\t\t\teach_value = /*currentStyleControlSets*/ ctx[7];\n\t\t\t\tgroup_outros();\n\t\t\t\teach_blocks = update_keyed_each(each_blocks, dirty, get_key, 1, ctx, each_value, each_1_lookup, div, outro_and_destroy_block, create_each_block, null, get_each_context);\n\t\t\t\tcheck_outros();\n\t\t\t}\n\n\t\t\tif (!current || dirty & /*scrollEnable, klass*/ 9 && div_class_value !== (div_class_value = arrayJoin([\n\t\t\t\t'PinturaShapeStyleEditor',\n\t\t\t\t!/*scrollEnable*/ ctx[3] && 'PinturaShapeStyleEditorOverflow',\n\t\t\t\t/*klass*/ ctx[0]\n\t\t\t]))) {\n\t\t\t\tattr(div, \"class\", div_class_value);\n\t\t\t}\n\t\t},\n\t\ti(local) {\n\t\t\tif (current) return;\n\n\t\t\tfor (let i = 0; i < each_value.length; i += 1) {\n\t\t\t\ttransition_in(each_blocks[i]);\n\t\t\t}\n\n\t\t\tcurrent = true;\n\t\t},\n\t\to(local) {\n\t\t\tfor (let i = 0; i < each_blocks.length; i += 1) {\n\t\t\t\ttransition_out(each_blocks[i]);\n\t\t\t}\n\n\t\t\tcurrent = false;\n\t\t},\n\t\td(detaching) {\n\t\t\tif (detaching) detach(div);\n\n\t\t\tfor (let i = 0; i < each_blocks.length; i += 1) {\n\t\t\t\teach_blocks[i].d();\n\t\t\t}\n\t\t}\n\t};\n}\n\nfunction instance$d($$self, $$props, $$invalidate) {\n\tlet controlKeys;\n\tlet activeControls;\n\tlet currentStyleControlSets;\n\tlet { class: klass = undefined } = $$props;\n\tlet { controls = {} } = $$props;\n\tlet { shapeProps = undefined } = $$props;\n\tlet { onchange } = $$props;\n\tlet { locale } = $$props;\n\tlet { scrollElasticity } = $$props;\n\tlet { scrollEnable = true } = $$props;\n\tlet { hideTitles = false } = $$props;\n\tlet { willRenderControls } = $$props;\n\n\tconst getShapeControls = shapeProps => {\n\t\tconst activeControls = controlKeys.filter(styleKey => styleKey.split('_').every(styleKey => // shapeProps needs to have the property\n\t\thasProp(shapeProps, styleKey) && // shapeProps needs to be able to style the property\n\t\tshapeCanStyle(shapeProps, styleKey))).map(styleKey => {\n\t\t\t// map to controlKey\n\t\t\tconst styleSettings = shapeProps.settings && shapeProps.settings[styleKey] || {};\n\n\t\t\tconst controlKey = styleSettings.control || styleKey;\n\n\t\t\t// handle multistyle keys\n\t\t\tconst styleKeys = styleKey.split('_');\n\n\t\t\tlet currentValue = styleKeys.length > 1\n\t\t\t? styleKeys.map(key => shapeProps[key])\n\t\t\t: shapeProps[styleKey];\n\n\t\t\t// get control\n\t\t\tif (isFunction(controls[controlKey])) {\n\t\t\t\tconst { title, component } = controls[controlKey](\n\t\t\t\t\tcurrentValue,\n\t\t\t\t\tnewValue => {\n\t\t\t\t\t\tonchange({ [styleKey]: newValue });\n\t\t\t\t\t},\n\t\t\t\t\t{ selectedShapeId: shapeProps.id }\n\t\t\t\t);\n\n\t\t\t\treturn {\n\t\t\t\t\tid: controlKey,\n\t\t\t\t\tcomponent: DynamicComponentTree_1,\n\t\t\t\t\tcomponentProps: { title, items: component }\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tlet [component, componentProps] = controls[controlKey];\n\n\t\t\t// is reference to other control or string representation of component\n\t\t\tif (isString(component)) {\n\t\t\t\t// exif if not a valid default control\n\t\t\t\tif (controls[component]) {\n\t\t\t\t\t// create component based on reference\n\t\t\t\t\tconst componentCustomProps = { ...componentProps };\n\n\t\t\t\t\t[component, componentProps] = controls[component];\n\n\t\t\t\t\tcomponentProps = {\n\t\t\t\t\t\t...componentProps,\n\t\t\t\t\t\t...componentCustomProps\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst options = isFunction(componentProps.options)\n\t\t\t? componentProps.options(shapeProps)\n\t\t\t: componentProps.options;\n\n\t\t\tif (component === 'RadioGroup') componentProps.selectedIndex = -1;\n\t\t\tif (componentProps.formatValue) currentValue = componentProps.formatValue(currentValue);\n\n\t\t\treturn {\n\t\t\t\tid: controlKey,\n\t\t\t\tcomponent,\n\t\t\t\tcomponentProps: {\n\t\t\t\t\t...componentProps,\n\t\t\t\t\t// set the options prop\n\t\t\t\t\toptions,\n\t\t\t\t\t// defaults\n\t\t\t\t\tlocale,\n\t\t\t\t\tvalue: currentValue,\n\t\t\t\t\toptionLabelClass: 'PinturaButtonLabel',\n\t\t\t\t\tonchange: detail => {\n\t\t\t\t\t\tconst value = isObject(detail) && !isArray(detail)\n\t\t\t\t\t\t? detail.value\n\t\t\t\t\t\t: detail;\n\n\t\t\t\t\t\t// allow custom changes\n\t\t\t\t\t\tif (componentProps.onchange) componentProps.onchange(value, shapeProps);\n\n\t\t\t\t\t\t// internal change\n\t\t\t\t\t\tconst props = styleKeys.length > 1\n\t\t\t\t\t\t? styleKeys.reduce(\n\t\t\t\t\t\t\t\t(prev, key, index) => {\n\t\t\t\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\t\t\t\t...prev,\n\t\t\t\t\t\t\t\t\t\t[key]: Array.isArray(value) ? value[index] : value\n\t\t\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t{}\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t: { [styleKey]: value };\n\n\t\t\t\t\t\tonchange(props);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t};\n\t\t}).filter(Boolean);\n\n\t\treturn activeControls;\n\t};\n\n\tconst styleControlSets = [];\n\n\tconst getStyleControlSets = (key, controls) => {\n\t\tlet controlSet = styleControlSets.find(controlSet => controlSet.key === key);\n\n\t\tif (!controlSet) {\n\t\t\t// create\n\t\t\tcontrolSet = { key, controls };\n\n\t\t\t// add\n\t\t\tstyleControlSets.push(controlSet);\n\t\t}\n\n\t\t// hide all\n\t\tstyleControlSets.forEach(controlSet => controlSet.isActive = false);\n\n\t\t// update active controls\n\t\tcontrolSet.controls = controls;\n\n\t\t// show active\n\t\tcontrolSet.isActive = true;\n\n\t\treturn styleControlSets;\n\t};\n\n\t$$self.$$set = $$props => {\n\t\tif ('class' in $$props) $$invalidate(0, klass = $$props.class);\n\t\tif ('controls' in $$props) $$invalidate(6, controls = $$props.controls);\n\t\tif ('shapeProps' in $$props) $$invalidate(8, shapeProps = $$props.shapeProps);\n\t\tif ('onchange' in $$props) $$invalidate(9, onchange = $$props.onchange);\n\t\tif ('locale' in $$props) $$invalidate(1, locale = $$props.locale);\n\t\tif ('scrollElasticity' in $$props) $$invalidate(2, scrollElasticity = $$props.scrollElasticity);\n\t\tif ('scrollEnable' in $$props) $$invalidate(3, scrollEnable = $$props.scrollEnable);\n\t\tif ('hideTitles' in $$props) $$invalidate(4, hideTitles = $$props.hideTitles);\n\t\tif ('willRenderControls' in $$props) $$invalidate(5, willRenderControls = $$props.willRenderControls);\n\t};\n\n\t$$self.$$.update = () => {\n\t\tif ($$self.$$.dirty & /*controls*/ 64) {\n\t\t\t// finds the controls needed to style the selected shapeProps\n\t\t\t$$invalidate(11, controlKeys = Object.keys(controls).filter(key => controls[key]));\n\t\t}\n\n\t\tif ($$self.$$.dirty & /*shapeProps, controlKeys*/ 2304) {\n\t\t\t$$invalidate(10, activeControls = shapeProps && Object.keys(shapeProps).length && controlKeys && shapeCanStyle(shapeProps)\n\t\t\t? getShapeControls(shapeProps)\n\t\t\t: []);\n\t\t}\n\n\t\tif ($$self.$$.dirty & /*shapeProps, activeControls*/ 1280) {\n\t\t\t$$invalidate(7, currentStyleControlSets = getStyleControlSets(\n\t\t\t\tshapeProps && Object.keys(shapeProps).length\n\t\t\t\t? Object.keys(shapeProps).join('_')\n\t\t\t\t: 'none',\n\t\t\t\tactiveControls || []\n\t\t\t));\n\t\t}\n\t};\n\n\treturn [\n\t\tklass,\n\t\tlocale,\n\t\tscrollElasticity,\n\t\tscrollEnable,\n\t\thideTitles,\n\t\twillRenderControls,\n\t\tcontrols,\n\t\tcurrentStyleControlSets,\n\t\tshapeProps,\n\t\tonchange,\n\t\tactiveControls,\n\t\tcontrolKeys\n\t];\n}\n\nclass ShapeStyleEditor extends SvelteComponent {\n\tconstructor(options) {\n\t\tsuper();\n\n\t\tinit(this, options, instance$d, create_fragment$d, safe_not_equal, {\n\t\t\tclass: 0,\n\t\t\tcontrols: 6,\n\t\t\tshapeProps: 8,\n\t\t\tonchange: 9,\n\t\t\tlocale: 1,\n\t\t\tscrollElasticity: 2,\n\t\t\tscrollEnable: 3,\n\t\t\thideTitles: 4,\n\t\t\twillRenderControls: 5\n\t\t});\n\t}\n}\n\nvar isSVGMarkup = (str) => /