@zsviczian/excalidraw 0.17.6-16 → 0.17.6-17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -803,7 +803,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpac
|
|
|
803
803
|
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
|
|
804
804
|
|
|
805
805
|
"use strict";
|
|
806
|
-
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"trackEvent\": () => (/* binding */ trackEvent)\n/* harmony export */ });\n// place here categories that you want to track. We want to track just a\n// small subset of categories at a given time.\nconst ALLOWED_CATEGORIES_TO_TRACK = new Set([\"command_palette\", \"export\"]);\nconst trackEvent = (category, action, label, value) => {\n try {\n if (typeof window === \"undefined\" || ({\"VITE_APP_BACKEND_V2_GET_URL\":\"https://json-dev.excalidraw.com/api/v2/\",\"VITE_APP_BACKEND_V2_POST_URL\":\"https://json-dev.excalidraw.com/api/v2/post/\",\"VITE_APP_LIBRARY_URL\":\"https://libraries.excalidraw.com\",\"VITE_APP_LIBRARY_BACKEND\":\"https://us-central1-excalidraw-room-persistence.cloudfunctions.net/libraries\",\"VITE_APP_WS_SERVER_URL\":\"http://localhost:3002\",\"VITE_APP_PLUS_LP\":\"https://plus.excalidraw.com\",\"VITE_APP_PLUS_APP\":\"http://localhost:3000\",\"VITE_APP_AI_BACKEND\":\"http://localhost:3015\",\"VITE_APP_FIREBASE_CONFIG\":\"{\\\"apiKey\\\":\\\"AIzaSyCMkxA60XIW8KbqMYL7edC4qT5l4qHX2h8\\\",\\\"authDomain\\\":\\\"excalidraw-oss-dev.firebaseapp.com\\\",\\\"projectId\\\":\\\"excalidraw-oss-dev\\\",\\\"storageBucket\\\":\\\"excalidraw-oss-dev.appspot.com\\\",\\\"messagingSenderId\\\":\\\"664559512677\\\",\\\"appId\\\":\\\"1:664559512677:web:a385181f2928d328a7aa8c\\\"}\",\"VITE_APP_DEV_DISABLE_LIVE_RELOAD\":\"\",\"VITE_APP_ENABLE_TRACKING\":\"true\",\"FAST_REFRESH\":\"false\",\"VITE_APP_PORT\":\"3000\",\"VITE_APP_DEBUG_ENABLE_TEXT_CONTAINER_BOUNDING_BOX\":\"\",\"VITE_APP_COLLAPSE_OVERLAY\":\"true\",\"VITE_APP_ENABLE_ESLINT\":\"true\",\"VITE_APP_ENABLE_PWA\":\"false\",\"VITE_APP_PLUS_EXPORT_PUBLIC_KEY\":\"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAm2g5T+Rub6Kbf1Mf57t0\\n7r2zeHuVg4dla3r5ryXMswtzz6x767octl6oLThn33mQsPSy3GKglFZoCTXJR4ij\\nba8SxB04sL/N8eRrKja7TFWjCVtRwTTfyy771NYYNFVJclkxHyE5qw4m27crHF1y\\nUNWEjuqNMi/lwAErS9fFa2oJlWyT8U7zzv/5kQREkxZI6y9v0AF3qcbsy2731FnD\\ns9ChJvOUW9toIab2gsIdrKW8ZNpu084ZFVKb6LNjvIXI1Se4oMTHeszXzNptzlot\\nkdxxjOoaQMAyfljFSot1F1FlU6MQlag7UnFGvFjRHN1JI5q4K+n3a67DX+TMyRqS\\nHQIDAQAB\",\"VITE_PKG_NAME\":\"@zsviczian/excalidraw\",\"VITE_PKG_VERSION\":\"0.17.6-
|
|
806
|
+
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"trackEvent\": () => (/* binding */ trackEvent)\n/* harmony export */ });\n// place here categories that you want to track. We want to track just a\n// small subset of categories at a given time.\nconst ALLOWED_CATEGORIES_TO_TRACK = new Set([\"command_palette\", \"export\"]);\nconst trackEvent = (category, action, label, value) => {\n try {\n if (typeof window === \"undefined\" || ({\"VITE_APP_BACKEND_V2_GET_URL\":\"https://json-dev.excalidraw.com/api/v2/\",\"VITE_APP_BACKEND_V2_POST_URL\":\"https://json-dev.excalidraw.com/api/v2/post/\",\"VITE_APP_LIBRARY_URL\":\"https://libraries.excalidraw.com\",\"VITE_APP_LIBRARY_BACKEND\":\"https://us-central1-excalidraw-room-persistence.cloudfunctions.net/libraries\",\"VITE_APP_WS_SERVER_URL\":\"http://localhost:3002\",\"VITE_APP_PLUS_LP\":\"https://plus.excalidraw.com\",\"VITE_APP_PLUS_APP\":\"http://localhost:3000\",\"VITE_APP_AI_BACKEND\":\"http://localhost:3015\",\"VITE_APP_FIREBASE_CONFIG\":\"{\\\"apiKey\\\":\\\"AIzaSyCMkxA60XIW8KbqMYL7edC4qT5l4qHX2h8\\\",\\\"authDomain\\\":\\\"excalidraw-oss-dev.firebaseapp.com\\\",\\\"projectId\\\":\\\"excalidraw-oss-dev\\\",\\\"storageBucket\\\":\\\"excalidraw-oss-dev.appspot.com\\\",\\\"messagingSenderId\\\":\\\"664559512677\\\",\\\"appId\\\":\\\"1:664559512677:web:a385181f2928d328a7aa8c\\\"}\",\"VITE_APP_DEV_DISABLE_LIVE_RELOAD\":\"\",\"VITE_APP_ENABLE_TRACKING\":\"true\",\"FAST_REFRESH\":\"false\",\"VITE_APP_PORT\":\"3000\",\"VITE_APP_DEBUG_ENABLE_TEXT_CONTAINER_BOUNDING_BOX\":\"\",\"VITE_APP_COLLAPSE_OVERLAY\":\"true\",\"VITE_APP_ENABLE_ESLINT\":\"true\",\"VITE_APP_ENABLE_PWA\":\"false\",\"VITE_APP_PLUS_EXPORT_PUBLIC_KEY\":\"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAm2g5T+Rub6Kbf1Mf57t0\\n7r2zeHuVg4dla3r5ryXMswtzz6x767octl6oLThn33mQsPSy3GKglFZoCTXJR4ij\\nba8SxB04sL/N8eRrKja7TFWjCVtRwTTfyy771NYYNFVJclkxHyE5qw4m27crHF1y\\nUNWEjuqNMi/lwAErS9fFa2oJlWyT8U7zzv/5kQREkxZI6y9v0AF3qcbsy2731FnD\\ns9ChJvOUW9toIab2gsIdrKW8ZNpu084ZFVKb6LNjvIXI1Se4oMTHeszXzNptzlot\\nkdxxjOoaQMAyfljFSot1F1FlU6MQlag7UnFGvFjRHN1JI5q4K+n3a67DX+TMyRqS\\nHQIDAQAB\",\"VITE_PKG_NAME\":\"@zsviczian/excalidraw\",\"VITE_PKG_VERSION\":\"0.17.6-16\",\"VITE_IS_EXCALIDRAW_NPM_PACKAGE\":true}).VITE_WORKER_ID || \"true\" !== \"true\") {\n return;\n }\n\n if (!ALLOWED_CATEGORIES_TO_TRACK.has(category)) {\n return;\n }\n\n if (true) {\n // comment out to debug in dev\n return;\n }\n\n if (true) {\n console.info(\"trackEvent\", {\n category,\n action,\n label,\n value\n });\n }\n\n if (window.sa_event) {\n window.sa_event(action, {\n category,\n label,\n value\n });\n }\n } catch (error) {\n console.error(\"error during analytics\", error);\n }\n};\n\n//# sourceURL=webpack://ExcalidrawLib/./analytics.ts?");
|
|
807
807
|
|
|
808
808
|
/***/ }),
|
|
809
809
|
|
|
@@ -3080,7 +3080,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpac
|
|
|
3080
3080
|
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
|
|
3081
3081
|
|
|
3082
3082
|
"use strict";
|
|
3083
|
-
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"textWysiwyg\": () => (/* binding */ textWysiwyg)\n/* harmony export */ });\n/* harmony import */ var _keys__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../keys */ \"./keys.ts\");\n/* harmony import */ var _utils__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../utils */ \"./utils.ts\");\n/* harmony import */ var _scene_Scene__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../scene/Scene */ \"./scene/Scene.ts\");\n/* harmony import */ var _typeChecks__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./typeChecks */ \"./element/typeChecks.ts\");\n/* harmony import */ var _constants__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../constants */ \"./constants.ts\");\n/* harmony import */ var _mutateElement__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./mutateElement */ \"./element/mutateElement.ts\");\n/* harmony import */ var _textElement__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./textElement */ \"./element/textElement.ts\");\n/* harmony import */ var _textWrapping__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./textWrapping */ \"./element/textWrapping.ts\");\n/* harmony import */ var _actions_actionProperties__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ../actions/actionProperties */ \"./actions/actionProperties.tsx\");\n/* harmony import */ var _actions_actionCanvas__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ../actions/actionCanvas */ \"./actions/actionCanvas.tsx\");\n/* harmony import */ var _linearElementEditor__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ./linearElementEditor */ \"./element/linearElementEditor.ts\");\n/* harmony import */ var _clipboard__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! ../clipboard */ \"./clipboard.ts\");\n/* harmony import */ var _containerCache__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(/*! ./containerCache */ \"./element/containerCache.ts\");\n\n\n\n\n\n\n\n\n\n\n\n\n\n\nconst getTransform = (width, height, angle, appState, maxWidth, maxHeight) => {\n const {\n zoom\n } = appState;\n const degree = 180 * angle / Math.PI;\n let translateX = width * (zoom.value - 1) / 2;\n let translateY = height * (zoom.value - 1) / 2;\n\n if (width > maxWidth && zoom.value !== 1) {\n translateX = maxWidth * (zoom.value - 1) / 2;\n }\n\n if (height > maxHeight && zoom.value !== 1) {\n translateY = maxHeight * (zoom.value - 1) / 2;\n }\n\n return `translate(${translateX}px, ${translateY}px) scale(${zoom.value}) rotate(${degree}deg)`;\n};\n\nconst textWysiwyg = ({\n id,\n onChange,\n onSubmit,\n getViewportCoords,\n element,\n canvas,\n excalidrawContainer,\n app,\n autoSelect = true\n}) => {\n const textPropertiesUpdated = (updatedTextElement, editable) => {\n if (!editable.style.fontFamily || !editable.style.fontSize) {\n return false;\n }\n\n const currentFont = editable.style.fontFamily.replace(/\"/g, \"\");\n\n if ((0,_utils__WEBPACK_IMPORTED_MODULE_1__.getFontFamilyString)({\n fontFamily: updatedTextElement.fontFamily\n }) !== currentFont) {\n return true;\n }\n\n if (`${updatedTextElement.fontSize}px` !== editable.style.fontSize) {\n return true;\n }\n\n return false;\n };\n\n const updateWysiwygStyle = () => {\n var _a;\n\n const appState = app.state;\n const updatedTextElement = (_a = _scene_Scene__WEBPACK_IMPORTED_MODULE_2__[\"default\"].getScene(element)) === null || _a === void 0 ? void 0 : _a.getElement(id);\n\n if (!updatedTextElement) {\n return;\n }\n\n const {\n textAlign,\n verticalAlign\n } = updatedTextElement;\n const elementsMap = app.scene.getNonDeletedElementsMap();\n\n if (updatedTextElement && (0,_typeChecks__WEBPACK_IMPORTED_MODULE_3__.isTextElement)(updatedTextElement)) {\n let coordX = updatedTextElement.x;\n let coordY = updatedTextElement.y;\n const container = (0,_textElement__WEBPACK_IMPORTED_MODULE_6__.getContainerElement)(updatedTextElement, app.scene.getNonDeletedElementsMap());\n let width = updatedTextElement.width; // set to element height by default since that's\n // what is going to be used for unbounded text\n\n let height = updatedTextElement.height;\n let maxWidth = updatedTextElement.width;\n let maxHeight = updatedTextElement.height;\n\n if (container && updatedTextElement.containerId) {\n if ((0,_typeChecks__WEBPACK_IMPORTED_MODULE_3__.isArrowElement)(container)) {\n const boundTextCoords = _linearElementEditor__WEBPACK_IMPORTED_MODULE_10__.LinearElementEditor.getBoundTextElementPosition(container, updatedTextElement, elementsMap);\n coordX = boundTextCoords.x;\n coordY = boundTextCoords.y;\n }\n\n const propertiesUpdated = textPropertiesUpdated(updatedTextElement, editable);\n let originalContainerData;\n\n if (propertiesUpdated) {\n originalContainerData = (0,_containerCache__WEBPACK_IMPORTED_MODULE_12__.updateOriginalContainerCache)(container.id, container.height);\n } else {\n originalContainerData = _containerCache__WEBPACK_IMPORTED_MODULE_12__.originalContainerCache[container.id];\n\n if (!originalContainerData) {\n originalContainerData = (0,_containerCache__WEBPACK_IMPORTED_MODULE_12__.updateOriginalContainerCache)(container.id, container.height);\n }\n }\n\n maxWidth = (0,_textElement__WEBPACK_IMPORTED_MODULE_6__.getBoundTextMaxWidth)(container, updatedTextElement);\n maxHeight = (0,_textElement__WEBPACK_IMPORTED_MODULE_6__.getBoundTextMaxHeight)(container, updatedTextElement); // autogrow container height if text exceeds\n\n if (!(0,_typeChecks__WEBPACK_IMPORTED_MODULE_3__.isArrowElement)(container) && height > maxHeight) {\n const targetContainerHeight = (0,_textElement__WEBPACK_IMPORTED_MODULE_6__.computeContainerDimensionForBoundText)(height, container.type);\n (0,_mutateElement__WEBPACK_IMPORTED_MODULE_5__.mutateElement)(container, {\n height: targetContainerHeight\n });\n return;\n } else if ( // autoshrink container height until original container height\n // is reached when text is removed\n !(0,_typeChecks__WEBPACK_IMPORTED_MODULE_3__.isArrowElement)(container) && container.height > originalContainerData.height && height < maxHeight) {\n const targetContainerHeight = (0,_textElement__WEBPACK_IMPORTED_MODULE_6__.computeContainerDimensionForBoundText)(height, container.type);\n (0,_mutateElement__WEBPACK_IMPORTED_MODULE_5__.mutateElement)(container, {\n height: targetContainerHeight\n });\n } else {\n const {\n y\n } = (0,_textElement__WEBPACK_IMPORTED_MODULE_6__.computeBoundTextPosition)(container, updatedTextElement, elementsMap);\n coordY = y;\n }\n }\n\n const [viewportX, viewportY] = getViewportCoords(coordX, coordY);\n const initialSelectionStart = editable.selectionStart;\n const initialSelectionEnd = editable.selectionEnd;\n const initialLength = editable.value.length; // restore cursor position after value updated so it doesn't\n // go to the end of text when container auto expanded\n\n if (initialSelectionStart === initialSelectionEnd && initialSelectionEnd !== initialLength) {\n // get diff between length and selection end and shift\n // the cursor by \"diff\" times to position correctly\n const diff = initialLength - initialSelectionEnd;\n editable.selectionStart = editable.value.length - diff;\n editable.selectionEnd = editable.value.length - diff;\n }\n\n if (!container) {\n maxWidth = (appState.width - 8 - viewportX) / appState.zoom.value;\n width = Math.min(width, maxWidth);\n } else {\n width += 0.5;\n } // add 5% buffer otherwise it causes wysiwyg to jump\n\n\n height *= 1.05;\n const font = (0,_utils__WEBPACK_IMPORTED_MODULE_1__.getFontString)(updatedTextElement); // Make sure text editor height doesn't go beyond viewport\n\n const editorMaxHeight = (appState.height - viewportY) / appState.zoom.value;\n Object.assign(editable.style, {\n font,\n // must be defined *after* font ¯\\_(ツ)_/¯\n lineHeight: updatedTextElement.lineHeight,\n width: `${Math.ceil(width)}px`,\n height: `${height}px`,\n left: `${viewportX}px`,\n top: `${viewportY}px`,\n transform: getTransform(width, height, (0,_textElement__WEBPACK_IMPORTED_MODULE_6__.getTextElementAngle)(updatedTextElement, container), appState, maxWidth, editorMaxHeight),\n textAlign,\n verticalAlign,\n color: updatedTextElement.strokeColor,\n opacity: updatedTextElement.opacity / 100,\n filter: \"var(--theme-filter)\",\n maxHeight: `${editorMaxHeight}px`\n });\n editable.scrollTop = 0; // For some reason updating font attribute doesn't set font family\n // hence updating font family explicitly for test environment\n\n if ((0,_utils__WEBPACK_IMPORTED_MODULE_1__.isTestEnv)()) {\n editable.style.fontFamily = (0,_utils__WEBPACK_IMPORTED_MODULE_1__.getFontFamilyString)(updatedTextElement);\n }\n\n (0,_mutateElement__WEBPACK_IMPORTED_MODULE_5__.mutateElement)(updatedTextElement, {\n x: coordX,\n y: coordY\n });\n }\n };\n\n const editable = document.createElement(\"textarea\");\n editable.dir = \"auto\";\n editable.tabIndex = 0;\n editable.dataset.type = \"wysiwyg\"; // prevent line wrapping on Safari\n\n editable.wrap = \"off\";\n editable.classList.add(\"excalidraw-wysiwyg\");\n let whiteSpace = \"pre\";\n let wordBreak = \"normal\";\n\n if ((0,_typeChecks__WEBPACK_IMPORTED_MODULE_3__.isBoundToContainer)(element) || !element.autoResize) {\n whiteSpace = \"pre-wrap\";\n wordBreak = \"break-word\";\n }\n\n Object.assign(editable.style, {\n position: \"absolute\",\n display: \"inline-block\",\n minHeight: \"1em\",\n backfaceVisibility: \"hidden\",\n margin: 0,\n padding: 0,\n border: 0,\n outline: 0,\n resize: \"none\",\n background: \"transparent\",\n overflow: \"hidden\",\n // must be specified because in dark mode canvas creates a stacking context\n zIndex: \"var(--zIndex-wysiwyg)\",\n wordBreak,\n // prevent line wrapping (`whitespace: nowrap` doesn't work on FF)\n whiteSpace,\n overflowWrap: \"break-word\",\n boxSizing: \"content-box\"\n });\n editable.value = element.originalText;\n updateWysiwygStyle();\n\n if (onChange) {\n editable.onpaste = async event => {\n const clipboardData = await (0,_clipboard__WEBPACK_IMPORTED_MODULE_11__.parseClipboard)(event, true);\n\n if (!clipboardData.text) {\n return;\n }\n\n const data = (0,_textElement__WEBPACK_IMPORTED_MODULE_6__.normalizeText)(clipboardData.text);\n\n if (!data) {\n return;\n }\n\n const container = (0,_textElement__WEBPACK_IMPORTED_MODULE_6__.getContainerElement)(element, app.scene.getNonDeletedElementsMap());\n const font = (0,_utils__WEBPACK_IMPORTED_MODULE_1__.getFontString)({\n fontSize: app.state.currentItemFontSize,\n fontFamily: app.state.currentItemFontFamily\n });\n\n if (container) {\n const boundTextElement = (0,_textElement__WEBPACK_IMPORTED_MODULE_6__.getBoundTextElement)(container, app.scene.getNonDeletedElementsMap());\n const wrappedText = (0,_textWrapping__WEBPACK_IMPORTED_MODULE_7__.wrapText)(`${editable.value}${data}`, font, (0,_textElement__WEBPACK_IMPORTED_MODULE_6__.getBoundTextMaxWidth)(container, boundTextElement));\n const width = (0,_textElement__WEBPACK_IMPORTED_MODULE_6__.getTextWidth)(wrappedText, font, true);\n editable.style.width = `${Math.ceil(width)}px`; //zsviczian Obsidian app zoom !== 100% issue\n }\n };\n\n editable.oninput = () => {\n const normalized = (0,_textElement__WEBPACK_IMPORTED_MODULE_6__.normalizeText)(editable.value);\n\n if (editable.value !== normalized) {\n const selectionStart = editable.selectionStart;\n editable.value = normalized; // put the cursor at some position close to where it was before\n // normalization (otherwise it'll end up at the end of the text)\n\n editable.selectionStart = selectionStart;\n editable.selectionEnd = selectionStart;\n }\n\n onChange(editable.value);\n };\n }\n\n editable.onkeydown = event => {\n if (!event.shiftKey && _actions_actionCanvas__WEBPACK_IMPORTED_MODULE_9__.actionZoomIn.keyTest(event)) {\n event.preventDefault();\n app.actionManager.executeAction(_actions_actionCanvas__WEBPACK_IMPORTED_MODULE_9__.actionZoomIn);\n updateWysiwygStyle();\n } else if (!event.shiftKey && _actions_actionCanvas__WEBPACK_IMPORTED_MODULE_9__.actionZoomOut.keyTest(event)) {\n event.preventDefault();\n app.actionManager.executeAction(_actions_actionCanvas__WEBPACK_IMPORTED_MODULE_9__.actionZoomOut);\n updateWysiwygStyle();\n } else if (!event.shiftKey && _actions_actionCanvas__WEBPACK_IMPORTED_MODULE_9__.actionResetZoom.keyTest(event)) {\n event.preventDefault();\n app.actionManager.executeAction(_actions_actionCanvas__WEBPACK_IMPORTED_MODULE_9__.actionResetZoom);\n updateWysiwygStyle();\n } else if (_actions_actionProperties__WEBPACK_IMPORTED_MODULE_8__.actionDecreaseFontSize.keyTest(event)) {\n app.actionManager.executeAction(_actions_actionProperties__WEBPACK_IMPORTED_MODULE_8__.actionDecreaseFontSize);\n } else if (_actions_actionProperties__WEBPACK_IMPORTED_MODULE_8__.actionIncreaseFontSize.keyTest(event)) {\n app.actionManager.executeAction(_actions_actionProperties__WEBPACK_IMPORTED_MODULE_8__.actionIncreaseFontSize);\n } else if (event.key === _keys__WEBPACK_IMPORTED_MODULE_0__.KEYS.ESCAPE) {\n event.preventDefault();\n submittedViaKeyboard = true;\n handleSubmit();\n } else if (event.key === _keys__WEBPACK_IMPORTED_MODULE_0__.KEYS.ENTER && event[_keys__WEBPACK_IMPORTED_MODULE_0__.KEYS.CTRL_OR_CMD]) {\n event.preventDefault();\n\n if (event.isComposing || event.keyCode === 229) {\n return;\n }\n\n submittedViaKeyboard = true;\n handleSubmit();\n } else if (event.key === _keys__WEBPACK_IMPORTED_MODULE_0__.KEYS.TAB || event[_keys__WEBPACK_IMPORTED_MODULE_0__.KEYS.CTRL_OR_CMD] && (event.code === _keys__WEBPACK_IMPORTED_MODULE_0__.CODES.BRACKET_LEFT || event.code === _keys__WEBPACK_IMPORTED_MODULE_0__.CODES.BRACKET_RIGHT)) {\n event.preventDefault();\n\n if (event.isComposing) {\n return;\n } else if (event.shiftKey || event.code === _keys__WEBPACK_IMPORTED_MODULE_0__.CODES.BRACKET_LEFT) {\n outdent();\n } else {\n indent();\n } // We must send an input event to resize the element\n\n\n editable.dispatchEvent(new Event(\"input\"));\n }\n };\n\n const TAB_SIZE = 4;\n const TAB = \" \".repeat(TAB_SIZE);\n const RE_LEADING_TAB = new RegExp(`^ {1,${TAB_SIZE}}`);\n\n const indent = () => {\n const {\n selectionStart,\n selectionEnd\n } = editable;\n const linesStartIndices = getSelectedLinesStartIndices();\n let value = editable.value;\n linesStartIndices.forEach(startIndex => {\n const startValue = value.slice(0, startIndex);\n const endValue = value.slice(startIndex);\n value = `${startValue}${TAB}${endValue}`;\n });\n editable.value = value;\n editable.selectionStart = selectionStart + TAB_SIZE;\n editable.selectionEnd = selectionEnd + TAB_SIZE * linesStartIndices.length;\n };\n\n const outdent = () => {\n const {\n selectionStart,\n selectionEnd\n } = editable;\n const linesStartIndices = getSelectedLinesStartIndices();\n const removedTabs = [];\n let value = editable.value;\n linesStartIndices.forEach(startIndex => {\n const tabMatch = value.slice(startIndex, startIndex + TAB_SIZE).match(RE_LEADING_TAB);\n\n if (tabMatch) {\n const startValue = value.slice(0, startIndex);\n const endValue = value.slice(startIndex + tabMatch[0].length); // Delete a tab from the line\n\n value = `${startValue}${endValue}`;\n removedTabs.push(startIndex);\n }\n });\n editable.value = value;\n\n if (removedTabs.length) {\n if (selectionStart > removedTabs[removedTabs.length - 1]) {\n editable.selectionStart = Math.max(selectionStart - TAB_SIZE, removedTabs[removedTabs.length - 1]);\n } else {\n // If the cursor is before the first tab removed, ex:\n // Line| #1\n // Line #2\n // Lin|e #3\n // we should reset the selectionStart to his initial value.\n editable.selectionStart = selectionStart;\n }\n\n editable.selectionEnd = Math.max(editable.selectionStart, selectionEnd - TAB_SIZE * removedTabs.length);\n }\n };\n /**\r\n * @returns indices of start positions of selected lines, in reverse order\r\n */\n\n\n const getSelectedLinesStartIndices = () => {\n let {\n selectionStart,\n selectionEnd,\n value\n } = editable; // chars before selectionStart on the same line\n\n const startOffset = value.slice(0, selectionStart).match(/[^\\n]*$/)[0].length; // put caret at the start of the line\n\n selectionStart = selectionStart - startOffset;\n const selected = value.slice(selectionStart, selectionEnd);\n return selected.split(\"\\n\").reduce((startIndices, line, idx, lines) => startIndices.concat(idx ? // curr line index is prev line's start + prev line's length + \\n\n startIndices[idx - 1] + lines[idx - 1].length + 1 : // first selected line\n selectionStart), []).reverse();\n };\n\n const stopEvent = event => {\n if (event.target instanceof HTMLCanvasElement) {\n event.preventDefault();\n event.stopPropagation();\n }\n }; // using a state variable instead of passing it to the handleSubmit callback\n // so that we don't need to create separate a callback for event handlers\n\n\n let submittedViaKeyboard = false;\n\n const handleSubmit = () => {\n var _a, _b; // prevent double submit\n\n\n if (isDestroyed) {\n return;\n }\n\n isDestroyed = true; // cleanup must be run before onSubmit otherwise when app blurs the wysiwyg\n // it'd get stuck in an infinite loop of blur→onSubmit after we re-focus the\n // wysiwyg on update\n\n cleanup();\n const updateElement = (_a = _scene_Scene__WEBPACK_IMPORTED_MODULE_2__[\"default\"].getScene(element)) === null || _a === void 0 ? void 0 : _a.getElement(element.id);\n\n if (!updateElement) {\n return;\n }\n\n const container = (0,_textElement__WEBPACK_IMPORTED_MODULE_6__.getContainerElement)(updateElement, app.scene.getNonDeletedElementsMap());\n\n if (container) {\n if (editable.value.trim()) {\n const boundTextElementId = (0,_textElement__WEBPACK_IMPORTED_MODULE_6__.getBoundTextElementId)(container);\n\n if (!boundTextElementId || boundTextElementId !== element.id) {\n (0,_mutateElement__WEBPACK_IMPORTED_MODULE_5__.mutateElement)(container, {\n boundElements: (container.boundElements || []).concat({\n type: \"text\",\n id: element.id\n })\n });\n } else if ((0,_typeChecks__WEBPACK_IMPORTED_MODULE_3__.isArrowElement)(container)) {\n // updating an arrow label may change bounds, prevent stale cache:\n (0,_mutateElement__WEBPACK_IMPORTED_MODULE_5__.bumpVersion)(container);\n }\n } else {\n (0,_mutateElement__WEBPACK_IMPORTED_MODULE_5__.mutateElement)(container, {\n boundElements: (_b = container.boundElements) === null || _b === void 0 ? void 0 : _b.filter(ele => !(0,_typeChecks__WEBPACK_IMPORTED_MODULE_3__.isTextElement)(ele))\n });\n }\n\n (0,_textElement__WEBPACK_IMPORTED_MODULE_6__.redrawTextBoundingBox)(updateElement, container, app.scene.getNonDeletedElementsMap());\n }\n\n app.setState({\n openPopup: null\n }); //zsviczian (container text color issue)\n\n onSubmit({\n viaKeyboard: submittedViaKeyboard,\n nextOriginalText: editable.value\n });\n };\n\n const cleanup = () => {\n // remove events to ensure they don't late-fire\n editable.onblur = null;\n editable.oninput = null;\n editable.onkeydown = null;\n\n if (observer) {\n observer.disconnect();\n }\n\n window.removeEventListener(\"resize\", updateWysiwygStyle);\n window.removeEventListener(\"wheel\", stopEvent, true);\n window.removeEventListener(\"pointerdown\", onPointerDown);\n window.removeEventListener(\"pointerup\", bindBlurEvent);\n window.removeEventListener(\"blur\", handleSubmit);\n window.removeEventListener(\"beforeunload\", handleSubmit);\n unbindUpdate();\n unbindOnScroll();\n editable.remove();\n };\n\n const bindBlurEvent = event => {\n window.removeEventListener(\"pointerup\", bindBlurEvent); // Deferred so that the pointerdown that initiates the wysiwyg doesn't\n // trigger the blur on ensuing pointerup.\n // Also to handle cases such as picking a color which would trigger a blur\n // in that same tick.\n\n const target = event === null || event === void 0 ? void 0 : event.target;\n const isPropertiesTrigger = target instanceof HTMLElement && target.classList.contains(\"properties-trigger\");\n setTimeout(() => {\n editable.onblur = handleSubmit; // case: clicking on the same property → no change → no update → no focus\n\n if (!isPropertiesTrigger) {\n editable.focus();\n }\n });\n };\n\n const temporarilyDisableSubmit = () => {\n editable.onblur = null;\n window.addEventListener(\"pointerup\", bindBlurEvent); // handle edge-case where pointerup doesn't fire e.g. due to user\n // alt-tabbing away\n\n window.addEventListener(\"blur\", handleSubmit);\n }; // prevent blur when changing properties from the menu\n\n\n const onPointerDown = event => {\n const target = event === null || event === void 0 ? void 0 : event.target;\n const isShapeActionsPanel = //zsviczian\n (event.target instanceof HTMLElement || event.target instanceof SVGElement) && (event.target.closest(`.${_constants__WEBPACK_IMPORTED_MODULE_4__.CLASSES.SHAPE_ACTIONS_MENU}`) || event.target.closest(`.${_constants__WEBPACK_IMPORTED_MODULE_4__.CLASSES.SHAPE_ACTIONS_MOBILE_MENU}`) || event.target.closest(`.${_constants__WEBPACK_IMPORTED_MODULE_4__.CLASSES.MOBILE_TOOLBAR}`)); // panning canvas\n\n if (event.button === _constants__WEBPACK_IMPORTED_MODULE_4__.POINTER_BUTTON.WHEEL) {\n // trying to pan by clicking inside text area itself -> handle here\n if (target instanceof HTMLTextAreaElement) {\n event.preventDefault();\n app.handleCanvasPanUsingWheelOrSpaceDrag(event);\n }\n\n temporarilyDisableSubmit();\n return;\n }\n\n const isPropertiesTrigger = target instanceof HTMLElement && target.classList.contains(\"properties-trigger\");\n\n if ((event.target instanceof HTMLElement || event.target instanceof SVGElement) && (isShapeActionsPanel || //zsviczian\n event.target.closest(`.${_constants__WEBPACK_IMPORTED_MODULE_4__.CLASSES.SHAPE_ACTIONS_MENU}, .${_constants__WEBPACK_IMPORTED_MODULE_4__.CLASSES.ZOOM_ACTIONS}`)) && !(0,_utils__WEBPACK_IMPORTED_MODULE_1__.isWritableElement)(event.target) || isPropertiesTrigger) {\n temporarilyDisableSubmit();\n } else if (event.target instanceof HTMLCanvasElement && // Vitest simply ignores stopPropagation, capture-mode, or rAF\n // so without introducing crazier hacks, nothing we can do\n !(0,_utils__WEBPACK_IMPORTED_MODULE_1__.isTestEnv)()) {\n // On mobile, blur event doesn't seem to always fire correctly,\n // so we want to also submit on pointerdown outside the wysiwyg.\n // Done in the next frame to prevent pointerdown from creating a new text\n // immediately (if tools locked) so that users on mobile have chance\n // to submit first (to hide virtual keyboard).\n // Note: revisit if we want to differ this behavior on Desktop\n requestAnimationFrame(() => {\n handleSubmit();\n });\n }\n }; // handle updates of textElement properties of editing element\n\n\n const unbindUpdate = app.scene.onUpdate(() => {\n var _a;\n\n updateWysiwygStyle();\n const isPopupOpened = !!((_a = document.activeElement) === null || _a === void 0 ? void 0 : _a.closest(\".properties-content\"));\n\n if (!isPopupOpened) {\n editable.focus();\n }\n });\n const unbindOnScroll = app.onScrollChangeEmitter.on(() => {\n updateWysiwygStyle();\n }); // ---------------------------------------------------------------------------\n\n let isDestroyed = false;\n\n if (autoSelect) {\n // select on init (focusing is done separately inside the bindBlurEvent()\n // because we need it to happen *after* the blur event from `pointerdown`)\n editable.select();\n }\n\n bindBlurEvent(); // reposition wysiwyg in case of canvas is resized. Using ResizeObserver\n // is preferred so we catch changes from host, where window may not resize.\n\n let observer = null;\n\n if (canvas && \"ResizeObserver\" in window) {\n observer = new window.ResizeObserver(() => {\n updateWysiwygStyle();\n });\n observer.observe(canvas);\n } else {\n window.addEventListener(\"resize\", updateWysiwygStyle);\n }\n\n editable.onpointerdown = event => event.stopPropagation(); // rAF (+ capture to by doubly sure) so we don't catch te pointerdown that\n // triggered the wysiwyg\n\n\n requestAnimationFrame(() => {\n window.addEventListener(\"pointerdown\", onPointerDown, {\n capture: true\n });\n });\n window.addEventListener(\"beforeunload\", handleSubmit);\n excalidrawContainer === null || excalidrawContainer === void 0 ? void 0 : excalidrawContainer.querySelector(\".excalidraw-textEditorContainer\").appendChild(editable);\n};\n\n//# sourceURL=webpack://ExcalidrawLib/./element/textWysiwyg.tsx?");
|
|
3083
|
+
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"textWysiwyg\": () => (/* binding */ textWysiwyg)\n/* harmony export */ });\n/* harmony import */ var _keys__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../keys */ \"./keys.ts\");\n/* harmony import */ var _utils__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../utils */ \"./utils.ts\");\n/* harmony import */ var _scene_Scene__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../scene/Scene */ \"./scene/Scene.ts\");\n/* harmony import */ var _typeChecks__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./typeChecks */ \"./element/typeChecks.ts\");\n/* harmony import */ var _constants__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../constants */ \"./constants.ts\");\n/* harmony import */ var _mutateElement__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./mutateElement */ \"./element/mutateElement.ts\");\n/* harmony import */ var _textElement__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./textElement */ \"./element/textElement.ts\");\n/* harmony import */ var _textWrapping__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./textWrapping */ \"./element/textWrapping.ts\");\n/* harmony import */ var _actions_actionProperties__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ../actions/actionProperties */ \"./actions/actionProperties.tsx\");\n/* harmony import */ var _actions_actionCanvas__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ../actions/actionCanvas */ \"./actions/actionCanvas.tsx\");\n/* harmony import */ var _linearElementEditor__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ./linearElementEditor */ \"./element/linearElementEditor.ts\");\n/* harmony import */ var _clipboard__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! ../clipboard */ \"./clipboard.ts\");\n/* harmony import */ var _containerCache__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(/*! ./containerCache */ \"./element/containerCache.ts\");\n\n\n\n\n\n\n\n\n\n\n\n\n\n\nconst getTransform = (width, height, angle, appState, maxWidth, maxHeight) => {\n const {\n zoom\n } = appState;\n const degree = 180 * angle / Math.PI;\n let translateX = width * (zoom.value - 1) / 2;\n let translateY = height * (zoom.value - 1) / 2;\n\n if (width > maxWidth && zoom.value !== 1) {\n translateX = maxWidth * (zoom.value - 1) / 2;\n }\n\n if (height > maxHeight && zoom.value !== 1) {\n translateY = maxHeight * (zoom.value - 1) / 2;\n }\n\n return `translate(${translateX}px, ${translateY}px) scale(${zoom.value}) rotate(${degree}deg)`;\n};\n\nconst textWysiwyg = ({\n id,\n onChange,\n onSubmit,\n getViewportCoords,\n element,\n canvas,\n excalidrawContainer,\n app,\n autoSelect = true\n}) => {\n const textPropertiesUpdated = (updatedTextElement, editable) => {\n if (!editable.style.fontFamily || !editable.style.fontSize) {\n return false;\n }\n\n const currentFont = editable.style.fontFamily.replace(/\"/g, \"\");\n\n if ((0,_utils__WEBPACK_IMPORTED_MODULE_1__.getFontFamilyString)({\n fontFamily: updatedTextElement.fontFamily\n }) !== currentFont) {\n return true;\n }\n\n if (`${updatedTextElement.fontSize}px` !== editable.style.fontSize) {\n return true;\n }\n\n return false;\n };\n\n const updateWysiwygStyle = () => {\n var _a;\n\n const appState = app.state;\n const updatedTextElement = (_a = _scene_Scene__WEBPACK_IMPORTED_MODULE_2__[\"default\"].getScene(element)) === null || _a === void 0 ? void 0 : _a.getElement(id);\n\n if (!updatedTextElement) {\n return;\n }\n\n const {\n textAlign,\n verticalAlign\n } = updatedTextElement;\n const elementsMap = app.scene.getNonDeletedElementsMap();\n\n if (updatedTextElement && (0,_typeChecks__WEBPACK_IMPORTED_MODULE_3__.isTextElement)(updatedTextElement)) {\n let coordX = updatedTextElement.x;\n let coordY = updatedTextElement.y;\n const container = (0,_textElement__WEBPACK_IMPORTED_MODULE_6__.getContainerElement)(updatedTextElement, app.scene.getNonDeletedElementsMap());\n let width = updatedTextElement.width; // set to element height by default since that's\n // what is going to be used for unbounded text\n\n let height = updatedTextElement.height;\n let maxWidth = updatedTextElement.width;\n let maxHeight = updatedTextElement.height;\n\n if (container && updatedTextElement.containerId) {\n if ((0,_typeChecks__WEBPACK_IMPORTED_MODULE_3__.isArrowElement)(container)) {\n const boundTextCoords = _linearElementEditor__WEBPACK_IMPORTED_MODULE_10__.LinearElementEditor.getBoundTextElementPosition(container, updatedTextElement, elementsMap);\n coordX = boundTextCoords.x;\n coordY = boundTextCoords.y;\n }\n\n const propertiesUpdated = textPropertiesUpdated(updatedTextElement, editable);\n let originalContainerData;\n\n if (propertiesUpdated) {\n originalContainerData = (0,_containerCache__WEBPACK_IMPORTED_MODULE_12__.updateOriginalContainerCache)(container.id, container.height);\n } else {\n originalContainerData = _containerCache__WEBPACK_IMPORTED_MODULE_12__.originalContainerCache[container.id];\n\n if (!originalContainerData) {\n originalContainerData = (0,_containerCache__WEBPACK_IMPORTED_MODULE_12__.updateOriginalContainerCache)(container.id, container.height);\n }\n }\n\n maxWidth = (0,_textElement__WEBPACK_IMPORTED_MODULE_6__.getBoundTextMaxWidth)(container, updatedTextElement);\n maxHeight = (0,_textElement__WEBPACK_IMPORTED_MODULE_6__.getBoundTextMaxHeight)(container, updatedTextElement); // autogrow container height if text exceeds\n\n if (!(0,_typeChecks__WEBPACK_IMPORTED_MODULE_3__.isArrowElement)(container) && height > maxHeight) {\n const targetContainerHeight = (0,_textElement__WEBPACK_IMPORTED_MODULE_6__.computeContainerDimensionForBoundText)(height, container.type);\n (0,_mutateElement__WEBPACK_IMPORTED_MODULE_5__.mutateElement)(container, {\n height: targetContainerHeight\n });\n return;\n } else if ( // autoshrink container height until original container height\n // is reached when text is removed\n !(0,_typeChecks__WEBPACK_IMPORTED_MODULE_3__.isArrowElement)(container) && container.height > originalContainerData.height && height < maxHeight) {\n const targetContainerHeight = (0,_textElement__WEBPACK_IMPORTED_MODULE_6__.computeContainerDimensionForBoundText)(height, container.type);\n (0,_mutateElement__WEBPACK_IMPORTED_MODULE_5__.mutateElement)(container, {\n height: targetContainerHeight\n });\n } else {\n const {\n y\n } = (0,_textElement__WEBPACK_IMPORTED_MODULE_6__.computeBoundTextPosition)(container, updatedTextElement, elementsMap);\n coordY = y;\n }\n }\n\n const [viewportX, viewportY] = getViewportCoords(coordX, coordY);\n const initialSelectionStart = editable.selectionStart;\n const initialSelectionEnd = editable.selectionEnd;\n const initialLength = editable.value.length; // restore cursor position after value updated so it doesn't\n // go to the end of text when container auto expanded\n\n if (initialSelectionStart === initialSelectionEnd && initialSelectionEnd !== initialLength) {\n // get diff between length and selection end and shift\n // the cursor by \"diff\" times to position correctly\n const diff = initialLength - initialSelectionEnd;\n editable.selectionStart = editable.value.length - diff;\n editable.selectionEnd = editable.value.length - diff;\n }\n\n if (!container) {\n maxWidth = (appState.width - 8 - viewportX) / appState.zoom.value;\n width = Math.min(width, maxWidth);\n } else {\n width += 0.5;\n } // add 5% buffer otherwise it causes wysiwyg to jump\n\n\n height *= 1.05;\n const font = (0,_utils__WEBPACK_IMPORTED_MODULE_1__.getFontString)(updatedTextElement); // Make sure text editor height doesn't go beyond viewport\n\n const editorMaxHeight = (appState.height - viewportY) / appState.zoom.value;\n Object.assign(editable.style, {\n font,\n // must be defined *after* font ¯\\_(ツ)_/¯\n lineHeight: updatedTextElement.lineHeight,\n width: `${Math.ceil(width)}px`,\n height: `${height}px`,\n left: `${viewportX}px`,\n top: `${viewportY}px`,\n transform: getTransform(width, height, (0,_textElement__WEBPACK_IMPORTED_MODULE_6__.getTextElementAngle)(updatedTextElement, container), appState, maxWidth, editorMaxHeight),\n textAlign,\n verticalAlign,\n color: updatedTextElement.strokeColor,\n opacity: updatedTextElement.opacity / 100,\n filter: \"var(--theme-filter)\",\n maxHeight: `${editorMaxHeight}px`,\n caretColor: updatedTextElement.strokeColor === \"transparent\" //zsviczian\n ? \"var(excalidraw-caret-color)\" : updatedTextElement.strokeColor //zsviczian Set caret color\n\n });\n editable.scrollTop = 0; // For some reason updating font attribute doesn't set font family\n // hence updating font family explicitly for test environment\n\n if ((0,_utils__WEBPACK_IMPORTED_MODULE_1__.isTestEnv)()) {\n editable.style.fontFamily = (0,_utils__WEBPACK_IMPORTED_MODULE_1__.getFontFamilyString)(updatedTextElement);\n }\n\n (0,_mutateElement__WEBPACK_IMPORTED_MODULE_5__.mutateElement)(updatedTextElement, {\n x: coordX,\n y: coordY\n });\n }\n };\n\n const editable = document.createElement(\"textarea\");\n editable.dir = \"auto\";\n editable.tabIndex = 0;\n editable.dataset.type = \"wysiwyg\"; // prevent line wrapping on Safari\n\n editable.wrap = \"off\";\n editable.classList.add(\"excalidraw-wysiwyg\");\n let whiteSpace = \"pre\";\n let wordBreak = \"normal\";\n\n if ((0,_typeChecks__WEBPACK_IMPORTED_MODULE_3__.isBoundToContainer)(element) || !element.autoResize) {\n whiteSpace = \"pre-wrap\";\n wordBreak = \"break-word\";\n }\n\n Object.assign(editable.style, {\n position: \"absolute\",\n display: \"inline-block\",\n minHeight: \"1em\",\n backfaceVisibility: \"hidden\",\n margin: 0,\n padding: 0,\n border: 0,\n outline: 0,\n resize: \"none\",\n background: \"transparent\",\n overflow: \"hidden\",\n // must be specified because in dark mode canvas creates a stacking context\n zIndex: \"var(--zIndex-wysiwyg)\",\n wordBreak,\n // prevent line wrapping (`whitespace: nowrap` doesn't work on FF)\n whiteSpace,\n overflowWrap: \"break-word\",\n boxSizing: \"content-box\"\n });\n editable.value = element.originalText;\n updateWysiwygStyle();\n\n if (onChange) {\n editable.onpaste = async event => {\n const clipboardData = await (0,_clipboard__WEBPACK_IMPORTED_MODULE_11__.parseClipboard)(event, true);\n\n if (!clipboardData.text) {\n return;\n }\n\n const data = (0,_textElement__WEBPACK_IMPORTED_MODULE_6__.normalizeText)(clipboardData.text);\n\n if (!data) {\n return;\n }\n\n const container = (0,_textElement__WEBPACK_IMPORTED_MODULE_6__.getContainerElement)(element, app.scene.getNonDeletedElementsMap());\n const font = (0,_utils__WEBPACK_IMPORTED_MODULE_1__.getFontString)({\n fontSize: app.state.currentItemFontSize,\n fontFamily: app.state.currentItemFontFamily\n });\n\n if (container) {\n const boundTextElement = (0,_textElement__WEBPACK_IMPORTED_MODULE_6__.getBoundTextElement)(container, app.scene.getNonDeletedElementsMap());\n const wrappedText = (0,_textWrapping__WEBPACK_IMPORTED_MODULE_7__.wrapText)(`${editable.value}${data}`, font, (0,_textElement__WEBPACK_IMPORTED_MODULE_6__.getBoundTextMaxWidth)(container, boundTextElement));\n const width = (0,_textElement__WEBPACK_IMPORTED_MODULE_6__.getTextWidth)(wrappedText, font, true);\n editable.style.width = `${Math.ceil(width)}px`; //zsviczian Obsidian app zoom !== 100% issue\n }\n };\n\n editable.oninput = () => {\n const normalized = (0,_textElement__WEBPACK_IMPORTED_MODULE_6__.normalizeText)(editable.value);\n\n if (editable.value !== normalized) {\n const selectionStart = editable.selectionStart;\n editable.value = normalized; // put the cursor at some position close to where it was before\n // normalization (otherwise it'll end up at the end of the text)\n\n editable.selectionStart = selectionStart;\n editable.selectionEnd = selectionStart;\n }\n\n onChange(editable.value);\n };\n }\n\n editable.onkeydown = event => {\n if (!event.shiftKey && _actions_actionCanvas__WEBPACK_IMPORTED_MODULE_9__.actionZoomIn.keyTest(event)) {\n event.preventDefault();\n app.actionManager.executeAction(_actions_actionCanvas__WEBPACK_IMPORTED_MODULE_9__.actionZoomIn);\n updateWysiwygStyle();\n } else if (!event.shiftKey && _actions_actionCanvas__WEBPACK_IMPORTED_MODULE_9__.actionZoomOut.keyTest(event)) {\n event.preventDefault();\n app.actionManager.executeAction(_actions_actionCanvas__WEBPACK_IMPORTED_MODULE_9__.actionZoomOut);\n updateWysiwygStyle();\n } else if (!event.shiftKey && _actions_actionCanvas__WEBPACK_IMPORTED_MODULE_9__.actionResetZoom.keyTest(event)) {\n event.preventDefault();\n app.actionManager.executeAction(_actions_actionCanvas__WEBPACK_IMPORTED_MODULE_9__.actionResetZoom);\n updateWysiwygStyle();\n } else if (_actions_actionProperties__WEBPACK_IMPORTED_MODULE_8__.actionDecreaseFontSize.keyTest(event)) {\n app.actionManager.executeAction(_actions_actionProperties__WEBPACK_IMPORTED_MODULE_8__.actionDecreaseFontSize);\n } else if (_actions_actionProperties__WEBPACK_IMPORTED_MODULE_8__.actionIncreaseFontSize.keyTest(event)) {\n app.actionManager.executeAction(_actions_actionProperties__WEBPACK_IMPORTED_MODULE_8__.actionIncreaseFontSize);\n } else if (event.key === _keys__WEBPACK_IMPORTED_MODULE_0__.KEYS.ESCAPE) {\n event.preventDefault();\n submittedViaKeyboard = true;\n handleSubmit();\n } else if (event.key === _keys__WEBPACK_IMPORTED_MODULE_0__.KEYS.ENTER && event[_keys__WEBPACK_IMPORTED_MODULE_0__.KEYS.CTRL_OR_CMD]) {\n event.preventDefault();\n\n if (event.isComposing || event.keyCode === 229) {\n return;\n }\n\n submittedViaKeyboard = true;\n handleSubmit();\n } else if (event.key === _keys__WEBPACK_IMPORTED_MODULE_0__.KEYS.TAB || event[_keys__WEBPACK_IMPORTED_MODULE_0__.KEYS.CTRL_OR_CMD] && (event.code === _keys__WEBPACK_IMPORTED_MODULE_0__.CODES.BRACKET_LEFT || event.code === _keys__WEBPACK_IMPORTED_MODULE_0__.CODES.BRACKET_RIGHT)) {\n event.preventDefault();\n\n if (event.isComposing) {\n return;\n } else if (event.shiftKey || event.code === _keys__WEBPACK_IMPORTED_MODULE_0__.CODES.BRACKET_LEFT) {\n outdent();\n } else {\n indent();\n } // We must send an input event to resize the element\n\n\n editable.dispatchEvent(new Event(\"input\"));\n }\n };\n\n const TAB_SIZE = 4;\n const TAB = \" \".repeat(TAB_SIZE);\n const RE_LEADING_TAB = new RegExp(`^ {1,${TAB_SIZE}}`);\n\n const indent = () => {\n const {\n selectionStart,\n selectionEnd\n } = editable;\n const linesStartIndices = getSelectedLinesStartIndices();\n let value = editable.value;\n linesStartIndices.forEach(startIndex => {\n const startValue = value.slice(0, startIndex);\n const endValue = value.slice(startIndex);\n value = `${startValue}${TAB}${endValue}`;\n });\n editable.value = value;\n editable.selectionStart = selectionStart + TAB_SIZE;\n editable.selectionEnd = selectionEnd + TAB_SIZE * linesStartIndices.length;\n };\n\n const outdent = () => {\n const {\n selectionStart,\n selectionEnd\n } = editable;\n const linesStartIndices = getSelectedLinesStartIndices();\n const removedTabs = [];\n let value = editable.value;\n linesStartIndices.forEach(startIndex => {\n const tabMatch = value.slice(startIndex, startIndex + TAB_SIZE).match(RE_LEADING_TAB);\n\n if (tabMatch) {\n const startValue = value.slice(0, startIndex);\n const endValue = value.slice(startIndex + tabMatch[0].length); // Delete a tab from the line\n\n value = `${startValue}${endValue}`;\n removedTabs.push(startIndex);\n }\n });\n editable.value = value;\n\n if (removedTabs.length) {\n if (selectionStart > removedTabs[removedTabs.length - 1]) {\n editable.selectionStart = Math.max(selectionStart - TAB_SIZE, removedTabs[removedTabs.length - 1]);\n } else {\n // If the cursor is before the first tab removed, ex:\n // Line| #1\n // Line #2\n // Lin|e #3\n // we should reset the selectionStart to his initial value.\n editable.selectionStart = selectionStart;\n }\n\n editable.selectionEnd = Math.max(editable.selectionStart, selectionEnd - TAB_SIZE * removedTabs.length);\n }\n };\n /**\r\n * @returns indices of start positions of selected lines, in reverse order\r\n */\n\n\n const getSelectedLinesStartIndices = () => {\n let {\n selectionStart,\n selectionEnd,\n value\n } = editable; // chars before selectionStart on the same line\n\n const startOffset = value.slice(0, selectionStart).match(/[^\\n]*$/)[0].length; // put caret at the start of the line\n\n selectionStart = selectionStart - startOffset;\n const selected = value.slice(selectionStart, selectionEnd);\n return selected.split(\"\\n\").reduce((startIndices, line, idx, lines) => startIndices.concat(idx ? // curr line index is prev line's start + prev line's length + \\n\n startIndices[idx - 1] + lines[idx - 1].length + 1 : // first selected line\n selectionStart), []).reverse();\n };\n\n const stopEvent = event => {\n if (event.target instanceof HTMLCanvasElement) {\n event.preventDefault();\n event.stopPropagation();\n }\n }; // using a state variable instead of passing it to the handleSubmit callback\n // so that we don't need to create separate a callback for event handlers\n\n\n let submittedViaKeyboard = false;\n\n const handleSubmit = () => {\n var _a, _b; // prevent double submit\n\n\n if (isDestroyed) {\n return;\n }\n\n isDestroyed = true; // cleanup must be run before onSubmit otherwise when app blurs the wysiwyg\n // it'd get stuck in an infinite loop of blur→onSubmit after we re-focus the\n // wysiwyg on update\n\n cleanup();\n const updateElement = (_a = _scene_Scene__WEBPACK_IMPORTED_MODULE_2__[\"default\"].getScene(element)) === null || _a === void 0 ? void 0 : _a.getElement(element.id);\n\n if (!updateElement) {\n return;\n }\n\n const container = (0,_textElement__WEBPACK_IMPORTED_MODULE_6__.getContainerElement)(updateElement, app.scene.getNonDeletedElementsMap());\n\n if (container) {\n if (editable.value.trim()) {\n const boundTextElementId = (0,_textElement__WEBPACK_IMPORTED_MODULE_6__.getBoundTextElementId)(container);\n\n if (!boundTextElementId || boundTextElementId !== element.id) {\n (0,_mutateElement__WEBPACK_IMPORTED_MODULE_5__.mutateElement)(container, {\n boundElements: (container.boundElements || []).concat({\n type: \"text\",\n id: element.id\n })\n });\n } else if ((0,_typeChecks__WEBPACK_IMPORTED_MODULE_3__.isArrowElement)(container)) {\n // updating an arrow label may change bounds, prevent stale cache:\n (0,_mutateElement__WEBPACK_IMPORTED_MODULE_5__.bumpVersion)(container);\n }\n } else {\n (0,_mutateElement__WEBPACK_IMPORTED_MODULE_5__.mutateElement)(container, {\n boundElements: (_b = container.boundElements) === null || _b === void 0 ? void 0 : _b.filter(ele => !(0,_typeChecks__WEBPACK_IMPORTED_MODULE_3__.isTextElement)(ele))\n });\n }\n\n (0,_textElement__WEBPACK_IMPORTED_MODULE_6__.redrawTextBoundingBox)(updateElement, container, app.scene.getNonDeletedElementsMap());\n }\n\n app.setState({\n openPopup: null\n }); //zsviczian (container text color issue)\n\n onSubmit({\n viaKeyboard: submittedViaKeyboard,\n nextOriginalText: editable.value\n });\n };\n\n const cleanup = () => {\n // remove events to ensure they don't late-fire\n editable.onblur = null;\n editable.oninput = null;\n editable.onkeydown = null;\n\n if (observer) {\n observer.disconnect();\n }\n\n window.removeEventListener(\"resize\", updateWysiwygStyle);\n window.removeEventListener(\"wheel\", stopEvent, true);\n window.removeEventListener(\"pointerdown\", onPointerDown);\n window.removeEventListener(\"pointerup\", bindBlurEvent);\n window.removeEventListener(\"blur\", handleSubmit);\n window.removeEventListener(\"beforeunload\", handleSubmit);\n unbindUpdate();\n unbindOnScroll();\n editable.remove();\n };\n\n const bindBlurEvent = event => {\n window.removeEventListener(\"pointerup\", bindBlurEvent); // Deferred so that the pointerdown that initiates the wysiwyg doesn't\n // trigger the blur on ensuing pointerup.\n // Also to handle cases such as picking a color which would trigger a blur\n // in that same tick.\n\n const target = event === null || event === void 0 ? void 0 : event.target;\n const isPropertiesTrigger = target instanceof HTMLElement && target.classList.contains(\"properties-trigger\");\n setTimeout(() => {\n editable.onblur = handleSubmit; // case: clicking on the same property → no change → no update → no focus\n\n if (!isPropertiesTrigger) {\n editable.focus();\n }\n });\n };\n\n const temporarilyDisableSubmit = () => {\n editable.onblur = null;\n window.addEventListener(\"pointerup\", bindBlurEvent); // handle edge-case where pointerup doesn't fire e.g. due to user\n // alt-tabbing away\n\n window.addEventListener(\"blur\", handleSubmit);\n }; // prevent blur when changing properties from the menu\n\n\n const onPointerDown = event => {\n const target = event === null || event === void 0 ? void 0 : event.target;\n const isShapeActionsPanel = //zsviczian\n (event.target instanceof HTMLElement || event.target instanceof SVGElement) && (event.target.closest(`.${_constants__WEBPACK_IMPORTED_MODULE_4__.CLASSES.SHAPE_ACTIONS_MENU}`) || event.target.closest(`.${_constants__WEBPACK_IMPORTED_MODULE_4__.CLASSES.SHAPE_ACTIONS_MOBILE_MENU}`) || event.target.closest(`.${_constants__WEBPACK_IMPORTED_MODULE_4__.CLASSES.MOBILE_TOOLBAR}`)); // panning canvas\n\n if (event.button === _constants__WEBPACK_IMPORTED_MODULE_4__.POINTER_BUTTON.WHEEL) {\n // trying to pan by clicking inside text area itself -> handle here\n if (target instanceof HTMLTextAreaElement) {\n event.preventDefault();\n app.handleCanvasPanUsingWheelOrSpaceDrag(event);\n }\n\n temporarilyDisableSubmit();\n return;\n }\n\n const isPropertiesTrigger = target instanceof HTMLElement && target.classList.contains(\"properties-trigger\");\n\n if ((event.target instanceof HTMLElement || event.target instanceof SVGElement) && (isShapeActionsPanel || //zsviczian\n event.target.closest(`.${_constants__WEBPACK_IMPORTED_MODULE_4__.CLASSES.SHAPE_ACTIONS_MENU}, .${_constants__WEBPACK_IMPORTED_MODULE_4__.CLASSES.ZOOM_ACTIONS}`)) && !(0,_utils__WEBPACK_IMPORTED_MODULE_1__.isWritableElement)(event.target) || isPropertiesTrigger) {\n temporarilyDisableSubmit();\n } else if (event.target instanceof HTMLCanvasElement && // Vitest simply ignores stopPropagation, capture-mode, or rAF\n // so without introducing crazier hacks, nothing we can do\n !(0,_utils__WEBPACK_IMPORTED_MODULE_1__.isTestEnv)()) {\n // On mobile, blur event doesn't seem to always fire correctly,\n // so we want to also submit on pointerdown outside the wysiwyg.\n // Done in the next frame to prevent pointerdown from creating a new text\n // immediately (if tools locked) so that users on mobile have chance\n // to submit first (to hide virtual keyboard).\n // Note: revisit if we want to differ this behavior on Desktop\n requestAnimationFrame(() => {\n handleSubmit();\n });\n }\n }; // handle updates of textElement properties of editing element\n\n\n const unbindUpdate = app.scene.onUpdate(() => {\n var _a;\n\n updateWysiwygStyle();\n const isPopupOpened = !!((_a = document.activeElement) === null || _a === void 0 ? void 0 : _a.closest(\".properties-content\"));\n\n if (!isPopupOpened) {\n editable.focus();\n }\n });\n const unbindOnScroll = app.onScrollChangeEmitter.on(() => {\n updateWysiwygStyle();\n }); // ---------------------------------------------------------------------------\n\n let isDestroyed = false;\n\n if (autoSelect) {\n // select on init (focusing is done separately inside the bindBlurEvent()\n // because we need it to happen *after* the blur event from `pointerdown`)\n editable.select();\n }\n\n bindBlurEvent(); // reposition wysiwyg in case of canvas is resized. Using ResizeObserver\n // is preferred so we catch changes from host, where window may not resize.\n\n let observer = null;\n\n if (canvas && \"ResizeObserver\" in window) {\n observer = new window.ResizeObserver(() => {\n updateWysiwygStyle();\n });\n observer.observe(canvas);\n } else {\n window.addEventListener(\"resize\", updateWysiwygStyle);\n }\n\n editable.onpointerdown = event => event.stopPropagation(); // rAF (+ capture to by doubly sure) so we don't catch te pointerdown that\n // triggered the wysiwyg\n\n\n requestAnimationFrame(() => {\n window.addEventListener(\"pointerdown\", onPointerDown, {\n capture: true\n });\n });\n window.addEventListener(\"beforeunload\", handleSubmit);\n excalidrawContainer === null || excalidrawContainer === void 0 ? void 0 : excalidrawContainer.querySelector(\".excalidraw-textEditorContainer\").appendChild(editable);\n};\n\n//# sourceURL=webpack://ExcalidrawLib/./element/textWysiwyg.tsx?");
|
|
3084
3084
|
|
|
3085
3085
|
/***/ }),
|
|
3086
3086
|
|
|
@@ -3179,7 +3179,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpac
|
|
|
3179
3179
|
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
|
|
3180
3180
|
|
|
3181
3181
|
"use strict";
|
|
3182
|
-
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"ExcalidrawFontFace\": () => (/* binding */ ExcalidrawFontFace)\n/* harmony export */ });\n/* harmony import */ var _data_encode__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../data/encode */ \"./data/encode.ts\");\n/* harmony import */ var _obsidianUtils__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../obsidianUtils */ \"./obsidianUtils.ts\");\n/* harmony import */ var _utils__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../utils */ \"./utils.ts\");\n/* harmony import */ var _FontMetadata__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./FontMetadata */ \"./fonts/FontMetadata.ts\");\n/* harmony import */ var _subset_subset_main__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../subset/subset-main */ \"./subset/subset-main.ts\");\n\n\n\n\n\nclass ExcalidrawFontFace {\n constructor(family, uri, descriptors) {\n this.urls = ExcalidrawFontFace.createUrls(uri);\n const sources = this.urls.map(url => `url(${url}) ${ExcalidrawFontFace.getFormat(url)}`).join(\", \");\n this.fontFace = new FontFace(family, sources, Object.assign({\n display: \"swap\",\n style: \"normal\",\n weight: \"400\"\n }, descriptors));\n }\n /**\r\n * Generates CSS `@font-face` definition with the (subsetted) font source as a data url for the characters within the unicode range.\r\n *\r\n * Retrieves `undefined` otherwise.\r\n */\n\n\n toCSS(characters) {\n // quick exit in case the characters are not within this font face's unicode range\n if (!this.getUnicodeRangeRegex().test(characters)) {\n return;\n } //zsviczian - only woffs are chopped into glyphs other fonts are returned as is\n\n\n if (typeof this.urls[0] === \"string\" && !this.urls[0].startsWith(\"data:font/woff2\")) {\n return Promise.resolve(`@font-face { font-family: ${this.fontFace.family}; src: url(${this.urls[0]}); }`);\n }\n\n const codepoints = Array.from(characters).map(char => char.codePointAt(0));\n return this.getContent(codepoints).then(content => `@font-face { font-family: ${this.fontFace.family}; src: url(${content}); }`);\n }\n /**\r\n * Tries to fetch woff2 content, based on the registered urls (from first to last, treated as fallbacks).\r\n *\r\n * @returns base64 with subsetted glyphs based on the passed codepoint, last defined url otherwise\r\n */\n\n\n async getContent(codePoints) {\n let i = 0;\n const errorMessages = [];\n\n while (i < this.urls.length) {\n const url = this.urls[i];\n\n try {\n const arrayBuffer = await this.fetchFont(url);\n const base64 = await (0,_subset_subset_main__WEBPACK_IMPORTED_MODULE_4__.subsetWoff2GlyphsByCodepoints)(arrayBuffer, codePoints);\n return base64;\n } catch (e) {\n errorMessages.push(`\"${url.toString()}\" returned error \"${e}\"`);\n }\n\n i++;\n }\n\n console.error(`Failed to fetch font family \"${this.fontFace.family}\"`, JSON.stringify(errorMessages, undefined, 2)); // in case of issues, at least return the last url as a content\n // defaults to unpkg for bundled fonts (so that we don't have to host them forever) and http url for others\n\n return this.urls.length ? this.urls[this.urls.length - 1].toString() : \"\";\n }\n\n fetchFont(url) {\n return (0,_utils__WEBPACK_IMPORTED_MODULE_2__.promiseTry)(async () => {\n const result = await (0,_obsidianUtils__WEBPACK_IMPORTED_MODULE_1__.fetchFontFromVault)(url); //zsviczian\n\n if (result) {\n return result;\n }\n\n const response = await fetch(url, {\n // always prefer cache (even stale), otherwise it always triggers an unnecessary validation request\n // which we don't need as we are controlling freshness of the fonts with the stable hash suffix in the url\n // https://developer.mozilla.org/en-US/docs/Web/API/Request/cache\n cache: \"force-cache\",\n headers: {\n Accept: \"font/woff2\"\n }\n });\n\n if (!response.ok) {\n const urlString = url instanceof URL ? url.toString() : \"dataurl\";\n throw new Error(`Failed to fetch \"${urlString}\": ${response.statusText}`);\n }\n\n const arrayBuffer = await response.arrayBuffer();\n return arrayBuffer;\n });\n }\n\n getUnicodeRangeRegex() {\n // using \\u{h} or \\u{hhhhh} to match any number of hex digits,\n // otherwise we would get an \"Invalid Unicode escape\" error\n // e.g. U+0-1007F -> \\u{0}-\\u{1007F}\n const unicodeRangeRegex = this.fontFace.unicodeRange.split(/,\\s*/).map(range => {\n const [start, end] = range.replace(\"U+\", \"\").split(\"-\");\n\n if (end) {\n return `\\\\u{${start}}-\\\\u{${end}}`;\n }\n\n return `\\\\u{${start}}`;\n }).join(\"\");\n return new RegExp(`[${unicodeRangeRegex}]`, \"u\");\n }\n\n static createUrls(uri) {\n if (uri.startsWith(\"data\")) {\n // don't create the URL instance, as parsing the huge dataurl string is expensive\n return [uri];\n }\n\n if (uri.startsWith(_FontMetadata__WEBPACK_IMPORTED_MODULE_3__.LOCAL_FONT_PROTOCOL)) {\n // no url for local fonts\n return [];\n }\n\n if (uri.startsWith(\"http\")) {\n // one url for http imports or data url\n return [new URL(uri)];\n } // absolute assets paths, which are found in tests and excalidraw-app build, won't work with base url, so we are stripping initial slash away\n\n\n const assetUrl = uri.replace(/^\\/+/, \"\");\n const urls = [];\n\n if (typeof window.EXCALIDRAW_ASSET_PATH === \"string\") {\n const normalizedBaseUrl = this.normalizeBaseUrl(window.EXCALIDRAW_ASSET_PATH);\n urls.push(new URL(assetUrl, normalizedBaseUrl));\n } else if (Array.isArray(window.EXCALIDRAW_ASSET_PATH)) {\n window.EXCALIDRAW_ASSET_PATH.forEach(path => {\n const normalizedBaseUrl = this.normalizeBaseUrl(path);\n urls.push(new URL(assetUrl, normalizedBaseUrl));\n });\n } // fallback url for bundled fonts\n\n\n urls.push(new URL(assetUrl, ExcalidrawFontFace.UNPKG_FALLBACK_URL));\n return urls;\n }\n\n static getFormat(url) {\n if (!(url instanceof URL)) {\n // format is irrelevant for data url\n return \"\";\n }\n\n try {\n const parts = new URL(url).pathname.split(\".\");\n\n if (parts.length === 1) {\n return \"\";\n }\n\n return `format('${parts.pop()}')`;\n } catch (error) {\n return \"\";\n }\n }\n\n static normalizeBaseUrl(baseUrl) {\n var _a;\n\n let result = baseUrl; // in case user passed a root-relative url (~absolute path),\n // like \"/\" or \"/some/path\", or relative (starts with \"./\"),\n // prepend it with `location.origin`\n\n if (/^\\.?\\//.test(result)) {\n result = new URL(result.replace(/^\\.?\\/+/, \"\"), (_a = window === null || window === void 0 ? void 0 : window.location) === null || _a === void 0 ? void 0 : _a.origin).toString();\n } // ensure there is a trailing slash, otherwise url won't be correctly concatenated\n\n\n result = `${result.replace(/\\/+$/, \"\")}/`;\n return result;\n }\n /**\r\n * zsviczian https://github.com/zsviczian/excalidraw/commit/b4cfaaa4b4f46ca01f94e27fb7bf651a9da99daa\r\n */\n\n\n async getContentLegacy() {\n let i = 0;\n const errorMessages = [];\n\n while (i < this.urls.length) {\n const url = this.urls[i];\n\n if (typeof url === \"string\" && url.startsWith(\"data:\")) {\n // it's dataurl, the font is inlined as base64, no need to fetch\n return url;\n }\n\n try {\n const result = await (0,_obsidianUtils__WEBPACK_IMPORTED_MODULE_1__.fetchFontFromVault)(url); //zsviczian\n\n if (result) {\n return `data:font/woff2;base64,${await (0,_data_encode__WEBPACK_IMPORTED_MODULE_0__.stringToBase64)(await (0,_data_encode__WEBPACK_IMPORTED_MODULE_0__.toByteString)(result), true)}`;\n }\n\n const response = await fetch(url, {\n headers: {\n Accept: \"font/woff2\"\n }\n });\n\n if (response.ok) {\n const mimeType = response.headers.get(\"Content-Type\");\n const buffer = await response.arrayBuffer();\n return `data:${mimeType};base64,${await (0,_data_encode__WEBPACK_IMPORTED_MODULE_0__.stringToBase64)(await (0,_data_encode__WEBPACK_IMPORTED_MODULE_0__.toByteString)(buffer), true)}`;\n } // response not ok, try to continue\n\n\n errorMessages.push(`\"${url.toString()}\" returned status \"${response.status}\"`);\n } catch (e) {\n errorMessages.push(`\"${url.toString()}\" returned error \"${e}\"`);\n }\n\n i++;\n }\n\n console.error(`Failed to fetch font \"${this.fontFace.family}\" from urls \"${this.urls.toString()}`, JSON.stringify(errorMessages, undefined, 2)); // in case of issues, at least return the last url as a content\n // defaults to unpkg for bundled fonts (so that we don't have to host them forever) and http url for others\n\n return this.urls.length ? this.urls[this.urls.length - 1].toString() : \"\";\n }\n\n}\nExcalidrawFontFace.UNPKG_FALLBACK_URL = `https://unpkg.com/${ true ? `${\"@zsviczian/excalidraw\"}@${({\"VITE_APP_BACKEND_V2_GET_URL\":\"https://json-dev.excalidraw.com/api/v2/\",\"VITE_APP_BACKEND_V2_POST_URL\":\"https://json-dev.excalidraw.com/api/v2/post/\",\"VITE_APP_LIBRARY_URL\":\"https://libraries.excalidraw.com\",\"VITE_APP_LIBRARY_BACKEND\":\"https://us-central1-excalidraw-room-persistence.cloudfunctions.net/libraries\",\"VITE_APP_WS_SERVER_URL\":\"http://localhost:3002\",\"VITE_APP_PLUS_LP\":\"https://plus.excalidraw.com\",\"VITE_APP_PLUS_APP\":\"http://localhost:3000\",\"VITE_APP_AI_BACKEND\":\"http://localhost:3015\",\"VITE_APP_FIREBASE_CONFIG\":\"{\\\"apiKey\\\":\\\"AIzaSyCMkxA60XIW8KbqMYL7edC4qT5l4qHX2h8\\\",\\\"authDomain\\\":\\\"excalidraw-oss-dev.firebaseapp.com\\\",\\\"projectId\\\":\\\"excalidraw-oss-dev\\\",\\\"storageBucket\\\":\\\"excalidraw-oss-dev.appspot.com\\\",\\\"messagingSenderId\\\":\\\"664559512677\\\",\\\"appId\\\":\\\"1:664559512677:web:a385181f2928d328a7aa8c\\\"}\",\"VITE_APP_DEV_DISABLE_LIVE_RELOAD\":\"\",\"VITE_APP_ENABLE_TRACKING\":\"true\",\"FAST_REFRESH\":\"false\",\"VITE_APP_PORT\":\"3000\",\"VITE_APP_DEBUG_ENABLE_TEXT_CONTAINER_BOUNDING_BOX\":\"\",\"VITE_APP_COLLAPSE_OVERLAY\":\"true\",\"VITE_APP_ENABLE_ESLINT\":\"true\",\"VITE_APP_ENABLE_PWA\":\"false\",\"VITE_APP_PLUS_EXPORT_PUBLIC_KEY\":\"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAm2g5T+Rub6Kbf1Mf57t0\\n7r2zeHuVg4dla3r5ryXMswtzz6x767octl6oLThn33mQsPSy3GKglFZoCTXJR4ij\\nba8SxB04sL/N8eRrKja7TFWjCVtRwTTfyy771NYYNFVJclkxHyE5qw4m27crHF1y\\nUNWEjuqNMi/lwAErS9fFa2oJlWyT8U7zzv/5kQREkxZI6y9v0AF3qcbsy2731FnD\\ns9ChJvOUW9toIab2gsIdrKW8ZNpu084ZFVKb6LNjvIXI1Se4oMTHeszXzNptzlot\\nkdxxjOoaQMAyfljFSot1F1FlU6MQlag7UnFGvFjRHN1JI5q4K+n3a67DX+TMyRqS\\nHQIDAQAB\",\"VITE_PKG_NAME\":\"@zsviczian/excalidraw\",\"VITE_PKG_VERSION\":\"0.17.6-15\",\"VITE_IS_EXCALIDRAW_NPM_PACKAGE\":true}).PKG_VERSION}` // should be provided by vite during package build\n: 0 // fallback to latest package version (i.e. for app)\n}/dist/prod/`;\n\n//# sourceURL=webpack://ExcalidrawLib/./fonts/ExcalidrawFontFace.ts?");
|
|
3182
|
+
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"ExcalidrawFontFace\": () => (/* binding */ ExcalidrawFontFace)\n/* harmony export */ });\n/* harmony import */ var _data_encode__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../data/encode */ \"./data/encode.ts\");\n/* harmony import */ var _obsidianUtils__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../obsidianUtils */ \"./obsidianUtils.ts\");\n/* harmony import */ var _utils__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../utils */ \"./utils.ts\");\n/* harmony import */ var _FontMetadata__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./FontMetadata */ \"./fonts/FontMetadata.ts\");\n/* harmony import */ var _subset_subset_main__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../subset/subset-main */ \"./subset/subset-main.ts\");\n\n\n\n\n\nclass ExcalidrawFontFace {\n constructor(family, uri, descriptors) {\n this.urls = ExcalidrawFontFace.createUrls(uri);\n const sources = this.urls.map(url => `url(${url}) ${ExcalidrawFontFace.getFormat(url)}`).join(\", \");\n this.fontFace = new FontFace(family, sources, Object.assign({\n display: \"swap\",\n style: \"normal\",\n weight: \"400\"\n }, descriptors));\n }\n /**\r\n * Generates CSS `@font-face` definition with the (subsetted) font source as a data url for the characters within the unicode range.\r\n *\r\n * Retrieves `undefined` otherwise.\r\n */\n\n\n toCSS(characters) {\n // quick exit in case the characters are not within this font face's unicode range\n if (!this.getUnicodeRangeRegex().test(characters)) {\n return;\n } //zsviczian - only woffs are chopped into glyphs other fonts are returned as is\n\n\n if (typeof this.urls[0] === \"string\" && !this.urls[0].startsWith(\"data:font/woff2\")) {\n return Promise.resolve(`@font-face { font-family: ${this.fontFace.family}; src: url(${this.urls[0]}); }`);\n }\n\n const codepoints = Array.from(characters).map(char => char.codePointAt(0));\n return this.getContent(codepoints).then(content => `@font-face { font-family: ${this.fontFace.family}; src: url(${content}); }`);\n }\n /**\r\n * Tries to fetch woff2 content, based on the registered urls (from first to last, treated as fallbacks).\r\n *\r\n * @returns base64 with subsetted glyphs based on the passed codepoint, last defined url otherwise\r\n */\n\n\n async getContent(codePoints) {\n let i = 0;\n const errorMessages = [];\n\n while (i < this.urls.length) {\n const url = this.urls[i];\n\n try {\n const arrayBuffer = await this.fetchFont(url);\n const base64 = await (0,_subset_subset_main__WEBPACK_IMPORTED_MODULE_4__.subsetWoff2GlyphsByCodepoints)(arrayBuffer, codePoints);\n return base64;\n } catch (e) {\n errorMessages.push(`\"${url.toString()}\" returned error \"${e}\"`);\n }\n\n i++;\n }\n\n console.error(`Failed to fetch font family \"${this.fontFace.family}\"`, JSON.stringify(errorMessages, undefined, 2)); // in case of issues, at least return the last url as a content\n // defaults to unpkg for bundled fonts (so that we don't have to host them forever) and http url for others\n\n return this.urls.length ? this.urls[this.urls.length - 1].toString() : \"\";\n }\n\n fetchFont(url) {\n return (0,_utils__WEBPACK_IMPORTED_MODULE_2__.promiseTry)(async () => {\n const result = await (0,_obsidianUtils__WEBPACK_IMPORTED_MODULE_1__.fetchFontFromVault)(url); //zsviczian\n\n if (result) {\n return result;\n }\n\n const response = await fetch(url, {\n // always prefer cache (even stale), otherwise it always triggers an unnecessary validation request\n // which we don't need as we are controlling freshness of the fonts with the stable hash suffix in the url\n // https://developer.mozilla.org/en-US/docs/Web/API/Request/cache\n cache: \"force-cache\",\n headers: {\n Accept: \"font/woff2\"\n }\n });\n\n if (!response.ok) {\n const urlString = url instanceof URL ? url.toString() : \"dataurl\";\n throw new Error(`Failed to fetch \"${urlString}\": ${response.statusText}`);\n }\n\n const arrayBuffer = await response.arrayBuffer();\n return arrayBuffer;\n });\n }\n\n getUnicodeRangeRegex() {\n // using \\u{h} or \\u{hhhhh} to match any number of hex digits,\n // otherwise we would get an \"Invalid Unicode escape\" error\n // e.g. U+0-1007F -> \\u{0}-\\u{1007F}\n const unicodeRangeRegex = this.fontFace.unicodeRange.split(/,\\s*/).map(range => {\n const [start, end] = range.replace(\"U+\", \"\").split(\"-\");\n\n if (end) {\n return `\\\\u{${start}}-\\\\u{${end}}`;\n }\n\n return `\\\\u{${start}}`;\n }).join(\"\");\n return new RegExp(`[${unicodeRangeRegex}]`, \"u\");\n }\n\n static createUrls(uri) {\n if (uri.startsWith(\"data\")) {\n // don't create the URL instance, as parsing the huge dataurl string is expensive\n return [uri];\n }\n\n if (uri.startsWith(_FontMetadata__WEBPACK_IMPORTED_MODULE_3__.LOCAL_FONT_PROTOCOL)) {\n // no url for local fonts\n return [];\n }\n\n if (uri.startsWith(\"http\")) {\n // one url for http imports or data url\n return [new URL(uri)];\n } // absolute assets paths, which are found in tests and excalidraw-app build, won't work with base url, so we are stripping initial slash away\n\n\n const assetUrl = uri.replace(/^\\/+/, \"\");\n const urls = [];\n\n if (typeof window.EXCALIDRAW_ASSET_PATH === \"string\") {\n const normalizedBaseUrl = this.normalizeBaseUrl(window.EXCALIDRAW_ASSET_PATH);\n urls.push(new URL(assetUrl, normalizedBaseUrl));\n } else if (Array.isArray(window.EXCALIDRAW_ASSET_PATH)) {\n window.EXCALIDRAW_ASSET_PATH.forEach(path => {\n const normalizedBaseUrl = this.normalizeBaseUrl(path);\n urls.push(new URL(assetUrl, normalizedBaseUrl));\n });\n } // fallback url for bundled fonts\n\n\n urls.push(new URL(assetUrl, ExcalidrawFontFace.UNPKG_FALLBACK_URL));\n return urls;\n }\n\n static getFormat(url) {\n if (!(url instanceof URL)) {\n // format is irrelevant for data url\n return \"\";\n }\n\n try {\n const parts = new URL(url).pathname.split(\".\");\n\n if (parts.length === 1) {\n return \"\";\n }\n\n return `format('${parts.pop()}')`;\n } catch (error) {\n return \"\";\n }\n }\n\n static normalizeBaseUrl(baseUrl) {\n var _a;\n\n let result = baseUrl; // in case user passed a root-relative url (~absolute path),\n // like \"/\" or \"/some/path\", or relative (starts with \"./\"),\n // prepend it with `location.origin`\n\n if (/^\\.?\\//.test(result)) {\n result = new URL(result.replace(/^\\.?\\/+/, \"\"), (_a = window === null || window === void 0 ? void 0 : window.location) === null || _a === void 0 ? void 0 : _a.origin).toString();\n } // ensure there is a trailing slash, otherwise url won't be correctly concatenated\n\n\n result = `${result.replace(/\\/+$/, \"\")}/`;\n return result;\n }\n /**\r\n * zsviczian https://github.com/zsviczian/excalidraw/commit/b4cfaaa4b4f46ca01f94e27fb7bf651a9da99daa\r\n */\n\n\n async getContentLegacy() {\n let i = 0;\n const errorMessages = [];\n\n while (i < this.urls.length) {\n const url = this.urls[i];\n\n if (typeof url === \"string\" && url.startsWith(\"data:\")) {\n // it's dataurl, the font is inlined as base64, no need to fetch\n return url;\n }\n\n try {\n const result = await (0,_obsidianUtils__WEBPACK_IMPORTED_MODULE_1__.fetchFontFromVault)(url); //zsviczian\n\n if (result) {\n return `data:font/woff2;base64,${await (0,_data_encode__WEBPACK_IMPORTED_MODULE_0__.stringToBase64)(await (0,_data_encode__WEBPACK_IMPORTED_MODULE_0__.toByteString)(result), true)}`;\n }\n\n const response = await fetch(url, {\n headers: {\n Accept: \"font/woff2\"\n }\n });\n\n if (response.ok) {\n const mimeType = response.headers.get(\"Content-Type\");\n const buffer = await response.arrayBuffer();\n return `data:${mimeType};base64,${await (0,_data_encode__WEBPACK_IMPORTED_MODULE_0__.stringToBase64)(await (0,_data_encode__WEBPACK_IMPORTED_MODULE_0__.toByteString)(buffer), true)}`;\n } // response not ok, try to continue\n\n\n errorMessages.push(`\"${url.toString()}\" returned status \"${response.status}\"`);\n } catch (e) {\n errorMessages.push(`\"${url.toString()}\" returned error \"${e}\"`);\n }\n\n i++;\n }\n\n console.error(`Failed to fetch font \"${this.fontFace.family}\" from urls \"${this.urls.toString()}`, JSON.stringify(errorMessages, undefined, 2)); // in case of issues, at least return the last url as a content\n // defaults to unpkg for bundled fonts (so that we don't have to host them forever) and http url for others\n\n return this.urls.length ? this.urls[this.urls.length - 1].toString() : \"\";\n }\n\n}\nExcalidrawFontFace.UNPKG_FALLBACK_URL = `https://unpkg.com/${ true ? `${\"@zsviczian/excalidraw\"}@${({\"VITE_APP_BACKEND_V2_GET_URL\":\"https://json-dev.excalidraw.com/api/v2/\",\"VITE_APP_BACKEND_V2_POST_URL\":\"https://json-dev.excalidraw.com/api/v2/post/\",\"VITE_APP_LIBRARY_URL\":\"https://libraries.excalidraw.com\",\"VITE_APP_LIBRARY_BACKEND\":\"https://us-central1-excalidraw-room-persistence.cloudfunctions.net/libraries\",\"VITE_APP_WS_SERVER_URL\":\"http://localhost:3002\",\"VITE_APP_PLUS_LP\":\"https://plus.excalidraw.com\",\"VITE_APP_PLUS_APP\":\"http://localhost:3000\",\"VITE_APP_AI_BACKEND\":\"http://localhost:3015\",\"VITE_APP_FIREBASE_CONFIG\":\"{\\\"apiKey\\\":\\\"AIzaSyCMkxA60XIW8KbqMYL7edC4qT5l4qHX2h8\\\",\\\"authDomain\\\":\\\"excalidraw-oss-dev.firebaseapp.com\\\",\\\"projectId\\\":\\\"excalidraw-oss-dev\\\",\\\"storageBucket\\\":\\\"excalidraw-oss-dev.appspot.com\\\",\\\"messagingSenderId\\\":\\\"664559512677\\\",\\\"appId\\\":\\\"1:664559512677:web:a385181f2928d328a7aa8c\\\"}\",\"VITE_APP_DEV_DISABLE_LIVE_RELOAD\":\"\",\"VITE_APP_ENABLE_TRACKING\":\"true\",\"FAST_REFRESH\":\"false\",\"VITE_APP_PORT\":\"3000\",\"VITE_APP_DEBUG_ENABLE_TEXT_CONTAINER_BOUNDING_BOX\":\"\",\"VITE_APP_COLLAPSE_OVERLAY\":\"true\",\"VITE_APP_ENABLE_ESLINT\":\"true\",\"VITE_APP_ENABLE_PWA\":\"false\",\"VITE_APP_PLUS_EXPORT_PUBLIC_KEY\":\"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAm2g5T+Rub6Kbf1Mf57t0\\n7r2zeHuVg4dla3r5ryXMswtzz6x767octl6oLThn33mQsPSy3GKglFZoCTXJR4ij\\nba8SxB04sL/N8eRrKja7TFWjCVtRwTTfyy771NYYNFVJclkxHyE5qw4m27crHF1y\\nUNWEjuqNMi/lwAErS9fFa2oJlWyT8U7zzv/5kQREkxZI6y9v0AF3qcbsy2731FnD\\ns9ChJvOUW9toIab2gsIdrKW8ZNpu084ZFVKb6LNjvIXI1Se4oMTHeszXzNptzlot\\nkdxxjOoaQMAyfljFSot1F1FlU6MQlag7UnFGvFjRHN1JI5q4K+n3a67DX+TMyRqS\\nHQIDAQAB\",\"VITE_PKG_NAME\":\"@zsviczian/excalidraw\",\"VITE_PKG_VERSION\":\"0.17.6-16\",\"VITE_IS_EXCALIDRAW_NPM_PACKAGE\":true}).PKG_VERSION}` // should be provided by vite during package build\n: 0 // fallback to latest package version (i.e. for app)\n}/dist/prod/`;\n\n//# sourceURL=webpack://ExcalidrawLib/./fonts/ExcalidrawFontFace.ts?");
|
|
3183
3183
|
|
|
3184
3184
|
/***/ }),
|
|
3185
3185
|
|
|
@@ -3212,7 +3212,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpac
|
|
|
3212
3212
|
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
|
|
3213
3213
|
|
|
3214
3214
|
"use strict";
|
|
3215
|
-
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"Fonts\": () => (/* binding */ Fonts),\n/* harmony export */ \"getLineHeight\": () => (/* binding */ getLineHeight),\n/* harmony export */ \"getVerticalOffset\": () => (/* binding */ getVerticalOffset)\n/* harmony export */ });\n/* harmony import */ var _constants__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../constants */ \"./constants.ts\");\n/* harmony import */ var _element__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../element */ \"./element/index.ts\");\n/* harmony import */ var _element_textElement__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../element/textElement */ \"./element/textElement.ts\");\n/* harmony import */ var _element_textWrapping__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../element/textWrapping */ \"./element/textWrapping.ts\");\n/* harmony import */ var _scene_ShapeCache__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../scene/ShapeCache */ \"./scene/ShapeCache.ts\");\n/* harmony import */ var _utils__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../utils */ \"./utils.ts\");\n/* harmony import */ var _ExcalidrawFontFace__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./ExcalidrawFontFace */ \"./fonts/ExcalidrawFontFace.ts\");\n/* harmony import */ var _Cascadia__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./Cascadia */ \"./fonts/Cascadia/index.ts\");\n/* harmony import */ var _ComicShanns__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ./ComicShanns */ \"./fonts/ComicShanns/index.ts\");\n/* harmony import */ var _Emoji__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ./Emoji */ \"./fonts/Emoji/index.ts\");\n/* harmony import */ var _Excalifont__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ./Excalifont */ \"./fonts/Excalifont/index.ts\");\n/* harmony import */ var _Helvetica__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! ./Helvetica */ \"./fonts/Helvetica/index.ts\");\n/* harmony import */ var _Liberation__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(/*! ./Liberation */ \"./fonts/Liberation/index.ts\");\n/* harmony import */ var _Lilita__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(/*! ./Lilita */ \"./fonts/Lilita/index.ts\");\n/* harmony import */ var _Nunito__WEBPACK_IMPORTED_MODULE_14__ = __webpack_require__(/*! ./Nunito */ \"./fonts/Nunito/index.ts\");\n/* harmony import */ var _Virgil__WEBPACK_IMPORTED_MODULE_15__ = __webpack_require__(/*! ./Virgil */ \"./fonts/Virgil/index.ts\");\n/* harmony import */ var _Xiaolai__WEBPACK_IMPORTED_MODULE_16__ = __webpack_require__(/*! ./Xiaolai */ \"./fonts/Xiaolai/index.ts\");\n/* harmony import */ var _FontMetadata__WEBPACK_IMPORTED_MODULE_17__ = __webpack_require__(/*! ./FontMetadata */ \"./fonts/FontMetadata.ts\");\nvar _a;\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\nclass Fonts {\n static get registered() {\n // lazy load the font registration\n if (!Fonts._registered) {\n Fonts._registered = Fonts.init();\n } else if (!Fonts._initialized) {\n // case when host app register fonts before they are lazy loaded\n // don't override whatever has been previously registered\n Fonts._registered = new Map([...Fonts.init().entries(), ...Fonts._registered.entries()]);\n }\n\n return Fonts._registered;\n }\n\n get registered() {\n return Fonts.registered;\n }\n\n constructor(scene) {\n /**\r\n * Get all the font families for the given scene.\r\n */\n this.getSceneFamilies = () => {\n return Fonts.getUniqueFamilies(this.scene.getNonDeletedElements());\n };\n /**\r\n * if we load a (new) font, it's likely that text elements using it have\r\n * already been rendered using a fallback font. Thus, we want invalidate\r\n * their shapes and rerender. See #637.\r\n *\r\n * Invalidates text elements and rerenders scene, provided that at least one\r\n * of the supplied fontFaces has not already been processed.\r\n */\n\n\n this.onLoaded = fontFaces => {\n // bail if all fonts with have been processed. We're checking just a\n // subset of the font properties (though it should be enough), so it\n // can technically bail on a false positive.\n let shouldBail = true; //zsviczian - Obsidian adds Excalidraw irrelevant fonts\n\n const registeredFontFamilies = new Set();\n Fonts.registered.forEach(x => registeredFontFamilies.add(x.fontFaces[0].fontFace.family));\n\n for (const fontFace of fontFaces) {\n //zsviczian\n if (!registeredFontFamilies.has(fontFace.family)) {\n continue;\n }\n\n const sig = `${fontFace.family}-${fontFace.style}-${fontFace.weight}-${fontFace.unicodeRange}`; // make sure to update our cache with all the loaded font faces\n\n if (!Fonts.loadedFontsCache.has(sig)) {\n Fonts.loadedFontsCache.add(sig);\n shouldBail = false;\n }\n }\n\n if (shouldBail) {\n return;\n }\n\n let didUpdate = false;\n const elementsMap = this.scene.getNonDeletedElementsMap();\n\n for (const element of this.scene.getNonDeletedElements()) {\n if ((0,_element__WEBPACK_IMPORTED_MODULE_1__.isTextElement)(element)) {\n didUpdate = true;\n _scene_ShapeCache__WEBPACK_IMPORTED_MODULE_4__.ShapeCache[\"delete\"](element); // clear the width cache, so that we don't perform subsequent wrapping based on the stale fallback font metrics\n\n _element_textElement__WEBPACK_IMPORTED_MODULE_2__.charWidth.clearCache((0,_utils__WEBPACK_IMPORTED_MODULE_5__.getFontString)(element));\n const container = (0,_element_textElement__WEBPACK_IMPORTED_MODULE_2__.getContainerElement)(element, elementsMap);\n\n if (container) {\n _scene_ShapeCache__WEBPACK_IMPORTED_MODULE_4__.ShapeCache[\"delete\"](container);\n }\n }\n }\n\n if (didUpdate) {\n this.scene.triggerUpdate();\n }\n };\n /**\r\n * Load font faces for a given scene and trigger scene update.\r\n *\r\n * FontFaceSet loadingdone event we listen on may not always\r\n * fire (looking at you Safari), so on init we manually load all\r\n * fonts and rerender scene text elements once done.\r\n *\r\n * For Safari we make sure to check against each loaded font face\r\n * with the unique characters per family in the scene,\r\n * otherwise fonts might remain unloaded.\r\n */\n\n\n this.loadSceneFonts = async () => {\n const sceneFamilies = this.getSceneFamilies();\n const charsPerFamily = true //isSafari\n ? Fonts.getCharsPerFamily(this.scene.getNonDeletedElements()) : 0;\n return Fonts.loadFontFaces(sceneFamilies, charsPerFamily);\n };\n\n this.scene = scene;\n }\n /**\r\n * Generate CSS @font-face declarations for the given elements.\r\n */\n\n\n static async generateFontFaceDeclarations(elements) {\n const families = Fonts.getUniqueFamilies(elements);\n const charsPerFamily = Fonts.getCharsPerFamily(elements); // for simplicity, assuming we have just one family with the CJK handdrawn fallback\n\n const familyWithCJK = families.find(x => (0,_constants__WEBPACK_IMPORTED_MODULE_0__.getFontFamilyFallbacks)(x).includes(_constants__WEBPACK_IMPORTED_MODULE_0__.CJK_HAND_DRAWN_FALLBACK_FONT));\n\n if (familyWithCJK) {\n const characters = Fonts.getCharacters(charsPerFamily, familyWithCJK);\n\n if ((0,_element_textWrapping__WEBPACK_IMPORTED_MODULE_3__.containsCJK)(characters)) {\n const family = _constants__WEBPACK_IMPORTED_MODULE_0__.FONT_FAMILY_FALLBACKS[_constants__WEBPACK_IMPORTED_MODULE_0__.CJK_HAND_DRAWN_FALLBACK_FONT]; // adding the same characters to the CJK handrawn family\n\n charsPerFamily[family] = new Set(characters); // the order between the families and fallbacks is important, as fallbacks need to be defined first and in the reversed order\n // so that they get overriden with the later defined font faces, i.e. in case they share some codepoints\n\n families.unshift(_constants__WEBPACK_IMPORTED_MODULE_0__.FONT_FAMILY_FALLBACKS[_constants__WEBPACK_IMPORTED_MODULE_0__.CJK_HAND_DRAWN_FALLBACK_FONT]);\n }\n } // don't trigger hundreds of concurrent requests (each performing fetch, creating a worker, etc.),\n // instead go three requests at a time, in a controlled manner, without completely blocking the main thread\n // and avoiding potential issues such as rate limits\n\n\n const iterator = Fonts.fontFacesStylesGenerator(families, charsPerFamily);\n const concurrency = 3;\n const fontFaces = await new _utils__WEBPACK_IMPORTED_MODULE_5__.PromisePool(iterator, concurrency).all(); // dedup just in case (i.e. could be the same font faces with 0 glyphs)\n\n return Array.from(new Set(fontFaces));\n } //zsviczian made public\n\n\n static async loadFontFaces(fontFamilies, charsPerFamily) {\n // add all registered font faces into the `document.fonts` (if not added already)\n for (const {\n fontFaces,\n metadata\n } of Fonts.registered.values()) {\n // skip registering font faces for local fonts (i.e. Helvetica)\n if (metadata.local) {\n continue;\n }\n\n for (const {\n fontFace\n } of fontFaces) {\n if (!window.document.fonts.has(fontFace) && fontFace.family !== \"Local Font\" //zsviczian if Local Font is registered I can't change it dymamically\n ) {\n window.document.fonts.add(fontFace);\n }\n }\n } // loading 10 font faces at a time, in a controlled manner\n\n\n const iterator = Fonts.fontFacesLoader(fontFamilies, charsPerFamily);\n const concurrency = 10;\n const fontFaces = await new _utils__WEBPACK_IMPORTED_MODULE_5__.PromisePool(iterator, concurrency).all();\n return fontFaces.flat().filter(Boolean);\n }\n\n static *fontFacesLoader(fontFamilies, charsPerFamily) {\n for (const [index, fontFamily] of fontFamilies.entries()) {\n const font = (0,_utils__WEBPACK_IMPORTED_MODULE_5__.getFontString)({\n fontFamily,\n fontSize: 16\n }); // WARN: without \"text\" param it does not have to mean that all font faces are loaded, instead it could be just one!\n // for Safari on init, we rather check with the \"text\" param, even though it's less efficient, as otherwise fonts might remain unloaded\n\n const text = charsPerFamily //zsviczian && isSafari\n ? Fonts.getCharacters(charsPerFamily, fontFamily) : \"\";\n\n if (!window.document.fonts.check(font, text) && !font.startsWith(\"16px Local Font\") && //zsviczian if Local Font is registered I can't change it dymamically\n !font.startsWith(\"16px Helvetica\") //zsviczian there is an error loading Helvetica font (need to validate the error still exists)\n ) {\n yield (0,_utils__WEBPACK_IMPORTED_MODULE_5__.promiseTry)(async () => {\n var _b;\n\n try {\n // WARN: browser prioritizes loading only font faces with unicode ranges for characters which are present in the document (html & canvas), other font faces could stay unloaded\n // we might want to retry here, i.e. in case CDN is down, but so far I didn't experience any issues - maybe it handles retry-like logic under the hood\n const fontFaces = await window.document.fonts.load(font, text);\n return [index, fontFaces];\n } catch (e) {\n // don't let it all fail if just one font fails to load\n console.error(`Failed to load font \"${font}\" from urls \"${(_b = Fonts.registered.get(fontFamily)) === null || _b === void 0 ? void 0 : _b.fontFaces.map(x => x.urls)}\"`, e);\n }\n });\n }\n }\n }\n\n static *fontFacesStylesGenerator(families, charsPerFamily) {\n var _b;\n\n for (const [familyIndex, family] of families.entries()) {\n const {\n fontFaces,\n metadata\n } = (_b = Fonts.registered.get(family)) !== null && _b !== void 0 ? _b : {};\n\n if (!Array.isArray(fontFaces)) {\n console.error(`Couldn't find registered fonts for font-family \"${family}\"`, Fonts.registered);\n continue;\n }\n\n if (metadata === null || metadata === void 0 ? void 0 : metadata.local) {\n // don't inline local fonts\n continue;\n }\n\n for (const [fontFaceIndex, fontFace] of fontFaces.entries()) {\n yield (0,_utils__WEBPACK_IMPORTED_MODULE_5__.promiseTry)(async () => {\n try {\n const characters = Fonts.getCharacters(charsPerFamily, family);\n const fontFaceCSS = await fontFace.toCSS(characters);\n\n if (!fontFaceCSS) {\n return;\n } // giving a buffer of 10K font faces per family\n\n\n const fontFaceOrder = familyIndex * 10000 + fontFaceIndex;\n const fontFaceTuple = [fontFaceOrder, fontFaceCSS];\n return fontFaceTuple;\n } catch (error) {\n console.error(`Couldn't transform font-face to css for family \"${fontFace.fontFace.family}\"`, error);\n }\n });\n }\n }\n }\n /**\r\n * Register a new font.\r\n *\r\n * @param family font family\r\n * @param metadata font metadata\r\n * @param fontFacesDecriptors font faces descriptors\r\n */\n\n\n static register(family, metadata, ...fontFacesDecriptors) {\n var _b; // TODO: likely we will need to abandon number value in order to support custom fonts\n\n\n const fontFamily = (_b = _constants__WEBPACK_IMPORTED_MODULE_0__.FONT_FAMILY[family]) !== null && _b !== void 0 ? _b : _constants__WEBPACK_IMPORTED_MODULE_0__.FONT_FAMILY_FALLBACKS[family];\n const registeredFamily = this.registered.get(fontFamily);\n\n if (!registeredFamily) {\n this.registered.set(fontFamily, {\n metadata,\n fontFaces: fontFacesDecriptors.map(({\n uri,\n descriptors\n }) => new _ExcalidrawFontFace__WEBPACK_IMPORTED_MODULE_6__.ExcalidrawFontFace(family, uri, descriptors))\n });\n }\n\n return this.registered;\n }\n /**\r\n * WARN: should be called just once on init, even across multiple instances.\r\n */\n\n\n static init() {\n const fonts = {\n registered: new Map()\n };\n\n const init = (family, ...fontFacesDescriptors) => {\n var _b, _c;\n\n const fontFamily = (_b = _constants__WEBPACK_IMPORTED_MODULE_0__.FONT_FAMILY[family]) !== null && _b !== void 0 ? _b : _constants__WEBPACK_IMPORTED_MODULE_0__.FONT_FAMILY_FALLBACKS[family]; // default to Excalifont metrics\n\n const metadata = (_c = _FontMetadata__WEBPACK_IMPORTED_MODULE_17__.FONT_METADATA[fontFamily]) !== null && _c !== void 0 ? _c : _FontMetadata__WEBPACK_IMPORTED_MODULE_17__.FONT_METADATA[_constants__WEBPACK_IMPORTED_MODULE_0__.FONT_FAMILY.Excalifont];\n Fonts.register.call(fonts, family, metadata, ...fontFacesDescriptors);\n };\n\n init(\"Cascadia\", ..._Cascadia__WEBPACK_IMPORTED_MODULE_7__.CascadiaFontFaces);\n init(\"Comic Shanns\", ..._ComicShanns__WEBPACK_IMPORTED_MODULE_8__.ComicShannsFontFaces);\n init(\"Excalifont\", ..._Excalifont__WEBPACK_IMPORTED_MODULE_10__.ExcalifontFontFaces); // keeping for backwards compatibility reasons, uses system font (Helvetica on MacOS, Arial on Win)\n\n init(\"Helvetica\", ..._Helvetica__WEBPACK_IMPORTED_MODULE_11__.HelveticaFontFaces); // used for server-side pdf & png export instead of helvetica (technically does not need metrics, but kept in for consistency)\n\n init(\"Liberation Sans\", ..._Liberation__WEBPACK_IMPORTED_MODULE_12__.LiberationFontFaces);\n init(\"Lilita One\", ..._Lilita__WEBPACK_IMPORTED_MODULE_13__.LilitaFontFaces);\n init(\"Nunito\", ..._Nunito__WEBPACK_IMPORTED_MODULE_14__.NunitoFontFaces);\n init(\"Virgil\", ..._Virgil__WEBPACK_IMPORTED_MODULE_15__.VirgilFontFaces); // fallback font faces\n\n init(_constants__WEBPACK_IMPORTED_MODULE_0__.CJK_HAND_DRAWN_FALLBACK_FONT, ..._Xiaolai__WEBPACK_IMPORTED_MODULE_16__.XiaolaiFontFaces);\n init(_constants__WEBPACK_IMPORTED_MODULE_0__.WINDOWS_EMOJI_FALLBACK_FONT, ..._Emoji__WEBPACK_IMPORTED_MODULE_9__.EmojiFontFaces);\n Fonts._initialized = true;\n return fonts.registered;\n }\n /**\r\n * Get all the unique font families for the given elements.\r\n */\n\n\n static getUniqueFamilies(elements) {\n return Array.from(elements.reduce((families, element) => {\n if ((0,_element__WEBPACK_IMPORTED_MODULE_1__.isTextElement)(element)) {\n families.add(element.fontFamily);\n }\n\n return families;\n }, new Set()));\n }\n /**\r\n * Get all the unique characters per font family for the given scene.\r\n */\n\n\n static getCharsPerFamily(elements) {\n const charsPerFamily = {};\n\n for (const element of elements) {\n if (!(0,_element__WEBPACK_IMPORTED_MODULE_1__.isTextElement)(element)) {\n continue;\n } // gather unique codepoints only when inlining fonts\n\n\n for (const char of element.originalText) {\n if (!charsPerFamily[element.fontFamily]) {\n charsPerFamily[element.fontFamily] = new Set();\n }\n\n charsPerFamily[element.fontFamily].add(char);\n }\n }\n\n return charsPerFamily;\n }\n /**\r\n * Get characters for a given family.\r\n */\n\n\n static getCharacters(charsPerFamily, family) {\n return charsPerFamily[family] ? Array.from(charsPerFamily[family]).join(\"\") : \"\";\n }\n /**\r\n * Get all registered font families.\r\n */\n\n\n static getAllFamilies() {\n return Array.from(Fonts.registered.keys());\n }\n\n}\n_a = Fonts; // it's ok to track fonts across multiple instances only once, so let's use\n// a static member to reduce memory footprint\n\nFonts.loadedFontsCache = new Set();\nFonts._initialized = false;\n/**\r\n * Load font faces for passed elements - use when the scene is unavailable (i.e. export).\r\n *\r\n * For Safari we make sure to check against each loaded font face,\r\n * with the unique characters per family in the elements\r\n * otherwise fonts might remain unloaded.\r\n */\n\nFonts.loadElementsFonts = async elements => {\n const fontFamilies = Fonts.getUniqueFamilies(elements);\n const charsPerFamily = true //isSafari\n ? Fonts.getCharsPerFamily(elements) : 0;\n return Fonts.loadFontFaces(fontFamilies, charsPerFamily);\n};\n/**\r\n * Load all registered font faces.\r\n */\n\n\nFonts.loadAllFonts = async () => {\n const allFamilies = Fonts.getAllFamilies();\n return Fonts.loadFontFaces(allFamilies);\n};\n/**\r\n * Calculates vertical offset for a text with alphabetic baseline.\r\n */\n\n\nconst getVerticalOffset = (fontFamily, fontSize, lineHeightPx) => {\n var _b;\n\n const {\n unitsPerEm,\n ascender,\n descender\n } = ((_b = Fonts.registered.get(fontFamily)) === null || _b === void 0 ? void 0 : _b.metadata.metrics) || _FontMetadata__WEBPACK_IMPORTED_MODULE_17__.FONT_METADATA[_constants__WEBPACK_IMPORTED_MODULE_0__.FONT_FAMILY.Virgil].metrics;\n const fontSizeEm = fontSize / unitsPerEm;\n const lineGap = (lineHeightPx - fontSizeEm * ascender + fontSizeEm * descender) / 2;\n const verticalOffset = fontSizeEm * ascender + lineGap;\n return verticalOffset;\n};\n/**\r\n * Gets line height forr a selected family.\r\n */\n\nconst getLineHeight = fontFamily => {\n var _b;\n\n const {\n lineHeight\n } = ((_b = Fonts.registered.get(fontFamily)) === null || _b === void 0 ? void 0 : _b.metadata.metrics) || _FontMetadata__WEBPACK_IMPORTED_MODULE_17__.FONT_METADATA[_constants__WEBPACK_IMPORTED_MODULE_0__.FONT_FAMILY.Excalifont].metrics;\n return lineHeight;\n};\n\n//# sourceURL=webpack://ExcalidrawLib/./fonts/Fonts.ts?");
|
|
3215
|
+
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"Fonts\": () => (/* binding */ Fonts),\n/* harmony export */ \"getLineHeight\": () => (/* binding */ getLineHeight),\n/* harmony export */ \"getVerticalOffset\": () => (/* binding */ getVerticalOffset)\n/* harmony export */ });\n/* harmony import */ var _constants__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../constants */ \"./constants.ts\");\n/* harmony import */ var _element__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../element */ \"./element/index.ts\");\n/* harmony import */ var _element_textElement__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../element/textElement */ \"./element/textElement.ts\");\n/* harmony import */ var _element_textWrapping__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../element/textWrapping */ \"./element/textWrapping.ts\");\n/* harmony import */ var _scene_ShapeCache__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../scene/ShapeCache */ \"./scene/ShapeCache.ts\");\n/* harmony import */ var _utils__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../utils */ \"./utils.ts\");\n/* harmony import */ var _ExcalidrawFontFace__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./ExcalidrawFontFace */ \"./fonts/ExcalidrawFontFace.ts\");\n/* harmony import */ var _Cascadia__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./Cascadia */ \"./fonts/Cascadia/index.ts\");\n/* harmony import */ var _ComicShanns__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ./ComicShanns */ \"./fonts/ComicShanns/index.ts\");\n/* harmony import */ var _Emoji__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ./Emoji */ \"./fonts/Emoji/index.ts\");\n/* harmony import */ var _Excalifont__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ./Excalifont */ \"./fonts/Excalifont/index.ts\");\n/* harmony import */ var _Helvetica__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! ./Helvetica */ \"./fonts/Helvetica/index.ts\");\n/* harmony import */ var _Liberation__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(/*! ./Liberation */ \"./fonts/Liberation/index.ts\");\n/* harmony import */ var _Lilita__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(/*! ./Lilita */ \"./fonts/Lilita/index.ts\");\n/* harmony import */ var _Nunito__WEBPACK_IMPORTED_MODULE_14__ = __webpack_require__(/*! ./Nunito */ \"./fonts/Nunito/index.ts\");\n/* harmony import */ var _Virgil__WEBPACK_IMPORTED_MODULE_15__ = __webpack_require__(/*! ./Virgil */ \"./fonts/Virgil/index.ts\");\n/* harmony import */ var _Xiaolai__WEBPACK_IMPORTED_MODULE_16__ = __webpack_require__(/*! ./Xiaolai */ \"./fonts/Xiaolai/index.ts\");\n/* harmony import */ var _FontMetadata__WEBPACK_IMPORTED_MODULE_17__ = __webpack_require__(/*! ./FontMetadata */ \"./fonts/FontMetadata.ts\");\nvar _a;\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\nclass Fonts {\n static get registered() {\n // lazy load the font registration\n if (!Fonts._registered) {\n Fonts._registered = Fonts.init();\n } else if (!Fonts._initialized) {\n // case when host app register fonts before they are lazy loaded\n // don't override whatever has been previously registered\n Fonts._registered = new Map([...Fonts.init().entries(), ...Fonts._registered.entries()]);\n }\n\n return Fonts._registered;\n }\n\n get registered() {\n return Fonts.registered;\n }\n\n constructor(scene) {\n /**\r\n * Get all the font families for the given scene.\r\n */\n this.getSceneFamilies = () => {\n return Fonts.getUniqueFamilies(this.scene.getNonDeletedElements());\n };\n /**\r\n * if we load a (new) font, it's likely that text elements using it have\r\n * already been rendered using a fallback font. Thus, we want invalidate\r\n * their shapes and rerender. See #637.\r\n *\r\n * Invalidates text elements and rerenders scene, provided that at least one\r\n * of the supplied fontFaces has not already been processed.\r\n */\n\n\n this.onLoaded = fontFaces => {\n // bail if all fonts with have been processed. We're checking just a\n // subset of the font properties (though it should be enough), so it\n // can technically bail on a false positive.\n let shouldBail = true; //zsviczian - Obsidian adds Excalidraw irrelevant fonts\n\n const registeredFontFamilies = new Set();\n Fonts.registered.forEach(x => registeredFontFamilies.add(x.fontFaces[0].fontFace.family));\n\n for (const fontFace of fontFaces) {\n //zsviczian\n if (!registeredFontFamilies.has(fontFace.family)) {\n continue;\n }\n\n const sig = `${fontFace.family}-${fontFace.style}-${fontFace.weight}-${fontFace.unicodeRange}`; // make sure to update our cache with all the loaded font faces\n\n if (!Fonts.loadedFontsCache.has(sig)) {\n Fonts.loadedFontsCache.add(sig);\n shouldBail = false;\n }\n }\n\n if (shouldBail) {\n return;\n }\n\n let didUpdate = false;\n const elementsMap = this.scene.getNonDeletedElementsMap();\n\n for (const element of this.scene.getNonDeletedElements()) {\n if ((0,_element__WEBPACK_IMPORTED_MODULE_1__.isTextElement)(element)) {\n didUpdate = true;\n _scene_ShapeCache__WEBPACK_IMPORTED_MODULE_4__.ShapeCache[\"delete\"](element); // clear the width cache, so that we don't perform subsequent wrapping based on the stale fallback font metrics\n\n _element_textElement__WEBPACK_IMPORTED_MODULE_2__.charWidth.clearCache((0,_utils__WEBPACK_IMPORTED_MODULE_5__.getFontString)(element));\n const container = (0,_element_textElement__WEBPACK_IMPORTED_MODULE_2__.getContainerElement)(element, elementsMap);\n\n if (container) {\n _scene_ShapeCache__WEBPACK_IMPORTED_MODULE_4__.ShapeCache[\"delete\"](container);\n }\n }\n }\n\n if (didUpdate) {\n this.scene.triggerUpdate();\n }\n };\n /**\r\n * Load font faces for a given scene and trigger scene update.\r\n *\r\n * FontFaceSet loadingdone event we listen on may not always\r\n * fire (looking at you Safari), so on init we manually load all\r\n * fonts and rerender scene text elements once done.\r\n *\r\n * For Safari we make sure to check against each loaded font face\r\n * with the unique characters per family in the scene,\r\n * otherwise fonts might remain unloaded.\r\n */\n\n\n this.loadSceneFonts = async () => {\n const sceneFamilies = this.getSceneFamilies();\n const charsPerFamily = true //isSafari\n ? Fonts.getCharsPerFamily(this.scene.getNonDeletedElements()) : 0;\n return Fonts.loadFontFaces(sceneFamilies, charsPerFamily);\n };\n\n this.scene = scene;\n }\n /**\r\n * Generate CSS @font-face declarations for the given elements.\r\n */\n\n\n static async generateFontFaceDeclarations(elements) {\n const families = Fonts.getUniqueFamilies(elements);\n const charsPerFamily = Fonts.getCharsPerFamily(elements); // for simplicity, assuming we have just one family with the CJK handdrawn fallback\n\n const familyWithCJK = families.find(x => (0,_constants__WEBPACK_IMPORTED_MODULE_0__.getFontFamilyFallbacks)(x).includes(_constants__WEBPACK_IMPORTED_MODULE_0__.CJK_HAND_DRAWN_FALLBACK_FONT));\n\n if (familyWithCJK) {\n const characters = Fonts.getCharacters(charsPerFamily, familyWithCJK);\n\n if ((0,_element_textWrapping__WEBPACK_IMPORTED_MODULE_3__.containsCJK)(characters)) {\n const family = _constants__WEBPACK_IMPORTED_MODULE_0__.FONT_FAMILY_FALLBACKS[_constants__WEBPACK_IMPORTED_MODULE_0__.CJK_HAND_DRAWN_FALLBACK_FONT]; // adding the same characters to the CJK handrawn family\n\n charsPerFamily[family] = new Set(characters); // the order between the families and fallbacks is important, as fallbacks need to be defined first and in the reversed order\n // so that they get overriden with the later defined font faces, i.e. in case they share some codepoints\n\n families.unshift(_constants__WEBPACK_IMPORTED_MODULE_0__.FONT_FAMILY_FALLBACKS[_constants__WEBPACK_IMPORTED_MODULE_0__.CJK_HAND_DRAWN_FALLBACK_FONT]);\n }\n } // don't trigger hundreds of concurrent requests (each performing fetch, creating a worker, etc.),\n // instead go three requests at a time, in a controlled manner, without completely blocking the main thread\n // and avoiding potential issues such as rate limits\n\n\n const iterator = Fonts.fontFacesStylesGenerator(families, charsPerFamily);\n const concurrency = 3;\n const fontFaces = await new _utils__WEBPACK_IMPORTED_MODULE_5__.PromisePool(iterator, concurrency).all(); // dedup just in case (i.e. could be the same font faces with 0 glyphs)\n\n return Array.from(new Set(fontFaces));\n } //zsviczian made public\n\n\n static async loadFontFaces(fontFamilies, charsPerFamily) {\n // add all registered font faces into the `document.fonts` (if not added already)\n for (const {\n fontFaces,\n metadata\n } of Fonts.registered.values()) {\n // skip registering font faces for local fonts (i.e. Helvetica)\n if (metadata.local) {\n continue;\n }\n\n for (const {\n fontFace\n } of fontFaces) {\n if (!window.document.fonts.has(fontFace) && fontFace.family !== \"Local Font\" //zsviczian if Local Font is registered I can't change it dymamically\n ) {\n window.document.fonts.add(fontFace);\n }\n }\n } // loading 10 font faces at a time, in a controlled manner\n\n\n const iterator = Fonts.fontFacesLoader(fontFamilies, charsPerFamily);\n const concurrency = 10;\n const fontFaces = await new _utils__WEBPACK_IMPORTED_MODULE_5__.PromisePool(iterator, concurrency).all();\n return fontFaces.flat().filter(Boolean);\n }\n\n static *fontFacesLoader(fontFamilies, charsPerFamily) {\n for (const [index, fontFamily] of fontFamilies.entries()) {\n const font = (0,_utils__WEBPACK_IMPORTED_MODULE_5__.getFontString)({\n fontFamily,\n fontSize: 16\n }); // WARN: without \"text\" param it does not have to mean that all font faces are loaded, instead it could be just one!\n // for Safari on init, we rather check with the \"text\" param, even though it's less efficient, as otherwise fonts might remain unloaded\n\n const text = charsPerFamily //zsviczian && isSafari\n ? Fonts.getCharacters(charsPerFamily, fontFamily) : \"\";\n\n if (!window.document.fonts.check(font, text) && !font.startsWith(\"16px Local Font\") && //zsviczian if Local Font is registered I can't change it dymamically\n !font.startsWith(\"16px Helvetica\") //zsviczian there is an error loading Helvetica font (need to validate the error still exists)\n ) {\n yield (0,_utils__WEBPACK_IMPORTED_MODULE_5__.promiseTry)(async () => {\n var _b;\n\n try {\n // WARN: browser prioritizes loading only font faces with unicode ranges for characters which are present in the document (html & canvas), other font faces could stay unloaded\n // we might want to retry here, i.e. in case CDN is down, but so far I didn't experience any issues - maybe it handles retry-like logic under the hood\n const fontFaces = await window.document.fonts.load(font, text);\n return [index, fontFaces];\n } catch (e) {\n // don't let it all fail if just one font fails to load\n console.error(`Failed to load font \"${font}\" from urls \"${(_b = Fonts.registered.get(fontFamily)) === null || _b === void 0 ? void 0 : _b.fontFaces.map(x => x.urls)}\"`, e);\n }\n });\n }\n }\n }\n\n static *fontFacesStylesGenerator(families, charsPerFamily) {\n var _b;\n\n for (const [familyIndex, family] of families.entries()) {\n const {\n fontFaces,\n metadata\n } = (_b = Fonts.registered.get(family)) !== null && _b !== void 0 ? _b : {};\n\n if (!Array.isArray(fontFaces)) {\n console.error(`Couldn't find registered fonts for font-family \"${family}\"`, Fonts.registered);\n continue;\n }\n\n if (metadata === null || metadata === void 0 ? void 0 : metadata.local) {\n // don't inline local fonts\n continue;\n }\n\n for (const [fontFaceIndex, fontFace] of fontFaces.entries()) {\n yield (0,_utils__WEBPACK_IMPORTED_MODULE_5__.promiseTry)(async () => {\n try {\n const characters = Fonts.getCharacters(charsPerFamily, family);\n const fontFaceCSS = await fontFace.toCSS(characters);\n\n if (!fontFaceCSS) {\n return;\n } // giving a buffer of 10K font faces per family\n\n\n const fontFaceOrder = familyIndex * 10000 + fontFaceIndex;\n const fontFaceTuple = [fontFaceOrder, fontFaceCSS];\n return fontFaceTuple;\n } catch (error) {\n console.error(`Couldn't transform font-face to css for family \"${fontFace.fontFace.family}\"`, error);\n }\n });\n }\n }\n }\n /**\r\n * Register a new font.\r\n *\r\n * @param family font family\r\n * @param metadata font metadata\r\n * @param fontFacesDecriptors font faces descriptors\r\n */\n\n\n static register(family, metadata, ...fontFacesDecriptors) {\n var _b; // TODO: likely we will need to abandon number value in order to support custom fonts\n\n\n const fontFamily = (_b = _constants__WEBPACK_IMPORTED_MODULE_0__.FONT_FAMILY[family]) !== null && _b !== void 0 ? _b : _constants__WEBPACK_IMPORTED_MODULE_0__.FONT_FAMILY_FALLBACKS[family];\n const registeredFamily = this.registered.get(fontFamily);\n\n if (!registeredFamily || family === \"Local Font\") {\n this.registered.set(fontFamily, {\n metadata,\n fontFaces: fontFacesDecriptors.map(({\n uri,\n descriptors\n }) => new _ExcalidrawFontFace__WEBPACK_IMPORTED_MODULE_6__.ExcalidrawFontFace(family, uri, descriptors))\n });\n }\n\n return this.registered;\n }\n /**\r\n * WARN: should be called just once on init, even across multiple instances.\r\n */\n\n\n static init() {\n const fonts = {\n registered: new Map()\n };\n\n const init = (family, ...fontFacesDescriptors) => {\n var _b, _c;\n\n const fontFamily = (_b = _constants__WEBPACK_IMPORTED_MODULE_0__.FONT_FAMILY[family]) !== null && _b !== void 0 ? _b : _constants__WEBPACK_IMPORTED_MODULE_0__.FONT_FAMILY_FALLBACKS[family]; // default to Excalifont metrics\n\n const metadata = (_c = _FontMetadata__WEBPACK_IMPORTED_MODULE_17__.FONT_METADATA[fontFamily]) !== null && _c !== void 0 ? _c : _FontMetadata__WEBPACK_IMPORTED_MODULE_17__.FONT_METADATA[_constants__WEBPACK_IMPORTED_MODULE_0__.FONT_FAMILY.Excalifont];\n Fonts.register.call(fonts, family, metadata, ...fontFacesDescriptors);\n };\n\n init(\"Cascadia\", ..._Cascadia__WEBPACK_IMPORTED_MODULE_7__.CascadiaFontFaces);\n init(\"Comic Shanns\", ..._ComicShanns__WEBPACK_IMPORTED_MODULE_8__.ComicShannsFontFaces);\n init(\"Excalifont\", ..._Excalifont__WEBPACK_IMPORTED_MODULE_10__.ExcalifontFontFaces); // keeping for backwards compatibility reasons, uses system font (Helvetica on MacOS, Arial on Win)\n\n init(\"Helvetica\", ..._Helvetica__WEBPACK_IMPORTED_MODULE_11__.HelveticaFontFaces); // used for server-side pdf & png export instead of helvetica (technically does not need metrics, but kept in for consistency)\n\n init(\"Liberation Sans\", ..._Liberation__WEBPACK_IMPORTED_MODULE_12__.LiberationFontFaces);\n init(\"Lilita One\", ..._Lilita__WEBPACK_IMPORTED_MODULE_13__.LilitaFontFaces);\n init(\"Nunito\", ..._Nunito__WEBPACK_IMPORTED_MODULE_14__.NunitoFontFaces);\n init(\"Virgil\", ..._Virgil__WEBPACK_IMPORTED_MODULE_15__.VirgilFontFaces); // fallback font faces\n\n init(_constants__WEBPACK_IMPORTED_MODULE_0__.CJK_HAND_DRAWN_FALLBACK_FONT, ..._Xiaolai__WEBPACK_IMPORTED_MODULE_16__.XiaolaiFontFaces);\n init(_constants__WEBPACK_IMPORTED_MODULE_0__.WINDOWS_EMOJI_FALLBACK_FONT, ..._Emoji__WEBPACK_IMPORTED_MODULE_9__.EmojiFontFaces);\n Fonts._initialized = true;\n return fonts.registered;\n }\n /**\r\n * Get all the unique font families for the given elements.\r\n */\n\n\n static getUniqueFamilies(elements) {\n return Array.from(elements.reduce((families, element) => {\n if ((0,_element__WEBPACK_IMPORTED_MODULE_1__.isTextElement)(element)) {\n families.add(element.fontFamily);\n }\n\n return families;\n }, new Set()));\n }\n /**\r\n * Get all the unique characters per font family for the given scene.\r\n */\n\n\n static getCharsPerFamily(elements) {\n const charsPerFamily = {};\n\n for (const element of elements) {\n if (!(0,_element__WEBPACK_IMPORTED_MODULE_1__.isTextElement)(element)) {\n continue;\n } // gather unique codepoints only when inlining fonts\n\n\n for (const char of element.originalText) {\n if (!charsPerFamily[element.fontFamily]) {\n charsPerFamily[element.fontFamily] = new Set();\n }\n\n charsPerFamily[element.fontFamily].add(char);\n }\n }\n\n return charsPerFamily;\n }\n /**\r\n * Get characters for a given family.\r\n */\n\n\n static getCharacters(charsPerFamily, family) {\n return charsPerFamily[family] ? Array.from(charsPerFamily[family]).join(\"\") : \"\";\n }\n /**\r\n * Get all registered font families.\r\n */\n\n\n static getAllFamilies() {\n return Array.from(Fonts.registered.keys());\n }\n\n}\n_a = Fonts; // it's ok to track fonts across multiple instances only once, so let's use\n// a static member to reduce memory footprint\n\nFonts.loadedFontsCache = new Set();\nFonts._initialized = false;\n/**\r\n * Load font faces for passed elements - use when the scene is unavailable (i.e. export).\r\n *\r\n * For Safari we make sure to check against each loaded font face,\r\n * with the unique characters per family in the elements\r\n * otherwise fonts might remain unloaded.\r\n */\n\nFonts.loadElementsFonts = async elements => {\n const fontFamilies = Fonts.getUniqueFamilies(elements);\n const charsPerFamily = true //isSafari\n ? Fonts.getCharsPerFamily(elements) : 0;\n return Fonts.loadFontFaces(fontFamilies, charsPerFamily);\n};\n/**\r\n * Load all registered font faces.\r\n */\n\n\nFonts.loadAllFonts = async () => {\n const allFamilies = Fonts.getAllFamilies();\n return Fonts.loadFontFaces(allFamilies);\n};\n/**\r\n * Calculates vertical offset for a text with alphabetic baseline.\r\n */\n\n\nconst getVerticalOffset = (fontFamily, fontSize, lineHeightPx) => {\n var _b;\n\n const {\n unitsPerEm,\n ascender,\n descender\n } = ((_b = Fonts.registered.get(fontFamily)) === null || _b === void 0 ? void 0 : _b.metadata.metrics) || _FontMetadata__WEBPACK_IMPORTED_MODULE_17__.FONT_METADATA[_constants__WEBPACK_IMPORTED_MODULE_0__.FONT_FAMILY.Virgil].metrics;\n const fontSizeEm = fontSize / unitsPerEm;\n const lineGap = (lineHeightPx - fontSizeEm * ascender + fontSizeEm * descender) / 2;\n const verticalOffset = fontSizeEm * ascender + lineGap;\n return verticalOffset;\n};\n/**\r\n * Gets line height forr a selected family.\r\n */\n\nconst getLineHeight = fontFamily => {\n var _b;\n\n const {\n lineHeight\n } = ((_b = Fonts.registered.get(fontFamily)) === null || _b === void 0 ? void 0 : _b.metadata.metrics) || _FontMetadata__WEBPACK_IMPORTED_MODULE_17__.FONT_METADATA[_constants__WEBPACK_IMPORTED_MODULE_0__.FONT_FAMILY.Excalifont].metrics;\n return lineHeight;\n};\n\n//# sourceURL=webpack://ExcalidrawLib/./fonts/Fonts.ts?");
|
|
3216
3216
|
|
|
3217
3217
|
/***/ }),
|
|
3218
3218
|
|
|
@@ -3564,7 +3564,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpac
|
|
|
3564
3564
|
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
|
|
3565
3565
|
|
|
3566
3566
|
"use strict";
|
|
3567
|
-
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _constants__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./constants */ \"./constants.ts\");\n\n\nif (\"development\" !== _constants__WEBPACK_IMPORTED_MODULE_0__.ENV.TEST) {\n /* eslint-disable */\n\n /* global __webpack_public_path__:writable */\n __webpack_require__.p = window.EXCALIDRAW_ASSET_PATH || `https://unpkg.com/${\"@zsviczian/excalidraw\"}@${\"0.17.6-
|
|
3567
|
+
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _constants__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./constants */ \"./constants.ts\");\n\n\nif (\"development\" !== _constants__WEBPACK_IMPORTED_MODULE_0__.ENV.TEST) {\n /* eslint-disable */\n\n /* global __webpack_public_path__:writable */\n __webpack_require__.p = window.EXCALIDRAW_ASSET_PATH || `https://unpkg.com/${\"@zsviczian/excalidraw\"}@${\"0.17.6-16\"}/dist/`;\n}\n\n//# sourceURL=webpack://ExcalidrawLib/./publicPath.js?");
|
|
3568
3568
|
|
|
3569
3569
|
/***/ }),
|
|
3570
3570
|
|
|
@@ -3982,7 +3982,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpac
|
|
|
3982
3982
|
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
|
|
3983
3983
|
|
|
3984
3984
|
"use strict";
|
|
3985
|
-
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"PromisePool\": () => (/* binding */ PromisePool),\n/* harmony export */ \"addEventListener\": () => (/* binding */ addEventListener),\n/* harmony export */ \"allowFullScreen\": () => (/* binding */ allowFullScreen),\n/* harmony export */ \"arrayToList\": () => (/* binding */ arrayToList),\n/* harmony export */ \"arrayToMap\": () => (/* binding */ arrayToMap),\n/* harmony export */ \"arrayToMapWithIndex\": () => (/* binding */ arrayToMapWithIndex),\n/* harmony export */ \"arrayToObject\": () => (/* binding */ arrayToObject),\n/* harmony export */ \"assertNever\": () => (/* binding */ assertNever),\n/* harmony export */ \"bytesToHexString\": () => (/* binding */ bytesToHexString),\n/* harmony export */ \"capitalizeString\": () => (/* binding */ capitalizeString),\n/* harmony export */ \"chunk\": () => (/* binding */ chunk),\n/* harmony export */ \"cloneJSON\": () => (/* binding */ cloneJSON),\n/* harmony export */ \"composeEventHandlers\": () => (/* binding */ composeEventHandlers),\n/* harmony export */ \"debounce\": () => (/* binding */ debounce),\n/* harmony export */ \"distance\": () => (/* binding */ distance),\n/* harmony export */ \"easeOut\": () => (/* binding */ easeOut),\n/* harmony export */ \"easeToValuesRAF\": () => (/* binding */ easeToValuesRAF),\n/* harmony export */ \"exitFullScreen\": () => (/* binding */ exitFullScreen),\n/* harmony export */ \"findIndex\": () => (/* binding */ findIndex),\n/* harmony export */ \"findLastIndex\": () => (/* binding */ findLastIndex),\n/* harmony export */ \"focusNearestParent\": () => (/* binding */ focusNearestParent),\n/* harmony export */ \"getDateTime\": () => (/* binding */ getDateTime),\n/* harmony export */ \"getFontFamilyString\": () => (/* binding */ getFontFamilyString),\n/* harmony export */ \"getFontString\": () => (/* binding */ getFontString),\n/* harmony export */ \"getFrame\": () => (/* binding */ getFrame),\n/* harmony export */ \"getGlobalCSSVariable\": () => (/* binding */ getGlobalCSSVariable),\n/* harmony export */ \"getNearestScrollableContainer\": () => (/* binding */ getNearestScrollableContainer),\n/* harmony export */ \"getShortcutKey\": () => (/* binding */ getShortcutKey),\n/* harmony export */ \"getSvgPathFromStroke\": () => (/* binding */ getSvgPathFromStroke),\n/* harmony export */ \"getUpdatedTimestamp\": () => (/* binding */ getUpdatedTimestamp),\n/* harmony export */ \"getVersion\": () => (/* binding */ getVersion),\n/* harmony export */ \"invariant\": () => (/* binding */ invariant),\n/* harmony export */ \"isAnyTrue\": () => (/* binding */ isAnyTrue),\n/* harmony export */ \"isDevEnv\": () => (/* binding */ isDevEnv),\n/* harmony export */ \"isFullScreen\": () => (/* binding */ isFullScreen),\n/* harmony export */ \"isInputLike\": () => (/* binding */ isInputLike),\n/* harmony export */ \"isInteractive\": () => (/* binding */ isInteractive),\n/* harmony export */ \"isMemberOf\": () => (/* binding */ isMemberOf),\n/* harmony export */ \"isPrimitive\": () => (/* binding */ isPrimitive),\n/* harmony export */ \"isPromiseLike\": () => (/* binding */ isPromiseLike),\n/* harmony export */ \"isRTL\": () => (/* binding */ isRTL),\n/* harmony export */ \"isRunningInIframe\": () => (/* binding */ isRunningInIframe),\n/* harmony export */ \"isServerEnv\": () => (/* binding */ isServerEnv),\n/* harmony export */ \"isShallowEqual\": () => (/* binding */ isShallowEqual),\n/* harmony export */ \"isTestEnv\": () => (/* binding */ isTestEnv),\n/* harmony export */ \"isToolIcon\": () => (/* binding */ isToolIcon),\n/* harmony export */ \"isTransparent\": () => (/* binding */ isTransparent),\n/* harmony export */ \"isWritableElement\": () => (/* binding */ isWritableElement),\n/* harmony export */ \"memoize\": () => (/* binding */ memoize),\n/* harmony export */ \"muteFSAbortError\": () => (/* binding */ muteFSAbortError),\n/* harmony export */ \"nFormatter\": () => (/* binding */ nFormatter),\n/* harmony export */ \"normalizeEOL\": () => (/* binding */ normalizeEOL),\n/* harmony export */ \"preventUnload\": () => (/* binding */ preventUnload),\n/* harmony export */ \"promiseTry\": () => (/* binding */ promiseTry),\n/* harmony export */ \"queryFocusableElements\": () => (/* binding */ queryFocusableElements),\n/* harmony export */ \"removeSelection\": () => (/* binding */ removeSelection),\n/* harmony export */ \"resolvablePromise\": () => (/* binding */ resolvablePromise),\n/* harmony export */ \"safelyParseJSON\": () => (/* binding */ safelyParseJSON),\n/* harmony export */ \"sceneCoordsToViewportCoords\": () => (/* binding */ sceneCoordsToViewportCoords),\n/* harmony export */ \"selectNode\": () => (/* binding */ selectNode),\n/* harmony export */ \"setDateTimeForTests\": () => (/* binding */ setDateTimeForTests),\n/* harmony export */ \"supportsEmoji\": () => (/* binding */ supportsEmoji),\n/* harmony export */ \"throttleRAF\": () => (/* binding */ throttleRAF),\n/* harmony export */ \"toBrandedType\": () => (/* binding */ toBrandedType),\n/* harmony export */ \"tupleToCoors\": () => (/* binding */ tupleToCoors),\n/* harmony export */ \"updateActiveTool\": () => (/* binding */ updateActiveTool),\n/* harmony export */ \"updateObject\": () => (/* binding */ updateObject),\n/* harmony export */ \"updateStable\": () => (/* binding */ updateStable),\n/* harmony export */ \"viewportCoordsToSceneCoords\": () => (/* binding */ viewportCoordsToSceneCoords),\n/* harmony export */ \"wrapEvent\": () => (/* binding */ wrapEvent)\n/* harmony export */ });\n/* harmony import */ var es6_promise_pool__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! es6-promise-pool */ \"../../node_modules/es6-promise-pool/es6-promise-pool.js\");\n/* harmony import */ var es6_promise_pool__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(es6_promise_pool__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var _math__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../math */ \"../math/index.ts\");\n/* harmony import */ var _colors__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./colors */ \"./colors.ts\");\n/* harmony import */ var _constants__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./constants */ \"./constants.ts\");\n\n\n\n\nlet mockDateTime = null;\nconst setDateTimeForTests = dateTime => {\n mockDateTime = dateTime;\n};\nconst getDateTime = () => {\n if (mockDateTime) {\n return mockDateTime;\n }\n\n const date = new Date();\n const year = date.getFullYear();\n const month = `${date.getMonth() + 1}`.padStart(2, \"0\");\n const day = `${date.getDate()}`.padStart(2, \"0\");\n const hr = `${date.getHours()}`.padStart(2, \"0\");\n const min = `${date.getMinutes()}`.padStart(2, \"0\");\n return `${year}-${month}-${day}-${hr}${min}`;\n};\nconst capitalizeString = str => str.charAt(0).toUpperCase() + str.slice(1);\nconst isToolIcon = target => target instanceof HTMLElement && target.className.includes(\"ToolIcon\");\nconst isInputLike = target => target instanceof HTMLElement && target.dataset.type === \"wysiwyg\" || target instanceof HTMLBRElement || // newline in wysiwyg\ntarget instanceof HTMLInputElement || target instanceof HTMLTextAreaElement || target instanceof HTMLSelectElement;\nconst isInteractive = target => {\n return isInputLike(target) || target instanceof Element && !!target.closest(\"label, button\");\n};\nconst isWritableElement = target => target instanceof HTMLElement && target.dataset.type === \"wysiwyg\" || target instanceof HTMLBRElement || // newline in wysiwyg\ntarget instanceof HTMLTextAreaElement || target instanceof HTMLInputElement && (target.type === \"text\" || target.type === \"number\" || target.type === \"password\");\nconst getFontFamilyString = ({\n fontFamily\n}) => {\n for (const [fontFamilyString, id] of Object.entries(_constants__WEBPACK_IMPORTED_MODULE_3__.FONT_FAMILY)) {\n if (id === fontFamily) {\n // TODO: we should fallback first to generic family names first\n return `${fontFamilyString}${(0,_constants__WEBPACK_IMPORTED_MODULE_3__.getFontFamilyFallbacks)(id).map(x => `, ${x}`).join(\"\")}`;\n }\n }\n\n return _constants__WEBPACK_IMPORTED_MODULE_3__.WINDOWS_EMOJI_FALLBACK_FONT;\n};\n/** returns fontSize+fontFamily string for assignment to DOM elements */\n\nconst getFontString = ({\n fontSize,\n fontFamily\n}) => {\n return `${fontSize}px ${getFontFamilyString({\n fontFamily\n })}`;\n};\nconst debounce = (fn, timeout) => {\n let handle = 0;\n let lastArgs = null;\n\n const ret = (...args) => {\n lastArgs = args;\n clearTimeout(handle);\n handle = window.setTimeout(() => {\n lastArgs = null;\n fn(...args);\n }, timeout);\n };\n\n ret.flush = () => {\n clearTimeout(handle);\n\n if (lastArgs) {\n const _lastArgs = lastArgs;\n lastArgs = null;\n fn(..._lastArgs);\n }\n };\n\n ret.cancel = () => {\n lastArgs = null;\n clearTimeout(handle);\n };\n\n return ret;\n}; // throttle callback to execute once per animation frame\n\nconst throttleRAF = (fn, opts) => {\n let timerId = null;\n let lastArgs = null;\n let lastArgsTrailing = null;\n\n const scheduleFunc = args => {\n timerId = window.requestAnimationFrame(() => {\n timerId = null;\n fn(...args);\n lastArgs = null;\n\n if (lastArgsTrailing) {\n lastArgs = lastArgsTrailing;\n lastArgsTrailing = null;\n scheduleFunc(lastArgs);\n }\n });\n };\n\n const ret = (...args) => {\n if (false) {}\n\n lastArgs = args;\n\n if (timerId === null) {\n scheduleFunc(lastArgs);\n } else if (opts === null || opts === void 0 ? void 0 : opts.trailing) {\n lastArgsTrailing = args;\n }\n };\n\n ret.flush = () => {\n if (timerId !== null) {\n cancelAnimationFrame(timerId);\n timerId = null;\n }\n\n if (lastArgs) {\n fn(...(lastArgsTrailing || lastArgs));\n lastArgs = lastArgsTrailing = null;\n }\n };\n\n ret.cancel = () => {\n lastArgs = lastArgsTrailing = null;\n\n if (timerId !== null) {\n cancelAnimationFrame(timerId);\n timerId = null;\n }\n };\n\n return ret;\n};\n/**\r\n * Exponential ease-out method\r\n *\r\n * @param {number} k - The value to be tweened.\r\n * @returns {number} The tweened value.\r\n */\n\nconst easeOut = k => {\n return 1 - Math.pow(1 - k, 4);\n};\n\nconst easeOutInterpolate = (from, to, progress) => {\n return (to - from) * easeOut(progress) + from;\n};\n/**\r\n * Animates values from `fromValues` to `toValues` using the requestAnimationFrame API.\r\n * Executes the `onStep` callback on each step with the interpolated values.\r\n * Returns a function that can be called to cancel the animation.\r\n *\r\n * @example\r\n * // Example usage:\r\n * const fromValues = { x: 0, y: 0 };\r\n * const toValues = { x: 100, y: 200 };\r\n * const onStep = ({x, y}) => {\r\n * setState(x, y)\r\n * };\r\n * const onCancel = () => {\r\n * console.log(\"Animation canceled\");\r\n * };\r\n *\r\n * const cancelAnimation = easeToValuesRAF({\r\n * fromValues,\r\n * toValues,\r\n * onStep,\r\n * onCancel,\r\n * });\r\n *\r\n * // To cancel the animation:\r\n * cancelAnimation();\r\n */\n\n\nconst easeToValuesRAF = ({\n fromValues,\n toValues,\n onStep,\n duration = 250,\n interpolateValue,\n onStart,\n onEnd,\n onCancel\n}) => {\n let canceled = false;\n let frameId = 0;\n let startTime;\n\n function step(timestamp) {\n if (canceled) {\n return;\n }\n\n if (startTime === undefined) {\n startTime = timestamp;\n onStart === null || onStart === void 0 ? void 0 : onStart();\n }\n\n const elapsed = Math.min(timestamp - startTime, duration);\n const factor = easeOut(elapsed / duration);\n const newValues = {};\n Object.keys(fromValues).forEach(key => {\n const _key = key;\n const result = (toValues[_key] - fromValues[_key]) * factor + fromValues[_key];\n newValues[_key] = result;\n });\n onStep(newValues);\n\n if (elapsed < duration) {\n const progress = elapsed / duration;\n const newValues = {};\n Object.keys(fromValues).forEach(key => {\n const _key = key;\n const startValue = fromValues[_key];\n const endValue = toValues[_key];\n let result;\n result = interpolateValue ? interpolateValue(startValue, endValue, progress, _key) : easeOutInterpolate(startValue, endValue, progress);\n\n if (result == null) {\n result = easeOutInterpolate(startValue, endValue, progress);\n }\n\n newValues[_key] = result;\n });\n onStep(newValues);\n frameId = window.requestAnimationFrame(step);\n } else {\n onStep(toValues);\n onEnd === null || onEnd === void 0 ? void 0 : onEnd();\n }\n }\n\n frameId = window.requestAnimationFrame(step);\n return () => {\n onCancel === null || onCancel === void 0 ? void 0 : onCancel();\n canceled = true;\n window.cancelAnimationFrame(frameId);\n };\n}; // https://github.com/lodash/lodash/blob/es/chunk.js\n\nconst chunk = (array, size) => {\n if (!array.length || size < 1) {\n return [];\n }\n\n let index = 0;\n let resIndex = 0;\n const result = Array(Math.ceil(array.length / size));\n\n while (index < array.length) {\n result[resIndex++] = array.slice(index, index += size);\n }\n\n return result;\n};\nconst selectNode = node => {\n const selection = window.getSelection();\n\n if (selection) {\n const range = document.createRange();\n range.selectNodeContents(node);\n selection.removeAllRanges();\n selection.addRange(range);\n }\n};\nconst removeSelection = () => {\n const selection = window.getSelection();\n\n if (selection) {\n selection.removeAllRanges();\n }\n};\nconst distance = (x, y) => Math.abs(x - y);\nconst updateActiveTool = (appState, data) => {\n var _a, _b;\n\n if (data.type === \"custom\") {\n return Object.assign(Object.assign({}, appState.activeTool), {\n type: \"custom\",\n customType: data.customType,\n locked: (_a = data.locked) !== null && _a !== void 0 ? _a : appState.activeTool.locked\n });\n }\n\n return Object.assign(Object.assign({}, appState.activeTool), {\n lastActiveTool: data.lastActiveToolBeforeEraser === undefined ? appState.activeTool.lastActiveTool : data.lastActiveToolBeforeEraser,\n type: data.type,\n customType: null,\n locked: (_b = data.locked) !== null && _b !== void 0 ? _b : appState.activeTool.locked\n });\n};\nconst isFullScreen = () => {\n var _a;\n\n return ((_a = document.fullscreenElement) === null || _a === void 0 ? void 0 : _a.nodeName) === \"HTML\";\n};\nconst allowFullScreen = () => document.documentElement.requestFullscreen();\nconst exitFullScreen = () => document.exitFullscreen();\nconst getShortcutKey = shortcut => {\n shortcut = shortcut.replace(/\\bAlt\\b/i, \"Alt\").replace(/\\bShift\\b/i, \"Shift\").replace(/\\b(Enter|Return)\\b/i, \"Enter\");\n\n if (_constants__WEBPACK_IMPORTED_MODULE_3__.isDarwin) {\n return shortcut.replace(/\\bCtrlOrCmd\\b/gi, \"Cmd\").replace(/\\bAlt\\b/i, \"Option\");\n }\n\n return shortcut.replace(/\\bCtrlOrCmd\\b/gi, \"Ctrl\");\n};\nconst viewportCoordsToSceneCoords = ({\n clientX,\n clientY\n}, {\n zoom,\n offsetLeft,\n offsetTop,\n scrollX,\n scrollY\n}) => {\n const x = (clientX - offsetLeft) / zoom.value - scrollX;\n const y = (clientY - offsetTop) / zoom.value - scrollY;\n return {\n x,\n y\n };\n};\nconst sceneCoordsToViewportCoords = ({\n sceneX,\n sceneY\n}, {\n zoom,\n offsetLeft,\n offsetTop,\n scrollX,\n scrollY\n}) => {\n const x = (sceneX + scrollX) * zoom.value + offsetLeft;\n const y = (sceneY + scrollY) * zoom.value + offsetTop;\n return {\n x,\n y\n };\n};\nconst getGlobalCSSVariable = name => getComputedStyle(document.documentElement).getPropertyValue(`--${name}`);\nconst RS_LTR_CHARS = \"A-Za-z\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02B8\\u0300-\\u0590\\u0800-\\u1FFF\" + \"\\u2C00-\\uFB1C\\uFDFE-\\uFE6F\\uFEFD-\\uFFFF\";\nconst RS_RTL_CHARS = \"\\u0591-\\u07FF\\uFB1D-\\uFDFD\\uFE70-\\uFEFC\";\nconst RE_RTL_CHECK = new RegExp(`^[^${RS_LTR_CHARS}]*[${RS_RTL_CHARS}]`);\n/**\r\n * Checks whether first directional character is RTL. Meaning whether it starts\r\n * with RTL characters, or indeterminate (numbers etc.) characters followed by\r\n * RTL.\r\n * See https://github.com/excalidraw/excalidraw/pull/1722#discussion_r436340171\r\n */\n\nconst isRTL = text => RE_RTL_CHECK.test(text);\nconst tupleToCoors = xyTuple => {\n const [x, y] = xyTuple;\n return {\n x,\n y\n };\n};\n/** use as a rejectionHandler to mute filesystem Abort errors */\n\nconst muteFSAbortError = error => {\n if ((error === null || error === void 0 ? void 0 : error.name) === \"AbortError\") {\n console.warn(error);\n return;\n }\n\n throw error;\n};\nconst findIndex = (array, cb, fromIndex = 0) => {\n if (fromIndex < 0) {\n fromIndex = array.length + fromIndex;\n }\n\n fromIndex = Math.min(array.length, Math.max(fromIndex, 0));\n let index = fromIndex - 1;\n\n while (++index < array.length) {\n if (cb(array[index], index, array)) {\n return index;\n }\n }\n\n return -1;\n};\nconst findLastIndex = (array, cb, fromIndex = array.length - 1) => {\n if (fromIndex < 0) {\n fromIndex = array.length + fromIndex;\n }\n\n fromIndex = Math.min(array.length - 1, Math.max(fromIndex, 0));\n let index = fromIndex + 1;\n\n while (--index > -1) {\n if (cb(array[index], index, array)) {\n return index;\n }\n }\n\n return -1;\n};\nconst isTransparent = color => {\n const isRGBTransparent = color.length === 5 && color.substr(4, 1) === \"0\";\n const isRRGGBBTransparent = color.length === 9 && color.substr(7, 2) === \"00\";\n return isRGBTransparent || isRRGGBBTransparent || color === _colors__WEBPACK_IMPORTED_MODULE_2__.COLOR_PALETTE.transparent;\n};\nconst resolvablePromise = () => {\n let resolve;\n let reject;\n const promise = new Promise((_resolve, _reject) => {\n resolve = _resolve;\n reject = _reject;\n });\n promise.resolve = resolve;\n promise.reject = reject;\n return promise;\n}; //https://stackoverflow.com/a/9462382/8418\n\nconst nFormatter = (num, digits) => {\n const si = [{\n value: 1,\n symbol: \"b\"\n }, {\n value: 1e3,\n symbol: \"k\"\n }, {\n value: 1e6,\n symbol: \"M\"\n }, {\n value: 1e9,\n symbol: \"G\"\n }];\n const rx = /\\.0+$|(\\.[0-9]*[1-9])0+$/;\n let index;\n\n for (index = si.length - 1; index > 0; index--) {\n if (num >= si[index].value) {\n break;\n }\n }\n\n return (num / si[index].value).toFixed(digits).replace(rx, \"$1\") + si[index].symbol;\n};\nconst getVersion = () => {\n var _a;\n\n return ((_a = document.querySelector('meta[name=\"version\"]')) === null || _a === void 0 ? void 0 : _a.content) || _constants__WEBPACK_IMPORTED_MODULE_3__.DEFAULT_VERSION;\n}; // Adapted from https://github.com/Modernizr/Modernizr/blob/master/feature-detects/emoji.js\n\nconst supportsEmoji = () => {\n const canvas = document.createElement(\"canvas\");\n const ctx = canvas.getContext(\"2d\");\n\n if (!ctx) {\n return false;\n }\n\n const offset = 12;\n ctx.fillStyle = \"#f00\";\n ctx.textBaseline = \"top\";\n ctx.font = \"32px Arial\"; // Modernizr used 🐨, but it is sort of supported on Windows 7.\n // Luckily 😀 isn't supported.\n\n ctx.fillText(\"😀\", 0, 0);\n return ctx.getImageData(offset, offset, 1, 1).data[0] !== 0;\n};\nconst getNearestScrollableContainer = element => {\n let parent = element.parentElement;\n\n while (parent) {\n if (parent === document.body) {\n return document;\n }\n\n const {\n overflowY\n } = window.getComputedStyle(parent);\n const hasScrollableContent = parent.scrollHeight > parent.clientHeight;\n\n if (hasScrollableContent && (overflowY === \"auto\" || overflowY === \"scroll\" || overflowY === \"overlay\")) {\n return parent;\n }\n\n parent = parent.parentElement;\n }\n\n return document;\n};\nconst focusNearestParent = element => {\n let parent = element.parentElement;\n\n while (parent) {\n if (parent.tabIndex > -1) {\n parent.focus();\n return;\n }\n\n parent = parent.parentElement;\n }\n};\nconst preventUnload = event => {\n event.preventDefault(); // NOTE: modern browsers no longer allow showing a custom message here\n\n event.returnValue = \"\";\n};\nconst bytesToHexString = bytes => {\n return Array.from(bytes).map(byte => `0${byte.toString(16)}`.slice(-2)).join(\"\");\n};\nconst getUpdatedTimestamp = () => isTestEnv() ? 1 : Date.now();\n/**\r\n * Transforms array of objects containing `id` attribute,\r\n * or array of ids (strings), into a Map, keyd by `id`.\r\n */\n\nconst arrayToMap = items => {\n if (items instanceof Map) {\n return items;\n }\n\n return items.reduce((acc, element) => {\n acc.set(typeof element === \"string\" ? element : element.id, element);\n return acc;\n }, new Map());\n};\nconst arrayToMapWithIndex = elements => elements.reduce((acc, element, idx) => {\n acc.set(element.id, [element, idx]);\n return acc;\n}, new Map());\n/**\r\n * Transform array into an object, use only when array order is irrelevant.\r\n */\n\nconst arrayToObject = (array, groupBy) => array.reduce((acc, value) => {\n acc[groupBy ? groupBy(value) : String(value)] = value;\n return acc;\n}, {});\n/**\r\n * Creates a circular doubly linked list by adding `prev` and `next` props to the existing array nodes.\r\n */\n\nconst arrayToList = array => array.reduce((acc, curr, index) => {\n const node = Object.assign(Object.assign({}, curr), {\n prev: null,\n next: null\n }); // no-op for first item, we don't want circular references on a single item\n\n if (index !== 0) {\n const prevNode = acc[index - 1];\n node.prev = prevNode;\n prevNode.next = node;\n\n if (index === array.length - 1) {\n // make the references circular and connect head & tail\n const firstNode = acc[0];\n node.next = firstNode;\n firstNode.prev = node;\n }\n }\n\n acc.push(node);\n return acc;\n}, []);\nconst isTestEnv = () => \"development\" === \"test\";\nconst isDevEnv = () => \"development\" === \"development\";\nconst isServerEnv = () => {\n var _a;\n\n return typeof process !== \"undefined\" && !!((_a = process === null || process === void 0 ? void 0 : ({\"VITE_APP_BACKEND_V2_GET_URL\":\"https://json-dev.excalidraw.com/api/v2/\",\"VITE_APP_BACKEND_V2_POST_URL\":\"https://json-dev.excalidraw.com/api/v2/post/\",\"VITE_APP_LIBRARY_URL\":\"https://libraries.excalidraw.com\",\"VITE_APP_LIBRARY_BACKEND\":\"https://us-central1-excalidraw-room-persistence.cloudfunctions.net/libraries\",\"VITE_APP_WS_SERVER_URL\":\"http://localhost:3002\",\"VITE_APP_PLUS_LP\":\"https://plus.excalidraw.com\",\"VITE_APP_PLUS_APP\":\"http://localhost:3000\",\"VITE_APP_AI_BACKEND\":\"http://localhost:3015\",\"VITE_APP_FIREBASE_CONFIG\":\"{\\\"apiKey\\\":\\\"AIzaSyCMkxA60XIW8KbqMYL7edC4qT5l4qHX2h8\\\",\\\"authDomain\\\":\\\"excalidraw-oss-dev.firebaseapp.com\\\",\\\"projectId\\\":\\\"excalidraw-oss-dev\\\",\\\"storageBucket\\\":\\\"excalidraw-oss-dev.appspot.com\\\",\\\"messagingSenderId\\\":\\\"664559512677\\\",\\\"appId\\\":\\\"1:664559512677:web:a385181f2928d328a7aa8c\\\"}\",\"VITE_APP_DEV_DISABLE_LIVE_RELOAD\":\"\",\"VITE_APP_ENABLE_TRACKING\":\"true\",\"FAST_REFRESH\":\"false\",\"VITE_APP_PORT\":\"3000\",\"VITE_APP_DEBUG_ENABLE_TEXT_CONTAINER_BOUNDING_BOX\":\"\",\"VITE_APP_COLLAPSE_OVERLAY\":\"true\",\"VITE_APP_ENABLE_ESLINT\":\"true\",\"VITE_APP_ENABLE_PWA\":\"false\",\"VITE_APP_PLUS_EXPORT_PUBLIC_KEY\":\"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAm2g5T+Rub6Kbf1Mf57t0\\n7r2zeHuVg4dla3r5ryXMswtzz6x767octl6oLThn33mQsPSy3GKglFZoCTXJR4ij\\nba8SxB04sL/N8eRrKja7TFWjCVtRwTTfyy771NYYNFVJclkxHyE5qw4m27crHF1y\\nUNWEjuqNMi/lwAErS9fFa2oJlWyT8U7zzv/5kQREkxZI6y9v0AF3qcbsy2731FnD\\ns9ChJvOUW9toIab2gsIdrKW8ZNpu084ZFVKb6LNjvIXI1Se4oMTHeszXzNptzlot\\nkdxxjOoaQMAyfljFSot1F1FlU6MQlag7UnFGvFjRHN1JI5q4K+n3a67DX+TMyRqS\\nHQIDAQAB\",\"VITE_PKG_NAME\":\"@zsviczian/excalidraw\",\"VITE_PKG_VERSION\":\"0.17.6-15\",\"VITE_IS_EXCALIDRAW_NPM_PACKAGE\":true})) === null || _a === void 0 ? void 0 : _a.NODE_ENV);\n};\nconst wrapEvent = (name, nativeEvent) => {\n return new CustomEvent(name, {\n detail: {\n nativeEvent\n },\n cancelable: true\n });\n};\nconst updateObject = (obj, updates) => {\n let didChange = false;\n\n for (const key in updates) {\n const value = updates[key];\n\n if (typeof value !== \"undefined\") {\n if (obj[key] === value && ( // if object, always update because its attrs could have changed\n typeof value !== \"object\" || value === null)) {\n continue;\n }\n\n didChange = true;\n }\n }\n\n if (!didChange) {\n return obj;\n }\n\n return Object.assign(Object.assign({}, obj), updates);\n};\nconst isPrimitive = val => {\n const type = typeof val;\n return val == null || type !== \"object\" && type !== \"function\";\n};\nconst getFrame = () => {\n try {\n return window.self === window.top ? \"top\" : \"iframe\";\n } catch (error) {\n return \"iframe\";\n }\n};\nconst isRunningInIframe = () => getFrame() === \"iframe\";\nconst isPromiseLike = value => {\n return !!value && typeof value === \"object\" && \"then\" in value && \"catch\" in value && \"finally\" in value;\n};\nconst queryFocusableElements = container => {\n const focusableElements = container === null || container === void 0 ? void 0 : container.querySelectorAll(\"button, a, input, select, textarea, div[tabindex], label[tabindex]\");\n return focusableElements ? Array.from(focusableElements).filter(element => element.tabIndex > -1 && !element.disabled) : [];\n};\n/** use as a fallback after identity check (for perf reasons) */\n\nconst _defaultIsShallowComparatorFallback = (a, b) => {\n // consider two empty arrays equal\n if (Array.isArray(a) && Array.isArray(b) && a.length === 0 && b.length === 0) {\n return true;\n }\n\n return a === b;\n};\n/**\r\n * Returns whether object/array is shallow equal.\r\n * Considers empty object/arrays as equal (whether top-level or second-level).\r\n */\n\n\nconst isShallowEqual = (objA, objB, comparators, debug = false) => {\n const aKeys = Object.keys(objA);\n const bKeys = Object.keys(objB);\n\n if (aKeys.length !== bKeys.length) {\n if (debug) {\n console.warn(`%cisShallowEqual: objects don't have same properties ->`, \"color: #8B4000\", objA, objB);\n }\n\n return false;\n }\n\n if (comparators && Array.isArray(comparators)) {\n for (const key of comparators) {\n const ret = objA[key] === objB[key] || _defaultIsShallowComparatorFallback(objA[key], objB[key]);\n\n if (!ret) {\n if (debug) {\n console.warn(`%cisShallowEqual: ${key} not equal ->`, \"color: #8B4000\", objA[key], objB[key]);\n }\n\n return false;\n }\n }\n\n return true;\n }\n\n return aKeys.every(key => {\n const comparator = comparators === null || comparators === void 0 ? void 0 : comparators[key];\n const ret = comparator ? comparator(objA[key], objB[key]) : objA[key] === objB[key] || _defaultIsShallowComparatorFallback(objA[key], objB[key]);\n\n if (!ret && debug) {\n console.warn(`%cisShallowEqual: ${key} not equal ->`, \"color: #8B4000\", objA[key], objB[key]);\n }\n\n return ret;\n });\n}; // taken from Radix UI\n// https://github.com/radix-ui/primitives/blob/main/packages/core/primitive/src/primitive.tsx\n\nconst composeEventHandlers = (originalEventHandler, ourEventHandler, {\n checkForDefaultPrevented = true\n} = {}) => {\n return function handleEvent(event) {\n originalEventHandler === null || originalEventHandler === void 0 ? void 0 : originalEventHandler(event);\n\n if (!checkForDefaultPrevented || !(event === null || event === void 0 ? void 0 : event.defaultPrevented)) {\n return ourEventHandler === null || ourEventHandler === void 0 ? void 0 : ourEventHandler(event);\n }\n };\n};\n/**\r\n * supply `null` as message if non-never value is valid, you just need to\r\n * typecheck against it\r\n */\n\nconst assertNever = (value, message, softAssert) => {\n if (!message) {\n return value;\n }\n\n if (softAssert) {\n console.error(message);\n return value;\n }\n\n throw new Error(message);\n};\nfunction invariant(condition, message) {\n if (!condition) {\n throw new Error(message);\n }\n}\n/**\r\n * Memoizes on values of `opts` object (strict equality).\r\n */\n\nconst memoize = func => {\n let lastArgs;\n let lastResult;\n\n const ret = function (opts) {\n const currentArgs = Object.entries(opts);\n\n if (lastArgs) {\n let argsAreEqual = true;\n\n for (const [key, value] of currentArgs) {\n if (lastArgs.get(key) !== value) {\n argsAreEqual = false;\n break;\n }\n }\n\n if (argsAreEqual) {\n return lastResult;\n }\n }\n\n const result = func(opts);\n lastArgs = new Map(currentArgs);\n lastResult = result;\n return result;\n };\n\n ret.clear = () => {\n lastArgs = undefined;\n lastResult = undefined;\n };\n\n return ret;\n};\n/** Checks if value is inside given collection. Useful for type-safety. */\n\nconst isMemberOf = (\n/** Set/Map/Array/Object */\ncollection,\n/** value to look for */\nvalue) => {\n return collection instanceof Set || collection instanceof Map ? collection.has(value) : \"includes\" in collection ? collection.includes(value) : collection.hasOwnProperty(value);\n};\nconst cloneJSON = obj => JSON.parse(JSON.stringify(obj));\nconst updateStable = (prevValue, nextValue) => {\n if (isShallowEqual(prevValue, nextValue)) {\n return prevValue;\n }\n\n return nextValue;\n}; // implem\n\nfunction addEventListener(\n/**\r\n * allows for falsy values so you don't have to type check when adding\r\n * event listeners to optional elements\r\n */\ntarget, type, listener, options) {\n var _a;\n\n if (!target) {\n return () => {};\n }\n\n (_a = target === null || target === void 0 ? void 0 : target.addEventListener) === null || _a === void 0 ? void 0 : _a.call(target, type, listener, options);\n return () => {\n var _a;\n\n (_a = target === null || target === void 0 ? void 0 : target.removeEventListener) === null || _a === void 0 ? void 0 : _a.call(target, type, listener, options);\n };\n}\nfunction getSvgPathFromStroke(points, closed = true) {\n const len = points.length;\n\n if (len < 4) {\n return ``;\n }\n\n let a = points[0];\n let b = points[1];\n const c = points[2];\n let result = `M${a[0].toFixed(2)},${a[1].toFixed(2)} Q${b[0].toFixed(2)},${b[1].toFixed(2)} ${(0,_math__WEBPACK_IMPORTED_MODULE_1__.average)(b[0], c[0]).toFixed(2)},${(0,_math__WEBPACK_IMPORTED_MODULE_1__.average)(b[1], c[1]).toFixed(2)} T`;\n\n for (let i = 2, max = len - 1; i < max; i++) {\n a = points[i];\n b = points[i + 1];\n result += `${(0,_math__WEBPACK_IMPORTED_MODULE_1__.average)(a[0], b[0]).toFixed(2)},${(0,_math__WEBPACK_IMPORTED_MODULE_1__.average)(a[1], b[1]).toFixed(2)} `;\n }\n\n if (closed) {\n result += \"Z\";\n }\n\n return result;\n}\nconst normalizeEOL = str => {\n return str.replace(/\\r?\\n|\\r/g, \"\\n\");\n};\n/**\r\n * Makes type into a branded type, ensuring that value is assignable to\r\n * the base ubranded type. Optionally you can explicitly supply current value\r\n * type to combine both (useful for composite branded types. Make sure you\r\n * compose branded types which are not composite themselves.)\r\n */\n\nconst toBrandedType = value => {\n return value;\n}; // -----------------------------------------------------------------------------\n// Promise.try, adapted from https://github.com/sindresorhus/p-try\n\nconst promiseTry = async (fn, ...args) => {\n return new Promise(resolve => {\n resolve(fn(...args));\n });\n};\nconst isAnyTrue = (...args) => Math.max(...args.map(arg => arg ? 1 : 0)) > 0;\nconst safelyParseJSON = json => {\n try {\n return JSON.parse(json);\n } catch (_a) {\n return null;\n }\n};\nclass PromisePool {\n constructor(source, concurrency) {\n this.entries = {};\n this.pool = new (es6_promise_pool__WEBPACK_IMPORTED_MODULE_0___default())(source, concurrency);\n }\n\n all() {\n const listener = event => {\n if (event.data.result) {\n // by default pool does not return the results, so we are gathering them manually\n // with the correct call order (represented by the index in the tuple)\n const [index, value] = event.data.result;\n this.entries[index] = value;\n }\n };\n\n this.pool.addEventListener(\"fulfilled\", listener);\n return this.pool.start().then(() => {\n setTimeout(() => {\n this.pool.removeEventListener(\"fulfilled\", listener);\n });\n return Object.values(this.entries);\n });\n }\n\n}\n\n//# sourceURL=webpack://ExcalidrawLib/./utils.ts?");
|
|
3985
|
+
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"PromisePool\": () => (/* binding */ PromisePool),\n/* harmony export */ \"addEventListener\": () => (/* binding */ addEventListener),\n/* harmony export */ \"allowFullScreen\": () => (/* binding */ allowFullScreen),\n/* harmony export */ \"arrayToList\": () => (/* binding */ arrayToList),\n/* harmony export */ \"arrayToMap\": () => (/* binding */ arrayToMap),\n/* harmony export */ \"arrayToMapWithIndex\": () => (/* binding */ arrayToMapWithIndex),\n/* harmony export */ \"arrayToObject\": () => (/* binding */ arrayToObject),\n/* harmony export */ \"assertNever\": () => (/* binding */ assertNever),\n/* harmony export */ \"bytesToHexString\": () => (/* binding */ bytesToHexString),\n/* harmony export */ \"capitalizeString\": () => (/* binding */ capitalizeString),\n/* harmony export */ \"chunk\": () => (/* binding */ chunk),\n/* harmony export */ \"cloneJSON\": () => (/* binding */ cloneJSON),\n/* harmony export */ \"composeEventHandlers\": () => (/* binding */ composeEventHandlers),\n/* harmony export */ \"debounce\": () => (/* binding */ debounce),\n/* harmony export */ \"distance\": () => (/* binding */ distance),\n/* harmony export */ \"easeOut\": () => (/* binding */ easeOut),\n/* harmony export */ \"easeToValuesRAF\": () => (/* binding */ easeToValuesRAF),\n/* harmony export */ \"exitFullScreen\": () => (/* binding */ exitFullScreen),\n/* harmony export */ \"findIndex\": () => (/* binding */ findIndex),\n/* harmony export */ \"findLastIndex\": () => (/* binding */ findLastIndex),\n/* harmony export */ \"focusNearestParent\": () => (/* binding */ focusNearestParent),\n/* harmony export */ \"getDateTime\": () => (/* binding */ getDateTime),\n/* harmony export */ \"getFontFamilyString\": () => (/* binding */ getFontFamilyString),\n/* harmony export */ \"getFontString\": () => (/* binding */ getFontString),\n/* harmony export */ \"getFrame\": () => (/* binding */ getFrame),\n/* harmony export */ \"getGlobalCSSVariable\": () => (/* binding */ getGlobalCSSVariable),\n/* harmony export */ \"getNearestScrollableContainer\": () => (/* binding */ getNearestScrollableContainer),\n/* harmony export */ \"getShortcutKey\": () => (/* binding */ getShortcutKey),\n/* harmony export */ \"getSvgPathFromStroke\": () => (/* binding */ getSvgPathFromStroke),\n/* harmony export */ \"getUpdatedTimestamp\": () => (/* binding */ getUpdatedTimestamp),\n/* harmony export */ \"getVersion\": () => (/* binding */ getVersion),\n/* harmony export */ \"invariant\": () => (/* binding */ invariant),\n/* harmony export */ \"isAnyTrue\": () => (/* binding */ isAnyTrue),\n/* harmony export */ \"isDevEnv\": () => (/* binding */ isDevEnv),\n/* harmony export */ \"isFullScreen\": () => (/* binding */ isFullScreen),\n/* harmony export */ \"isInputLike\": () => (/* binding */ isInputLike),\n/* harmony export */ \"isInteractive\": () => (/* binding */ isInteractive),\n/* harmony export */ \"isMemberOf\": () => (/* binding */ isMemberOf),\n/* harmony export */ \"isPrimitive\": () => (/* binding */ isPrimitive),\n/* harmony export */ \"isPromiseLike\": () => (/* binding */ isPromiseLike),\n/* harmony export */ \"isRTL\": () => (/* binding */ isRTL),\n/* harmony export */ \"isRunningInIframe\": () => (/* binding */ isRunningInIframe),\n/* harmony export */ \"isServerEnv\": () => (/* binding */ isServerEnv),\n/* harmony export */ \"isShallowEqual\": () => (/* binding */ isShallowEqual),\n/* harmony export */ \"isTestEnv\": () => (/* binding */ isTestEnv),\n/* harmony export */ \"isToolIcon\": () => (/* binding */ isToolIcon),\n/* harmony export */ \"isTransparent\": () => (/* binding */ isTransparent),\n/* harmony export */ \"isWritableElement\": () => (/* binding */ isWritableElement),\n/* harmony export */ \"memoize\": () => (/* binding */ memoize),\n/* harmony export */ \"muteFSAbortError\": () => (/* binding */ muteFSAbortError),\n/* harmony export */ \"nFormatter\": () => (/* binding */ nFormatter),\n/* harmony export */ \"normalizeEOL\": () => (/* binding */ normalizeEOL),\n/* harmony export */ \"preventUnload\": () => (/* binding */ preventUnload),\n/* harmony export */ \"promiseTry\": () => (/* binding */ promiseTry),\n/* harmony export */ \"queryFocusableElements\": () => (/* binding */ queryFocusableElements),\n/* harmony export */ \"removeSelection\": () => (/* binding */ removeSelection),\n/* harmony export */ \"resolvablePromise\": () => (/* binding */ resolvablePromise),\n/* harmony export */ \"safelyParseJSON\": () => (/* binding */ safelyParseJSON),\n/* harmony export */ \"sceneCoordsToViewportCoords\": () => (/* binding */ sceneCoordsToViewportCoords),\n/* harmony export */ \"selectNode\": () => (/* binding */ selectNode),\n/* harmony export */ \"setDateTimeForTests\": () => (/* binding */ setDateTimeForTests),\n/* harmony export */ \"supportsEmoji\": () => (/* binding */ supportsEmoji),\n/* harmony export */ \"throttleRAF\": () => (/* binding */ throttleRAF),\n/* harmony export */ \"toBrandedType\": () => (/* binding */ toBrandedType),\n/* harmony export */ \"tupleToCoors\": () => (/* binding */ tupleToCoors),\n/* harmony export */ \"updateActiveTool\": () => (/* binding */ updateActiveTool),\n/* harmony export */ \"updateObject\": () => (/* binding */ updateObject),\n/* harmony export */ \"updateStable\": () => (/* binding */ updateStable),\n/* harmony export */ \"viewportCoordsToSceneCoords\": () => (/* binding */ viewportCoordsToSceneCoords),\n/* harmony export */ \"wrapEvent\": () => (/* binding */ wrapEvent)\n/* harmony export */ });\n/* harmony import */ var es6_promise_pool__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! es6-promise-pool */ \"../../node_modules/es6-promise-pool/es6-promise-pool.js\");\n/* harmony import */ var es6_promise_pool__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(es6_promise_pool__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var _math__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../math */ \"../math/index.ts\");\n/* harmony import */ var _colors__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./colors */ \"./colors.ts\");\n/* harmony import */ var _constants__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./constants */ \"./constants.ts\");\n\n\n\n\nlet mockDateTime = null;\nconst setDateTimeForTests = dateTime => {\n mockDateTime = dateTime;\n};\nconst getDateTime = () => {\n if (mockDateTime) {\n return mockDateTime;\n }\n\n const date = new Date();\n const year = date.getFullYear();\n const month = `${date.getMonth() + 1}`.padStart(2, \"0\");\n const day = `${date.getDate()}`.padStart(2, \"0\");\n const hr = `${date.getHours()}`.padStart(2, \"0\");\n const min = `${date.getMinutes()}`.padStart(2, \"0\");\n return `${year}-${month}-${day}-${hr}${min}`;\n};\nconst capitalizeString = str => str.charAt(0).toUpperCase() + str.slice(1);\nconst isToolIcon = target => target instanceof HTMLElement && target.className.includes(\"ToolIcon\");\nconst isInputLike = target => target instanceof HTMLElement && target.dataset.type === \"wysiwyg\" || target instanceof HTMLBRElement || // newline in wysiwyg\ntarget instanceof HTMLInputElement || target instanceof HTMLTextAreaElement || target instanceof HTMLSelectElement;\nconst isInteractive = target => {\n return isInputLike(target) || target instanceof Element && !!target.closest(\"label, button\");\n};\nconst isWritableElement = target => target instanceof HTMLElement && target.dataset.type === \"wysiwyg\" || target instanceof HTMLBRElement || // newline in wysiwyg\ntarget instanceof HTMLTextAreaElement || target instanceof HTMLInputElement && (target.type === \"text\" || target.type === \"number\" || target.type === \"password\");\nconst getFontFamilyString = ({\n fontFamily\n}) => {\n for (const [fontFamilyString, id] of Object.entries(_constants__WEBPACK_IMPORTED_MODULE_3__.FONT_FAMILY)) {\n if (id === fontFamily) {\n // TODO: we should fallback first to generic family names first\n return `${fontFamilyString}${(0,_constants__WEBPACK_IMPORTED_MODULE_3__.getFontFamilyFallbacks)(id).map(x => `, ${x}`).join(\"\")}`;\n }\n }\n\n return _constants__WEBPACK_IMPORTED_MODULE_3__.WINDOWS_EMOJI_FALLBACK_FONT;\n};\n/** returns fontSize+fontFamily string for assignment to DOM elements */\n\nconst getFontString = ({\n fontSize,\n fontFamily\n}) => {\n return `${fontSize}px ${getFontFamilyString({\n fontFamily\n })}`;\n};\nconst debounce = (fn, timeout) => {\n let handle = 0;\n let lastArgs = null;\n\n const ret = (...args) => {\n lastArgs = args;\n clearTimeout(handle);\n handle = window.setTimeout(() => {\n lastArgs = null;\n fn(...args);\n }, timeout);\n };\n\n ret.flush = () => {\n clearTimeout(handle);\n\n if (lastArgs) {\n const _lastArgs = lastArgs;\n lastArgs = null;\n fn(..._lastArgs);\n }\n };\n\n ret.cancel = () => {\n lastArgs = null;\n clearTimeout(handle);\n };\n\n return ret;\n}; // throttle callback to execute once per animation frame\n\nconst throttleRAF = (fn, opts) => {\n let timerId = null;\n let lastArgs = null;\n let lastArgsTrailing = null;\n\n const scheduleFunc = args => {\n timerId = window.requestAnimationFrame(() => {\n timerId = null;\n fn(...args);\n lastArgs = null;\n\n if (lastArgsTrailing) {\n lastArgs = lastArgsTrailing;\n lastArgsTrailing = null;\n scheduleFunc(lastArgs);\n }\n });\n };\n\n const ret = (...args) => {\n if (false) {}\n\n lastArgs = args;\n\n if (timerId === null) {\n scheduleFunc(lastArgs);\n } else if (opts === null || opts === void 0 ? void 0 : opts.trailing) {\n lastArgsTrailing = args;\n }\n };\n\n ret.flush = () => {\n if (timerId !== null) {\n cancelAnimationFrame(timerId);\n timerId = null;\n }\n\n if (lastArgs) {\n fn(...(lastArgsTrailing || lastArgs));\n lastArgs = lastArgsTrailing = null;\n }\n };\n\n ret.cancel = () => {\n lastArgs = lastArgsTrailing = null;\n\n if (timerId !== null) {\n cancelAnimationFrame(timerId);\n timerId = null;\n }\n };\n\n return ret;\n};\n/**\r\n * Exponential ease-out method\r\n *\r\n * @param {number} k - The value to be tweened.\r\n * @returns {number} The tweened value.\r\n */\n\nconst easeOut = k => {\n return 1 - Math.pow(1 - k, 4);\n};\n\nconst easeOutInterpolate = (from, to, progress) => {\n return (to - from) * easeOut(progress) + from;\n};\n/**\r\n * Animates values from `fromValues` to `toValues` using the requestAnimationFrame API.\r\n * Executes the `onStep` callback on each step with the interpolated values.\r\n * Returns a function that can be called to cancel the animation.\r\n *\r\n * @example\r\n * // Example usage:\r\n * const fromValues = { x: 0, y: 0 };\r\n * const toValues = { x: 100, y: 200 };\r\n * const onStep = ({x, y}) => {\r\n * setState(x, y)\r\n * };\r\n * const onCancel = () => {\r\n * console.log(\"Animation canceled\");\r\n * };\r\n *\r\n * const cancelAnimation = easeToValuesRAF({\r\n * fromValues,\r\n * toValues,\r\n * onStep,\r\n * onCancel,\r\n * });\r\n *\r\n * // To cancel the animation:\r\n * cancelAnimation();\r\n */\n\n\nconst easeToValuesRAF = ({\n fromValues,\n toValues,\n onStep,\n duration = 250,\n interpolateValue,\n onStart,\n onEnd,\n onCancel\n}) => {\n let canceled = false;\n let frameId = 0;\n let startTime;\n\n function step(timestamp) {\n if (canceled) {\n return;\n }\n\n if (startTime === undefined) {\n startTime = timestamp;\n onStart === null || onStart === void 0 ? void 0 : onStart();\n }\n\n const elapsed = Math.min(timestamp - startTime, duration);\n const factor = easeOut(elapsed / duration);\n const newValues = {};\n Object.keys(fromValues).forEach(key => {\n const _key = key;\n const result = (toValues[_key] - fromValues[_key]) * factor + fromValues[_key];\n newValues[_key] = result;\n });\n onStep(newValues);\n\n if (elapsed < duration) {\n const progress = elapsed / duration;\n const newValues = {};\n Object.keys(fromValues).forEach(key => {\n const _key = key;\n const startValue = fromValues[_key];\n const endValue = toValues[_key];\n let result;\n result = interpolateValue ? interpolateValue(startValue, endValue, progress, _key) : easeOutInterpolate(startValue, endValue, progress);\n\n if (result == null) {\n result = easeOutInterpolate(startValue, endValue, progress);\n }\n\n newValues[_key] = result;\n });\n onStep(newValues);\n frameId = window.requestAnimationFrame(step);\n } else {\n onStep(toValues);\n onEnd === null || onEnd === void 0 ? void 0 : onEnd();\n }\n }\n\n frameId = window.requestAnimationFrame(step);\n return () => {\n onCancel === null || onCancel === void 0 ? void 0 : onCancel();\n canceled = true;\n window.cancelAnimationFrame(frameId);\n };\n}; // https://github.com/lodash/lodash/blob/es/chunk.js\n\nconst chunk = (array, size) => {\n if (!array.length || size < 1) {\n return [];\n }\n\n let index = 0;\n let resIndex = 0;\n const result = Array(Math.ceil(array.length / size));\n\n while (index < array.length) {\n result[resIndex++] = array.slice(index, index += size);\n }\n\n return result;\n};\nconst selectNode = node => {\n const selection = window.getSelection();\n\n if (selection) {\n const range = document.createRange();\n range.selectNodeContents(node);\n selection.removeAllRanges();\n selection.addRange(range);\n }\n};\nconst removeSelection = () => {\n const selection = window.getSelection();\n\n if (selection) {\n selection.removeAllRanges();\n }\n};\nconst distance = (x, y) => Math.abs(x - y);\nconst updateActiveTool = (appState, data) => {\n var _a, _b;\n\n if (data.type === \"custom\") {\n return Object.assign(Object.assign({}, appState.activeTool), {\n type: \"custom\",\n customType: data.customType,\n locked: (_a = data.locked) !== null && _a !== void 0 ? _a : appState.activeTool.locked\n });\n }\n\n return Object.assign(Object.assign({}, appState.activeTool), {\n lastActiveTool: data.lastActiveToolBeforeEraser === undefined ? appState.activeTool.lastActiveTool : data.lastActiveToolBeforeEraser,\n type: data.type,\n customType: null,\n locked: (_b = data.locked) !== null && _b !== void 0 ? _b : appState.activeTool.locked\n });\n};\nconst isFullScreen = () => {\n var _a;\n\n return ((_a = document.fullscreenElement) === null || _a === void 0 ? void 0 : _a.nodeName) === \"HTML\";\n};\nconst allowFullScreen = () => document.documentElement.requestFullscreen();\nconst exitFullScreen = () => document.exitFullscreen();\nconst getShortcutKey = shortcut => {\n shortcut = shortcut.replace(/\\bAlt\\b/i, \"Alt\").replace(/\\bShift\\b/i, \"Shift\").replace(/\\b(Enter|Return)\\b/i, \"Enter\");\n\n if (_constants__WEBPACK_IMPORTED_MODULE_3__.isDarwin) {\n return shortcut.replace(/\\bCtrlOrCmd\\b/gi, \"Cmd\").replace(/\\bAlt\\b/i, \"Option\");\n }\n\n return shortcut.replace(/\\bCtrlOrCmd\\b/gi, \"Ctrl\");\n};\nconst viewportCoordsToSceneCoords = ({\n clientX,\n clientY\n}, {\n zoom,\n offsetLeft,\n offsetTop,\n scrollX,\n scrollY\n}) => {\n const x = (clientX - offsetLeft) / zoom.value - scrollX;\n const y = (clientY - offsetTop) / zoom.value - scrollY;\n return {\n x,\n y\n };\n};\nconst sceneCoordsToViewportCoords = ({\n sceneX,\n sceneY\n}, {\n zoom,\n offsetLeft,\n offsetTop,\n scrollX,\n scrollY\n}) => {\n const x = (sceneX + scrollX) * zoom.value + offsetLeft;\n const y = (sceneY + scrollY) * zoom.value + offsetTop;\n return {\n x,\n y\n };\n};\nconst getGlobalCSSVariable = name => getComputedStyle(document.documentElement).getPropertyValue(`--${name}`);\nconst RS_LTR_CHARS = \"A-Za-z\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02B8\\u0300-\\u0590\\u0800-\\u1FFF\" + \"\\u2C00-\\uFB1C\\uFDFE-\\uFE6F\\uFEFD-\\uFFFF\";\nconst RS_RTL_CHARS = \"\\u0591-\\u07FF\\uFB1D-\\uFDFD\\uFE70-\\uFEFC\";\nconst RE_RTL_CHECK = new RegExp(`^[^${RS_LTR_CHARS}]*[${RS_RTL_CHARS}]`);\n/**\r\n * Checks whether first directional character is RTL. Meaning whether it starts\r\n * with RTL characters, or indeterminate (numbers etc.) characters followed by\r\n * RTL.\r\n * See https://github.com/excalidraw/excalidraw/pull/1722#discussion_r436340171\r\n */\n\nconst isRTL = text => RE_RTL_CHECK.test(text);\nconst tupleToCoors = xyTuple => {\n const [x, y] = xyTuple;\n return {\n x,\n y\n };\n};\n/** use as a rejectionHandler to mute filesystem Abort errors */\n\nconst muteFSAbortError = error => {\n if ((error === null || error === void 0 ? void 0 : error.name) === \"AbortError\") {\n console.warn(error);\n return;\n }\n\n throw error;\n};\nconst findIndex = (array, cb, fromIndex = 0) => {\n if (fromIndex < 0) {\n fromIndex = array.length + fromIndex;\n }\n\n fromIndex = Math.min(array.length, Math.max(fromIndex, 0));\n let index = fromIndex - 1;\n\n while (++index < array.length) {\n if (cb(array[index], index, array)) {\n return index;\n }\n }\n\n return -1;\n};\nconst findLastIndex = (array, cb, fromIndex = array.length - 1) => {\n if (fromIndex < 0) {\n fromIndex = array.length + fromIndex;\n }\n\n fromIndex = Math.min(array.length - 1, Math.max(fromIndex, 0));\n let index = fromIndex + 1;\n\n while (--index > -1) {\n if (cb(array[index], index, array)) {\n return index;\n }\n }\n\n return -1;\n};\nconst isTransparent = color => {\n const isRGBTransparent = color.length === 5 && color.substr(4, 1) === \"0\";\n const isRRGGBBTransparent = color.length === 9 && color.substr(7, 2) === \"00\";\n return isRGBTransparent || isRRGGBBTransparent || color === _colors__WEBPACK_IMPORTED_MODULE_2__.COLOR_PALETTE.transparent;\n};\nconst resolvablePromise = () => {\n let resolve;\n let reject;\n const promise = new Promise((_resolve, _reject) => {\n resolve = _resolve;\n reject = _reject;\n });\n promise.resolve = resolve;\n promise.reject = reject;\n return promise;\n}; //https://stackoverflow.com/a/9462382/8418\n\nconst nFormatter = (num, digits) => {\n const si = [{\n value: 1,\n symbol: \"b\"\n }, {\n value: 1e3,\n symbol: \"k\"\n }, {\n value: 1e6,\n symbol: \"M\"\n }, {\n value: 1e9,\n symbol: \"G\"\n }];\n const rx = /\\.0+$|(\\.[0-9]*[1-9])0+$/;\n let index;\n\n for (index = si.length - 1; index > 0; index--) {\n if (num >= si[index].value) {\n break;\n }\n }\n\n return (num / si[index].value).toFixed(digits).replace(rx, \"$1\") + si[index].symbol;\n};\nconst getVersion = () => {\n var _a;\n\n return ((_a = document.querySelector('meta[name=\"version\"]')) === null || _a === void 0 ? void 0 : _a.content) || _constants__WEBPACK_IMPORTED_MODULE_3__.DEFAULT_VERSION;\n}; // Adapted from https://github.com/Modernizr/Modernizr/blob/master/feature-detects/emoji.js\n\nconst supportsEmoji = () => {\n const canvas = document.createElement(\"canvas\");\n const ctx = canvas.getContext(\"2d\");\n\n if (!ctx) {\n return false;\n }\n\n const offset = 12;\n ctx.fillStyle = \"#f00\";\n ctx.textBaseline = \"top\";\n ctx.font = \"32px Arial\"; // Modernizr used 🐨, but it is sort of supported on Windows 7.\n // Luckily 😀 isn't supported.\n\n ctx.fillText(\"😀\", 0, 0);\n return ctx.getImageData(offset, offset, 1, 1).data[0] !== 0;\n};\nconst getNearestScrollableContainer = element => {\n let parent = element.parentElement;\n\n while (parent) {\n if (parent === document.body) {\n return document;\n }\n\n const {\n overflowY\n } = window.getComputedStyle(parent);\n const hasScrollableContent = parent.scrollHeight > parent.clientHeight;\n\n if (hasScrollableContent && (overflowY === \"auto\" || overflowY === \"scroll\" || overflowY === \"overlay\")) {\n return parent;\n }\n\n parent = parent.parentElement;\n }\n\n return document;\n};\nconst focusNearestParent = element => {\n let parent = element.parentElement;\n\n while (parent) {\n if (parent.tabIndex > -1) {\n parent.focus();\n return;\n }\n\n parent = parent.parentElement;\n }\n};\nconst preventUnload = event => {\n event.preventDefault(); // NOTE: modern browsers no longer allow showing a custom message here\n\n event.returnValue = \"\";\n};\nconst bytesToHexString = bytes => {\n return Array.from(bytes).map(byte => `0${byte.toString(16)}`.slice(-2)).join(\"\");\n};\nconst getUpdatedTimestamp = () => isTestEnv() ? 1 : Date.now();\n/**\r\n * Transforms array of objects containing `id` attribute,\r\n * or array of ids (strings), into a Map, keyd by `id`.\r\n */\n\nconst arrayToMap = items => {\n if (items instanceof Map) {\n return items;\n }\n\n return items.reduce((acc, element) => {\n acc.set(typeof element === \"string\" ? element : element.id, element);\n return acc;\n }, new Map());\n};\nconst arrayToMapWithIndex = elements => elements.reduce((acc, element, idx) => {\n acc.set(element.id, [element, idx]);\n return acc;\n}, new Map());\n/**\r\n * Transform array into an object, use only when array order is irrelevant.\r\n */\n\nconst arrayToObject = (array, groupBy) => array.reduce((acc, value) => {\n acc[groupBy ? groupBy(value) : String(value)] = value;\n return acc;\n}, {});\n/**\r\n * Creates a circular doubly linked list by adding `prev` and `next` props to the existing array nodes.\r\n */\n\nconst arrayToList = array => array.reduce((acc, curr, index) => {\n const node = Object.assign(Object.assign({}, curr), {\n prev: null,\n next: null\n }); // no-op for first item, we don't want circular references on a single item\n\n if (index !== 0) {\n const prevNode = acc[index - 1];\n node.prev = prevNode;\n prevNode.next = node;\n\n if (index === array.length - 1) {\n // make the references circular and connect head & tail\n const firstNode = acc[0];\n node.next = firstNode;\n firstNode.prev = node;\n }\n }\n\n acc.push(node);\n return acc;\n}, []);\nconst isTestEnv = () => \"development\" === \"test\";\nconst isDevEnv = () => \"development\" === \"development\";\nconst isServerEnv = () => {\n var _a;\n\n return typeof process !== \"undefined\" && !!((_a = process === null || process === void 0 ? void 0 : ({\"VITE_APP_BACKEND_V2_GET_URL\":\"https://json-dev.excalidraw.com/api/v2/\",\"VITE_APP_BACKEND_V2_POST_URL\":\"https://json-dev.excalidraw.com/api/v2/post/\",\"VITE_APP_LIBRARY_URL\":\"https://libraries.excalidraw.com\",\"VITE_APP_LIBRARY_BACKEND\":\"https://us-central1-excalidraw-room-persistence.cloudfunctions.net/libraries\",\"VITE_APP_WS_SERVER_URL\":\"http://localhost:3002\",\"VITE_APP_PLUS_LP\":\"https://plus.excalidraw.com\",\"VITE_APP_PLUS_APP\":\"http://localhost:3000\",\"VITE_APP_AI_BACKEND\":\"http://localhost:3015\",\"VITE_APP_FIREBASE_CONFIG\":\"{\\\"apiKey\\\":\\\"AIzaSyCMkxA60XIW8KbqMYL7edC4qT5l4qHX2h8\\\",\\\"authDomain\\\":\\\"excalidraw-oss-dev.firebaseapp.com\\\",\\\"projectId\\\":\\\"excalidraw-oss-dev\\\",\\\"storageBucket\\\":\\\"excalidraw-oss-dev.appspot.com\\\",\\\"messagingSenderId\\\":\\\"664559512677\\\",\\\"appId\\\":\\\"1:664559512677:web:a385181f2928d328a7aa8c\\\"}\",\"VITE_APP_DEV_DISABLE_LIVE_RELOAD\":\"\",\"VITE_APP_ENABLE_TRACKING\":\"true\",\"FAST_REFRESH\":\"false\",\"VITE_APP_PORT\":\"3000\",\"VITE_APP_DEBUG_ENABLE_TEXT_CONTAINER_BOUNDING_BOX\":\"\",\"VITE_APP_COLLAPSE_OVERLAY\":\"true\",\"VITE_APP_ENABLE_ESLINT\":\"true\",\"VITE_APP_ENABLE_PWA\":\"false\",\"VITE_APP_PLUS_EXPORT_PUBLIC_KEY\":\"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAm2g5T+Rub6Kbf1Mf57t0\\n7r2zeHuVg4dla3r5ryXMswtzz6x767octl6oLThn33mQsPSy3GKglFZoCTXJR4ij\\nba8SxB04sL/N8eRrKja7TFWjCVtRwTTfyy771NYYNFVJclkxHyE5qw4m27crHF1y\\nUNWEjuqNMi/lwAErS9fFa2oJlWyT8U7zzv/5kQREkxZI6y9v0AF3qcbsy2731FnD\\ns9ChJvOUW9toIab2gsIdrKW8ZNpu084ZFVKb6LNjvIXI1Se4oMTHeszXzNptzlot\\nkdxxjOoaQMAyfljFSot1F1FlU6MQlag7UnFGvFjRHN1JI5q4K+n3a67DX+TMyRqS\\nHQIDAQAB\",\"VITE_PKG_NAME\":\"@zsviczian/excalidraw\",\"VITE_PKG_VERSION\":\"0.17.6-16\",\"VITE_IS_EXCALIDRAW_NPM_PACKAGE\":true})) === null || _a === void 0 ? void 0 : _a.NODE_ENV);\n};\nconst wrapEvent = (name, nativeEvent) => {\n return new CustomEvent(name, {\n detail: {\n nativeEvent\n },\n cancelable: true\n });\n};\nconst updateObject = (obj, updates) => {\n let didChange = false;\n\n for (const key in updates) {\n const value = updates[key];\n\n if (typeof value !== \"undefined\") {\n if (obj[key] === value && ( // if object, always update because its attrs could have changed\n typeof value !== \"object\" || value === null)) {\n continue;\n }\n\n didChange = true;\n }\n }\n\n if (!didChange) {\n return obj;\n }\n\n return Object.assign(Object.assign({}, obj), updates);\n};\nconst isPrimitive = val => {\n const type = typeof val;\n return val == null || type !== \"object\" && type !== \"function\";\n};\nconst getFrame = () => {\n try {\n return window.self === window.top ? \"top\" : \"iframe\";\n } catch (error) {\n return \"iframe\";\n }\n};\nconst isRunningInIframe = () => getFrame() === \"iframe\";\nconst isPromiseLike = value => {\n return !!value && typeof value === \"object\" && \"then\" in value && \"catch\" in value && \"finally\" in value;\n};\nconst queryFocusableElements = container => {\n const focusableElements = container === null || container === void 0 ? void 0 : container.querySelectorAll(\"button, a, input, select, textarea, div[tabindex], label[tabindex]\");\n return focusableElements ? Array.from(focusableElements).filter(element => element.tabIndex > -1 && !element.disabled) : [];\n};\n/** use as a fallback after identity check (for perf reasons) */\n\nconst _defaultIsShallowComparatorFallback = (a, b) => {\n // consider two empty arrays equal\n if (Array.isArray(a) && Array.isArray(b) && a.length === 0 && b.length === 0) {\n return true;\n }\n\n return a === b;\n};\n/**\r\n * Returns whether object/array is shallow equal.\r\n * Considers empty object/arrays as equal (whether top-level or second-level).\r\n */\n\n\nconst isShallowEqual = (objA, objB, comparators, debug = false) => {\n const aKeys = Object.keys(objA);\n const bKeys = Object.keys(objB);\n\n if (aKeys.length !== bKeys.length) {\n if (debug) {\n console.warn(`%cisShallowEqual: objects don't have same properties ->`, \"color: #8B4000\", objA, objB);\n }\n\n return false;\n }\n\n if (comparators && Array.isArray(comparators)) {\n for (const key of comparators) {\n const ret = objA[key] === objB[key] || _defaultIsShallowComparatorFallback(objA[key], objB[key]);\n\n if (!ret) {\n if (debug) {\n console.warn(`%cisShallowEqual: ${key} not equal ->`, \"color: #8B4000\", objA[key], objB[key]);\n }\n\n return false;\n }\n }\n\n return true;\n }\n\n return aKeys.every(key => {\n const comparator = comparators === null || comparators === void 0 ? void 0 : comparators[key];\n const ret = comparator ? comparator(objA[key], objB[key]) : objA[key] === objB[key] || _defaultIsShallowComparatorFallback(objA[key], objB[key]);\n\n if (!ret && debug) {\n console.warn(`%cisShallowEqual: ${key} not equal ->`, \"color: #8B4000\", objA[key], objB[key]);\n }\n\n return ret;\n });\n}; // taken from Radix UI\n// https://github.com/radix-ui/primitives/blob/main/packages/core/primitive/src/primitive.tsx\n\nconst composeEventHandlers = (originalEventHandler, ourEventHandler, {\n checkForDefaultPrevented = true\n} = {}) => {\n return function handleEvent(event) {\n originalEventHandler === null || originalEventHandler === void 0 ? void 0 : originalEventHandler(event);\n\n if (!checkForDefaultPrevented || !(event === null || event === void 0 ? void 0 : event.defaultPrevented)) {\n return ourEventHandler === null || ourEventHandler === void 0 ? void 0 : ourEventHandler(event);\n }\n };\n};\n/**\r\n * supply `null` as message if non-never value is valid, you just need to\r\n * typecheck against it\r\n */\n\nconst assertNever = (value, message, softAssert) => {\n if (!message) {\n return value;\n }\n\n if (softAssert) {\n console.error(message);\n return value;\n }\n\n throw new Error(message);\n};\nfunction invariant(condition, message) {\n if (!condition) {\n throw new Error(message);\n }\n}\n/**\r\n * Memoizes on values of `opts` object (strict equality).\r\n */\n\nconst memoize = func => {\n let lastArgs;\n let lastResult;\n\n const ret = function (opts) {\n const currentArgs = Object.entries(opts);\n\n if (lastArgs) {\n let argsAreEqual = true;\n\n for (const [key, value] of currentArgs) {\n if (lastArgs.get(key) !== value) {\n argsAreEqual = false;\n break;\n }\n }\n\n if (argsAreEqual) {\n return lastResult;\n }\n }\n\n const result = func(opts);\n lastArgs = new Map(currentArgs);\n lastResult = result;\n return result;\n };\n\n ret.clear = () => {\n lastArgs = undefined;\n lastResult = undefined;\n };\n\n return ret;\n};\n/** Checks if value is inside given collection. Useful for type-safety. */\n\nconst isMemberOf = (\n/** Set/Map/Array/Object */\ncollection,\n/** value to look for */\nvalue) => {\n return collection instanceof Set || collection instanceof Map ? collection.has(value) : \"includes\" in collection ? collection.includes(value) : collection.hasOwnProperty(value);\n};\nconst cloneJSON = obj => JSON.parse(JSON.stringify(obj));\nconst updateStable = (prevValue, nextValue) => {\n if (isShallowEqual(prevValue, nextValue)) {\n return prevValue;\n }\n\n return nextValue;\n}; // implem\n\nfunction addEventListener(\n/**\r\n * allows for falsy values so you don't have to type check when adding\r\n * event listeners to optional elements\r\n */\ntarget, type, listener, options) {\n var _a;\n\n if (!target) {\n return () => {};\n }\n\n (_a = target === null || target === void 0 ? void 0 : target.addEventListener) === null || _a === void 0 ? void 0 : _a.call(target, type, listener, options);\n return () => {\n var _a;\n\n (_a = target === null || target === void 0 ? void 0 : target.removeEventListener) === null || _a === void 0 ? void 0 : _a.call(target, type, listener, options);\n };\n}\nfunction getSvgPathFromStroke(points, closed = true) {\n const len = points.length;\n\n if (len < 4) {\n return ``;\n }\n\n let a = points[0];\n let b = points[1];\n const c = points[2];\n let result = `M${a[0].toFixed(2)},${a[1].toFixed(2)} Q${b[0].toFixed(2)},${b[1].toFixed(2)} ${(0,_math__WEBPACK_IMPORTED_MODULE_1__.average)(b[0], c[0]).toFixed(2)},${(0,_math__WEBPACK_IMPORTED_MODULE_1__.average)(b[1], c[1]).toFixed(2)} T`;\n\n for (let i = 2, max = len - 1; i < max; i++) {\n a = points[i];\n b = points[i + 1];\n result += `${(0,_math__WEBPACK_IMPORTED_MODULE_1__.average)(a[0], b[0]).toFixed(2)},${(0,_math__WEBPACK_IMPORTED_MODULE_1__.average)(a[1], b[1]).toFixed(2)} `;\n }\n\n if (closed) {\n result += \"Z\";\n }\n\n return result;\n}\nconst normalizeEOL = str => {\n return str.replace(/\\r?\\n|\\r/g, \"\\n\");\n};\n/**\r\n * Makes type into a branded type, ensuring that value is assignable to\r\n * the base ubranded type. Optionally you can explicitly supply current value\r\n * type to combine both (useful for composite branded types. Make sure you\r\n * compose branded types which are not composite themselves.)\r\n */\n\nconst toBrandedType = value => {\n return value;\n}; // -----------------------------------------------------------------------------\n// Promise.try, adapted from https://github.com/sindresorhus/p-try\n\nconst promiseTry = async (fn, ...args) => {\n return new Promise(resolve => {\n resolve(fn(...args));\n });\n};\nconst isAnyTrue = (...args) => Math.max(...args.map(arg => arg ? 1 : 0)) > 0;\nconst safelyParseJSON = json => {\n try {\n return JSON.parse(json);\n } catch (_a) {\n return null;\n }\n};\nclass PromisePool {\n constructor(source, concurrency) {\n this.entries = {};\n this.pool = new (es6_promise_pool__WEBPACK_IMPORTED_MODULE_0___default())(source, concurrency);\n }\n\n all() {\n const listener = event => {\n if (event.data.result) {\n // by default pool does not return the results, so we are gathering them manually\n // with the correct call order (represented by the index in the tuple)\n const [index, value] = event.data.result;\n this.entries[index] = value;\n }\n };\n\n this.pool.addEventListener(\"fulfilled\", listener);\n return this.pool.start().then(() => {\n setTimeout(() => {\n this.pool.removeEventListener(\"fulfilled\", listener);\n });\n return Object.values(this.entries);\n });\n }\n\n}\n\n//# sourceURL=webpack://ExcalidrawLib/./utils.ts?");
|
|
3986
3986
|
|
|
3987
3987
|
/***/ }),
|
|
3988
3988
|
|