From 094f02e1c8acb5227e722644921120cf259f861c Mon Sep 17 00:00:00 2001 From: Abdo Date: Sat, 10 May 2025 13:00:56 +0300 Subject: [PATCH 1/7] Include Closet --- .gitignore | 3 + src/anking_notetypes/__init__.py | 3 + src/anking_notetypes/editor.py | 318 +++ src/anking_notetypes/gui/config_window.py | 126 +- src/anking_notetypes/icons/occlude.png | Bin 0 -> 400 bytes .../notetype_setting_definitions.py | 18 + ..._closet-0.6.1.css => __ankingio-0.6.2.css} | 3 - .../resources/__ankingio-0.6.2.js | 130 + .../resources/__closet-0.6.2.js | 4 - src/anking_notetypes/web/editor.css | 36 + src/anking_notetypes/web/editor.js | 325 +++ ts/.eslintrc.js | 21 + ts/.prettierrc | 6 + ts/.version | 1 + ts/build.mjs | 38 + ts/package.json | 44 + ts/scripts/build.sh | 13 + ts/scripts/compile-parser.sh | 61 + ts/scripts/compile-style.js | 40 + ts/scripts/dev.sh | 19 + ts/scripts/run.sh | 12 + ts/src/browser/index.ts | 7 + ts/src/browser/menu.ts | 146 + ts/src/browser/menuConstruction.ts | 56 + ts/src/browser/moveResize.ts | 137 + ts/src/browser/occlusionEditor.ts | 346 +++ ts/src/browser/rect.ts | 201 ++ ts/src/browser/scaleZoom.ts | 28 + ts/src/browser/svgClasses.ts | 500 ++++ ts/src/browser/utils.ts | 124 + ts/src/filterManager.ts | 121 + ts/src/filterManager/deferred.ts | 103 + ts/src/filterManager/filters.ts | 100 + ts/src/filterManager/index.ts | 209 ++ ts/src/filterManager/priorityQueue.ts | 95 + ts/src/filterManager/registrar.ts | 29 + ts/src/filterManager/storage.ts | 80 + ts/src/flashcard/cloze.ts | 100 + ts/src/flashcard/deciders.ts | 100 + ts/src/flashcard/flashcardTemplate.ts | 142 + ts/src/flashcard/inactiveAdapter.ts | 119 + ts/src/flashcard/index.ts | 31 + ts/src/flashcard/multipleChoice.ts | 151 + ts/src/flashcard/shuffleQuestion.ts | 146 + ts/src/flashcard/spec.ts | 32 + ts/src/generator.ts | 49 + ts/src/index.ts | 12 + ts/src/patterns.ts | 37 + ts/src/recipes/debug.ts | 38 + ts/src/recipes/delim.ts | 41 + ts/src/recipes/generating.ts | 63 + ts/src/recipes/index.ts | 10 + ts/src/recipes/meta.ts | 62 + ts/src/recipes/ordering.ts | 114 + ts/src/recipes/preferenceStore/boolStore.ts | 52 + ts/src/recipes/preferenceStore/index.ts | 11 + ts/src/recipes/preferenceStore/numberStore.ts | 34 + .../recipes/preferenceStore/storeTemplate.ts | 60 + ts/src/recipes/sharedStore/index.ts | 7 + ts/src/recipes/sharedStore/listStore.ts | 23 + ts/src/recipes/sharedStore/pickers.ts | 128 + ts/src/recipes/sharedStore/setList.ts | 67 + ts/src/recipes/sharedStore/storeTemplate.ts | 36 + ts/src/recipes/shuffling.ts | 46 + ts/src/recipes/simple.ts | 43 + ts/src/sequencers.ts | 210 ++ ts/src/sortInStrategies.ts | 60 + ts/src/styleList.ts | 37 + ts/src/stylizer.ts | 62 + ts/src/template/anki/delay.ts | 153 + ts/src/template/anki/index.ts | 3 + ts/src/template/anki/initialize.ts | 120 + ts/src/template/anki/persistence.ts | 237 ++ ts/src/template/anki/qaNodes.ts | 43 + ts/src/template/anki/utils.ts | 23 + ts/src/template/browser/childNodes.ts | 282 ++ ts/src/template/browser/index.ts | 115 + ts/src/template/browser/intersplice.ts | 42 + ts/src/template/delimiters.ts | 11 + ts/src/template/index.ts | 14 + ts/src/template/nodes.ts | 345 +++ ts/src/template/optics/circumfix.ts | 17 + ts/src/template/optics/consumers.ts | 5 + ts/src/template/optics/index.ts | 11 + ts/src/template/optics/mapped.ts | 12 + ts/src/template/optics/profunctors.ts | 93 + ts/src/template/optics/separated.ts | 72 + ts/src/template/optics/stripped.ts | 48 + ts/src/template/optics/templated.ts | 84 + ts/src/template/optics/utils.ts | 15 + ts/src/template/parser/.gitignore | 1 + ts/src/template/parser/grammar.ne | 48 + ts/src/template/parser/index.ts | 90 + ts/src/template/parser/tagBuilder.ts | 74 + ts/src/template/parser/tokenizer.ts | 127 + ts/src/template/tagSelector/.gitignore | 1 + ts/src/template/tagSelector/grammar.ne | 146 + ts/src/template/tagSelector/index.ts | 77 + ts/src/template/tagSelector/tokenizer.ts | 66 + ts/src/template/template.ts | 119 + ts/src/template/types.ts | 40 + ts/src/template/utils.ts | 18 + ts/src/types.ts | 117 + ts/src/utils.ts | 49 + ts/src/wrappers/collection.ts | 25 + ts/src/wrappers/index.ts | 8 + ts/src/wrappers/product.ts | 55 + ts/src/wrappers/sum.ts | 112 + ts/src/wrappers/wrappers.ts | 99 + ts/style/README.adoc | 28 + ts/style/_cloze.scss | 26 + ts/style/_multiple-choice.scss | 31 + ts/style/_rect.scss | 28 + ts/style/_shuffle-question.scss | 23 + ts/style/_shuffle.scss | 7 + ts/style/_utils.scss | 25 + ts/style/base.scss | 32 + ts/style/editor.scss | 17 + ts/test/README.md | 8 + ts/test/browser/.parentlock | 0 ts/test/browser/dist/README.md | 3 + ts/test/browser/index.html | 59 + ts/test/browser/specs/childnode.spec.js | 203 ++ ts/test/browser/specs/intersplice.spec.js | 35 + ts/test/src/template/tagSelector.spec.ts | 35 + ts/tsconfig.json | 18 + ts/yarn.lock | 2526 +++++++++++++++++ 127 files changed, 11668 insertions(+), 79 deletions(-) create mode 100644 src/anking_notetypes/editor.py create mode 100644 src/anking_notetypes/icons/occlude.png rename src/anking_notetypes/resources/{__closet-0.6.1.css => __ankingio-0.6.2.css} (96%) create mode 100644 src/anking_notetypes/resources/__ankingio-0.6.2.js delete mode 100644 src/anking_notetypes/resources/__closet-0.6.2.js create mode 100644 src/anking_notetypes/web/editor.css create mode 100644 src/anking_notetypes/web/editor.js create mode 100644 ts/.eslintrc.js create mode 100644 ts/.prettierrc create mode 100644 ts/.version create mode 100644 ts/build.mjs create mode 100644 ts/package.json create mode 100644 ts/scripts/build.sh create mode 100644 ts/scripts/compile-parser.sh create mode 100644 ts/scripts/compile-style.js create mode 100644 ts/scripts/dev.sh create mode 100644 ts/scripts/run.sh create mode 100644 ts/src/browser/index.ts create mode 100644 ts/src/browser/menu.ts create mode 100644 ts/src/browser/menuConstruction.ts create mode 100644 ts/src/browser/moveResize.ts create mode 100644 ts/src/browser/occlusionEditor.ts create mode 100644 ts/src/browser/rect.ts create mode 100644 ts/src/browser/scaleZoom.ts create mode 100644 ts/src/browser/svgClasses.ts create mode 100644 ts/src/browser/utils.ts create mode 100644 ts/src/filterManager.ts create mode 100644 ts/src/filterManager/deferred.ts create mode 100644 ts/src/filterManager/filters.ts create mode 100644 ts/src/filterManager/index.ts create mode 100644 ts/src/filterManager/priorityQueue.ts create mode 100644 ts/src/filterManager/registrar.ts create mode 100644 ts/src/filterManager/storage.ts create mode 100644 ts/src/flashcard/cloze.ts create mode 100644 ts/src/flashcard/deciders.ts create mode 100644 ts/src/flashcard/flashcardTemplate.ts create mode 100644 ts/src/flashcard/inactiveAdapter.ts create mode 100644 ts/src/flashcard/index.ts create mode 100644 ts/src/flashcard/multipleChoice.ts create mode 100644 ts/src/flashcard/shuffleQuestion.ts create mode 100644 ts/src/flashcard/spec.ts create mode 100644 ts/src/generator.ts create mode 100644 ts/src/index.ts create mode 100644 ts/src/patterns.ts create mode 100644 ts/src/recipes/debug.ts create mode 100644 ts/src/recipes/delim.ts create mode 100644 ts/src/recipes/generating.ts create mode 100644 ts/src/recipes/index.ts create mode 100644 ts/src/recipes/meta.ts create mode 100644 ts/src/recipes/ordering.ts create mode 100644 ts/src/recipes/preferenceStore/boolStore.ts create mode 100644 ts/src/recipes/preferenceStore/index.ts create mode 100644 ts/src/recipes/preferenceStore/numberStore.ts create mode 100644 ts/src/recipes/preferenceStore/storeTemplate.ts create mode 100644 ts/src/recipes/sharedStore/index.ts create mode 100644 ts/src/recipes/sharedStore/listStore.ts create mode 100644 ts/src/recipes/sharedStore/pickers.ts create mode 100644 ts/src/recipes/sharedStore/setList.ts create mode 100644 ts/src/recipes/sharedStore/storeTemplate.ts create mode 100644 ts/src/recipes/shuffling.ts create mode 100644 ts/src/recipes/simple.ts create mode 100644 ts/src/sequencers.ts create mode 100644 ts/src/sortInStrategies.ts create mode 100644 ts/src/styleList.ts create mode 100644 ts/src/stylizer.ts create mode 100644 ts/src/template/anki/delay.ts create mode 100644 ts/src/template/anki/index.ts create mode 100644 ts/src/template/anki/initialize.ts create mode 100644 ts/src/template/anki/persistence.ts create mode 100644 ts/src/template/anki/qaNodes.ts create mode 100644 ts/src/template/anki/utils.ts create mode 100644 ts/src/template/browser/childNodes.ts create mode 100644 ts/src/template/browser/index.ts create mode 100644 ts/src/template/browser/intersplice.ts create mode 100644 ts/src/template/delimiters.ts create mode 100644 ts/src/template/index.ts create mode 100644 ts/src/template/nodes.ts create mode 100644 ts/src/template/optics/circumfix.ts create mode 100644 ts/src/template/optics/consumers.ts create mode 100644 ts/src/template/optics/index.ts create mode 100644 ts/src/template/optics/mapped.ts create mode 100644 ts/src/template/optics/profunctors.ts create mode 100644 ts/src/template/optics/separated.ts create mode 100644 ts/src/template/optics/stripped.ts create mode 100644 ts/src/template/optics/templated.ts create mode 100644 ts/src/template/optics/utils.ts create mode 100644 ts/src/template/parser/.gitignore create mode 100644 ts/src/template/parser/grammar.ne create mode 100644 ts/src/template/parser/index.ts create mode 100644 ts/src/template/parser/tagBuilder.ts create mode 100644 ts/src/template/parser/tokenizer.ts create mode 100644 ts/src/template/tagSelector/.gitignore create mode 100644 ts/src/template/tagSelector/grammar.ne create mode 100644 ts/src/template/tagSelector/index.ts create mode 100644 ts/src/template/tagSelector/tokenizer.ts create mode 100644 ts/src/template/template.ts create mode 100644 ts/src/template/types.ts create mode 100644 ts/src/template/utils.ts create mode 100644 ts/src/types.ts create mode 100644 ts/src/utils.ts create mode 100644 ts/src/wrappers/collection.ts create mode 100644 ts/src/wrappers/index.ts create mode 100644 ts/src/wrappers/product.ts create mode 100644 ts/src/wrappers/sum.ts create mode 100644 ts/src/wrappers/wrappers.ts create mode 100644 ts/style/README.adoc create mode 100644 ts/style/_cloze.scss create mode 100644 ts/style/_multiple-choice.scss create mode 100644 ts/style/_rect.scss create mode 100644 ts/style/_shuffle-question.scss create mode 100644 ts/style/_shuffle.scss create mode 100644 ts/style/_utils.scss create mode 100644 ts/style/base.scss create mode 100644 ts/style/editor.scss create mode 100644 ts/test/README.md create mode 100644 ts/test/browser/.parentlock create mode 100644 ts/test/browser/dist/README.md create mode 100644 ts/test/browser/index.html create mode 100644 ts/test/browser/specs/childnode.spec.js create mode 100644 ts/test/browser/specs/intersplice.spec.js create mode 100644 ts/test/src/template/tagSelector.spec.ts create mode 100644 ts/tsconfig.json create mode 100644 ts/yarn.lock diff --git a/.gitignore b/.gitignore index deb2952..d2dd0ac 100644 --- a/.gitignore +++ b/.gitignore @@ -19,3 +19,6 @@ __pycache__/ *.code-workspace **/.history + +node_modules +yarn-error.log diff --git a/src/anking_notetypes/__init__.py b/src/anking_notetypes/__init__.py index 0e5d450..83f4ef6 100644 --- a/src/anking_notetypes/__init__.py +++ b/src/anking_notetypes/__init__.py @@ -33,6 +33,7 @@ HINT_BUTTONS, anking_notetype_models, ) +from . import editor ADDON_DIR_NAME = str(Path(__file__).parent.name) RESOURCES_PATH = Path(__file__).parent / "resources" @@ -53,6 +54,8 @@ def setup(): editor_will_show_context_menu.append(on_editor_will_show_context_menu) + editor.init_webview() + def on_profile_did_open(): copy_resources_into_media_folder() diff --git a/src/anking_notetypes/editor.py b/src/anking_notetypes/editor.py new file mode 100644 index 0000000..244a2dc --- /dev/null +++ b/src/anking_notetypes/editor.py @@ -0,0 +1,318 @@ +import json +import re +from os.path import dirname, realpath +from pathlib import Path +from typing import Any, List, Optional, Tuple + +from aqt import mw +from aqt.editor import Editor +from aqt.gui_hooks import ( + editor_did_init_buttons, + editor_did_init_shortcuts, + editor_did_load_note, + editor_will_load_note, + editor_will_munge_html, + webview_did_receive_js_message, + webview_will_set_content, +) +from aqt.qt import QKeySequence +from aqt.utils import shortcut, showInfo +from .notetype_setting_definitions import is_io_note_type + + +addon_package = mw.addonManager.addonFromModule(__name__) +mw.addonManager.setWebExports(__name__, r"(web|resources)/.*(css|js)") +occlude_shortcut = "Ctrl+O" +occlusion_behavior = "autopaste" + + +def get_base_top(editor, prefix: str, suffix: str) -> int: + matches = [] + query = re.escape(prefix) + r"(\d+)" + re.escape(suffix) + + for _name, item in editor.note.items(): + matches.extend(re.findall(query, item)) + + values = [0] + values.extend([int(x) for x in matches]) + + return max(values) + + +def get_top_index(editor, prefix: str, suffix: str) -> int: + base_top = get_base_top(editor, prefix, suffix) + + return base_top + 1 if base_top == 0 else base_top + + +def get_incremented_index(editor, prefix: str, suffix: str) -> int: + base_top = get_base_top(editor, prefix, suffix) + + return base_top + 1 + + +def activate_matching_field(indexer): + def get_value(editor, prefix: str, suffix: str) -> int: + current_index = indexer(editor, prefix, suffix) + was_filled = activate_matching_fields(editor, [current_index])[0] + + return current_index if was_filled else 0 + + return get_value + + +def no_index(_editor, _prefix: str, _suffix: str) -> str: + return "" + + +def top_index(type_): + if type_ == "free": + return get_top_index + elif type_ == "flashcard": + return activate_matching_field(get_top_index) + else: # type_ == "none": + return no_index + + +def incremented_index(type_): + if type_ == "free": + return get_incremented_index + elif type_ == "flashcard": + return activate_matching_field(get_incremented_index) + else: # type_ == "none": + return no_index + + +def is_text_empty(editor, text) -> bool: + return editor.mungeHTML(text) == "" + + +def escape_js_text(text: str) -> str: + return text.replace("\\", "\\\\").replace('"', '\\"').replace("'", "\\'") + + +def make_insertion_js(field_index: int, text: str) -> str: + escaped = escape_js_text(text) + + cmd = ( + f"pycmd(`key:{field_index}:${{getNoteId()}}:{escaped}`); " + f"EditorCloset.setFieldHTML({field_index}, `{escaped}`); " + ) + return cmd + + +def insert_into_zero_indexed(editor, text: str) -> None: + for index, (name, _item) in enumerate(editor.note.items()): + match = re.search(r"\d+$", name) + + if not match or int(match[0]) != 0: + continue + + editor.web.eval(f"EditorCloset.insertIntoZeroIndexed(`{text}`, {index}); ") + break + + +def activate_matching_fields(editor, indices: List[int]) -> List[bool]: + founds = [False for index in indices] + + for index, (name, item) in enumerate(editor.note.items()): + match = re.search(r"\d+$", name) + + if not match: + continue + + matched = int(match[0]) + + if matched not in indices: + continue + + founds[indices.index(matched)] = True + + if not is_text_empty(editor, item): + continue + + editor.web.eval(make_insertion_js(index, "active")) + + return founds + + +def include_closet_code(webcontent, context) -> None: + if not isinstance(context, Editor): + return + + webcontent.css.append(f"/_addons/{addon_package}/web/editor.css") + webcontent.js.append(f"/_addons/{addon_package}/web/editor.js") + + +def process_occlusion_index_text(index_text: str) -> List[int]: + return [] if len(index_text) == 0 else [int(text) for text in index_text.split(",")] + + +old_occlusion_indices = [] +occlusion_editor_active = False + + +def add_occlusion_messages( + handled: Tuple[bool, Any], message: str, context +) -> Tuple[bool, Any]: + if isinstance(context, Editor): + editor: Editor = context + global old_occlusion_indices, occlusion_editor_active # pylint: disable=global-statement + if message.startswith("oldOcclusions"): + _, _src, index_text = message.split(":", 2) + old_occlusion_indices = process_occlusion_index_text(index_text) + + return (True, None) + + elif message.startswith("newOcclusions"): + _, _src, index_text = message.split(":", 2) + indices = process_occlusion_index_text(index_text) + + fill_indices = list(set(indices).difference(set(old_occlusion_indices))) + could_fill = activate_matching_fields(editor, fill_indices) + + return (True, could_fill) + + elif message.startswith("occlusionText"): + text = message.split(":", 1)[1] + + if occlusion_behavior == "autopaste": + insert_into_zero_indexed(editor, text) + else: # occlusion_behavior == 'copy': + mw.app.clipboard().setText(text) + + return (True, None) + + elif message == "occlusionEditorActive": + occlusion_editor_active = True + return (True, None) + + elif message == "occlusionEditorInactive": + occlusion_editor_active = False + return (True, None) + + elif message.startswith("closetRefocus"): + refocus(editor) + return (True, None) + + elif message.startswith("closetMultipleImages"): + showInfo("Cannot start occlusion editor if field contains multiple images.") + return (True, None) + + return handled + + +# code 0 field is optional +trailing_number = re.compile(r"[123456789]\d*$") + + +def get_max_code_field(editor) -> int: + number_suffixes = map( + lambda item: trailing_number.search(item[0]), editor.note.items() + ) + indices = [suffix[0] for suffix in number_suffixes if suffix] + sorted_indices = sorted(set([int(index) for index in indices])) + + max = 0 + for index, value in enumerate(sorted_indices): + # probably skipped an index + if value != index + 1: + break + + max = value + + return max + + +def anking_io_js_filename() -> Optional[str]: + path = next((Path(__file__).parent / "resources").glob("__ankingio-*.js"), None) + if not path: + return None + return path.name + + +def toggle_occlusion_mode(editor): + model = editor.note.note_type() + + if not is_io_note_type(model["name"]): + showInfo("Please choose an AnKing image occlusion note type") + editor.web.eval("EditorCloset.setInactive()") + return + + js_path = json.dumps(f"_addons/{addon_package}/resources/{anking_io_js_filename()}") + max_code_fields = get_max_code_field(editor) + + if not mw.focusWidget() is editor: + refocus(editor) + + editor.web.eval(f"EditorCloset.toggleOcclusionMode({js_path}, {max_code_fields})") + + +def add_occlusion_button(buttons, editor): + file_path = dirname(realpath(__file__)) + icon_path = Path(file_path, "icons", "occlude.png") + + shortcut_as_text = shortcut(QKeySequence(occlude_shortcut).toString()) + + occlusion_button = editor._addButton( # pylint: disable=protected-access + str(icon_path.absolute()), + "occlude", + f"Put all fields into occlusion mode ({shortcut_as_text})", + id="closetOcclude", + disables=False, + ) + + editor._links["occlude"] = toggle_occlusion_mode # pylint: disable=protected-access + buttons.insert(-1, occlusion_button) + + +def wrap_as_option(name: str, code: str, tooltip: str, shortcut_text: str) -> str: + return f'' + + +def add_buttons(buttons, editor): + editor.occlusion_editor_active = False + + add_occlusion_button(buttons, editor) + + +def add_occlusion_shortcut(cuts, editor): + cuts.append((occlude_shortcut, lambda: toggle_occlusion_mode(editor), True)) + + +occlusion_container_pattern = re.compile( + # remove trailing
to prevent accumulation + r'
.*?().*?
(
)*' +) + + +def remove_occlusion_code(txt: str, _editor) -> str: + if match := re.search(occlusion_container_pattern, txt): + rawImage = match[1] + + return re.sub(occlusion_container_pattern, rawImage, txt) + + return txt + + +def clear_occlusion_mode(js, _note, _editor): + return f"EditorCloset.clearOcclusionMode().then(() => {{ {js} }}); " + + +def refocus(editor): + editor.web.setFocus() + editor.web.eval("EditorCloset.refocus(); ") + + +def maybe_refocus(editor): + editor.web.eval("EditorCloset.maybeRefocus(); ") + + +def init_webview(): + webview_will_set_content.append(include_closet_code) + webview_did_receive_js_message.append(add_occlusion_messages) + editor_did_init_buttons.append(add_buttons) + editor_did_init_shortcuts.append(add_occlusion_shortcut) + editor_will_load_note.append(clear_occlusion_mode) + editor_did_load_note.append(maybe_refocus) + editor_will_munge_html.append(remove_occlusion_code) diff --git a/src/anking_notetypes/gui/config_window.py b/src/anking_notetypes/gui/config_window.py index 22ca5e0..3327659 100644 --- a/src/anking_notetypes/gui/config_window.py +++ b/src/anking_notetypes/gui/config_window.py @@ -19,6 +19,7 @@ general_settings, general_settings_defaults_dict, setting_configs, + notetype_base_name, ) from ..utils import update_notetype_to_newest_version from .anking_widgets import AnkingIconsLayout, GithubLinkLayout @@ -109,10 +110,10 @@ def open(self): new_idx = notetype_base_names.index("IO-one by one") + 1 notetype_base_names.insert(new_idx, "AnKingOverlapping") - for notetype_base_name in notetype_base_names: + for nt_base_name in notetype_base_names: self.conf.add_config_tab( - lambda window, notetype_base_name=notetype_base_name: self._add_notetype_settings_tab( - notetype_base_name=notetype_base_name, window=window + lambda window, nt_base_name=nt_base_name: self._add_notetype_settings_tab( + nt_base_name=nt_base_name, window=window ) ) @@ -120,14 +121,14 @@ def open(self): def live_update_clayout_model(key: str, _: Any): notetype_base_name_from_setting, setting_name = key.split(".") model = self.clayout.model - notetype_base_name_from_model = _notetype_base_name(model["name"]) + notetype_base_name_from_model = notetype_base_name(model["name"]) if notetype_base_name_from_setting != notetype_base_name_from_model: return nts = NotetypeSetting.from_config(setting_configs[setting_name]) self._safe_update_model_settings( model=model, - notetype_base_name=notetype_base_name_from_model, + nt_base_name=notetype_base_name_from_model, ntss=[nts], ) @@ -160,7 +161,7 @@ def on_save(window: ConfigWindow): window.save_btn.clicked.connect(lambda: on_save(window)) # type: ignore if self.clayout: - self._set_active_tab(_notetype_base_name(self.clayout.model["name"])) + self._set_active_tab(notetype_base_name(self.clayout.model["name"])) # add anking links layouts self.anking_icons_widget = QWidget() @@ -195,24 +196,24 @@ def on_save(window: ConfigWindow): # tabs and NotetypeSettings (ntss) def _add_notetype_settings_tab( self, - notetype_base_name: str, + nt_base_name: str, window: ConfigWindow, index: Optional[int] = None, ): if ( self.clayout - and _notetype_base_name(self.clayout.model["name"]) == notetype_base_name + and notetype_base_name(self.clayout.model["name"]) == nt_base_name ): model = self.clayout.model else: - model = _most_basic_notetype_version(notetype_base_name) + model = _most_basic_notetype_version(nt_base_name) - tab = window.add_tab(notetype_base_name, index=index) + tab = window.add_tab(nt_base_name, index=index) if model: ntss = ntss_for_model(model) ordered_ntss = self._adjust_configurable_field_nts_order( - ntss=ntss, notetype_base_name=notetype_base_name + ntss=ntss, nt_base_name=nt_base_name ) scroll = tab.scroll_layout() self._add_nts_widgets_to_layout(scroll, ordered_ntss, model) @@ -230,9 +231,7 @@ def _add_notetype_settings_tab( tab.button( "Import", - on_click=lambda: self._import_notetype_and_reload_tab( - notetype_base_name - ), + on_click=lambda: self._import_notetype_and_reload_tab(nt_base_name), ) def _add_general_tab(self, window: ConfigWindow): @@ -294,7 +293,7 @@ def _add_nts_widgets_to_layout( for nts, section in nts_to_section.items(): section_to_ntss[section].append(nts) - notetype_base_name = _notetype_base_name(model["name"]) if model else None + nt_base_name = notetype_base_name(model["name"]) if model else None for section_name, section_ntss in sorted(section_to_ntss.items()): section = layout.collapsible_section(section_name) for nts in section_ntss: @@ -302,7 +301,7 @@ def _add_nts_widgets_to_layout( nts.add_widget_to_general_config_layout(section) else: nts.add_widget_to_config_layout( - section, notetype_base_name=notetype_base_name, model=model + section, notetype_base_name=nt_base_name, model=model ) section.space(7) layout.hseparator() @@ -316,12 +315,12 @@ def _add_nts_widgets_to_layout( nts.add_widget_to_general_config_layout(layout) else: nts.add_widget_to_config_layout( - layout, notetype_base_name=notetype_base_name, model=model + layout, notetype_base_name=nt_base_name, model=model ) layout.space(7) def _adjust_configurable_field_nts_order( - self, ntss: List[NotetypeSetting], notetype_base_name: str + self, ntss: List[NotetypeSetting], nt_base_name: str ) -> List[NotetypeSetting]: # adjusts the order of the hint button settings to be the same as # on the original anking card @@ -331,7 +330,7 @@ def _adjust_configurable_field_nts_order( field_ntss = [ nts for nts in ntss if nts.config.get("configurable_field_name", False) ] - ordered_field_names = configurable_fields_for_notetype(notetype_base_name) + ordered_field_names = configurable_fields_for_notetype(nt_base_name) ordered_field_ntss = sorted( field_ntss, key=lambda nts: ( @@ -359,7 +358,7 @@ def _reload_tab(self, tab_name: str) -> None: self._add_general_tab(self.window) else: self._add_notetype_settings_tab( - notetype_base_name=tab_name, window=self.window, index=index + nt_base_name=tab_name, window=self.window, index=index ) self._read_in_settings() @@ -390,15 +389,15 @@ def _reset_notetype_and_reload_ui(self, model: "NotetypeDict"): ): return - notetype_base_name = _notetype_base_name(model["name"]) + nt_base_name = notetype_base_name(model["name"]) for model_version in _note_type_versions(model["name"]): - update_notetype_to_newest_version(model_version, notetype_base_name) + update_notetype_to_newest_version(model_version, nt_base_name) mw.col.models.update_dict(model_version) # type: ignore if self.clayout: self._update_clayout_model(model) - self._reload_tab(notetype_base_name) + self._reload_tab(nt_base_name) tooltip("Notetype was reset", parent=self.window, period=1200) @@ -409,15 +408,15 @@ def _reset_general_settings_and_reload_ui(self): defaultno=True, ): return - for notetype_base_name in anking_notetype_names(): - model = _most_basic_notetype_version(notetype_base_name) + for nt_base_name in anking_notetype_names(): + model = _most_basic_notetype_version(nt_base_name) if not model: continue settings_defaults = general_settings_defaults_dict() for nts in general_ntss(): value = settings_defaults[nts.name()] - self.conf[nts.key(notetype_base_name)] = value + self.conf[nts.key(nt_base_name)] = value self.conf.set(f"general.{nts.name()}", value, on_change_trigger=False) self._apply_setting_changes_for_all_notetypes() @@ -436,13 +435,13 @@ def task(): to_be_updated = models_with_available_updates() for model in to_be_updated: # update the model to the newest version - base_name = _notetype_base_name(model["name"]) + base_name = notetype_base_name(model["name"]) update_notetype_to_newest_version(model, base_name) # restore the values from before the update for the settings that exist in both versions self._safe_update_model_settings( model=model, - notetype_base_name=base_name, + nt_base_name=base_name, ntss=ntss_for_model(model), show_tooltip_on_exception=False, ) @@ -461,10 +460,10 @@ def on_done(updated_models_fut: Future): self._reload_tab("General") updated_base_models = [ - m for m in updated_models if m["name"] == _notetype_base_name(m["name"]) + m for m in updated_models if m["name"] == notetype_base_name(m["name"]) ] for model in sorted(updated_base_models, key=lambda m: m["name"]): - self._reload_tab(_notetype_base_name(model["name"])) + self._reload_tab(notetype_base_name(model["name"])) self._set_active_tab("General") @@ -478,12 +477,12 @@ def on_done(updated_models_fut: Future): immediate=True, ) - def _import_notetype_and_reload_tab(self, notetype_base_name: str) -> None: - self._import_notetype(notetype_base_name) - self._reload_tab(notetype_base_name) + def _import_notetype_and_reload_tab(self, nt_base_name: str) -> None: + self._import_notetype(nt_base_name) + self._reload_tab(nt_base_name) - def _import_notetype(self, notetype_base_name: str) -> None: - model = anking_notetype_model(notetype_base_name) + def _import_notetype(self, nt_base_name: str) -> None: + model = anking_notetype_model(nt_base_name) model["id"] = 0 mw.col.models.add_dict(model) # type: ignore @@ -498,22 +497,22 @@ def _read_in_settings(self): def _read_in_settings_from_notetypes(self): error_msg = "" - for notetype_base_name in anking_notetype_names(): - if self.clayout and notetype_base_name == _notetype_base_name( + for nt_base_name in anking_notetype_names(): + if self.clayout and nt_base_name == notetype_base_name( self.clayout.model["name"] ): # if in live preview mode read in current not confirmed settings model = self.clayout.model else: - model = _most_basic_notetype_version(notetype_base_name) + model = _most_basic_notetype_version(nt_base_name) if not model: continue for nts in ntss_for_model(model): try: - self.conf[nts.key(notetype_base_name)] = nts.setting_value(model) + self.conf[nts.key(nt_base_name)] = nts.setting_value(model) except NotetypeSettingException as e: - error_msg += f"failed parsing {notetype_base_name}:\n{str(e)}\n\n" + error_msg += f"failed parsing {nt_base_name}:\n{str(e)}\n\n" if error_msg: showInfo(error_msg) @@ -525,8 +524,8 @@ def _read_in_general_settings(self): # if all notetypes that have a nts have the same value set the value to it models_by_nts: Dict[NotetypeSetting, "NotetypeDict"] = defaultdict(lambda: []) - for notetype_base_name in anking_notetype_names(): - model = _most_basic_notetype_version(notetype_base_name) + for nt_base_name in anking_notetype_names(): + model = _most_basic_notetype_version(nt_base_name) if not model: continue @@ -547,13 +546,13 @@ def _read_in_general_settings(self): def _safe_update_model_settings( self, model: "NotetypeDict", - notetype_base_name: str, + nt_base_name: str, ntss: List["NotetypeSetting"], show_tooltip_on_exception=True, ) -> bool: """Updates the model with the passed settings. Returns True if successful, False if there was an exception. model: The model to update - notetype_base_name: The base name of the note type. This is used to get the correct setting values from self.conf + nt_base_name: The base name of the note type. This is used to get the correct setting values from self.conf ntss: The settings to update""" parse_exception = None for nts in ntss: @@ -561,7 +560,7 @@ def _safe_update_model_settings( model.update( nts.updated_model( model=model, - notetype_base_name=notetype_base_name, + notetype_base_name=nt_base_name, conf=self.conf, ) ) @@ -578,13 +577,13 @@ def _safe_update_model_settings( return True def _apply_setting_changes_for_all_notetypes(self): - for notetype_base_name in anking_notetype_names(): - for model in _note_type_versions(notetype_base_name): + for nt_base_name in anking_notetype_names(): + for model in _note_type_versions(nt_base_name): if not model: continue ntss = ntss_for_model(model) self._safe_update_model_settings( - model=model, notetype_base_name=notetype_base_name, ntss=ntss + model=model, nt_base_name=nt_base_name, ntss=ntss ) mw.col.models.update_dict(model) @@ -623,32 +622,28 @@ def models_with_available_updates() -> List["NotetypeDict"]: def _new_version_available_for_model(model: "NotetypeDict") -> bool: current_version = note_type_version(model) - base_name = _notetype_base_name(model["name"]) + base_name = notetype_base_name(model["name"]) newest_version = note_type_version(anking_notetype_model(base_name)) return current_version != newest_version -def _note_type_versions(notetype_base_name: str) -> List["NotetypeDict"]: +def _note_type_versions(nt_base_name: str) -> List["NotetypeDict"]: """Returns a list of all notetype versions of the notetype in the collection. Version of a note type are created by the AnkiHub add-on and by copying the base AnKing note types or importing them from different sources.""" models = [ mw.col.models.get(x.id) # type: ignore for x in mw.col.models.all_names_and_ids() - if x.name == notetype_base_name - or re.match( - ANKIHUB_NOTETYPE_RE.format(notetype_base_name=notetype_base_name), x.name - ) - or re.match( - NOTETYPE_COPY_RE.format(notetype_base_name=notetype_base_name), x.name - ) + if x.name == nt_base_name + or re.match(ANKIHUB_NOTETYPE_RE.format(notetype_base_name=nt_base_name), x.name) + or re.match(NOTETYPE_COPY_RE.format(notetype_base_name=nt_base_name), x.name) ] return models -def _most_basic_notetype_version(notetype_base_name: str) -> Optional["NotetypeDict"]: +def _most_basic_notetype_version(nt_base_name: str) -> Optional["NotetypeDict"]: """Returns the most basic version of a note type, that is the version with the shortest name.""" - model_versions = _note_type_versions(notetype_base_name) + model_versions = _note_type_versions(nt_base_name) result = min( model_versions, # sort by length of name and then alphabetically @@ -658,19 +653,6 @@ def _most_basic_notetype_version(notetype_base_name: str) -> Optional["NotetypeD return result -def _notetype_base_name(model_name: str) -> str: - """Returns the base name of a note type, that is if it's a version of a an anking note type - it will return the base name, otherwise it will return the name itself.""" - return next( - ( - notetype_base_name - for notetype_base_name in anking_notetype_names() - if re.match(rf"{notetype_base_name}($| |-)", model_name) - ), - None, - ) - - def _names_of_all_supported_note_types() -> List[str]: """Returns a list of names of note types supported by the add-on that are in the collection, including all versions of the base note types.""" diff --git a/src/anking_notetypes/icons/occlude.png b/src/anking_notetypes/icons/occlude.png new file mode 100644 index 0000000000000000000000000000000000000000..9a531706d4ad287f099aabfbc974b7f7c20d561b GIT binary patch literal 400 zcmeAS@N?(olHy`uVBq!ia0vp^0U*r51|<6gKdl8)Y)RhkE)4%caKYZ?lNlHoT|HeK zLn`LHy<_Ob>?qRkaQPD7`F3HJ4M`XDCS1FtBrZ_Ia*+Fp*fP;KJsw32*1x}4Is|d5 zy?Jq}f2QP*{Y4Ttj-P*Bc9X+%l8UF**3FNbKOX)a?ZRVxCaC%8=QqE;{XSTESoQJJ zyE5`6LFUgdul_H))adfhRUh|cGJj=1$$RE);YY#wvvaRW{@R;&=RR-Up5QlZ&-$*H zwI!K-%heHytz|sBUdB5w-rYD(i zxc~Z@oT6UTnSeKJ|GxQpX_8?Y$UfEmJ|2J7UE61w6;yuG%-VQ+?=v|^puZ<6edAM6 W$mRSr?>;by89ZJ6T-G@yGywoHFQN4S literal 0 HcmV?d00001 diff --git a/src/anking_notetypes/notetype_setting_definitions.py b/src/anking_notetypes/notetype_setting_definitions.py index 685b9e2..7088428 100644 --- a/src/anking_notetypes/notetype_setting_definitions.py +++ b/src/anking_notetypes/notetype_setting_definitions.py @@ -704,6 +704,24 @@ def anking_notetype_models() -> List["NotetypeDict"]: return [anking_notetype_model(name) for name in anking_notetype_names()] +def notetype_base_name(model_name: str) -> str: + """Returns the base name of a note type, that is if it's a version of a an anking note type + it will return the base name, otherwise it will return the name itself.""" + return next( + ( + notetype_base_name + for notetype_base_name in anking_notetype_names() + if re.match(rf"{notetype_base_name}($| |-)", model_name) + ), + None, + ) + + +def is_io_note_type(model_name: str) -> bool: + "Return True if the given note type is an image occlusion type." + return notetype_base_name(model_name) in ("IO-one by one", "Physeo-IO one by one") + + def all_btns_setting_configs(): result = OrderedDict() for notetype_name in anking_notetype_templates().keys(): diff --git a/src/anking_notetypes/resources/__closet-0.6.1.css b/src/anking_notetypes/resources/__ankingio-0.6.2.css similarity index 96% rename from src/anking_notetypes/resources/__closet-0.6.1.css rename to src/anking_notetypes/resources/__ankingio-0.6.2.css index 2e7c306..653f46b 100644 --- a/src/anking_notetypes/resources/__closet-0.6.1.css +++ b/src/anking_notetypes/resources/__ankingio-0.6.2.css @@ -1,6 +1,3 @@ -/* This file is part of closet which is released under GPL-3.0 -GitHub: https://github.com/hgiesel/closet */ - @charset "UTF-8"; .android .card:not(.mathjax-rendered) { visibility: hidden; diff --git a/src/anking_notetypes/resources/__ankingio-0.6.2.js b/src/anking_notetypes/resources/__ankingio-0.6.2.js new file mode 100644 index 0000000..b9ba26a --- /dev/null +++ b/src/anking_notetypes/resources/__ankingio-0.6.2.js @@ -0,0 +1,130 @@ +var nn=Object.create;var _e=Object.defineProperty,on=Object.defineProperties,an=Object.getOwnPropertyDescriptor,ln=Object.getOwnPropertyDescriptors,un=Object.getOwnPropertyNames,fr=Object.getOwnPropertySymbols,cn=Object.getPrototypeOf,br=Object.prototype.hasOwnProperty,pn=Object.prototype.propertyIsEnumerable;var yr=(t,e,r)=>e in t?_e(t,e,{enumerable:!0,configurable:!0,writable:!0,value:r}):t[e]=r,oe=(t,e)=>{for(var r in e||(e={}))br.call(e,r)&&yr(t,r,e[r]);if(fr)for(var r of fr(e))pn.call(e,r)&&yr(t,r,e[r]);return t},ie=(t,e)=>on(t,ln(e));var Tr=(t,e)=>()=>(e||t((e={exports:{}}).exports,e),e.exports),_=(t,e)=>{for(var r in e)_e(t,r,{get:e[r],enumerable:!0})},mn=(t,e,r,s)=>{if(e&&typeof e=="object"||typeof e=="function")for(let n of un(e))!br.call(t,n)&&n!==r&&_e(t,n,{get:()=>e[n],enumerable:!(s=an(e,n))||s.enumerable});return t};var ze=(t,e,r)=>(r=t!=null?nn(cn(t)):{},mn(e||!t||!t.__esModule?_e(r,"default",{value:t,enumerable:!0}):r,t));var Pt=Tr((Rr,tt)=>{(function(t,e){typeof tt=="object"&&tt.exports?tt.exports=e():t.nearley=e()})(Rr,function(){function t(i,a,c){return this.id=++t.highestId,this.name=i,this.symbols=a,this.postprocess=c,this}t.highestId=0,t.prototype.toString=function(i){var a=typeof i=="undefined"?this.symbols.map(u).join(" "):this.symbols.slice(0,i).map(u).join(" ")+" \u25CF "+this.symbols.slice(i).map(u).join(" ");return this.name+" \u2192 "+a};function e(i,a,c,m){this.rule=i,this.dot=a,this.reference=c,this.data=[],this.wantedBy=m,this.isComplete=this.dot===i.symbols.length}e.prototype.toString=function(){return"{"+this.rule.toString(this.dot)+"}, from: "+(this.reference||0)},e.prototype.nextState=function(i){var a=new e(this.rule,this.dot+1,this.reference,this.wantedBy);return a.left=this,a.right=i,a.isComplete&&(a.data=a.build(),a.right=void 0),a},e.prototype.build=function(){var i=[],a=this;do i.push(a.right.data),a=a.left;while(a.left);return i.reverse(),i},e.prototype.finish=function(){this.rule.postprocess&&(this.data=this.rule.postprocess(this.data,this.reference,o.fail))};function r(i,a){this.grammar=i,this.index=a,this.states=[],this.wants={},this.scannable=[],this.completed={}}r.prototype.process=function(i){for(var a=this.states,c=this.wants,m=this.completed,h=0;h0&&a.push(" ^ "+m+" more lines identical to this"),m=0,a.push(" "+f)),c=f}},o.prototype.getSymbolDisplay=function(i){return l(i)},o.prototype.buildFirstStateStack=function(i,a){if(a.indexOf(i)!==-1)return null;if(i.wantedBy.length===0)return[i];var c=i.wantedBy[0],m=[i].concat(a),h=this.buildFirstStateStack(c,m);return h===null?null:[i].concat(h)},o.prototype.save=function(){var i=this.table[this.current];return i.lexerState=this.lexerState,i},o.prototype.restore=function(i){var a=i.index;this.current=a,this.table[a]=i,this.table.splice(a+1),this.lexerState=i.lexerState,this.results=this.finish()},o.prototype.rewind=function(i){if(!this.options.keepHistory)throw new Error("set option `keepHistory` to enable rewinding");this.restore(this.table[i])},o.prototype.finish=function(){var i=[],a=this.grammar.start,c=this.table[this.table.length-1];return c.states.forEach(function(m){m.rule.name===a&&m.dot===m.rule.symbols.length&&m.reference===0&&m.data!==o.fail&&i.push(m)}),i.map(function(m){return m.data})};function l(i){var a=typeof i;if(a==="string")return i;if(a==="object"){if(i.literal)return JSON.stringify(i.literal);if(i instanceof RegExp)return"character matching "+i;if(i.type)return i.type+" token";if(i.test)return"token matching "+String(i.test);throw new Error("Unknown symbol type: "+i)}}function u(i){var a=typeof i;if(a==="string")return i;if(a==="object"){if(i.literal)return JSON.stringify(i.literal);if(i instanceof RegExp)return i.toString();if(i.type)return"%"+i.type;if(i.test)return"<"+String(i.test)+">";throw new Error("Unknown symbol type: "+i)}}return{Parser:o,Grammar:s,Rule:t}})});var Lt=Tr((Ir,rt)=>{(function(t,e){typeof define=="function"&&define.amd?define([],e):typeof rt=="object"&&rt.exports?rt.exports=e():t.moo=e()})(Ir,function(){"use strict";var t=Object.prototype.hasOwnProperty,e=Object.prototype.toString,r=typeof new RegExp().sticky=="boolean";function s(p){return p&&e.call(p)==="[object RegExp]"}function n(p){return p&&typeof p=="object"&&!s(p)&&!Array.isArray(p)}function o(p){return p.replace(/[-\/\\^$*+?.()|[\]{}]/g,"\\$&")}function l(p){var g=new RegExp("|"+p);return g.exec("").length-1}function u(p){return"("+p+")"}function i(p){if(!p.length)return"(?!)";var g=p.map(function(b){return"(?:"+b+")"}).join("|");return"(?:"+g+")"}function a(p){if(typeof p=="string")return"(?:"+o(p)+")";if(s(p)){if(p.ignoreCase)throw new Error("RegExp /i flag not allowed");if(p.global)throw new Error("RegExp /g flag is implied");if(p.sticky)throw new Error("RegExp /y flag is implied");if(p.multiline)throw new Error("RegExp /m flag is implied");return p.source}else throw new Error("Not a pattern: "+p)}function c(p,g){return p.length>g?p:Array(g-p.length+1).join(" ")+p}function m(p,g){for(var b=p.length,T=0;;){var A=p.lastIndexOf(` +`,b-1);if(A===-1||(T++,b=A,T===g)||b===0)break}var S=T0)throw new Error("RegExp has capture groups: "+V+` +Use (?: \u2026 ) instead`);if(!k.lineBreaks&&V.test(` +`))throw new Error("Rule should declare lineBreaks: "+V);B.push(u(ge))}}var fe=b&&b.fallback,Re=r&&!fe?"ym":"gm",Ge=r||fe?"":"|";S===!0&&(Re+="u");var sn=new RegExp(i(B)+Ge,Re);return{regexp:sn,groups:F,fast:T,error:b||v}}function R(p){var g=x(y(p));return new D({start:g},"start")}function C(p,g,b){var T=p&&(p.push||p.next);if(T&&!b[T])throw new Error("Missing state '"+T+"' (in token '"+p.defaultType+"' of state '"+g+"')");if(p&&p.pop&&+p.pop!=1)throw new Error("pop must be 1 (in token '"+p.defaultType+"' of state '"+g+"')")}function I(p,g){var b=p.$all?y(p.$all):[];delete p.$all;var T=Object.getOwnPropertyNames(p);g||(g=T[0]);for(var A=Object.create(null),S=0;S({ready:e,result:t,parse:!1}),dn=t=>xr(t,!0),hn=(t,e)=>xr(t,e),gn=(t,e)=>hn(t.toReprString(),e.ready),fn={ready:!1,result:[],parse:!1},yn=t=>{var e,r,s;switch(typeof t){case"string":return dn(t);case"object":return{ready:(e=t.ready)!=null?e:!0,result:(r=t.result)!=null?r:[],parse:(s=t.parse)!=null?s:!1};default:return fn}},We=class{constructor(){this.filters=new Map}register(e,r){this.filters.set(e,r)}has(e){return this.filters.has(e)}get(e){var r;return(r=this.filters.get(e))!=null?r:null}getOrDefault(e){var r;return(r=this.get(e))!=null?r:gn}unregisterFilter(e){this.filters.delete(e)}clearFilters(){this.filters.clear()}execute(e,r,s){let n=this.getOrDefault(e);return yn(n(r,s))}};var Et=t=>(t+1>>>1)-1,Ve=t=>(t<<1)+1,Ee=t=>t+1<<1,He=class{constructor(e){this._heap=[],this._comparator=e}greater(e,r){return this._comparator(this._heap[e],this._heap[r])}swap(e,r){[this._heap[e],this._heap[r]]=[this._heap[r],this._heap[e]]}siftUp(){let e=this.size()-1;for(;e>0&&this.greater(e,Et(e));)this.swap(e,Et(e)),e=Et(e)}siftDown(){let e=0;for(;Ve(e){this._heap.push(r),this.siftUp()}),this.size()}pop(){let e=this.peek(),r=this.size()-1;return r>0&&this.swap(0,r),this._heap.pop(),this.siftDown(),e}*generate(){let e=this.pop();for(;e;)yield e,e=this.pop();return null}};var Ue={priority:0,persistent:!1},bn=(t,e)=>t.priority>e.priority,Ne=class{constructor(){this._deferred=new Map,this._blocked=new Set}register(e,r,s=Ue){var n,o;this._deferred.set(e,{keyword:e,procedure:r,priority:(n=s.priority)!=null?n:Ue.priority,persistent:(o=s.persistent)!=null?o:Ue.persistent})}registerIfNotExists(e,r,s=Ue){this.isRegistered(e)||this.register(e,r,s)}unregister(e){this._deferred.delete(e)}isRegistered(e){return this._deferred.has(e)}block(e){this._blocked.add(e)}unblock(e){this._blocked.delete(e)}isBlocked(e){return this._blocked.has(e)}clear(){this._deferred.clear(),this._blocked.clear()}executeEach(e){let r=new He(bn);r.push(...this._deferred.values());for(let s of r.generate())this.isBlocked(s.keyword)||s.procedure(s,e),s.persistent||this.unregister(s.keyword);this._blocked.clear()}};var qe=class{constructor(e,r){this.filters=e,this.options=r}register(e,r,s={}){this.filters.register(e,r),this.options.set(e,s)}getOptions(e){return this.options.get(e,{})}};var je=class{constructor(e,r,s,n,o,l,u,i){this.preset=e,this.filters=r,this.options=s,this.registrar=new qe(r,s),this.deferred=n,this.aftermath=o,this.cache=l,this.memory=u,this.environment=i}getBaseInternals(e){return Object.assign({},{filters:this.filters,options:this.options,registrar:this.registrar,cache:this.cache,memory:this.memory,environment:this.environment,deferred:this.deferred,aftermath:this.aftermath,preset:this.preset},e)}getAftermathInternals(e,r){return Object.assign(this.getBaseInternals(e),r)}getDeferredInternals(e,r){return Object.assign(this.getBaseInternals(e),r)}getInternals(e,r,s){return Object.assign(this.getDeferredInternals(e,r),s)}filterAccessor(e,r){return{getProcessor:s=>({execute:(n,o)=>this.filters.execute(s,n,this.getInternals(e,r,o)),getOptions:()=>this.options.get(s,{})})}}executeDeferred(e,r){let s=this.getDeferredInternals(e,r);this.deferred.executeEach(s)}executeAftermath(e,r){let s=this.getAftermathInternals(e,r);this.aftermath.executeEach(s)}switchPreset(e){this.preset=e}clear(){this.memory.clear(),this.aftermath.clear()}reset(){this.cache.clear(),this.deferred.clear()}install(...e){for(let r of e)r(this.registrar)}};var xn=t=>({status:t.parse?t.ready?2:3:t.ready?0:1,result:t.result}),vn=t=>{var r,s,n,o;let e=(r=t.optics)!=null?r:[];return{inlineOptics:(s=t.inlineOptics)!=null?s:e,blockOptics:(n=t.blockOptics)!=null?n:e,capture:(o=t.capture)!=null?o:!1}},Nt="_closetEnvironment",vr=class t extends je{static make(e={},r=new Map){let s=Object.prototype.hasOwnProperty.call(globalThis,Nt)?globalThis[Nt]:globalThis[Nt]=new Map;return new t(e,new We,new j(new Map),new Ne,new Ne,new j(new Map),new j(r),new j(s))}makeAccessor(e,r){let s=this.filterAccessor(e,r);return n=>{let o=s.getProcessor(n);return{execute:(l,u)=>xn(o.execute(l,super.getInternals(e,r,u))),getOptions:()=>vn(o.getOptions())}}}finishIteration(e,r){this.executeDeferred(e,r)}finishRun(e,r){this.executeAftermath(e,r),this.reset()}};var Ut={};_(Ut,{BrowserTemplate:()=>pe,Parser:()=>xe,TagSelector:()=>Q,Template:()=>ve,anki:()=>ut,browser:()=>ot,optics:()=>Ye});var $=t=>t,Xe=(t,e)=>e,ye=t=>e=>t;var Sr=t=>{let e=t.slice(0),r=t.length;for(;r!==0;){let s=Math.floor(Math.random()*r);r-=1;let n=e[r];e[r]=e[s],e[s]=n}return e},Ke=(t,e)=>{let r=[];for(let s of e){let n=t[s];n&&r.push(n)}if(e.lengtho+e.length);for(let n of s)r.push(t[n])}return r};var Ye={};_(Ye,{dictForget:()=>Ce,dictFunction:()=>Ie,mapped:()=>G,run:()=>ae,separated:()=>E,stripped:()=>Ar,strippedRegex:()=>Dt,templated:()=>kr,templatedRegex:()=>Ct});var ae=(t,e,r)=>t.reverse().reduce((s,n)=>n(e,s),r);var Sn=(t,e,r)=>s=>e(r(t(s))),kn=t=>([e,r])=>[t(e),r],An=t=>([e,r])=>e?[e,t(r)]:[e,r],Ie={classes:[1,0,2],dimap:Sn,first:kn,right:An},Fn=(t,e)=>r=>e(t(r)),wn=(t,e,r)=>Fn(t,r),Rn=t=>([e])=>t(e),En=t=>([e,r])=>e?t(r):[],Ce={classes:[3,1,0,2],dimap:wn,first:Rn,right:En};var Nn=t=>{var e,r,s,n;return typeof t=="string"?{sep:t,max:1/0,trim:!1,keepEmpty:!0}:{sep:(e=t.sep)!=null?e:"::",max:(r=t.max)!=null?r:1/0,trim:(s=t.trim)!=null?s:!1,keepEmpty:(n=t.keepEmpty)!=null?n:!0}},E=t=>{let{sep:e,max:r,trim:s,keepEmpty:n}=Nn(t),o=u=>{let i=[],a=u,c=!1;for(;!c;){let m=a.indexOf(e),[h,d,f]=m<0||i.length+1===r?[a,"",!0]:[a.slice(0,m),a.slice(m+e.length),!1],y=s?h.trim():h;(n||y.length>=1)&&i.push(y),a=d,c=f}return[i]},l=([u])=>u;return(u,i)=>{let a=u.first(i);return u.dimap(o,l,a)}};var Je=t=>{var e,r;return typeof t=="string"?{before:t,after:t}:{before:(e=t.before)!=null?e:"",after:(r=t.after)!=null?r:""}};var be=t=>t.replace(/[.*+?^${}()|[\]\\]/g,"\\$&"),It=t=>t instanceof RegExp?t.source:t;var In=(t,e)=>r=>{let s=r[r.length-1]||{},n=[t[0]];return e.forEach((o,l)=>{let u=Number.isInteger(o)?r[o]:s[o];n.push(u,t[l+1])}),n.join("")},Ct=t=>{let{before:e,after:r}=Je(t),s=o=>{let l=new RegExp(`(${e})(.*?)(${r})`,"gsu"),u=[],i=[],a=0;o.replace(l,(h,d,f,y,v)=>{let x=o.substring(a,v+d.length);return u.push(x),i.push(f),a=v+h.length-y.length,""}),u.push(o.substring(a));let c=[...Array(i.length).keys()],m=In(u,c);return[i,m]},n=([o,l])=>l(o);return(o,l)=>{let u=o.first(l);return o.dimap(s,n,u)}},kr=({before:t,after:e})=>Ct({before:be(t),after:be(e)});var Dt=t=>{let{before:e,after:r}=Je(t),s=o=>{let l=new RegExp(`^${It(e)}(.*?)${It(r)}$`,"su"),u=o;return o.replace(l,(i,a)=>(u=a,"")),[u,null]},n=([o])=>o;return(o,l)=>{let u=o.first(l);return o.dimap(s,n,u)}},Ar=({before:t,after:e})=>Dt({before:`^${be(t)}`,after:`${be(e)}$`});var Cn=t=>e=>e.map(t),G=()=>(t,e)=>Cn(e);var Bt=(t,e)=>t&&e.isReady(),Fr=t=>t.map(e=>e.toString()).join(""),wr=t=>t.map(e=>e.toReprString()).join(""),Dn=/^(?:|\n)|(?:|\n)$/gu,Bn=t=>t.replace(Dn,""),Qe=class{constructor(e,r,s,n,o,l,u,i,a,c){this._inlineOptics=[];this._inlineGetter=$;this._blockOptics=[];this._blockGetter=$;this.fullKey=e,this.key=r,this.num=s,this.fullOccur=n,this.occur=o,this.delimiters=l,this._inlineNodes=u,this.hasInline=i,this._blockNodes=a,this.hasBlock=c}get inlineNodes(){return this._inlineNodes}get blockNodes(){return this._blockNodes}get innerNodes(){return this.hasBlock?this.blockNodes:this.inlineNodes}set internalNodes(e){this.hasBlock?this._blockNodes=e:this._inlineNodes=e}get inlineText(){return Fr(this.inlineNodes)}get blockText(){return Bn(Fr(this.blockNodes))}get text(){return this.hasBlock?this.blockText:this.inlineText}set inlineOptics(e){this._inlineOptics=e,this._inlineGetter=ae(e,Ce,$)}get inlineOptics(){return this._inlineOptics}set blockOptics(e){this._blockOptics=e,this._blockGetter=ae(e,Ce,$)}get blockOptics(){return this._blockOptics}set optics(e){this.hasBlock?this.blockOptics=e:this.inlineOptics=e}get optics(){return this.hasBlock?this.blockOptics:this.inlineOptics}get inlineGetter(){return this._inlineGetter}get blockGetter(){return this._blockGetter}get getter(){return this.hasBlock?this.blockGetter:this.inlineGetter}get inlineValues(){return this.inlineGetter(this.inlineText)}get blockValues(){return this.blockGetter(this.blockText)}get values(){return this.hasBlock?this.blockValues:this.inlineValues}inlineTraverse(e){return ae(this.inlineOptics,Ie,e)(this.inlineText)}blockTraverse(e){return ae(this.blockOptics,Ie,e)(this.blockText)}traverse(e){return this.hasBlock?this.blockTraverse(e):this.inlineTraverse(e)}wrapWithDelimiters(e,r){return`${this.delimiters.open}${e}${this.fullKey}${r?this.delimiters.sep+r:""}${this.delimiters.close}`}wrapBlock(e,r){return`${this.wrapWithDelimiters("#",r)}${e}${this.wrapWithDelimiters("/")}`}baseToString(e,r){return this.hasInline?this.hasBlock?this.wrapBlock(r(),e()):this.wrapWithDelimiters("",e()):this.hasBlock?this.wrapBlock(r()):this.wrapWithDelimiters("")}toString(){return this.baseToString(()=>this.inlineText,()=>this.blockText)}toReprString(){return this.baseToString(()=>wr(this.inlineNodes),()=>wr(this.blockNodes))}isReady(){return!1}evaluate(e,r,s,n=!1){let o=r(this.key),l=s.length-1,u=o.getOptions().capture,i=(y,v)=>y.evaluate(e,r,[...s,v]),a=!u&&!n,c=u&&!n;a&&this.innerNodes.splice(0,this.innerNodes.length,...this.innerNodes.flatMap(i));let m=this.innerNodes.reduce(Bt,!0);this.inlineOptics=o.getOptions().inlineOptics,this.blockOptics=o.getOptions().blockOptics;let h={path:s,depth:l,ready:m,isCapture:c},d=o.execute(this,h),f;switch(d.status){case 0:f=typeof d.result=="string"?[new le(d.result)]:d.result;break;case 1:f=c?this.innerNodes:[this];break;case 2:f=e.rawParse(d.result).flatMap(i);break;case 3:f=e.rawParse(d.result);break}return c?(this.internalNodes=f,this.evaluate(e,r,s,!0)):f}},De=class{isReady(){return!0}evaluate(){return[this]}toReprString(){var e;return(e=this.toString())!=null?e:""}},le=class extends De{constructor(r){super();this.text=r}toString(){return this.text}},Ze=class extends De{constructor(r){super();this.escaped=r}toString(){return this.escaped.length===1?this.escaped:this.escaped.slice(1)}toReprString(){return this.escaped}},et=class extends De{toString(){return null}};var Be=ze(Pt());var X=/^((?:[a-zA-Z_/]|%\w)+)(\d*)(?::(\d+))?$/u,Er=(t,e)=>{var s;let r=((s=t.get(e))!=null?s:-1)+1;return t.set(e,r),r},$t=class{constructor(){this.tagCounter=null;this.tagCounterFull=null;this.delimiters=null}increment(e,r){return[Er(this.tagCounterFull,e),Er(this.tagCounter,r)]}build(e,r,s,n=[],o=!1){let l=e.match(X);if(!l)throw new Error("Could not match key. This should never happen.");let u=l[1],i=l[2].length>0?Number(l[2]):null,[a,c]=this.increment(e,u);return new Qe(e,u,i,a,c,this.delimiters,r,s,n,o)}push(e,r){this.tagCounterFull=e[0],this.tagCounter=e[1],this.delimiters=r}},Te=new $t;function ue(t){return t[0]}var Pn=t=>({Lexer:t,ParserRules:[{name:"start",symbols:["content"],postprocess:ue},{name:"content$ebnf$1",symbols:[]},{name:"content$ebnf$1",symbols:["content$ebnf$1","node"],postprocess:r=>r[0].concat([r[1]])},{name:"content",symbols:["content$ebnf$1"],postprocess:ue},{name:"node",symbols:["text"],postprocess:ue},{name:"node",symbols:["inlinetag"],postprocess:ue},{name:"node",symbols:["blocktag"],postprocess:ue},{name:"text",symbols:[t.has("text")?{type:"text"}:text],postprocess:([r])=>new le(r.value)},{name:"text",symbols:[t.has("escapeseq")?{type:"escapeseq"}:escapeseq],postprocess:([r])=>new Ze(r.value)},{name:"inlinetag",symbols:[t.has("inlineopen")?{type:"inlineopen"}:inlineopen,"inline"],postprocess:([,[r,s,n]])=>Te.build(r.value,s,n)},{name:"blocktag",symbols:[t.has("blockopen")?{type:"blockopen"}:blockopen,"inline","content","blockclose"],postprocess:([,[r,s,n],o,l],u,i)=>!l||r.value===l.value?Te.build(r.value,s,n,o,!0):i},{name:"inline$ebnf$1$subexpression$1",symbols:[t.has("sep")?{type:"sep"}:sep,"content"]},{name:"inline$ebnf$1",symbols:["inline$ebnf$1$subexpression$1"],postprocess:ue},{name:"inline$ebnf$1",symbols:[],postprocess:()=>null},{name:"inline",symbols:[t.has("keyname")?{type:"keyname"}:keyname,"inline$ebnf$1",t.has("close")?{type:"close"}:close],postprocess:([r,s])=>s?[r,s[1],!0]:[r,[],!1]},{name:"blockclose$ebnf$1",symbols:[t.has("keyname")?{type:"keyname"}:keyname],postprocess:ue},{name:"blockclose$ebnf$1",symbols:[],postprocess:()=>null},{name:"blockclose",symbols:[t.has("blockclose")?{type:"blockclose"}:blockclose,"blockclose$ebnf$1",t.has("close")?{type:"close"}:close],postprocess:([,r])=>r}],ParserStart:"start"}),Ot=Pn;var Mt={open:"[[",sep:"::",close:"]]"};function*Nr(t,e){let r=!0;for(let s of t){r?r=!1:yield e;for(let n of s)yield n}}var Dr=ze(Lt()),Gt=/(?:[a-zA-Z_]|%\w)+\d*/u,Cr=t=>t.replace(/[.*+\-?^${}()|[\]\\]/g,"\\$&"),_t=t=>{let e=Cr(t.open),r=Cr(t.close),s=new RegExp(`[\\s\\S]+?(?=\\\\|${e}|$)`,"u"),n=new RegExp(`[\\s\\S]+?(?=\\\\|${e}|${r})`,"u"),o=new RegExp(`\\\\(?:${e}|${r})?`,"u"),l={match:t.open,push:"inlinekey"},u={match:`${t.open}#`,push:"blockkey"},i={match:`${t.open}/`,push:"blockclose"},a={match:t.close,pop:1},c={match:t.close,next:"blockmain"},m={match:Gt},h={match:s,lineBreaks:!0},d={match:n,lineBreaks:!0},f={match:o},y=v=>({match:t.sep,next:v});return(0,Dr.states)({main:{blockopen:u,inlineopen:l,escapeseq:f,text:h},blockkey:{keyname:m,sep:y("blocktag"),close:c},inlinekey:{keyname:m,sep:y("inlinetag"),close:a},blocktag:{blockopen:u,inlineopen:l,escapeseq:f,close:c,text:d},inlinetag:{blockopen:u,inlineopen:l,escapeseq:f,close:a,text:d},blockmain:{blockopen:u,blockclose:i,inlineopen:l,escapeseq:f,text:h},blockclose:{keyname:m,close:a}})};var $n=(t,e)=>{let r=new Be.Parser(e);try{return r.feed(t).results}catch(s){return[[new le(t)]]}},Br=(t,e)=>{let r=$n(t,e);return r.length>1,r[0]},On=(t,e)=>[...Nr(t.map(r=>Br(r,e)),new et)];var xe=class{constructor(e){this.tagBuilderSettings=[new Map,new Map];this.delimiters=e!=null?e:Mt,this.lexer=_t(this.delimiters),this.grammar=Be.Grammar.fromCompiled(Ot(this.lexer))}update(e){this.delimiters=Object.assign({},Mt,e),this.lexer=_t(this.delimiters),this.grammar=Be.Grammar.fromCompiled(Ot(this.lexer))}parse(e){return Te.push(this.tagBuilderSettings,this.delimiters),On(e,this.grammar)}rawParse(e){return Te.push(this.tagBuilderSettings,this.delimiters),Br(e,this.grammar)}};var Mn=50,Ln=t=>{let e=[];if(t.length===0)return e;let r="";for(let s of t){let n=s.toString();typeof n=="string"?r+=n:(e.push(r),r="")}return e.push(r),e},ve=class t{constructor(e,r,s){this.textFragments=e,this.parser=new xe(s),this.nodes=r!=null?r:this.parser.parse(e)}static make(e,r){return new t([e],null,r)}static makeFromFragments(e,r){return new t(e,null,r)}render(e,r=()=>{}){let s={template:this,parser:this.parser},n=this.nodes,o=!1;for(let u=0;uc.evaluate(this.parser,a,[m])),o=n.reduce(Bt,!0),e.finishIteration(s,i)}let l=Ln(n);return r(l),e.finishRun(s,{result:l}),l}};var ot={};_(ot,{BrowserTemplate:()=>pe,cleanup:()=>Wt,interspliceChildNodes:()=>nt});var Pr=(t,e,r)=>t<0?r+t+1:e+t,zt=(t,e)=>{var o;if(typeof t=="string")return t;let r=null,s=null,n=null;switch(t.nodeType){case Node.TEXT_NODE:return r=t,(o=r.textContent)!=null?o:"";case Node.ELEMENT_NODE:return s=t,e?s.outerHTML:s.innerHTML;case ce.CHILD_NODE_SPAN:return n=t,n.spanAsStrings().join("");default:return""}},$r=(t,e)=>{if(typeof t=="string")return;let r=null,s=null,n=null,o=null;switch(t.nodeType){case Node.TEXT_NODE:r=t,s=document.createElement("div"),r.parentElement&&(r.parentElement.insertBefore(s,r),r.parentElement.removeChild(r)),s.outerHTML=e;break;case Node.ELEMENT_NODE:n=t,n.innerHTML=e;break;case ce.CHILD_NODE_SPAN:o=t,o.replaceSpan(e);break}},Gn={type:"index",value:0},_n={type:"index",value:-1},st=class st{constructor(e,r=Gn,s=_n){this.nodeType=st.CHILD_NODE_SPAN;var l,u,i,a;this.parentElement=e,this.childNodes=Array.from(this.parentElement.childNodes),this.max=e.childNodes.length-1;let n=this.getFromMethod(r.type),o=this.getToMethod(s.type);this._fromIndex=n.call(this,r.value,(l=r.startAtIndex)!=null?l:0,(u=r.exclusive)!=null?u:!1),this._toIndex=o.call(this,s.value,Math.max((i=s.startAtIndex)!=null?i:0,this.from),(a=s.exclusive)!=null?a:!1)}get from(){return this._fromIndex}get to(){return this._toIndex}getFromMethod(e){return e==="index"?this.fromIndex:e==="node"?this.fromNode:this.fromPredicate}getToMethod(e){return e==="index"?this.toIndex:e==="node"?this.toNode:this.toPredicate}fromSafe(e,r){return ethis.max?this.max:e}fromIndex(e,r,s){let n=Pr(e,r,this.max)+(s?1:0);return this.fromSafe(n,r)}fromNode(e,r,s){let n=this.childNodes.slice(r).findIndex(o=>o===e)+r+(s?1:0);return this.fromSafe(n,r)}fromPredicate(e,r,s){let n=this.childNodes.slice(r).findIndex(e)+r+(s?1:0);return this.fromSafe(n,r)}toSafe(e,r){return ethis.max?0:e}toIndex(e,r,s){let n=Pr(e,r,this.max)-(s?1:0);return this.toSafe(n,r)}toNode(e,r,s){let n=this.childNodes.slice(r).findIndex(o=>o===e)+r-(s?1:0);return this.toSafe(n,r)}toPredicate(e,r,s){let n=this.childNodes.slice(r).findIndex(e)+r-(s?1:0);return this.toSafe(n,r)}get valid(){return this._fromIndex<=this._toIndex}get length(){return Math.max(0,this._toIndex-this._fromIndex+1)}getRootNode(){return this.parentElement.getRootNode()}span(){return this.valid?this.childNodes.slice(this._fromIndex,this._toIndex+1):[]}spanAsStrings(){return this.span().map(e=>zt(e,!0))}replaceSpan(e){if(!this.valid)return;let r=document.createElement("div"),s=this.parentElement.childNodes.length;this.parentElement.insertBefore(r,this.parentElement.childNodes[this._fromIndex]);for(let n of this.span())n.parentElement===this.parentElement&&this.parentElement.removeChild(n);r.outerHTML=e,this.childNodes=Array.from(this.parentElement.childNodes),this.max=this.childNodes.length,this._toIndex=this._toIndex+(this.max-s)}};st.CHILD_NODE_SPAN=3353;var ce=st;var Or=(t,e=0)=>[{type:"predicate",value:t.value,startAtIndex:e,exclusive:!1},{type:"predicate",value:n=>!t.value(n),startAtIndex:e,exclusive:!0}],nt=(t,e)=>{let r=[],s=new ce(t,...Or(e));for(;s.valid;)r.push(s),s=new ce(t,...Or(e,s.to+1));return r};var Mr=(t,e)=>{let r=document.createElement("style");return r.type="text/css",r.id=`closet-${t}`,r.textContent=e,r},Lr=(t,e)=>!!t.getElementById(`closet-${e}`),Gr=(t,e)=>{Lr(document,t)||document.head.appendChild(Mr(t,e))},zn=(t,e,r)=>{let s=t.getRootNode();s instanceof Document?Gr(e,r):Lr(s,e)||s.insertBefore(Mr(e,r),s.firstElementChild)},pe=class t extends ve{constructor(r,s,n,o){super(r,s,o);this.inputs=n}static makeFromNode(r,s){return t.makeFromNodes([r],s)}static makeFromNodes(r,s){return new t(r.map(n=>zt(n,!1)),null,r,s)}renderToNodes(r){super.render(r,s=>s.forEach((n,o)=>$r(this.inputs[o],n)))}appendDocumentStyle(r,s){Gr(r,s)}appendStyle(r,s,n){zn(r,s,n)}appendStyleAll(r,s){for(let n of this.inputs)typeof n!="string"&&this.appendStyle(n,r,s)}},Wn="closet-delay",Wt=()=>{for(let t of Array.from(document.getElementsByClassName(Wn)))t.style.visibility="visible"};var it=ze(Pt());var _r=ze(Lt()),N=(0,_r.compile)({text:{match:/[a-zA-Z_]+/u},numDigits:{match:/\d+(?=:|$)/u},digits:{match:/\d+/u},slash:{match:"/"},escapeseq:{match:/%\w/u},multiplierSeq:{match:/\*n\+/u},allStar:{match:/^\*$/u},numStar:{match:/\*(?=:|$)/u},star:{match:"*"},groupOpen:{match:"{"},groupAlternative:{match:","},rangeSpecifier:{match:/-/u},numGroupClose:{match:/\}(?=:|$)/u},groupClose:{match:"}"},occurSep:{match:":"}});function P(t){return t[0]}var Vn={Lexer:N,ParserRules:[{name:"start",symbols:["allStar"],postprocess:P},{name:"start",symbols:["pattern"],postprocess:P},{name:"allStar",symbols:[N.has("allStar")?{type:"allStar"}:allStar],postprocess:()=>()=>!0},{name:"pattern",symbols:["key","num","occur"],postprocess:([t,e,r])=>(s,n,o)=>{if(typeof n=="undefined"){let l=s.match(X);if(!l)return!1;let u=l[1],i=Number(l[2]),a=Number.isNaN(i)?null:i;return t.test(u)&&e(a)&&r(o)}else return t.test(s)&&e(n)&&r(o)}},{name:"key$ebnf$1$subexpression$1",symbols:["keyComps"]},{name:"key$ebnf$1",symbols:["key$ebnf$1$subexpression$1"]},{name:"key$ebnf$1$subexpression$2",symbols:["keyComps"]},{name:"key$ebnf$1",symbols:["key$ebnf$1","key$ebnf$1$subexpression$2"],postprocess:t=>t[0].concat([t[1]])},{name:"key",symbols:["key$ebnf$1"],postprocess:([t])=>new RegExp(`^${t.join("")}$`,"u")},{name:"keyComps",symbols:["mixedText"],postprocess:P},{name:"keyComps",symbols:["escapeseq"],postprocess:P},{name:"keyComps",symbols:["keyGroup"],postprocess:P},{name:"mixedText",symbols:[N.has("text")?{type:"text"}:text],postprocess:([t])=>t.value},{name:"mixedText",symbols:[N.has("star")?{type:"star"}:star],postprocess:()=>".*"},{name:"mixedText",symbols:[N.has("slash")?{type:"slash"}:slash],postprocess:()=>"\\\\"},{name:"escapeseq",symbols:[N.has("escapeseq")?{type:"escapeseq"}:escapeseq],postprocess:([t])=>t.value},{name:"keyGroup",symbols:[N.has("groupOpen")?{type:"groupOpen"}:groupOpen,"keyGroupInner",N.has("groupClose")?{type:"groupClose"}:groupClose],postprocess:([,t])=>t.join("")},{name:"keyGroupInner$ebnf$1",symbols:[]},{name:"keyGroupInner$ebnf$1$subexpression$1",symbols:[N.has("groupAlternative")?{type:"groupAlternative"}:groupAlternative,"keyGroupItem"]},{name:"keyGroupInner$ebnf$1",symbols:["keyGroupInner$ebnf$1","keyGroupInner$ebnf$1$subexpression$1"],postprocess:t=>t[0].concat([t[1]])},{name:"keyGroupInner",symbols:["keyGroupItem","keyGroupInner$ebnf$1"],postprocess:([t,e])=>[t,...e.map(r=>r.value)]},{name:"keyGroupItem$ebnf$1",symbols:[]},{name:"keyGroupItem$ebnf$1$subexpression$1",symbols:["mixedText"]},{name:"keyGroupItem$ebnf$1",symbols:["keyGroupItem$ebnf$1","keyGroupItem$ebnf$1$subexpression$1"],postprocess:t=>t[0].concat([t[1]])},{name:"keyGroupItem",symbols:["keyGroupItem$ebnf$1"],postprocess:([t])=>t.join("")},{name:"num$ebnf$1$subexpression$1",symbols:["numPredicate"]},{name:"num$ebnf$1",symbols:["num$ebnf$1$subexpression$1"],postprocess:P},{name:"num$ebnf$1",symbols:[],postprocess:()=>null},{name:"num",symbols:["num$ebnf$1"],postprocess:([t])=>t?t[0]:e=>e===null},{name:"numPredicate",symbols:["numDigits"],postprocess:P},{name:"numPredicate",symbols:["numStar"],postprocess:P},{name:"numPredicate",symbols:["numGroup"],postprocess:P},{name:"numDigits",symbols:[N.has("numDigits")?{type:"numDigits"}:numDigits],postprocess:([t])=>e=>e===Number(t.value)},{name:"numStar",symbols:[N.has("numStar")?{type:"numStar"}:numStar],postprocess:()=>t=>!0},{name:"numGroup",symbols:[N.has("groupOpen")?{type:"groupOpen"}:groupOpen,"numGroupInner",N.has("numGroupClose")?{type:"numGroupClose"}:numGroupClose],postprocess:([,t])=>e=>t.reduce((r,s)=>r||s(e),!1)},{name:"numGroupInner$ebnf$1",symbols:[]},{name:"numGroupInner$ebnf$1$subexpression$1",symbols:[N.has("groupAlternative")?{type:"groupAlternative"}:groupAlternative,"numGroupItem"]},{name:"numGroupInner$ebnf$1",symbols:["numGroupInner$ebnf$1","numGroupInner$ebnf$1$subexpression$1"],postprocess:t=>t[0].concat([t[1]])},{name:"numGroupInner",symbols:["numGroupItem","numGroupInner$ebnf$1"],postprocess:([t,e])=>[t,...e.map(([,r])=>r)]},{name:"numGroupItem$ebnf$1$subexpression$1",symbols:["numGroupPredicate"]},{name:"numGroupItem$ebnf$1",symbols:["numGroupItem$ebnf$1$subexpression$1"],postprocess:P},{name:"numGroupItem$ebnf$1",symbols:[],postprocess:()=>null},{name:"numGroupItem",symbols:["numGroupItem$ebnf$1"],postprocess:([t])=>t?t[0]:e=>e===null},{name:"numGroupPredicate",symbols:["digits"],postprocess:P},{name:"numGroupPredicate",symbols:["range"],postprocess:P},{name:"numGroupPredicate",symbols:["multiple"],postprocess:P},{name:"digits",symbols:[N.has("digits")?{type:"digits"}:digits],postprocess:([t])=>e=>e===Number(t.value)},{name:"range",symbols:["bounded"],postprocess:P},{name:"range",symbols:["leftBounded"],postprocess:P},{name:"range",symbols:["rightBounded"],postprocess:P},{name:"bounded",symbols:[N.has("digits")?{type:"digits"}:digits,N.has("rangeSpecifier")?{type:"rangeSpecifier"}:rangeSpecifier,N.has("digits")?{type:"digits"}:digits],postprocess:([t,,e])=>r=>typeof r=="number"&&Number(t.value)<=r&&r<=Number(e.value)},{name:"leftBounded",symbols:[N.has("digits")?{type:"digits"}:digits,N.has("rangeSpecifier")?{type:"rangeSpecifier"}:rangeSpecifier],postprocess:([t])=>e=>typeof e=="number"&&Number(t.value)<=e},{name:"rightBounded",symbols:[N.has("rangeSpecifier")?{type:"rangeSpecifier"}:rangeSpecifier,N.has("digits")?{type:"digits"}:digits],postprocess:([,t])=>e=>typeof e=="number"&&e<=Number(t.value)},{name:"multiple",symbols:[N.has("digits")?{type:"digits"}:digits,N.has("multiplierSeq")?{type:"multiplierSeq"}:multiplierSeq,N.has("digits")?{type:"digits"}:digits],postprocess:([t,,e])=>r=>(r-e)%t===0},{name:"occur$ebnf$1$subexpression$1",symbols:[N.has("occurSep")?{type:"occurSep"}:occurSep,"occurPredicate"]},{name:"occur$ebnf$1",symbols:["occur$ebnf$1$subexpression$1"],postprocess:P},{name:"occur$ebnf$1",symbols:[],postprocess:()=>null},{name:"occur",symbols:["occur$ebnf$1"],postprocess:([t])=>t?e=>e===null?!0:t[1](e):()=>!0},{name:"occurPredicate",symbols:["numDigits"],postprocess:P},{name:"occurPredicate",symbols:["numStar"],postprocess:P},{name:"occurPredicate",symbols:["occurGroup"],postprocess:P},{name:"occurGroup",symbols:[N.has("groupOpen")?{type:"groupOpen"}:groupOpen,"occurGroupInner",N.has("groupClose")?{type:"groupClose"}:groupClose],postprocess:([,t])=>e=>t.reduce((r,s)=>s||r(e),!1)},{name:"occurGroupInner$ebnf$1",symbols:[]},{name:"occurGroupInner$ebnf$1$subexpression$1",symbols:[N.has("groupAlternative")?{type:"groupAlternative"}:groupAlternative,"numGroupPredicate"]},{name:"occurGroupInner$ebnf$1",symbols:["occurGroupInner$ebnf$1","occurGroupInner$ebnf$1$subexpression$1"],postprocess:t=>t[0].concat([t[1]])},{name:"occurGroupInner",symbols:["numGroupPredicate","occurGroupInner$ebnf$1"],postprocess:([t,e])=>[t,...e.map(([,r])=>r)]}],ParserStart:"start"},zr=Vn;var Hn=it.Grammar.fromCompiled(zr),Un=t=>{let e=new it.Parser(Hn);try{let r=e.feed(t).results;return r.length>1,r[0]}catch(r){return()=>!1}},Q=class t{constructor(e){this.predicate=Un(e)}static make(e){return new t(e)}check(e,r,s=null){return this.predicate(e,r,s)}checkFullKey(e,r=null){return this.predicate(e,void 0,r)}checkTagIdentifier(e){let r=e.match(X);if(!r)return!1;let s=r[1],n=r[2].length>0?Number(r[2]):null,o=r[3]?Number(r[3]):null;return this.predicate(s,n,o)}};var ut={};_(ut,{ankiLog:()=>H,getQaChildNodes:()=>Ur,init:()=>Ht,initialize:()=>Hr});var H=(...t)=>{if(!globalThis.closetDebug)return!1;let e=Object.assign(document.createElement("div"),{className:"closet-log",innerText:t.map(String).join(` +`)}),r=document.getElementById("qa");return r?(r.appendChild(e),!0):!1};var U="github.com/hgiesel/closet",Vt=class{constructor(){this._isAvailable=!1;try{typeof globalThis.sessionStorage=="object"&&(this._isAvailable=!0)}catch(e){}}clear(){for(let e=0;e{let t;if(t=new Vt,t.isAvailable())return H("Used Persistence sessionStorage implementation"),t;if(t=new lt("py"),t.isAvailable())return H("Used Persistence windowKey py implementation"),t;let e=location.toString().indexOf("title"),r=location.toString().indexOf("main",e);return e>0&&r>0&&r-e<10?(H("Used Persistence windowKey qt implementation"),new lt("qt")):(H("Defaulted to Persistence windowKey implementation"),t)},jn=t=>{let e=0,r,s;for(r=0;rr=>{var i;let s=(i=globalThis.closetCardHash)!=null?i:null,o=(()=>{if(!at.isAvailable())return new Map;if(t==="front"){let c=jn(e);if(c!==s)return globalThis.closetCardHash=c,new Map}let a=at.getItem(r);return new Map(a)})();return{key:r,map:o,writeBack:()=>{if(at.isAvailable()){let a=Array.from(o.entries());at.setItem(r,a)}}}};var Xn=t=>{switch(t){case 1:return"You have set the variable globalThis.closetImmediately.";case 2:return"Closet executes while MathJax is still rendering. You are on AnkiDesktop. The action will be pushed onto onShownHook.";case 3:return"Closet executes while MathJax is still rendering. You are on AnkiMobile. The action will be enqueued on MathJax.Hub.Queue.";case 4:return"Closet executes after MathJax finished rendering. The action will be executed immediatley.";case 5:return"You are on AnkiWeb, which does not have MathJax. Alternatively, you are on platform with MathJax 3. The action will be executed immediatley.";default:return"No Description found. This should never happen."}},Pe={raw:t=>t(),viaShownHook:t=>globalThis.onShownHook.push(t),viaMathJaxQueue:t=>globalThis.MathJax.Hub.Queue(t)},Kn=()=>globalThis.MathJax.Hub.queue,Jn=()=>{let t=Kn(),e=Object.assign({},t);return e.queue=e.queue.map(String),e},Yn=()=>{if(globalThis.closetImmediately)return[Pe.raw,1,[]];try{let t=globalThis._updatingQA,e=Jn();if(t){let r=globalThis.onShownHook;return r?[Pe.viaShownHook,2,[JSON.stringify(r.map(String)),JSON.stringify(e)]]:[Pe.viaMathJaxQueue,3,[JSON.stringify(e)]]}else return[Pe.raw,4,[JSON.stringify(e)]]}catch(t){return[Pe.raw,5,[t]]}},Vr=t=>{let[e,r,s]=Yn();H(`Init mode ${r}`,Xn(r),...s),e(t)};var Qn=t=>Number(t.match(/[0-9]*$/)),Zn=(t,e,r)=>({card:t,cardNumber:Qn(t),tagsFull:e,tags:e.length===0?[]:e.split(" "),side:r}),eo=(t,e,r,s)=>{let n=window.performance.now();return pe.makeFromNodes(t,s==null?void 0:s.delimiters).renderToNodes(r),e.writeBack(),window.performance.now()-n},Ht=(t,e,r,s,n)=>{var u,i;let o=Zn(r,s,n),l=Wr(n,(i=(u=document.getElementById("qa"))==null?void 0:u.innerHTML)!=null?i:"");return e(t,o,l).map(a=>eo(...a))},to=(t,e,r,s,n)=>{try{`${Ht(t,e,r,s,n).map(l=>l.toFixed(3))}`}catch(o){H("An error occured while executing Closet",o)}finally{Wt()}},Hr=(t,e,r,s,n)=>(Vr(()=>to(t,e,r,s,n)),t);var ro=t=>t.nodeType===Node.ELEMENT_NODE,so=t=>t.tagName==="STYLE",no=t=>t.tagName==="SCRIPT",oo=t=>no(t)&&(t.type.length===0||t.type.endsWith("javascript")),io=t=>t.id==="anki-am",ao=t=>ro(t)&&(so(t)||oo(t)||io(t)),Ur=()=>{if(!globalThis.document)return null;let t=globalThis.document.getElementById("qa");return t?nt(t,{type:"predicate",value:e=>!ao(e)}):null};var er={};_(er,{activate:()=>Xr,apply:()=>os,constantGet:()=>me,deactivate:()=>Kr,debug:()=>ds,define:()=>hs,delimiter:()=>gs,generateInteger:()=>ps,generateReal:()=>ms,order:()=>us,pick:()=>rs,pickCardNumber:()=>ns,pickIndex:()=>ss,process:()=>is,setList:()=>Yr,setNumber:()=>qr,shuffle:()=>ls,style:()=>as});var $e=E({sep:";"}),Oe=E({sep:"=",trim:!0,max:2}),Se=class{constructor(e){this.defaultValue=e,this.selectors=[]}set(e,r){this.selectors.unshift([Q.make(e),r])}get(e,r,s){let n=this.selectors.find(([o])=>o.check(e,r,s));return n?n[1]:this.defaultValue}},ct=t=>(e,r,s)=>(n,{cache:o})=>(n.values.forEach(u=>{o.over(e,s(...u),new t(r))}),{ready:!0});var qt=class extends Se{setNumber(e,r){this.set(e,Number.isNaN(r)?0:r)}},lo=ct(qt),qr=({tagname:t="set",storeId:e="numerical",separator:r=$e,assignmentSeparator:s=Oe}={})=>n=>n.register(t,lo(e,0,(o,l)=>u=>u.setNumber(o,Number(l))),{optics:[r,G(),s]});var jt=class extends Se{on(e){this.set(e,!0)}off(e){this.set(e,!1)}},jr=ct(jt),Xr=({tagname:t="on",storeId:e="active",optic:r=$e}={})=>s=>s.register(t,jr(e,!1,n=>o=>{o.on(n)}),{separators:[r,G(),Oe]}),Kr=({tagname:t="off",storeId:e="active",optic:r=$e}={})=>s=>s.register(t,jr(e,!1,n=>o=>o.off(n)),{separators:[r,G(),Oe]});var me=t=>({get:()=>t});var pt=class extends j{},Jr=t=>(e,r)=>(s,n)=>{var l;return{ready:!0,result:(l=n.cache.over(e,r(s,n),new t(new Map)))!=null?l:""}};var Xt=class extends pt{setList(e,r){this.set(e,r)}overwriteList(e,r,s=0){let n=this.getList(e);for(let o=0;o(r,s)=>"",co=(t,e)=>(r,s)=>n=>{let[o,l]=r.values.length===2?r.values:[["default"],r.values[0]],u=o[0],i=n.getList(u),a=t(n,u,l)(r,s);return e(a,i)(r,s)},po=t=>({tagname:e="setl",storeId:r="lists",postprocess:s=uo,optics:n=[E({sep:"::"}),G(),E({sep:"||"})]}={})=>o=>o.register(e,mt(r,co(t,s)),{optics:n}),mo=(t,e,r)=>(s,n)=>(s.num===null?t.setList(e,r):t.overwriteList(e,r,s.num),""),Yr=po(mo);var dt=function*(t,e,r=[],s=()=>!1){let n=[],o=0;for(;!s(r)&&o<1e3;){let l=t();!r.includes(l)&&!n.includes(l)&&(e&&n.push(l),yield l),o++}return n},ht=(t,e)=>()=>t+Math.floor(Math.random()*(e-t)),Qr=(t,e)=>String(t*e),Zr=(t,e)=>r=>r.length>=e-t,es=(t,e)=>()=>t+Math.random()*(e-t),ts=(t,e)=>t.toFixed(e);var ho=(t,e)=>(r,s)=>t,go=(t,e)=>(r,s)=>n=>{let o=r.values||"default",l=n.getList(o),u=t(l,o)(r,s);return e(u,l)(r,s)},Kt=t=>({tagname:e="pick",storeId:r="lists",postprocess:s=ho}={})=>n=>n.register(e,mt(r,go(t,s))),fo=(t,e)=>(r,{memory:s})=>{let n=`pick:picked:${r.fullKey}:${r.occur}`,o=s.lazy(n,()=>{let l=ht(0,t.length),u=Zr(0,t.length),i=!!r.num,a=`pick:uniqList:${r.fullKey}`,c=i?s.over(a,y=>(Object.prototype.hasOwnProperty.call(y,e)||(y[e]=[]),y),{})[e]:[],m=Array.from(t).reduce((y,v,x)=>(v===void 0&&y.push(x),y),[]),h=c.concat(m),f=dt(l,i,h,u).next();if(!f.done)return c.push(f.value),f.value});return Number.isInteger(o)?t[o]:""},yo=(t,e)=>(r,s)=>t[Number(r.num)||0],bo=(t,e)=>(r,s)=>{var l;let n=t[s.preset.cardNumber];return(l=n!=null?n:t[0])!=null?l:""},rs=Kt(fo),ss=Kt(yo),ns=Kt(bo);var L=class t{constructor(e,r,s){this.separator=e,this.mapper=r,this.processor=s}static make({separator:e="",mapper:r=$,processor:s=$}={}){return new t(e,r,s)}toStylizer({separator:e=this.separator,mapper:r=this.mapper,processor:s=this.processor}={}){return new t(e,r,s)}stylize(e,...r){return this.processor(e.flatMap((s,n,o)=>this.mapper(s,n,...r.map(l=>l[n]),o)).join(this.separator))}stylizeFull(e,r=[],s=[]){return this.processor(e.flatMap((n,o,l)=>this.mapper(n,o,...r.map(u=>u[o]),l)).join(this.separator),...s)}};var Me=t=>({tagname:e="s"}={})=>r=>{r.register(e,t)},Jt=t=>e=>t(e.values),os=({tagname:t="s",apply:e=Jt($),optics:r=[]}={})=>s=>{s.register(t,e,{optics:r})},is=({tagname:t="s",processor:e=$,optics:r=[]}={})=>s=>{s.register(t,Jt(e),{optics:r})},as=({tagname:t="s",stylizer:e=L.make(),optics:r=[E("::")]}={})=>s=>{s.register(t,Jt(e.stylize.bind(e)),{optics:r})};var Qt={};_(Qt,{acrossCustom:()=>Eo,acrossNumberedCustom:()=>No,acrossNumberedTag:()=>Yt,acrossTag:()=>Le,withinCustom:()=>Ro,withinTag:()=>wo});var To=(t,e,r,s)=>(n,o)=>{let l=`${t}:apply`,u=`${t}:length`,i=`${e}:waitingSet`,a=`${e}:shuffle`;if(o.cache.get(l,!1)){if(o.cache.get(i,new Set).size>0)return;let h=[],d=o.cache.get(a,[]),f=o.cache.get(u,0);for(let y=0;ym.add(t),new Set);return}let c=s(n,o);if(c.length===0)return c;o.cache.fold(a,m=>m.concat(c),[]),o.cache.set(u,c.length),o.deferred.registerIfNotExists(l,()=>{o.cache.set(l,!0),o.cache.over(i,m=>m.delete(t),new Set)},{priority:65}),o.deferred.registerIfNotExists(a,()=>{o.cache.over(i,m=>m.size>0,new Set)||o.cache.fold(a,m=>{let h=o.memory.fold(a,d=>r(d,o.cache.get(a,[]).length),[]);return Ke(m,h)},[])})},ke=t=>(e,r)=>(s,n)=>{let o="sequences",[l,u]=t(s,n);return n.cache.over(o,i=>{i.includes(u)||i.push(u)},[]),To(l,u,r,e)(s,n)},xo=({fullKey:t,fullOccur:e},r)=>[`${t}:${e}`,`${t}:${e}`],vo=({fullKey:t,fullOccur:e},r)=>[`${t}:${e}`,t],So=({fullKey:t,fullOccur:e,num:r},s)=>[`${t}:${e}`,r?t:`${t}:${e}`],ko=t=>({fullKey:e,fullOccur:r,num:s},n)=>[`${e}:${r}`,`${t}${s}:${r}`],Ao=t=>({fullKey:e,fullOccur:r,num:s},n)=>[`${e}:${r}`,`${t}${s}`],Fo=t=>({fullKey:e,fullOccur:r,num:s},n)=>{let o=`${t}${s}`;return[`${e}:${r}`,s?o:`${o}:${r}`]},wo=ke(xo),Le=ke(vo),Yt=ke(So),Ro=t=>ke(ko(t)),Eo=t=>ke(Ao(t)),No=t=>ke(Fo(t));var Zt={};_(Zt,{mixIn:()=>Io,topUp:()=>K});var K=(t,e)=>{if(t.length>=e)return t;let r=Sr(Array.from(new Array(e-t.length),(n,o)=>o+t.length));return[...t,...r]},Io=(t,e)=>{if(t.length>=e)return t;let r=Array.from(new Array(e-t.length),(n,o)=>o+t.length),s=[...t];return r.forEach(n=>s.splice(Math.floor(Math.random()*(s.length+1)),0,n)),s};var Co=L.make({processor:t=>`${t}`,mapper:t=>`${t}`,separator:''}),Do=(t,e)=>()=>t.stylize(e),Bo=[E({sep:"||"})],ls=({tagname:t="mix",stylizer:e=Co,evaluate:r=Do,sortInStrategy:s=K,sequencer:n=Yt,optics:o=Bo}={})=>l=>{let u=(i,a)=>{let h=n(({values:d})=>d!=null?d:[],s)(i,a);if(h)return r(e,h)(i,a)};l.register(t,u,{optics:o})};var Po={priority:35,persistent:!0},$o=[E("::"),G(),E(",")],us=({tagname:t="ord",optics:e=$o,sortInStrategy:r=K}={})=>s=>{let n=(o,{deferred:l,cache:u,memory:i})=>{let a="sequences",c=new Set(u.get(a,[]));for(let[m,h]of o.values.entries()){let d=h.map(Q.make),f=`${o.key}:${o.fullOccur}:ord:${m}`,y=()=>{let x=Array.from(c).filter(C=>d.some(I=>I.checkTagIdentifier(C))?(c.delete(C),!0):!1),R=x.map(C=>`${C}:shuffle`);for(let[C,I]of x.entries()){let O=R[C],D=`${I}:waitingSet`;if(l.block(O),u.get(D,new Set).size!==0)continue;let Fe=R.reduce((g,b)=>{let T=i.get(b,[]);return g.lengthKe(g,p),[]),u.set(f,p),i.set(O,p)}c.size===0&&l.unregister(f)};l.register(f,y,Po)}return{ready:!0}};s.register(t,n,{optics:e})};var Oo=[E({sep:","})],cs=(t,e,r)=>({tagname:s="gen",uniqueConstraintId:n="uniq",optics:o=Oo}={})=>l=>{let u=`gen:${n}`,i=({values:a,fullOccur:c,num:m},{memory:h})=>{let[d=1,f=100,y=r]=a.length===1?[1,Number(a[0]),r]:a.map(Number),v=Number.isInteger(m)?`${u}:${m}`:u,x=`gen:${s}:${c}`;return{ready:!0,result:h.lazy(x,()=>{let I=dt(t(d,f),!0,Number.isInteger(m)?h.get(v,[]):[]).next();return I.done?"":e(I.value,y)})}};l.register(s,i,{optics:o})},ps=cs(ht,Qr,1),ms=cs(es,ts,2);var ds=()=>t=>{let e=(r,{path:s})=>s.join(":");t.register("tagpath",e),t.register("never",()=>{}),t.register("empty",()=>""),t.register("key",({key:r})=>r),t.register("stopIteration",({values:r},{filters:s})=>{let n=Number(r),o=s.getOrDefault("base");return s.register("base",(l,u)=>{var i;return u.iteration>=n?(i=l.text)!=null?i:"":o(l,u)}),{ready:!0}}),t.register("memorytest",(r,{memory:s})=>{let n="base:memorytest";return String(s.fold(n,o=>++o,0))})};var Mo=/%(.)/gu,Lo={optics:[E({sep:"::",max:2})],capture:!0},Go=t=>(e,r)=>{var n;let s=null;switch(r){case"%":return r;case"n":return typeof t.num=="number"?String(t.num):"";case"k":return t.key;case"f":return t.fullKey;default:return s=Number(r),Number.isNaN(s)?e:(n=t.values[s])!=null?n:""}},hs=(t={})=>e=>{let{tagname:r="def"}=t,s={optics:[E({sep:"::"})]},n=o=>{let[l,u]=o.values,i=a=>({result:u.replace(Mo,Go(a)),parse:!0});return e.register(l,i,s),{ready:!0}};e.register(r,n,Lo)};var _o={inlineOptics:[E({sep:"::",max:2})],capture:!0},gs=(t={})=>e=>{let{tagname:r="delim"}=t,s=(n,{template:o,isCapture:l})=>{if(l){let[u,i]=n.inlineValues;return o.parser.update({open:u,close:i}),{result:n.blockText,parse:!0}}return o.parser.update(),{result:n.innerNodes}};e.register(r,s,_o)};var lr={};_(lr,{behaviors:()=>nr,deciders:()=>gt,recipes:()=>gi});var gt={};_(gt,{isActive:()=>zo,isActiveAll:()=>tr,isActiveGetRange:()=>ys,isActiveOverwritten:()=>bs,isActiveWithinRange:()=>fs,isBack:()=>Ts,isBackAll:()=>rr});var fs=(t,e,r,s)=>t-r<=e&&e<=t+s,zo=({num:t},{preset:e})=>{switch(t){case null:return!1;case 0:return!0;default:return Object.prototype.hasOwnProperty.call(e,"cardNumber")?t===e.cardNumber:!1}},ys=({key:t,num:e,fullOccur:r},{preset:s,cache:n})=>{let o="flashcardActiveBottom",l="flashcardActiveTop",u=me(0),i=null,a=null;switch(e){case null:return!1;case 0:return!0;default:return typeof s.cardNumber!="number"?!1:(i=n.get(o,u).get(t,s.cardNumber,r),a=n.get(l,u).get(t,s.cardNumber,r),fs(s.cardNumber,e,i,a))}},bs=(t,e)=>{let r="flashcardActive";return e.cache.get(r,me(!1)).get(t.key,t.num,t.fullOccur)},tr=(t,e)=>bs(t,e)||ys(t,e),Ts=(t,{preset:e})=>e.side==="back",rr=Ts;var J=t=>Object.prototype.hasOwnProperty.call(t,"tagname")?[t.tagname]:[],Y=(t,e)=>ie(oe({},t),{tagname:e[0]}),xs="wrapped",sr=t=>(e,{wrapId:r,getTagnames:s,setTagnames:n}={wrapId:xs,getTagnames:J,setTagnames:Y})=>(o={})=>l=>{let u=s(o),i=h=>`${h}:${r}:internal`,a=new Map,c=u.map(h=>{let d=i(h);return a.set(h,d),d});e(n(o,c))(l);let m=(h,d)=>{let f=a.get(h.key);return t(Object.assign({},h,{keyInternal:f}),d),d.filters.getOrDefault(f)(h,d)};for(let h of u)l.register(h,m,l.getOptions(i(h)))},vs=t=>(e,r,s={},n={wrapId:xs,getTagnames:J,setTagnames:Y})=>sr((o,l)=>{t(l).registerIfNotExists(o.keyInternal,r,s)})(e,n),Ss=vs(t=>t.deferred),ks=vs(t=>t.aftermath);var ft=(t,e,r,{wrapId:s,setTagnames:n}={wrapId:"sum",getTagnames:J,setTagnames:Y})=>({tagname:o="sum",optionsFalse:l={},optionsTrue:u={}}={})=>i=>{let a=`${o}:${s}:false`,c=`${o}:${s}:true`;t(n(l,[a]))(i),e(n(u,[c]))(i);let m=(h,d)=>r(h,d)?d.filters.getOrDefault(c)(h,d):d.filters.getOrDefault(a)(h,d);i.register(o,m,i.getOptions(c))},yt=(t,e,r,s,n,o,{wrapId:l,setTagnames:u}={wrapId:"sumFour",getTagnames:J,setTagnames:Y})=>({tagname:i="sum",optionsZero:a={},optionsOne:c={},optionsTwo:m={},optionsThree:h={}}={})=>d=>{let f=`${i}:${l}:zero`,y=`${i}:${l}:two`;ft(t,e,n)({tagname:f,optionsFalse:u(a,[f]),optionsTrue:u(c,[f])})(d),ft(r,s,n)({tagname:y,optionsFalse:u(m,[y]),optionsTrue:u(h,[y])})(d);let v=(x,R)=>o(x,R)?R.filters.getOrDefault(y)(x,R):R.filters.getOrDefault(f)(x,R);d.register(i,v)};var bt=t=>e=>r=>{let s=e!=null?e:{};for(let[n,o={}]of t){let{setTagnames:l=Y,getTagnames:u=J}=o;n(l(s,u(s)))(r)}};var As=me(!1),Tt=me(0),Wo=t=>(e,r)=>(s,n)=>t(e,r)(s,n),Vo=t=>e=>(r,s)=>(n,o)=>{let l="flashcardShow",u="flashcardHide";return o.cache.get(l,As).get(n.key,n.num,n.fullOccur)?r(n,o):o.cache.get(u,As).get(n.key,n.num,n.fullOccur)?s(n,o):t(e)(r,s)(n,o)},Ho=t=>e=>(r,s)=>(n,o)=>{if(!Object.prototype.hasOwnProperty.call(o.preset,"cardNumber"))return t(e)(r,s)(n,o);let[l,u,i,a]=Uo(n,o),c=o.preset.cardNumber;return!c||!n.num?e(r,s)(n,o):c-l<=n.num&&n.num<=c+u?r(n,o):c-i<=n.num&&n.num<=c+a?s(n,o):t(e)(r,s)(n,o)},Uo=(t,{cache:e,preset:r})=>{let s="flashcardShowBottom",n="flashcardShowTop";if(!r.cardNumber)return[0,0,0,0];let o=e.get(s,Tt).get(t.key,r.cardNumber,t.fullOccur),l=e.get(n,Tt).get(t.key,r.cardNumber,t.fullOccur),u="flashcardHideBottom",i="flashcardHideTop",a=e.get(u,Tt).get(t.key,r.cardNumber,t.fullOccur),c=e.get(i,Tt).get(t.key,r.cardNumber,t.fullOccur);return[o,l,a,c]},Fs=Vo(Ho(Wo));var q=(t=tr,e=rr,r=Fs)=>(s,n)=>(o,l,u,i,a,c={})=>m=>{let h=`${o}:internal`;yt(Me(r(s)(i,a)),Me(l),Me(r(n)(i,a)),Me(u),t,e)({tagname:h})(m);let f=(y,v)=>v.filters.getOrDefault(h)(y,v);m.register(o,f,c)},dc=ye("[...]"),nr=(s=>(s.Show="show",s.Hide="hide",s.Reveal="reveal",s))(nr||{}),z=t=>{let e=t($,$),r=t(Xe,Xe),s=t(Xe,$),[n,o,l]=[["show","s"],["hide","h"],["reveal","r"]].map(([i,a])=>({getTagnames:c=>{var m,h;return[((m=c.defaultBehavior)!=null?m:"show")===i?c.tagname:(h=c.tagnameShow)!=null?h:`${c.tagname}${a}`]}})),u=bt([[r,o],[e,n],[s,l]]);return u.show=e,u.hide=r,u.reveal=s,u};var ws=t=>Array.isArray(t[0])?t[0].map((e,r)=>t.map(s=>s[r])):[t],Z=(t,e)=>(r,s)=>s.ready?t.stylize(...ws(e(r,s))):{ready:!1},ee=(t,e)=>(r,s)=>{if(!s.ready)return{ready:!1};let n=e(r,s);return n?t.stylize(...ws(n)):{ready:!1}};var qo=ye(''),Rs=t=>e=>`${e}`,jo=L.make({processor:Rs("front")}),Xo=L.make({processor:Rs("back")}),Ko=(t,e)=>{var r;return[`${(r=t.values[1])!=null?r:""}`]},Es=t=>`${t.values[0]}`,Jo=(t,{ready:e})=>({ready:e,result:`${Es(t)}`}),Yo=t=>[Es(t)],Qo=(t,e)=>(r={})=>{let{tagname:s="c",inactiveEllipser:n=qo,frontStylizer:o=jo,frontEllipser:l=Ko,backStylizer:u=Xo,backEllipser:i=Yo,optics:a=[E({sep:"::",max:2})],flashcardTemplate:c=q()}=r,m=Z(o,l),h=Z(u,i),d={optics:a};return c(t,e)(s,m,h,Jo,n,d)},Ns=z(Qo);var Zo=ye(''),ei=t=>t.values?t.values.flatMap((e,r)=>e.map(s=>[s,r])):[],ti=t=>t.values[0],or='',ri=L.make({processor:t=>`${t}`,mapper:t=>`${t}`,separator:or}),Is=t=>e=>`${e}`,si=L.make({processor:Is("front"),mapper:t=>`${t}`,separator:or}),ni=L.make({processor:Is("back"),mapper:(t,e,r)=>`${t}`,separator:or}),oi=[E({sep:"::"}),G(),E({sep:"||",keepEmpty:!1})],ii=(t,e)=>(r={})=>{let{tagname:s="mc",frontStylizer:n=si,backStylizer:o=ni,inactiveStylizer:l=ri,contexter:u=ti,ellipser:i=Zo,getValues:a=ei,sequence:c=Le,sortInStrategy:m=K,optics:h=oi,flashcardTemplate:d=q()}=r,f=c(a,m),y=ee(n,f),v=ee(o,f),x=d(t,e),R=Z(l,u);return x(s,y,v,R,i,{optics:h})},Cs=z(ii);var ir=t=>({result:t.values,parse:!0}),ai=()=>"",li=(t,e)=>(r={})=>{let{tagname:s="spec",flashcardTemplate:n=q()}=r,o={capture:!0};return n(t,e)(s,ir,ir,ir,ai,o)},Ds=z(li);var ui=(t,e)=>t.values,Bs=t=>e=>`${e}`,Ps=t=>``,ci=t=>()=>``,pi=t=>L.make({processor:e=>`${e}`,mapper:Bs(t),separator:Ps(t)}),mi=t=>L.make({processor:e=>`${e}`,mapper:Bs(t),separator:Ps(t)}),di=t=>t.values?t.values:[],$s=(t,e)=>Z(t,ui),hi=[E({sep:"::"})],ar=(t,e,r)=>(s,n)=>(o={})=>{let{tagname:l="shuf",activeStylizer:u=mi(t),inactiveStylizer:i=pi(t),contexter:a=di,ellipser:c=ci(t),sequence:m=Le,sortInStrategy:h=K,optics:d=hi,flashcardTemplate:f=q()}=o,y={optics:d},v=m(a,h),x=e(u,v),R=r(u,v),C=Z(i,a);return f(s,n)(l,x,R,C,c,y)},Os=z(ar("mingle",ee,ee)),Ms=z(ar("sort",ee,$s)),Ls=z(ar("jumble",$s,ee));var gi={cloze:Ns,multipleChoice:Cs,specification:Ds,mingle:Os,sort:Ms,jumble:Ls};var hr={};_(hr,{recipes:()=>zi});var ur={};_(ur,{key:()=>Gt,keySeparation:()=>X,unicodeAlphanumericPattern:()=>bi,unicodeLetterPattern:()=>fi,unicodeNumberPattern:()=>yi});var Gs="A-Za-z\\xAA\\xB5\\xBA\\xC0-\\xD6\\xD8-\\xF6\\xF8-\\u02C1\\u02C6-\\u02D1\\u02E0-\\u02E4\\u02EC\\u02EE\\u0370-\\u0374\\u0376\\u0377\\u037A-\\u037D\\u037F\\u0386\\u0388-\\u038A\\u038C\\u038E-\\u03A1\\u03A3-\\u03F5\\u03F7-\\u0481\\u048A-\\u052F\\u0531-\\u0556\\u0559\\u0561-\\u0587\\u05D0-\\u05EA\\u05F0-\\u05F2\\u0620-\\u064A\\u066E\\u066F\\u0671-\\u06D3\\u06D5\\u06E5\\u06E6\\u06EE\\u06EF\\u06FA-\\u06FC\\u06FF\\u0710\\u0712-\\u072F\\u074D-\\u07A5\\u07B1\\u07CA-\\u07EA\\u07F4\\u07F5\\u07FA\\u0800-\\u0815\\u081A\\u0824\\u0828\\u0840-\\u0858\\u08A0-\\u08B4\\u08B6-\\u08BD\\u0904-\\u0939\\u093D\\u0950\\u0958-\\u0961\\u0971-\\u0980\\u0985-\\u098C\\u098F\\u0990\\u0993-\\u09A8\\u09AA-\\u09B0\\u09B2\\u09B6-\\u09B9\\u09BD\\u09CE\\u09DC\\u09DD\\u09DF-\\u09E1\\u09F0\\u09F1\\u0A05-\\u0A0A\\u0A0F\\u0A10\\u0A13-\\u0A28\\u0A2A-\\u0A30\\u0A32\\u0A33\\u0A35\\u0A36\\u0A38\\u0A39\\u0A59-\\u0A5C\\u0A5E\\u0A72-\\u0A74\\u0A85-\\u0A8D\\u0A8F-\\u0A91\\u0A93-\\u0AA8\\u0AAA-\\u0AB0\\u0AB2\\u0AB3\\u0AB5-\\u0AB9\\u0ABD\\u0AD0\\u0AE0\\u0AE1\\u0AF9\\u0B05-\\u0B0C\\u0B0F\\u0B10\\u0B13-\\u0B28\\u0B2A-\\u0B30\\u0B32\\u0B33\\u0B35-\\u0B39\\u0B3D\\u0B5C\\u0B5D\\u0B5F-\\u0B61\\u0B71\\u0B83\\u0B85-\\u0B8A\\u0B8E-\\u0B90\\u0B92-\\u0B95\\u0B99\\u0B9A\\u0B9C\\u0B9E\\u0B9F\\u0BA3\\u0BA4\\u0BA8-\\u0BAA\\u0BAE-\\u0BB9\\u0BD0\\u0C05-\\u0C0C\\u0C0E-\\u0C10\\u0C12-\\u0C28\\u0C2A-\\u0C39\\u0C3D\\u0C58-\\u0C5A\\u0C60\\u0C61\\u0C80\\u0C85-\\u0C8C\\u0C8E-\\u0C90\\u0C92-\\u0CA8\\u0CAA-\\u0CB3\\u0CB5-\\u0CB9\\u0CBD\\u0CDE\\u0CE0\\u0CE1\\u0CF1\\u0CF2\\u0D05-\\u0D0C\\u0D0E-\\u0D10\\u0D12-\\u0D3A\\u0D3D\\u0D4E\\u0D54-\\u0D56\\u0D5F-\\u0D61\\u0D7A-\\u0D7F\\u0D85-\\u0D96\\u0D9A-\\u0DB1\\u0DB3-\\u0DBB\\u0DBD\\u0DC0-\\u0DC6\\u0E01-\\u0E30\\u0E32\\u0E33\\u0E40-\\u0E46\\u0E81\\u0E82\\u0E84\\u0E87\\u0E88\\u0E8A\\u0E8D\\u0E94-\\u0E97\\u0E99-\\u0E9F\\u0EA1-\\u0EA3\\u0EA5\\u0EA7\\u0EAA\\u0EAB\\u0EAD-\\u0EB0\\u0EB2\\u0EB3\\u0EBD\\u0EC0-\\u0EC4\\u0EC6\\u0EDC-\\u0EDF\\u0F00\\u0F40-\\u0F47\\u0F49-\\u0F6C\\u0F88-\\u0F8C\\u1000-\\u102A\\u103F\\u1050-\\u1055\\u105A-\\u105D\\u1061\\u1065\\u1066\\u106E-\\u1070\\u1075-\\u1081\\u108E\\u10A0-\\u10C5\\u10C7\\u10CD\\u10D0-\\u10FA\\u10FC-\\u1248\\u124A-\\u124D\\u1250-\\u1256\\u1258\\u125A-\\u125D\\u1260-\\u1288\\u128A-\\u128D\\u1290-\\u12B0\\u12B2-\\u12B5\\u12B8-\\u12BE\\u12C0\\u12C2-\\u12C5\\u12C8-\\u12D6\\u12D8-\\u1310\\u1312-\\u1315\\u1318-\\u135A\\u1380-\\u138F\\u13A0-\\u13F5\\u13F8-\\u13FD\\u1401-\\u166C\\u166F-\\u167F\\u1681-\\u169A\\u16A0-\\u16EA\\u16F1-\\u16F8\\u1700-\\u170C\\u170E-\\u1711\\u1720-\\u1731\\u1740-\\u1751\\u1760-\\u176C\\u176E-\\u1770\\u1780-\\u17B3\\u17D7\\u17DC\\u1820-\\u1877\\u1880-\\u1884\\u1887-\\u18A8\\u18AA\\u18B0-\\u18F5\\u1900-\\u191E\\u1950-\\u196D\\u1970-\\u1974\\u1980-\\u19AB\\u19B0-\\u19C9\\u1A00-\\u1A16\\u1A20-\\u1A54\\u1AA7\\u1B05-\\u1B33\\u1B45-\\u1B4B\\u1B83-\\u1BA0\\u1BAE\\u1BAF\\u1BBA-\\u1BE5\\u1C00-\\u1C23\\u1C4D-\\u1C4F\\u1C5A-\\u1C7D\\u1C80-\\u1C88\\u1CE9-\\u1CEC\\u1CEE-\\u1CF1\\u1CF5\\u1CF6\\u1D00-\\u1DBF\\u1E00-\\u1F15\\u1F18-\\u1F1D\\u1F20-\\u1F45\\u1F48-\\u1F4D\\u1F50-\\u1F57\\u1F59\\u1F5B\\u1F5D\\u1F5F-\\u1F7D\\u1F80-\\u1FB4\\u1FB6-\\u1FBC\\u1FBE\\u1FC2-\\u1FC4\\u1FC6-\\u1FCC\\u1FD0-\\u1FD3\\u1FD6-\\u1FDB\\u1FE0-\\u1FEC\\u1FF2-\\u1FF4\\u1FF6-\\u1FFC\\u2071\\u207F\\u2090-\\u209C\\u2102\\u2107\\u210A-\\u2113\\u2115\\u2119-\\u211D\\u2124\\u2126\\u2128\\u212A-\\u212D\\u212F-\\u2139\\u213C-\\u213F\\u2145-\\u2149\\u214E\\u2183\\u2184\\u2C00-\\u2C2E\\u2C30-\\u2C5E\\u2C60-\\u2CE4\\u2CEB-\\u2CEE\\u2CF2\\u2CF3\\u2D00-\\u2D25\\u2D27\\u2D2D\\u2D30-\\u2D67\\u2D6F\\u2D80-\\u2D96\\u2DA0-\\u2DA6\\u2DA8-\\u2DAE\\u2DB0-\\u2DB6\\u2DB8-\\u2DBE\\u2DC0-\\u2DC6\\u2DC8-\\u2DCE\\u2DD0-\\u2DD6\\u2DD8-\\u2DDE\\u2E2F\\u3005\\u3006\\u3031-\\u3035\\u303B\\u303C\\u3041-\\u3096\\u309D-\\u309F\\u30A1-\\u30FA\\u30FC-\\u30FF\\u3105-\\u312D\\u3131-\\u318E\\u31A0-\\u31BA\\u31F0-\\u31FF\\u3400-\\u4DB5\\u4E00-\\u9FD5\\uA000-\\uA48C\\uA4D0-\\uA4FD\\uA500-\\uA60C\\uA610-\\uA61F\\uA62A\\uA62B\\uA640-\\uA66E\\uA67F-\\uA69D\\uA6A0-\\uA6E5\\uA717-\\uA71F\\uA722-\\uA788\\uA78B-\\uA7AE\\uA7B0-\\uA7B7\\uA7F7-\\uA801\\uA803-\\uA805\\uA807-\\uA80A\\uA80C-\\uA822\\uA840-\\uA873\\uA882-\\uA8B3\\uA8F2-\\uA8F7\\uA8FB\\uA8FD\\uA90A-\\uA925\\uA930-\\uA946\\uA960-\\uA97C\\uA984-\\uA9B2\\uA9CF\\uA9E0-\\uA9E4\\uA9E6-\\uA9EF\\uA9FA-\\uA9FE\\uAA00-\\uAA28\\uAA40-\\uAA42\\uAA44-\\uAA4B\\uAA60-\\uAA76\\uAA7A\\uAA7E-\\uAAAF\\uAAB1\\uAAB5\\uAAB6\\uAAB9-\\uAABD\\uAAC0\\uAAC2\\uAADB-\\uAADD\\uAAE0-\\uAAEA\\uAAF2-\\uAAF4\\uAB01-\\uAB06\\uAB09-\\uAB0E\\uAB11-\\uAB16\\uAB20-\\uAB26\\uAB28-\\uAB2E\\uAB30-\\uAB5A\\uAB5C-\\uAB65\\uAB70-\\uABE2\\uAC00-\\uD7A3\\uD7B0-\\uD7C6\\uD7CB-\\uD7FB\\uF900-\\uFA6D\\uFA70-\\uFAD9\\uFB00-\\uFB06\\uFB13-\\uFB17\\uFB1D\\uFB1F-\\uFB28\\uFB2A-\\uFB36\\uFB38-\\uFB3C\\uFB3E\\uFB40\\uFB41\\uFB43\\uFB44\\uFB46-\\uFBB1\\uFBD3-\\uFD3D\\uFD50-\\uFD8F\\uFD92-\\uFDC7\\uFDF0-\\uFDFB\\uFE70-\\uFE74\\uFE76-\\uFEFC\\uFF21-\\uFF3A\\uFF41-\\uFF5A\\uFF66-\\uFFBE\\uFFC2-\\uFFC7\\uFFCA-\\uFFCF\\uFFD2-\\uFFD7\\uFFDA-\\uFFDC",_s="0-9\\xB2\\xB3\\xB9\\xBC-\\xBE\\u0660-\\u0669\\u06F0-\\u06F9\\u07C0-\\u07C9\\u0966-\\u096F\\u09E6-\\u09EF\\u09F4-\\u09F9\\u0A66-\\u0A6F\\u0AE6-\\u0AEF\\u0B66-\\u0B6F\\u0B72-\\u0B77\\u0BE6-\\u0BF2\\u0C66-\\u0C6F\\u0C78-\\u0C7E\\u0CE6-\\u0CEF\\u0D58-\\u0D5E\\u0D66-\\u0D78\\u0DE6-\\u0DEF\\u0E50-\\u0E59\\u0ED0-\\u0ED9\\u0F20-\\u0F33\\u1040-\\u1049\\u1090-\\u1099\\u1369-\\u137C\\u16EE-\\u16F0\\u17E0-\\u17E9\\u17F0-\\u17F9\\u1810-\\u1819\\u1946-\\u194F\\u19D0-\\u19DA\\u1A80-\\u1A89\\u1A90-\\u1A99\\u1B50-\\u1B59\\u1BB0-\\u1BB9\\u1C40-\\u1C49\\u1C50-\\u1C59\\u2070\\u2074-\\u2079\\u2080-\\u2089\\u2150-\\u2182\\u2185-\\u2189\\u2460-\\u249B\\u24EA-\\u24FF\\u2776-\\u2793\\u2CFD\\u3007\\u3021-\\u3029\\u3038-\\u303A\\u3192-\\u3195\\u3220-\\u3229\\u3248-\\u324F\\u3251-\\u325F\\u3280-\\u3289\\u32B1-\\u32BF\\uA620-\\uA629\\uA6E6-\\uA6EF\\uA830-\\uA835\\uA8D0-\\uA8D9\\uA900-\\uA909\\uA9D0-\\uA9D9\\uA9F0-\\uA9F9\\uAA50-\\uAA59\\uABF0-\\uABF9\\uFF10-\\uFF19",fi=new RegExp(`[${Gs}]`,"gu"),yi=new RegExp(`[${_s}]`,"gu"),bi=new RegExp(`[${Gs}${_s}]`,"gu");var Ti=/]*?src="(.+?)"[^>]*>/g,xi=(t,e)=>{let r=[],s;do s=Ti.exec(t),s&&r.push([s[1],e]);while(s);return r},xt=t=>{let e=([r,s])=>typeof s=="string"?[]:xi(r,s.getRootNode());return t.textFragments.map((r,s)=>[r,t.inputs[s]]).flatMap(e)},de=t=>{if(navigator.userAgent.search("Chrome")>=0)return[t.offsetX,t.offsetY];{let e=t.currentTarget,r=e.getBoundingClientRect(),s=e.parentElement.getBoundingClientRect(),n=t.layerX-(s.left-r.left),o=t.layerY-(s.top-r.top);return[n,o]}},vt=(t,e,r)=>{let s=e.querySelector(t);s&&(s.complete?r({target:s}):s.addEventListener("load",r))},zs=t=>{let e=0;for(let r of t){let s=r.match(X);if(!s)continue;let n=Number(s[2]);Number.isNaN(n)||(e=Math.max(e,n))}return e},St="occlusionSvgCss",kt=` +img { + max-width: 100% !important; +} + +.closet-occlusion-container { + display: inline-block; + position: relative; +} + +.closet-occlusion-container > * { + display: block; + + margin-left: auto; + margin-right: auto; +} + +.closet-occlusion-container > svg { + position: absolute; + top: 0; +} + +.closet-shape > text { + text-anchor: middle; + dominant-baseline: central; + pointer-events: none; +}`;var At="http://www.w3.org/2000/svg",Ae=class t{constructor(e,r,s){this.scaleFactorX=1;this.scaleFactorY=1;this.maxOcclusions=500;this.elements=[];this.container=e,this.image=r,this.svg=s,this.resizer=new globalThis.ResizeObserver(()=>this.resize()),this.resizer.observe(r),this.setScaleFactors()}setScaleFactors(){this.scaleFactorX=this.image.width/this.image.naturalWidth,this.scaleFactorY=this.image.height/this.image.naturalHeight}static wrapImage(e){let r=document.createElement("div");e.parentNode&&e.parentNode.replaceChild(r,e),r.appendChild(e);let s=document.createElementNS(At,"svg");s.setAttributeNS(null,"width","100%"),s.setAttributeNS(null,"height","100%");let n=window.getComputedStyle(e);return s.style.zoom=n.getPropertyValue("zoom"),s.style.transform=n.getPropertyValue("transform"),r.appendChild(s),r.classList.add("closet-occlusion-container"),new t(r,e,s)}get scaleFactors(){return[this.scaleFactorX,this.scaleFactorY]}resize(){if(this.setScaleFactors(),this.scaleFactors.includes(0)){this.resizer.disconnect();return}for(let e of this.elements)e.resize(this)}append(e){this.elements.push(e);for(let r of e.getElements())this.svg.appendChild(r)}getElements(){return this.elements}getLabels(){return this.getElements().map(e=>e.getLabel())}remove(e){this.elements.find((r,s)=>{if(r.getAnchorElement()===e.getAnchorElement()){for(let n of e.getElements())n.remove();return this.elements.splice(s,1),!0}})}setMaxOcclusions(e){this.maxOcclusions=e}getNextNum(e){let r=e.altKey?0:1,s=Math.max(1,zs(this.getLabels())+r);return s>this.maxOcclusions?0:s}cleanup(){let e=this.container.removeChild(this.image);this.container.replaceWith(e)}},he=class t{constructor(e,r,s,n,o){this.scaleFactorX=1;this.scaleFactorY=1;this.container=e,this.rect=r,this.label=s,this.setScaleFactors(n,o)}static make(e){let r=document.createElementNS(At,"svg"),s=document.createElementNS(At,"rect"),n=document.createElementNS(At,"text");r.appendChild(s),r.appendChild(n),r.classList.add("closet-rect"),r.classList.add("closet-shape"),r.tabIndex=-1;let[o,l]=e?e.scaleFactors:[1,1];return new t(r,s,n,o,l)}static wrap(e,r){let[s,n]=r?r.scaleFactors:[1,1];return new t(e.parentElement,e,e.nextSibling,s,n)}remove(){this.container.remove()}getElements(){return[this.container]}getAnchorElement(){return this.rect}getLabel(){return this.labelText}setScaleFactors(e,r){this.scaleFactorX=e,this.scaleFactorY=r}readjust(e){let r=e?e.scaleFactors:[1,1],s=this.scaleFactorX,n=this.scaleFactorY;return this.setScaleFactors(r[0],r[1]),[s,n]}resize(e){let r=this.pos;this.readjust(e),this.pos=r}toDefinition(){return["rect",void 0,this.labelText,this.x,this.y,this.width,this.height,{}]}toText(e){return`${e.open}${this.labelText}${e.sep}${this.x.toFixed()},${this.y.toFixed()},${this.width.toFixed()},${this.height.toFixed()}${e.close}`}fontSizeUpdate(){let[e,r]=this.readjust(),s=Math.max(.1,Math.min(32,.4*((this.width+this.height)/2-2*this.strokeWidth)));this.fontSize=s,this.setScaleFactors(e,r)}get pos(){return[this.x,this.y,this.width,this.height]}set pos([e,r,s,n]){this.width=s,this.height=n,this.x=e,this.y=r}get scaled(){return[Number(this.rect.getAttributeNS(null,"x")),Number(this.rect.getAttributeNS(null,"y")),Number(this.rect.getAttributeNS(null,"width")),Number(this.rect.getAttributeNS(null,"height"))]}get x(){return Number(this.rect.getAttributeNS(null,"x"))/this.scaleFactorX}set x(e){let r=e*this.scaleFactorX,s=(e+this.width/2)*this.scaleFactorX;this.rect.setAttributeNS(null,"x",String(r)),this.label.setAttributeNS(null,"x",String(s))}get y(){return Number(this.rect.getAttributeNS(null,"y"))/this.scaleFactorY}set y(e){let r=e*this.scaleFactorY,s=(e+this.height/2)*this.scaleFactorY;this.rect.setAttributeNS(null,"y",String(r)),this.label.setAttributeNS(null,"y",String(s))}get width(){return Number(this.rect.getAttributeNS(null,"width"))/this.scaleFactorX}set width(e){let r=String(Math.max(10,e)*this.scaleFactorX);this.rect.setAttributeNS(null,"width",r),this.label.setAttributeNS(null,"width",r),this.fontSizeUpdate()}get height(){return Number(this.rect.getAttributeNS(null,"height"))/this.scaleFactorY}set height(e){let r=String(Math.max(10,e)*this.scaleFactorY);this.rect.setAttributeNS(null,"height",r),this.label.setAttributeNS(null,"height",r),this.fontSizeUpdate()}props(e){for(let r in e){let s=r;this.prop(s,e[s])}}prop(e,r){this[e]=r}set containerClasses(e){e.split(" ").forEach(r=>this.container.classList.add(r))}get containerClasses(){return Array.from(this.container.className).join(" ")}set rx(e){this.rect.setAttributeNS(null,"rx",String(e))}get rx(){return Number(this.rect.getAttributeNS(null,"rx"))}set ry(e){this.rect.setAttributeNS(null,"ry",String(e))}get ry(){return Number(this.rect.getAttributeNS(null,"ry"))}set fill(e){this.rect.setAttributeNS(null,"fill",e)}get fill(){var e;return(e=this.rect.getAttributeNS(null,"fill"))!=null?e:""}set fillOpacity(e){this.rect.setAttributeNS(null,"fill-opacity",String(e))}get fillOpacity(){return Number(this.rect.getAttributeNS(null,"fill-opacity"))}set stroke(e){this.rect.setAttributeNS(null,"stroke",e)}get stroke(){var e;return(e=this.rect.getAttributeNS(null,"stroke"))!=null?e:""}set strokeOpacity(e){this.rect.setAttributeNS(null,"stroke-opacity",String(e))}get strokeOpacity(){return Number(this.rect.getAttributeNS(null,"stroke-opacity"))}set strokeWidth(e){this.rect.setAttributeNS(null,"stroke-width",String(e))}get strokeWidth(){return Number(this.rect.getAttributeNS(null,"stroke-width"))}set classes(e){e.split(" ").forEach(r=>this.rect.classList.add(r))}get classes(){return Array.from(this.rect.classList).join(" ")}set labelText(e){this.label.innerHTML=e}get labelText(){return this.label.innerHTML}set fontSize(e){this.label.setAttributeNS(null,"font-size",String(e))}get fontSize(){return Number(this.label.getAttributeNS(null,"font-size"))}set labelClasses(e){e.split(" ").forEach(r=>this.label.classList.add(r))}get labelClasses(){return Array.from(this.label.classList).join(" ")}};var cr=(t,e,r,s,n,o,l,u)=>i=>{let[a,c]=t(de(i));r&&al=>{let[u,i]=t(de(l)),a=r+(u-n),c=s+(i-o);e.x=a,e.y=c},Vs=(t,e,r,s,n,o,l,u,i)=>(a,c,m)=>{let[d,f,y,v]=a.scaled,x=c-d,R=d+y-c,C=m-f,I=f+v-m;return x<=5?C<=5?t(a):I<=5?e(a):r(a):R<=5?C<=5?s(a):I<=5?n(a):o(a):C<=5?l(a):I<=5?u(a):i(a)},Hs=Vs(t=>[!0,!1,!0,!1,t.x+t.width,t.y+t.height],t=>[!0,!1,!1,!0,t.x+t.width,t.y],t=>[!0,!1,!1,!1,t.x+t.width,0],t=>[!1,!0,!0,!1,t.x,t.y+t.height],t=>[!1,!0,!1,!0,t.x,t.y],t=>[!1,!0,!1,!1,t.x,0],t=>[!1,!1,!0,!1,0,t.y+t.height],t=>[!1,!1,!1,!0,0,t.y],()=>[!1,!1,!1,!1,0,0]),vi=Vs(()=>"nwse-resize",()=>"nesw-resize",()=>"ew-resize",()=>"nesw-resize",()=>"nwse-resize",()=>"ew-resize",()=>"ns-resize",()=>"ns-resize",()=>"move"),Us=(t,e)=>r=>{if(r.shiftKey)e.rect.style.cursor="no-drop";else{let[s,n]=t(de(r)),o=vi(e,s,n);e.rect.style.cursor=o}};var Si=t=>([e,r])=>{let s=t.getPropertyValue("zoom"),n=Number(s);return Number.isNaN(n)||n<=0?[e,r]:[e/n,r/n]},Ft=t=>([e,r])=>Si(t)([e,r]);var ki=t=>{let e=document.createElement("li"),r=document.createElement("a");return t.html?r.innerHTML=t.label:r.innerText=t.label,r.id=t.itemId,r.classList.add("context-menu-item"),t.clickEvent&&r.addEventListener("click",t.clickEvent),e.appendChild(r),t.sub&&t.sub.length>0&&e.appendChild(qs(t.sub)),e},qs=t=>{let e=document.createElement("ul"),r=t.map(ki);for(let s of r)e.appendChild(s);return e},js=(t,e)=>{let r=document.createElement("nav");return r.classList.add("context-menu"),r.id=t,r.appendChild(qs(e)),r};var Xs=` +:root { + --cm-niceblue: #3f8cf1; + --cm-nicegray: #444; + --cm-backgray: #ececec; + --cm-boxgray: #cfcfcf; +} + +nav.context-menu { + position: absolute; + z-index: 9999; + display: none; +} + +nav.context-menu--active { + display: block; +} + +/******************** UL */ + +nav.context-menu ul { + position: absolute; + + padding: 0.2rem 0; + width: 10rem; + + white-space: nowrap; + list-style: none; + + background-color: var(--cm-backgray); + border: solid 1px var(--cm-boxgray); + box-shadow: 1px 1px 2px var(--cm-boxgray); +} + +/******************** LI */ + +nav.context-menu li { + margin-bottom: 2px; +} + +nav.context-menu li.context-menu--hover { + background-color: var(--cm-niceblue); +} + +nav.context-menu li:last-child { + margin-bottom: 0; +} + +nav.context-menu li ul { + display: none; + margin-left: 2rem; +} + +nav.context-menu li.context-menu--hover ul { + display: block; +} + +nav.context-menu li li ul { + display: none; + margin-left: 4rem; +} + +nav.context-menu li li.context-menu--hover ul { + display: block; +} + +/******************** A */ + +nav.context-menu a { + display: block; + padding: 0.3rem 1rem; + user-select: none; + + color: var(--cm-nicegray); +} + +nav.context-menu li.context-menu--hover a { + color: white; +} +`,Ks=t=>()=>{t.classList.remove("context-menu--active")},Ai=(t,e,r)=>{t.style.left=`${e}px`,t.style.top=`${r}px`},Fi=t=>{t.addEventListener("click",e=>{e.stopPropagation()}),t.querySelectorAll("li").forEach(e=>{e.addEventListener("mouseenter",()=>{e.classList.add("context-menu--hover")}),e.addEventListener("mouseleave",()=>{e.classList.remove("context-menu--hover")}),e.addEventListener("click",()=>{Ks(t)()})})},pr=(t,e)=>{let r=Ks(t);e.addEventListener("contextmenu",s=>{s.preventDefault(),s.stopPropagation(),document.querySelectorAll(".context-menu--active").forEach(n=>n.classList.remove("context-menu--active")),t.classList.add("context-menu--active"),window.addEventListener("click",r,{once:!0}),Ai(t,s.pageX,s.pageY)})},mr=(t,e)=>{let r=js(t,e);return document.body.insertAdjacentElement("beforeend",r),Fi(r),r};var Rt="occlusionRenderRect",wi=(t,{template:e,cache:r})=>{let s=xt(e),n=r.get(t.keyword,[]),o=s[0];o&&vt(`img[src="${o[0]}"]`,o[1],l=>{e.appendStyle(l.target,St,kt);let i=Ae.wrapImage(l.target);for(let[,a,,c,m,h,d,f]of n){if(!a)continue;let y=he.make(i);`${c}${m}${h}${d}`,y.pos=[c,m,h,d];for(let v in f){let x=v;y.prop(x,f[x])}i.append(y)}})},Ri=(t,e={})=>{let r={};return t.map(s=>s.split("=")).forEach(([s,n])=>r[s]=n),Object.assign(r,e)},wt=t=>(e,r)=>{let[s=0,n=0,o=50,l=o,...u]=e.values,i=typeof t=="function"?t(e,r):t;return r.cache.over(Rt,a=>a.push(["rect",!0,e.fullKey,Number(s),Number(n),Number(o),Number(l),Ri(u,i)]),[]),r.aftermath.registerIfNotExists(Rt,wi),{ready:!0}},dr={classes:"closet-rect__rect",labelClasses:"closet-rect__label"},Ei={classes:"closet-rect__ellipsis",labelClasses:"closet-rect__ellipsis"},Ni="is-active is-front",Ii="is-active is-back",Js="is-inactive",Ci=(t,e)=>(r={})=>{let{tagname:s="rect",front:n=wt,back:o=wt,optics:l=[E({sep:","})],flashcardTemplate:u=q(),frontProperties:i=ie(oe({},dr),{containerClasses:Ni}),backProperties:a=ie(oe({},dr),{containerClasses:Ii}),ellipseProperties:c=ie(oe({},Ei),{containerClasses:Js}),contextProperties:m=ie(oe({},dr),{containerClasses:Js})}=r,h={optics:l};return u(t,e)(s,n(i),o(a),wt(c),wt(m),h)},Ys=z(Ci);var Di=(t,e)=>{let r=he.wrap(e.target);if(e.shiftKey){t.remove(r);return}let s=Ft(window.getComputedStyle(t.image)),[n,o]=s(de(e)),l=Hs(r,n,o),u=l.includes(!0)?cr(s,r,...l):Ws(s,r,r.x,r.y,n,o);t.svg.addEventListener("mousemove",u),t.svg.addEventListener("mouseup",i=>{i.preventDefault(),t.svg.removeEventListener("mousemove",u)},{once:!0})},Zs=(t,e)=>{let r=Ft(window.getComputedStyle(t.image));e.rect.addEventListener("mousemove",Us(r,e));let s=mr("occlusion-shape-menu",[{label:"Change Label",itemId:"change-label",clickEvent:()=>{let n=prompt("Specify the new label",e.labelText);typeof n=="string"&&(e.labelText=n)}},{label:"Close Menu",itemId:"close-occlusion-menu"}]);pr(s,e.rect),t.append(e)},en=(t,e,r,s,n)=>{let o=he.make();return o.pos=[t,e,r,s],o.labelText=n,o.classes="closet-rect__rect",o.labelClasses="closet-rect__label",o},Bi=(t,e)=>{let r=null,s=null,n=null,o=null,l=null,u=null,[i,,...a]=e;switch(i){case"rect":[r,s,n,o,l]=a,u=en(s,n,o,l,r),Zs(t,u);break;default:}},Pi=(t,e)=>{let r=Ft(window.getComputedStyle(t.image)),[s,n]=r(de(e)),o=t.getNextNum(e),l=en(s,n,0,0,`rect${o}`);Zs(t,l);let u=cr(r,l,!0,!0,!0,!0,s,n);t.svg.addEventListener("mousemove",u),t.svg.addEventListener("mouseup",()=>{t.svg.removeEventListener("mousemove",u),l.readjust(t)})},$i=(t,e)=>{e.preventDefault(),(e.target.nodeName!=="svg"?Di:Pi)(t,e)},Oi=(t,e)=>{let s=e([{label:"Accept occlusions",itemId:"occlusion-accept",clickEvent:n=>{n.preventDefault(),t.dispatchEvent(new Event("accept"))}},{label:"Close Menu",itemId:"close-occlusion-menu"}]);return mr("occlusion-menu",s)},Mi=(t,e)=>{let r=s=>{s.button===0&&$i(t,s)};t.svg.addEventListener("mousedown",r),pr(e,t.svg)},Li=(t,e)=>r=>{navigator.clipboard.writeText(r.map(s=>s.toText(e.template.parser.delimiters)).join(` +`)).catch(()=>console.log("Window needs active focus for copying to clipboard"))},Gi=()=>(t,e)=>e.cleanup(),_i=` +.closet-occlusion-container { + outline: 3px dotted hotpink; + outline-offset: -3px; +}`,Qs="occlusionMakerCss",tn=(t={})=>e=>{let r="makeOcclusions",s=new EventTarget,{tagname:n=r,maxOcclusions:o=500,acceptHandler:l=Li,rejectHandler:u=Gi,setupOcclusionMenu:i=$,existingShapesFilter:a=()=>$,shapeKeywords:c=[Rt]}=t,m=(h,d)=>{let f=d.template,y=xt(f);return d.aftermath.registerIfNotExists(r,(v,x)=>{let R=[];for(let I of c){let O=x.cache.get(I,[]);R.push(...O),x.aftermath.block(I)}let C=I=>{let O=I.target,D=x.template;D.appendDocumentStyle(Qs,Xs),D.appendStyle(O,Qs,_i),D.appendStyle(O,St,kt);let W=Ae.wrapImage(O);W.setMaxOcclusions(o),a(v,x)(R,W).forEach(g=>Bi(W,g));let Fe=()=>{l(v,x)(W.getElements(),W)},te=()=>{u(v,x)(W.getElements(),W)};s.addEventListener("accept",Fe),s.addEventListener("reject",te);let p=Oi(s,i);Mi(W,p)};for(let[I,O]of y)vt(`img[src="${I}"]`,O,C)},{priority:100}),{ready:!0}};return e.register(n,m),s};var zi={occlusionEditor:tn,rect:Ys};var gr={};_(gr,{aftermath:()=>ks,collection:()=>bt,deferred:()=>Ss,product:()=>rn,sum:()=>ft,sumFour:()=>yt,wrap:()=>sr});var rn=(t,e,r=()=>()=>({ready:!0}),{wrapId:s,setTagnames:n}={wrapId:"product",getTagnames:J,setTagnames:Y})=>({tagname:o="prod",optionsFirst:l={},optionsSecond:u={}}={})=>i=>{let a=`${o}:${s}:fst`,c=`${o}:${s}:snd`;t(n(l,[a]))(i),e(n(u,[c]))(i);let m=(h,d)=>r(d.filters.getOrDefault(a)(h,d),d.filters.getOrDefault(c)(h,d))(h,d);i.register(o,m,i.getOptions(a))};export{vr as FilterManager,L as Stylizer,hr as browser,lr as flashcard,ur as patterns,er as recipes,Qt as sequencers,Zt as sortInStrategies,Ut as template,gr as wrappers}; diff --git a/src/anking_notetypes/resources/__closet-0.6.2.js b/src/anking_notetypes/resources/__closet-0.6.2.js deleted file mode 100644 index 8341052..0000000 --- a/src/anking_notetypes/resources/__closet-0.6.2.js +++ /dev/null @@ -1,4 +0,0 @@ -/* This file is part of closet which is released under GPL-3.0 -GitHub: https://github.com/hgiesel/closet */ - -var e = [0, 6, 1], t = [], n = e.join(".") + (t.length > 0 ? "-" + t.join(".") : ""), r = function (e, t) { return r = Object.setPrototypeOf || { __proto__: [] } instanceof Array && function (e, t) { e.__proto__ = t } || function (e, t) { for (var n in t) t.hasOwnProperty(n) && (e[n] = t[n]) }, r(e, t) }; function o(e, t) { function n() { this.constructor = e } r(e, t), e.prototype = null === t ? Object.create(t) : (n.prototype = t.prototype, new n) } var i = function () { return i = Object.assign || function (e) { for (var t, n = 1, r = arguments.length; n < r; n++)for (var o in t = arguments[n]) Object.prototype.hasOwnProperty.call(t, o) && (e[o] = t[o]); return e }, i.apply(this, arguments) }; function u(e, t) { var n, r, o, i, u = { label: 0, sent: function () { if (1 & o[0]) throw o[1]; return o[1] }, trys: [], ops: [] }; return i = { next: s(0), throw: s(1), return: s(2) }, "function" == typeof Symbol && (i[Symbol.iterator] = function () { return this }), i; function s(i) { return function (s) { return function (i) { if (n) throw new TypeError("Generator is already executing."); for (; u;)try { if (n = 1, r && (o = 2 & i[0] ? r.return : i[0] ? r.throw || ((o = r.return) && o.call(r), 0) : r.next) && !(o = o.call(r, i[1])).done) return o; switch (r = 0, o && (i = [2 & i[0], o.value]), i[0]) { case 0: case 1: o = i; break; case 4: return u.label++, { value: i[1], done: !1 }; case 5: u.label++, r = i[1], i = [0]; continue; case 7: i = u.ops.pop(), u.trys.pop(); continue; default: if (!(o = u.trys, (o = o.length > 0 && o[o.length - 1]) || 6 !== i[0] && 2 !== i[0])) { u = 0; continue } if (3 === i[0] && (!o || i[1] > o[0] && i[1] < o[3])) { u.label = i[1]; break } if (6 === i[0] && u.label < o[1]) { u.label = o[1], o = i; break } if (o && u.label < o[2]) { u.label = o[2], u.ops.push(i); break } o[2] && u.ops.pop(), u.trys.pop(); continue }i = t.call(e, u) } catch (e) { i = [6, e], r = 0 } finally { n = o = 0 } if (5 & i[0]) throw i[1]; return { value: i[0] ? i[1] : void 0, done: !0 } }([i, s]) } } } function s(e) { var t = "function" == typeof Symbol && Symbol.iterator, n = t && e[t], r = 0; if (n) return n.call(e); if (e && "number" == typeof e.length) return { next: function () { return e && r >= e.length && (e = void 0), { value: e && e[r++], done: !e } } }; throw new TypeError(t ? "Object is not iterable." : "Symbol.iterator is not defined.") } function a(e, t) { var n = "function" == typeof Symbol && e[Symbol.iterator]; if (!n) return e; var r, o, i = n.call(e), u = []; try { for (; (void 0 === t || t-- > 0) && !(r = i.next()).done;)u.push(r.value) } catch (e) { o = { error: e } } finally { try { r && !r.done && (n = i.return) && n.call(i) } finally { if (o) throw o.error } } return u } function c() { for (var e = [], t = 0; t < arguments.length; t++)e = e.concat(a(arguments[t])); return e } var l, p = function () { function e(e, t) { this.filters = e, this.options = t } return e.prototype.register = function (e, t, n) { void 0 === n && (n = {}), this.filters.register(e, t), this.options.set(e, n) }, e.prototype.getOptions = function (e) { return this.options.get(e, {}) }, e }(), f = function () { function e(e, t, n, r, o, i, u, s) { this.preset = e, this.filters = t, this.options = n, this.registrar = new p(t, n), this.deferred = r, this.aftermath = o, this.cache = i, this.memory = u, this.environment = s } return e.prototype.getBaseInternals = function (e) { return Object.assign({}, { filters: this.filters, options: this.options, registrar: this.registrar, cache: this.cache, memory: this.memory, environment: this.environment, deferred: this.deferred, aftermath: this.aftermath, preset: this.preset }, e) }, e.prototype.getAftermathInternals = function (e, t) { return Object.assign(this.getBaseInternals(e), t) }, e.prototype.getDeferredInternals = function (e, t) { return Object.assign(this.getBaseInternals(e), t) }, e.prototype.getInternals = function (e, t, n) { return Object.assign(this.getDeferredInternals(e, t), n) }, e.prototype.filterAccessor = function (e, t) { var n = this; return { getProcessor: function (r) { return { execute: function (o, i) { return n.filters.execute(r, o, n.getInternals(e, t, i)) }, getOptions: function () { return n.options.get(r, {}) } } } } }, e.prototype.executeDeferred = function (e, t) { var n = this.getDeferredInternals(e, t); this.deferred.executeEach(n) }, e.prototype.executeAftermath = function (e, t) { var n = this.getAftermathInternals(e, t); this.aftermath.executeEach(n) }, e.prototype.switchPreset = function (e) { this.preset = e }, e.prototype.clear = function () { this.memory.clear(), this.aftermath.clear() }, e.prototype.reset = function () { this.cache.clear(), this.deferred.clear() }, e.prototype.install = function () { for (var e, t, n = [], r = 0; r < arguments.length; r++)n[r] = arguments[r]; try { for (var o = s(n), i = o.next(); !i.done; i = o.next()) { var u = i.value; u(this.registrar) } } catch (t) { e = { error: t } } finally { try { i && !i.done && (t = o.return) && t.call(o) } finally { if (e) throw e.error } } }, e }(), h = function (e, t) { return { ready: t, result: e, parse: !1 } }, d = function (e, t) { return n = e.toReprString(), r = t.ready, h(n, r); var n, r }, m = { ready: !1, result: [], parse: !1 }, v = function (e) { var t, n, r; switch (typeof e) { case "string": return h(e, !0); case "object": return { ready: null === (t = e.ready) || void 0 === t || t, result: null !== (n = e.result) && void 0 !== n ? n : [], parse: null !== (r = e.parse) && void 0 !== r && r }; default: return m } }, y = function () { function e() { this.filters = new Map } return e.prototype.register = function (e, t) { this.filters.set(e, t) }, e.prototype.has = function (e) { return this.filters.has(e) }, e.prototype.get = function (e) { var t; return null !== (t = this.filters.get(e)) && void 0 !== t ? t : null }, e.prototype.getOrDefault = function (e) { var t; return null !== (t = this.get(e)) && void 0 !== t ? t : d }, e.prototype.unregisterFilter = function (e) { this.filters.delete(e) }, e.prototype.clearFilters = function () { this.filters.clear() }, e.prototype.execute = function (e, t, n) { var r = this.getOrDefault(e); return v(r(t, n)) }, e }(), g = function () { function e(e) { this.storage = e } return e.prototype.set = function (e, t) { this.storage.set(e, t) }, e.prototype.get = function (e, t) { var n; return null !== (n = this.storage.get(e)) && void 0 !== n ? n : t }, e.prototype.has = function (e) { return this.storage.has(e) }, e.prototype.fold = function (e, t, n) { var r = t(this.get(e, n)); return this.set(e, r), r }, e.prototype.post = function (e, t, n) { var r = this.get(e, n); return this.set(e, t(r)), r }, e.prototype.lazy = function (e, t) { var n; return null !== (n = this.get(e, null)) && void 0 !== n ? n : this.fold(e, t, null) }, e.prototype.over = function (e, t, n) { var r = this.get(e, n); return this.set(e, r), t(r) }, e.prototype.delete = function (e) { this.storage.delete(e) }, e.prototype.clear = function () { this.storage.clear() }, e }(), b = function (e) { return (e + 1 >>> 1) - 1 }, x = function (e) { return 1 + (e << 1) }, A = function (e) { return e + 1 << 1 }, w = function () { function e(e) { this._heap = [], this._comparator = e } return e.prototype.greater = function (e, t) { return this._comparator(this._heap[e], this._heap[t]) }, e.prototype.swap = function (e, t) { var n; n = a([this._heap[t], this._heap[e]], 2), this._heap[e] = n[0], this._heap[t] = n[1] }, e.prototype.siftUp = function () { for (var e = this.size() - 1; e > 0 && this.greater(e, b(e));)this.swap(e, b(e)), e = b(e) }, e.prototype.siftDown = function () { for (var e = 0; x(e) < this.size() && this.greater(x(e), e) || A(e) < this.size() && this.greater(A(e), e);) { var t = A(e) < this.size() && this.greater(A(e), x(e)) ? A(e) : x(e); this.swap(e, t), e = t } }, e.prototype.size = function () { return this._heap.length }, e.prototype.isEmpty = function () { return 0 === this.size() }, e.prototype.peek = function () { return this._heap[0] }, e.prototype.push = function () { for (var e = this, t = [], n = 0; n < arguments.length; n++)t[n] = arguments[n]; return t.forEach((function (t) { e._heap.push(t), e.siftUp() })), this.size() }, e.prototype.pop = function () { var e = this.peek(), t = this.size() - 1; return t > 0 && this.swap(0, t), this._heap.pop(), this.siftDown(), e }, e.prototype.generate = function () { var e; return u(this, (function (t) { switch (t.label) { case 0: e = this.pop(), t.label = 1; case 1: return e ? [4, e] : [3, 3]; case 2: return t.sent(), e = this.pop(), [3, 1]; case 3: return [2, null] } })) }, e }(), k = { priority: 0, persistent: !1 }, E = function (e, t) { return e.priority > t.priority }, F = function () { function e() { this._deferred = new Map, this._blocked = new Set } return e.prototype.register = function (e, t, n) { var r, o; void 0 === n && (n = k), this._deferred.set(e, { keyword: e, procedure: t, priority: null !== (r = n.priority) && void 0 !== r ? r : k.priority, persistent: null !== (o = n.persistent) && void 0 !== o ? o : k.persistent }) }, e.prototype.registerIfNotExists = function (e, t, n) { void 0 === n && (n = k), this.isRegistered(e) || this.register(e, t, n) }, e.prototype.unregister = function (e) { this._deferred.delete(e) }, e.prototype.isRegistered = function (e) { return this._deferred.has(e) }, e.prototype.block = function (e) { this._blocked.add(e) }, e.prototype.unblock = function (e) { this._blocked.delete(e) }, e.prototype.isBlocked = function (e) { return this._blocked.has(e) }, e.prototype.clear = function () { this._deferred.clear(), this._blocked.clear() }, e.prototype.executeEach = function (e) { var t, n, r = new w(E); r.push.apply(r, c(this._deferred.values())); try { for (var o = s(r.generate()), i = o.next(); !i.done; i = o.next()) { var u = i.value; this.isBlocked(u.keyword) || u.procedure(u, e), u.persistent || this.unregister(u.keyword) } } catch (e) { t = { error: e } } finally { try { i && !i.done && (n = o.return) && n.call(o) } finally { if (t) throw t.error } } this._blocked.clear() }, e }(); !function (e) { e[e.Ready = 0] = "Ready", e[e.NotReady = 1] = "NotReady", e[e.ContinueTags = 2] = "ContinueTags", e[e.ContainsTags = 3] = "ContainsTags" }(l || (l = {})); var S, C = function (e) { function t() { return null !== e && e.apply(this, arguments) || this } return o(t, e), t.make = function (e, n) { void 0 === e && (e = {}), void 0 === n && (n = new Map); var r = Object.prototype.hasOwnProperty.call(globalThis, "_closetEnvironment") ? globalThis._closetEnvironment : globalThis._closetEnvironment = new Map; return new t(e, new y, new g(new Map), new F, new F, new g(new Map), new g(n), new g(r)) }, t.prototype.makeAccessor = function (t, n) { var r = this, o = this.filterAccessor(t, n); return function (i) { var u = o.getProcessor(i); return { execute: function (o, i) { return { status: (s = u.execute(o, e.prototype.getInternals.call(r, t, n, i))).parse ? s.ready ? l.ContinueTags : l.ContainsTags : s.ready ? l.Ready : l.NotReady, result: s.result }; var s }, getOptions: function () { return e = u.getOptions(), i = null !== (t = e.optics) && void 0 !== t ? t : [], { inlineOptics: null !== (n = e.inlineOptics) && void 0 !== n ? n : i, blockOptics: null !== (r = e.blockOptics) && void 0 !== r ? r : i, capture: null !== (o = e.capture) && void 0 !== o && o }; var e, t, n, r, o, i } } } }, t.prototype.finishIteration = function (e, t) { this.executeDeferred(e, t) }, t.prototype.finishRun = function (e, t) { this.executeAftermath(e, t), this.reset() }, t }(f), B = function (e) { return e }, N = function (e, t) { return t }, D = function (e) { return function (t) { return e } }, O = function (e, t) { var n, r, o, i, u = []; try { for (var a = s(t), c = a.next(); !c.done; c = a.next()) { var l = e[d = c.value]; l && u.push(l) } } catch (e) { n = { error: e } } finally { try { c && !c.done && (r = a.return) && r.call(a) } finally { if (n) throw n.error } } if (t.length < e.length) { var p = Array.from(new Array(e.length - t.length), (function (e, n) { return n + t.length })); try { for (var f = s(p), h = f.next(); !h.done; h = f.next()) { var d = h.value; u.push(e[d]) } } catch (e) { o = { error: e } } finally { try { h && !h.done && (i = f.return) && i.call(f) } finally { if (o) throw o.error } } } return u }, _ = function (e, t, n) { return e.reverse().reduce((function (e, n) { return n(t, e) }), n) }; !function (e) { e[e.Strong = 0] = "Strong", e[e.Choice = 1] = "Choice", e[e.Traversing = 2] = "Traversing", e[e.Bicontravariant = 3] = "Bicontravariant" }(S || (S = {})); var $ = { classes: [S.Choice, S.Strong, S.Traversing], dimap: function (e, t, n) { return function (r) { return t(n(e(r))) } }, first: function (e) { return function (t) { var n = a(t, 2), r = n[0], o = n[1]; return [e(r), o] } }, right: function (e) { return function (t) { var n = a(t, 2), r = n[0], o = n[1]; return r ? [r, e(o)] : [r, o] } } }, T = { classes: [S.Bicontravariant, S.Choice, S.Strong, S.Traversing], dimap: function (e, t, n) { return function (e, t) { return function (n) { return t(e(n)) } }(e, n) }, first: function (e) { return function (t) { var n = a(t, 1)[0]; return e(n) } }, right: function (e) { return function (t) { var n = a(t, 2), r = n[0], o = n[1]; return r ? e(o) : [] } } }, I = function (e) { var t = function (e) { var t, n, r, o; return "string" == typeof e ? { sep: e, max: 1 / 0, trim: !1, keepEmpty: !0 } : { sep: null !== (t = e.sep) && void 0 !== t ? t : "::", max: null !== (n = e.max) && void 0 !== n ? n : 1 / 0, trim: null !== (r = e.trim) && void 0 !== r && r, keepEmpty: null === (o = e.keepEmpty) || void 0 === o || o } }(e), n = t.sep, r = t.max, o = t.trim, i = t.keepEmpty, u = function (e) { for (var t = [], u = e, s = !1; !s;) { var c = u.indexOf(n), l = a(c < 0 || t.length + 1 === r ? [u, "", !0] : [u.slice(0, c), u.slice(c + n.length), !1], 3), p = l[0], f = l[1], h = l[2], d = o ? p.trim() : p; (i || d.length >= 1) && t.push(d), u = f, s = h } return [t] }, s = function (e) { return a(e, 1)[0] }; return function (e, t) { var n = e.first(t); return e.dimap(u, s, n) } }, j = function (e) { var t, n; return "string" == typeof e ? { before: e, after: e } : { before: null !== (t = e.before) && void 0 !== t ? t : "", after: null !== (n = e.after) && void 0 !== n ? n : "" } }, P = function (e) { return e.replace(/[.*+?^${}()|[\]\\]/g, "\\$&") }, z = function (e) { return e instanceof RegExp ? e.source : e }, M = function (e) { var t = j(e), n = t.before, r = t.after, o = function (e) { var t = new RegExp("(" + n + ")(.*?)(" + r + ")", "gsu"), o = [], i = [], u = 0; e.replace(t, (function (t, n, r, s, a) { var c = e.substring(u, a + n.length); return o.push(c), i.push(r), u = a + t.length - s.length, "" })), o.push(e.substring(u)); var s, a, l = c(Array(i.length).keys()), p = (s = o, a = l, function (e) { var t = e[e.length - 1] || {}, n = [s[0]]; return a.forEach((function (r, o) { var i = Number.isInteger(r) ? e[r] : t[r]; n.push(i, s[o + 1]) })), n.join("") }); return [i, p] }, i = function (e) { var t = a(e, 2), n = t[0]; return (0, t[1])(n) }; return function (e, t) { var n = e.first(t); return e.dimap(o, i, n) } }, L = function (e) { var t = j(e), n = t.before, r = t.after, o = function (e) { var t = new RegExp("^" + z(n) + "(.*?)" + z(r) + "$", "su"), o = e; return e.replace(t, (function (e, t) { return o = t, "" })), [o, null] }, i = function (e) { return a(e, 1)[0] }; return function (e, t) { var n = e.first(t); return e.dimap(o, i, n) } }, G = function () { return function (e, t) { return n = t, function (e) { return e.map(n) }; var n } }, R = Object.freeze({ __proto__: null, run: _, dictFunction: $, dictForget: T, separated: I, templated: function (e) { var t = e.before, n = e.after; return M({ before: P(t), after: P(n) }) }, templatedRegex: M, stripped: function (e) { var t = e.before, n = e.after; return L({ before: "^" + P(t), after: P(n) + "$" }) }, strippedRegex: L, mapped: G }), q = function (e, t) { return e && t.isReady() }, H = function (e) { return e.map((function (e) { return e.toString() })).join("") }, J = function (e) { return e.map((function (e) { return e.toReprString() })).join("") }, K = /^(?:|\n)|(?:|\n)$/gu, Y = function () { function e(e, t, n, r, o, i, u, s, a, c) { this._inlineOptics = [], this._inlineGetter = B, this._blockOptics = [], this._blockGetter = B, this.fullKey = e, this.key = t, this.num = n, this.fullOccur = r, this.occur = o, this.delimiters = i, this._inlineNodes = u, this.hasInline = s, this._blockNodes = a, this.hasBlock = c } return Object.defineProperty(e.prototype, "inlineNodes", { get: function () { return this._inlineNodes }, enumerable: !1, configurable: !0 }), Object.defineProperty(e.prototype, "blockNodes", { get: function () { return this._blockNodes }, enumerable: !1, configurable: !0 }), Object.defineProperty(e.prototype, "innerNodes", { get: function () { return this.hasBlock ? this.blockNodes : this.inlineNodes }, enumerable: !1, configurable: !0 }), Object.defineProperty(e.prototype, "internalNodes", { set: function (e) { this.hasBlock ? this._blockNodes = e : this._inlineNodes = e }, enumerable: !1, configurable: !0 }), Object.defineProperty(e.prototype, "inlineText", { get: function () { return H(this.inlineNodes) }, enumerable: !1, configurable: !0 }), Object.defineProperty(e.prototype, "blockText", { get: function () { return function (e) { return e.replace(K, "") }(H(this.blockNodes)) }, enumerable: !1, configurable: !0 }), Object.defineProperty(e.prototype, "text", { get: function () { return this.hasBlock ? this.blockText : this.inlineText }, enumerable: !1, configurable: !0 }), Object.defineProperty(e.prototype, "inlineOptics", { get: function () { return this._inlineOptics }, set: function (e) { this._inlineOptics = e, this._inlineGetter = _(e, T, B) }, enumerable: !1, configurable: !0 }), Object.defineProperty(e.prototype, "blockOptics", { get: function () { return this._blockOptics }, set: function (e) { this._blockOptics = e, this._blockGetter = _(e, T, B) }, enumerable: !1, configurable: !0 }), Object.defineProperty(e.prototype, "optics", { get: function () { return this.hasBlock ? this.blockOptics : this.inlineOptics }, set: function (e) { this.hasBlock ? this.blockOptics = e : this.inlineOptics = e }, enumerable: !1, configurable: !0 }), Object.defineProperty(e.prototype, "inlineGetter", { get: function () { return this._inlineGetter }, enumerable: !1, configurable: !0 }), Object.defineProperty(e.prototype, "blockGetter", { get: function () { return this._blockGetter }, enumerable: !1, configurable: !0 }), Object.defineProperty(e.prototype, "getter", { get: function () { return this.hasBlock ? this.blockGetter : this.inlineGetter }, enumerable: !1, configurable: !0 }), Object.defineProperty(e.prototype, "inlineValues", { get: function () { return this.inlineGetter(this.inlineText) }, enumerable: !1, configurable: !0 }), Object.defineProperty(e.prototype, "blockValues", { get: function () { return this.blockGetter(this.blockText) }, enumerable: !1, configurable: !0 }), Object.defineProperty(e.prototype, "values", { get: function () { return this.hasBlock ? this.blockValues : this.inlineValues }, enumerable: !1, configurable: !0 }), e.prototype.inlineTraverse = function (e) { return _(this.inlineOptics, $, e)(this.inlineText) }, e.prototype.blockTraverse = function (e) { return _(this.blockOptics, $, e)(this.blockText) }, e.prototype.traverse = function (e) { return this.hasBlock ? this.blockTraverse(e) : this.inlineTraverse(e) }, e.prototype.wrapWithDelimiters = function (e, t) { return "" + this.delimiters.open + e + this.fullKey + (t ? this.delimiters.sep + t : "") + this.delimiters.close }, e.prototype.wrapBlock = function (e, t) { return "" + this.wrapWithDelimiters("#", t) + e + this.wrapWithDelimiters("/") }, e.prototype.baseToString = function (e, t) { return this.hasInline ? this.hasBlock ? this.wrapBlock(t(), e()) : this.wrapWithDelimiters("", e()) : this.hasBlock ? this.wrapBlock(t()) : this.wrapWithDelimiters("") }, e.prototype.toString = function () { var e = this; return this.baseToString((function () { return e.inlineText }), (function () { return e.blockText })) }, e.prototype.toReprString = function () { var e = this; return this.baseToString((function () { return J(e.inlineNodes) }), (function () { return J(e.blockNodes) })) }, e.prototype.isReady = function () { return !1 }, e.prototype.evaluate = function (e, t, n, r) { var o; void 0 === r && (r = !1); var i = t(this.key), u = n.length - 1, s = i.getOptions().capture, a = function (r, o) { return r.evaluate(e, t, c(n, [o])) }, p = s && !r; !s && !r && (o = this.innerNodes).splice.apply(o, c([0, this.innerNodes.length], this.innerNodes.flatMap(a))); var f = this.innerNodes.reduce(q, !0); this.inlineOptics = i.getOptions().inlineOptics, this.blockOptics = i.getOptions().blockOptics; var h, d = { path: n, depth: u, ready: f, isCapture: p }, m = i.execute(this, d); switch (m.status) { case l.Ready: h = "string" == typeof m.result ? [new X(m.result)] : m.result; break; case l.NotReady: h = p ? this.innerNodes : [this]; break; case l.ContinueTags: h = e.rawParse(m.result).flatMap(a); break; case l.ContainsTags: h = e.rawParse(m.result) }return p ? (this.internalNodes = h, this.evaluate(e, t, n, !0)) : h }, e }(), W = function () { function e() { } return e.prototype.isReady = function () { return !0 }, e.prototype.evaluate = function () { return [this] }, e.prototype.toReprString = function () { var e; return null !== (e = this.toString()) && void 0 !== e ? e : "" }, e }(), X = function (e) { function t(t) { var n = e.call(this) || this; return n.text = t, n } return o(t, e), t.prototype.toString = function () { return this.text }, t }(W), U = function (e) { function t(t) { var n = e.call(this) || this; return n.escaped = t, n } return o(t, e), t.prototype.toString = function () { return 1 === this.escaped.length ? this.escaped : this.escaped.slice(1) }, t.prototype.toReprString = function () { return this.escaped }, t }(W), V = function (e) { function t() { return null !== e && e.apply(this, arguments) || this } return o(t, e), t.prototype.toString = function () { return null }, t }(W), Q = "undefined" != typeof globalThis ? globalThis : "undefined" != typeof window ? window : "undefined" != typeof global ? global : "undefined" != typeof self ? self : {}; function Z(e, t, n) { return e(n = { path: t, exports: {}, require: function (e, t) { return function () { throw new Error("Dynamic requires are not currently supported by @rollup/plugin-commonjs") }(null == t && n.path) } }, n.exports), n.exports } var ee = Z((function (e) { var t, n; t = Q, n = function () { function e(t, n, r) { return this.id = ++e.highestId, this.name = t, this.symbols = n, this.postprocess = r, this } function t(e, t, n, r) { this.rule = e, this.dot = t, this.reference = n, this.data = [], this.wantedBy = r, this.isComplete = this.dot === e.symbols.length } function n(e, t) { this.grammar = e, this.index = t, this.states = [], this.wants = {}, this.scannable = [], this.completed = {} } function r(e, t) { this.rules = e, this.start = t || this.rules[0].name; var n = this.byName = {}; this.rules.forEach((function (e) { n.hasOwnProperty(e.name) || (n[e.name] = []), n[e.name].push(e) })) } function o() { this.reset("") } function i(e, t, i) { if (e instanceof r) { var u = e; i = t } else u = r.fromCompiled(e, t); for (var s in this.grammar = u, this.options = { keepHistory: !1, lexer: u.lexer || new o }, i || {}) this.options[s] = i[s]; this.lexer = this.options.lexer, this.lexerState = void 0; var a = new n(u, 0); this.table = [a], a.wants[u.start] = [], a.predict(u.start), a.process(), this.current = 0 } function u(e) { var t = typeof e; if ("string" === t) return e; if ("object" === t) { if (e.literal) return JSON.stringify(e.literal); if (e instanceof RegExp) return e.toString(); if (e.type) return "%" + e.type; if (e.test) return "<" + String(e.test) + ">"; throw new Error("Unknown symbol type: " + e) } } return e.highestId = 0, e.prototype.toString = function (e) { var t = void 0 === e ? this.symbols.map(u).join(" ") : this.symbols.slice(0, e).map(u).join(" ") + " ● " + this.symbols.slice(e).map(u).join(" "); return this.name + " → " + t }, t.prototype.toString = function () { return "{" + this.rule.toString(this.dot) + "}, from: " + (this.reference || 0) }, t.prototype.nextState = function (e) { var n = new t(this.rule, this.dot + 1, this.reference, this.wantedBy); return n.left = this, n.right = e, n.isComplete && (n.data = n.build(), n.right = void 0), n }, t.prototype.build = function () { var e = [], t = this; do { e.push(t.right.data), t = t.left } while (t.left); return e.reverse(), e }, t.prototype.finish = function () { this.rule.postprocess && (this.data = this.rule.postprocess(this.data, this.reference, i.fail)) }, n.prototype.process = function (e) { for (var t = this.states, n = this.wants, r = this.completed, o = 0; o < t.length; o++) { var u = t[o]; if (u.isComplete) { if (u.finish(), u.data !== i.fail) { for (var s = u.wantedBy, a = s.length; a--;) { var c = s[a]; this.complete(c, u) } if (u.reference === this.index) { var l = u.rule.name; (this.completed[l] = this.completed[l] || []).push(u) } } } else { if ("string" != typeof (l = u.rule.symbols[u.dot])) { this.scannable.push(u); continue } if (n[l]) { if (n[l].push(u), r.hasOwnProperty(l)) { var p = r[l]; for (a = 0; a < p.length; a++) { var f = p[a]; this.complete(u, f) } } } else n[l] = [u], this.predict(l) } } }, n.prototype.predict = function (e) { for (var n = this.grammar.byName[e] || [], r = 0; r < n.length; r++) { var o = n[r], i = this.wants[e], u = new t(o, 0, this.index, i); this.states.push(u) } }, n.prototype.complete = function (e, t) { var n = e.nextState(t); this.states.push(n) }, r.fromCompiled = function (t, n) { var o = t.Lexer; t.ParserStart && (n = t.ParserStart, t = t.ParserRules); var i = new r(t = t.map((function (t) { return new e(t.name, t.symbols, t.postprocess) })), n); return i.lexer = o, i }, o.prototype.reset = function (e, t) { this.buffer = e, this.index = 0, this.line = t ? t.line : 1, this.lastLineBreak = t ? -t.col : 0 }, o.prototype.next = function () { if (this.index < this.buffer.length) { var e = this.buffer[this.index++]; return "\n" === e && (this.line += 1, this.lastLineBreak = this.index), { value: e } } }, o.prototype.save = function () { return { line: this.line, col: this.index - this.lastLineBreak } }, o.prototype.formatError = function (e, t) { var n = this.buffer; if ("string" == typeof n) { var r = n.split("\n").slice(Math.max(0, this.line - 5), this.line); n.indexOf("\n", this.index); var o = this.index - this.lastLineBreak, i = String(this.line).length; return t += " at line " + this.line + " col " + o + ":\n\n", t += r.map((function (e, t) { return u(this.line - r.length + t + 1, i) + " " + e }), this).join("\n"), t += "\n" + u("", i + o) + "^\n" } return t + " at index " + (this.index - 1); function u(e, t) { var n = String(e); return Array(t - n.length + 1).join(" ") + n } }, i.fail = {}, i.prototype.feed = function (e) { var t, r = this.lexer; for (r.reset(e, this.lexerState); ;) { try { if (!(t = r.next())) break } catch (e) { var i = new n(this.grammar, this.current + 1); throw this.table.push(i), (a = new Error(this.reportLexerError(e))).offset = this.current, a.token = e.token, a } var u = this.table[this.current]; this.options.keepHistory || delete this.table[this.current - 1]; var s = this.current + 1; i = new n(this.grammar, s), this.table.push(i); for (var a, c = void 0 !== t.text ? t.text : t.value, l = r.constructor === o ? t.value : t, p = u.scannable, f = p.length; f--;) { var h = p[f], d = h.rule.symbols[h.dot]; if (d.test ? d.test(l) : d.type ? d.type === t.type : d.literal === c) { var m = h.nextState({ data: l, token: t, isToken: !0, reference: s - 1 }); i.states.push(m) } } if (i.process(), 0 === i.states.length) throw (a = new Error(this.reportError(t))).offset = this.current, a.token = t, a; this.options.keepHistory && (u.lexerState = r.save()), this.current++ } return u && (this.lexerState = r.save()), this.results = this.finish(), this }, i.prototype.reportLexerError = function (e) { var t, n, r = e.token; return r ? (t = "input " + JSON.stringify(r.text[0]) + " (lexer error)", n = this.lexer.formatError(r, "Syntax error")) : (t = "input (lexer error)", n = e.message), this.reportErrorCommon(n, t) }, i.prototype.reportError = function (e) { var t = (e.type ? e.type + " token: " : "") + JSON.stringify(void 0 !== e.value ? e.value : e), n = this.lexer.formatError(e, "Syntax error"); return this.reportErrorCommon(n, t) }, i.prototype.reportErrorCommon = function (e, t) { var n = []; n.push(e); var r = this.table.length - 2, o = this.table[r], i = o.states.filter((function (e) { var t = e.rule.symbols[e.dot]; return t && "string" != typeof t })); return 0 === i.length ? (n.push("Unexpected " + t + ". I did not expect any more input. Here is the state of my parse table:\n"), this.displayStateStack(o.states, n)) : (n.push("Unexpected " + t + ". Instead, I was expecting to see one of the following:\n"), i.map((function (e) { return this.buildFirstStateStack(e, []) || [e] }), this).forEach((function (e) { var t = e[0], r = t.rule.symbols[t.dot], o = this.getSymbolDisplay(r); n.push("A " + o + " based on:"), this.displayStateStack(e, n) }), this)), n.push(""), n.join("\n") }, i.prototype.displayStateStack = function (e, t) { for (var n, r = 0, o = 0; o < e.length; o++) { var i = e[o], u = i.rule.toString(i.dot); u === n ? r++ : (r > 0 && t.push(" ^ " + r + " more lines identical to this"), r = 0, t.push(" " + u)), n = u } }, i.prototype.getSymbolDisplay = function (e) { return function (e) { var t = typeof e; if ("string" === t) return e; if ("object" === t) { if (e.literal) return JSON.stringify(e.literal); if (e instanceof RegExp) return "character matching " + e; if (e.type) return e.type + " token"; if (e.test) return "token matching " + String(e.test); throw new Error("Unknown symbol type: " + e) } }(e) }, i.prototype.buildFirstStateStack = function (e, t) { if (-1 !== t.indexOf(e)) return null; if (0 === e.wantedBy.length) return [e]; var n = e.wantedBy[0], r = [e].concat(t), o = this.buildFirstStateStack(n, r); return null === o ? null : [e].concat(o) }, i.prototype.save = function () { var e = this.table[this.current]; return e.lexerState = this.lexerState, e }, i.prototype.restore = function (e) { var t = e.index; this.current = t, this.table[t] = e, this.table.splice(t + 1), this.lexerState = e.lexerState, this.results = this.finish() }, i.prototype.rewind = function (e) { if (!this.options.keepHistory) throw new Error("set option `keepHistory` to enable rewinding"); this.restore(this.table[e]) }, i.prototype.finish = function () { var e = [], t = this.grammar.start; return this.table[this.table.length - 1].states.forEach((function (n) { n.rule.name === t && n.dot === n.rule.symbols.length && 0 === n.reference && n.data !== i.fail && e.push(n) })), e.map((function (e) { return e.data })) }, { Parser: i, Grammar: r, Rule: e } }, e.exports ? e.exports = n() : t.nearley = n() })), te = /^((?:[a-zA-Z_/]|%\w)+)(\d*)(?::(\d+))?$/u, ne = function (e, t) { var n, r = (null !== (n = e.get(t)) && void 0 !== n ? n : -1) + 1; return e.set(t, r), r }, re = new (function () { function e() { this.tagCounter = null, this.tagCounterFull = null, this.delimiters = null } return e.prototype.increment = function (e, t) { return [ne(this.tagCounterFull, e), ne(this.tagCounter, t)] }, e.prototype.build = function (e, t, n, r, o) { void 0 === r && (r = []), void 0 === o && (o = !1); var i = e.match(te); if (!i) throw new Error("Could not match key. This should never happen."); var u = i[1], s = i[2].length > 0 ? Number(i[2]) : null, c = a(this.increment(e, u), 2), l = c[0], p = c[1]; return new Y(e, u, s, l, p, this.delimiters, t, n, r, o) }, e.prototype.push = function (e, t) { this.tagCounterFull = e[0], this.tagCounter = e[1], this.delimiters = t }, e }()); function oe(e) { return e[0] } var ie = function (e) { return { Lexer: e, ParserRules: [{ name: "start", symbols: ["content"], postprocess: oe }, { name: "content$ebnf$1", symbols: [] }, { name: "content$ebnf$1", symbols: ["content$ebnf$1", "node"], postprocess: function (e) { return e[0].concat([e[1]]) } }, { name: "content", symbols: ["content$ebnf$1"], postprocess: oe }, { name: "node", symbols: ["text"], postprocess: oe }, { name: "node", symbols: ["inlinetag"], postprocess: oe }, { name: "node", symbols: ["blocktag"], postprocess: oe }, { name: "text", symbols: [e.has("text") ? { type: "text" } : text], postprocess: function (e) { var t = a(e, 1)[0]; return new X(t.value) } }, { name: "text", symbols: [e.has("escapeseq") ? { type: "escapeseq" } : escapeseq], postprocess: function (e) { var t = a(e, 1)[0]; return new U(t.value) } }, { name: "inlinetag", symbols: [e.has("inlineopen") ? { type: "inlineopen" } : inlineopen, "inline"], postprocess: function (e) { var t = a(e, 2), n = a(t[1], 3), r = n[0], o = n[1], i = n[2]; return re.build(r.value, o, i) } }, { name: "blocktag", symbols: [e.has("blockopen") ? { type: "blockopen" } : blockopen, "inline", "content", "blockclose"], postprocess: function (e, t, n) { var r = a(e, 4), o = a(r[1], 3), i = o[0], u = o[1], s = o[2], c = r[2], l = r[3]; return l && i.value !== l.value ? n : re.build(i.value, u, s, c, !0) } }, { name: "inline$ebnf$1$subexpression$1", symbols: [e.has("sep") ? { type: "sep" } : sep, "content"] }, { name: "inline$ebnf$1", symbols: ["inline$ebnf$1$subexpression$1"], postprocess: oe }, { name: "inline$ebnf$1", symbols: [], postprocess: function () { return null } }, { name: "inline", symbols: [e.has("keyname") ? { type: "keyname" } : keyname, "inline$ebnf$1", e.has("close") ? { type: "close" } : close], postprocess: function (e) { var t = a(e, 2), n = t[0], r = t[1]; return r ? [n, r[1], !0] : [n, [], !1] } }, { name: "blockclose$ebnf$1", symbols: [e.has("keyname") ? { type: "keyname" } : keyname], postprocess: oe }, { name: "blockclose$ebnf$1", symbols: [], postprocess: function () { return null } }, { name: "blockclose", symbols: [e.has("blockclose") ? { type: "blockclose" } : blockclose, "blockclose$ebnf$1", e.has("close") ? { type: "close" } : close], postprocess: function (e) { return a(e, 2)[1] } }], ParserStart: "start" } }, ue = { open: "[[", sep: "::", close: "]]" }; var se, ae = Z((function (e) { var t, n; t = Q, n = function () { var e = Object.prototype.hasOwnProperty, t = Object.prototype.toString, n = "boolean" == typeof (new RegExp).sticky; function r(e) { return e && "[object RegExp]" === t.call(e) } function o(e) { return e && "object" == typeof e && !r(e) && !Array.isArray(e) } function i(e) { return "(" + e + ")" } function u(e) { return e.length ? "(?:" + e.map((function (e) { return "(?:" + e + ")" })).join("|") + ")" : "(?!)" } function s(e) { if ("string" == typeof e) return "(?:" + e.replace(/[-\/\\^$*+?.()|[\]{}]/g, "\\$&") + ")"; if (r(e)) { if (e.ignoreCase) throw new Error("RegExp /i flag not allowed"); if (e.global) throw new Error("RegExp /g flag is implied"); if (e.sticky) throw new Error("RegExp /y flag is implied"); if (e.multiline) throw new Error("RegExp /m flag is implied"); return e.source } throw new Error("Not a pattern: " + e) } function a(t, n) { if (o(n) || (n = { match: n }), n.include) throw new Error("Matching rules cannot also include states"); var i = { defaultType: t, lineBreaks: !!n.error || !!n.fallback, pop: !1, next: null, push: null, error: !1, fallback: !1, value: null, type: null, shouldThrow: !1 }; for (var u in n) e.call(n, u) && (i[u] = n[u]); if ("string" == typeof i.type && t !== i.type) throw new Error("Type transform cannot be a string (type '" + i.type + "' for token '" + t + "')"); var s = i.match; return i.match = Array.isArray(s) ? s : s ? [s] : [], i.match.sort((function (e, t) { return r(e) && r(t) ? 0 : r(t) ? -1 : r(e) ? 1 : t.length - e.length })), i } function c(e) { return Array.isArray(e) ? function (e) { for (var t = [], n = 0; n < e.length; n++) { var r = e[n]; if (r.include) for (var o = [].concat(r.include), i = 0; i < o.length; i++)t.push({ include: o[i] }); else { if (!r.type) throw new Error("Rule has no type: " + JSON.stringify(r)); t.push(a(r.type, r)) } } return t }(e) : function (e) { for (var t = Object.getOwnPropertyNames(e), n = [], r = 0; r < t.length; r++) { var i = t[r], u = e[i], s = [].concat(u); if ("include" !== i) { var c = []; s.forEach((function (e) { o(e) ? (c.length && n.push(a(i, c)), n.push(a(i, e)), c = []) : c.push(e) })), c.length && n.push(a(i, c)) } else for (var l = 0; l < s.length; l++)n.push({ include: s[l] }) } return n }(e) } var l = a("error", { lineBreaks: !0, shouldThrow: !0 }); function p(e, t) { for (var o = null, a = Object.create(null), c = !0, p = null, f = [], h = [], d = 0; d < e.length; d++)e[d].fallback && (c = !1); for (d = 0; d < e.length; d++) { var m = e[d]; if (m.include) throw new Error("Inheritance is not allowed in stateless lexers"); if (m.error || m.fallback) { if (o) throw !m.fallback == !o.fallback ? new Error("Multiple " + (m.fallback ? "fallback" : "error") + " rules not allowed (for token '" + m.defaultType + "')") : new Error("fallback and error are mutually exclusive (for token '" + m.defaultType + "')"); o = m } var v = m.match.slice(); if (c) for (; v.length && "string" == typeof v[0] && 1 === v[0].length;)a[v.shift().charCodeAt(0)] = m; if (m.pop || m.push || m.next) { if (!t) throw new Error("State-switching options are not allowed in stateless lexers (for token '" + m.defaultType + "')"); if (m.fallback) throw new Error("State-switching options are not allowed on fallback tokens (for token '" + m.defaultType + "')") } if (0 !== v.length) { c = !1, f.push(m); for (var y = 0; y < v.length; y++) { var g = v[y]; if (r(g)) if (null === p) p = g.unicode; else if (p !== g.unicode && !1 === m.fallback) throw new Error("If one rule is /u then all must be") } var b = u(v.map(s)), x = new RegExp(b); if (x.test("")) throw new Error("RegExp matches empty string: " + x); if (new RegExp("|" + b).exec("").length - 1 > 0) throw new Error("RegExp has capture groups: " + x + "\nUse (?: … ) instead"); if (!m.lineBreaks && x.test("\n")) throw new Error("Rule should declare lineBreaks: " + x); h.push(i(b)) } } var A = o && o.fallback, w = n && !A ? "ym" : "gm", k = n || A ? "" : "|"; return !0 === p && (w += "u"), { regexp: new RegExp(u(h) + k, w), groups: f, fast: a, error: o || l } } function f(e, t, n) { var r = e && (e.push || e.next); if (r && !n[r]) throw new Error("Missing state '" + r + "' (in token '" + e.defaultType + "' of state '" + t + "')"); if (e && e.pop && 1 != +e.pop) throw new Error("pop must be 1 (in token '" + e.defaultType + "' of state '" + t + "')") } var h = function (e, t) { this.startState = t, this.states = e, this.buffer = "", this.stack = [], this.reset() }; h.prototype.reset = function (e, t) { return this.buffer = e || "", this.index = 0, this.line = t ? t.line : 1, this.col = t ? t.col : 1, this.queuedToken = t ? t.queuedToken : null, this.queuedThrow = t ? t.queuedThrow : null, this.setState(t ? t.state : this.startState), this.stack = t && t.stack ? t.stack.slice() : [], this }, h.prototype.save = function () { return { line: this.line, col: this.col, state: this.state, stack: this.stack.slice(), queuedToken: this.queuedToken, queuedThrow: this.queuedThrow } }, h.prototype.setState = function (e) { if (e && this.state !== e) { this.state = e; var t = this.states[e]; this.groups = t.groups, this.error = t.error, this.re = t.regexp, this.fast = t.fast } }, h.prototype.popState = function () { this.setState(this.stack.pop()) }, h.prototype.pushState = function (e) { this.stack.push(this.state), this.setState(e) }; var d = n ? function (e, t) { return e.exec(t) } : function (e, t) { var n = e.exec(t); return 0 === n[0].length ? null : n }; function m() { return this.value } if (h.prototype._getGroup = function (e) { for (var t = this.groups.length, n = 0; n < t; n++)if (void 0 !== e[n + 1]) return this.groups[n]; throw new Error("Cannot find token type for matched text") }, h.prototype.next = function () { var e = this.index; if (this.queuedGroup) { var t = this._token(this.queuedGroup, this.queuedText, e); return this.queuedGroup = null, this.queuedText = "", t } var n = this.buffer; if (e !== n.length) { if (u = this.fast[n.charCodeAt(e)]) return this._token(u, n.charAt(e), e); var r = this.re; r.lastIndex = e; var o = d(r, n), i = this.error; if (null == o) return this._token(i, n.slice(e, n.length), e); var u = this._getGroup(o), s = o[0]; return i.fallback && o.index !== e ? (this.queuedGroup = u, this.queuedText = s, this._token(i, n.slice(e, o.index), e)) : this._token(u, s, e) } }, h.prototype._token = function (e, t, n) { var r = 0; if (e.lineBreaks) { var o = /\n/g, i = 1; if ("\n" === t) r = 1; else for (; o.exec(t);)r++, i = o.lastIndex } var u = { type: "function" == typeof e.type && e.type(t) || e.defaultType, value: "function" == typeof e.value ? e.value(t) : t, text: t, toString: m, offset: n, lineBreaks: r, line: this.line, col: this.col }, s = t.length; if (this.index += s, this.line += r, 0 !== r ? this.col = s - i + 1 : this.col += s, e.shouldThrow) throw new Error(this.formatError(u, "invalid syntax")); return e.pop ? this.popState() : e.push ? this.pushState(e.push) : e.next && this.setState(e.next), u }, "undefined" != typeof Symbol && Symbol.iterator) { var v = function (e) { this.lexer = e }; v.prototype.next = function () { var e = this.lexer.next(); return { value: e, done: !e } }, v.prototype[Symbol.iterator] = function () { return this }, h.prototype[Symbol.iterator] = function () { return new v(this) } } return h.prototype.formatError = function (e, t) { if (null == e) { var n = this.buffer.slice(this.index); e = { text: n, offset: this.index, lineBreaks: -1 === n.indexOf("\n") ? 0 : 1, line: this.line, col: this.col } } var r = Math.max(0, e.offset - e.col + 1), o = e.lineBreaks ? e.text.indexOf("\n") : e.text.length, i = this.buffer.substring(r, e.offset + o); return t += " at line " + e.line + " col " + e.col + ":\n\n", t += " " + i + "\n", t += " " + Array(e.col).join(" ") + "^" }, h.prototype.clone = function () { return new h(this.states, this.state) }, h.prototype.has = function (e) { return !0 }, { compile: function (e) { var t = p(c(e)); return new h({ start: t }, "start") }, states: function (e, t) { var n = e.$all ? c(e.$all) : []; delete e.$all; var r = Object.getOwnPropertyNames(e); t || (t = r[0]); for (var o = Object.create(null), i = 0; i < r.length; i++)o[b = r[i]] = c(e[b]).concat(n); for (i = 0; i < r.length; i++)for (var u = o[b = r[i]], s = Object.create(null), a = 0; a < u.length; a++) { var l = u[a]; if (l.include) { var d = [a, 1]; if (l.include !== b && !s[l.include]) { s[l.include] = !0; var m = o[l.include]; if (!m) throw new Error("Cannot include nonexistent state '" + l.include + "' (in state '" + b + "')"); for (var v = 0; v < m.length; v++) { var y = m[v]; -1 === u.indexOf(y) && d.push(y) } } u.splice.apply(u, d), a-- } } var g = Object.create(null); for (i = 0; i < r.length; i++) { var b; g[b = r[i]] = p(o[b], !0) } for (i = 0; i < r.length; i++) { var x = r[i], A = g[x], w = A.groups; for (a = 0; a < w.length; a++)f(w[a], x, g); var k = Object.getOwnPropertyNames(A.fast); for (a = 0; a < k.length; a++)f(A.fast[k[a]], x, g) } return new h(g, t) }, error: Object.freeze({ error: !0 }), fallback: Object.freeze({ fallback: !0 }), keywords: function (e) { for (var t = Object.create(null), n = Object.create(null), r = Object.getOwnPropertyNames(e), o = 0; o < r.length; o++) { var i = r[o], u = e[i]; (Array.isArray(u) ? u : [u]).forEach((function (e) { if ((n[e.length] = n[e.length] || []).push(e), "string" != typeof e) throw new Error("keyword must be string (in keyword '" + i + "')"); t[e] = i })) } function s(e) { return JSON.stringify(e) } var a = ""; for (var c in a += "switch (value.length) {\n", n) { var l = n[c]; a += "case " + c + ":\n", a += "switch (value) {\n", l.forEach((function (e) { var n = t[e]; a += "case " + s(e) + ": return " + s(n) + "\n" })), a += "}\n" } return a += "}\n", Function("value", a) } } }, e.exports ? e.exports = n() : t.moo = n() })), ce = /(?:[a-zA-Z_]|%\w)+\d*/u, le = function (e) { return e.replace(/[.*+\-?^${}()|[\]\\]/g, "\\$&") }, pe = function (e) { var t = le(e.open), n = le(e.close), r = new RegExp("[\\s\\S]+?(?=\\\\|" + t + "|$)", "u"), o = new RegExp("[\\s\\S]+?(?=\\\\|" + t + "|" + n + ")", "u"), i = new RegExp("\\\\(?:" + t + "|" + n + ")?", "u"), u = { match: e.open, push: "inlinekey" }, s = { match: e.open + "#", push: "blockkey" }, a = { match: e.open + "/", push: "blockclose" }, c = { match: e.close, pop: 1 }, l = { match: e.close, next: "blockmain" }, p = { match: ce }, f = { match: r, lineBreaks: !0 }, h = { match: o, lineBreaks: !0 }, d = { match: i }, m = function (t) { return { match: e.sep, next: t } }; return ae.states({ main: { blockopen: s, inlineopen: u, escapeseq: d, text: f }, blockkey: { keyname: p, sep: m("blocktag"), close: l }, inlinekey: { keyname: p, sep: m("inlinetag"), close: c }, blocktag: { blockopen: s, inlineopen: u, escapeseq: d, close: l, text: h }, inlinetag: { blockopen: s, inlineopen: u, escapeseq: d, close: c, text: h }, blockmain: { blockopen: s, blockclose: a, inlineopen: u, escapeseq: d, text: f }, blockclose: { keyname: p, close: c } }) }, fe = function (e, t) { var n = function (e, t) { var n = new ee.Parser(t); try { return n.feed(e).results } catch (t) { return console.log("Will default to Trivial Base Tag, due to error parsing text:", t), [[new X(e)]] } }(e, t); return n.length > 1 && console.log("Ambiguous template grammar"), n[0] }, he = function (e, t) { return c(function (e, t) { var n, r, o, i, a, c, l, p, f, h, d, m; return u(this, (function (u) { switch (u.label) { case 0: n = !0, u.label = 1; case 1: u.trys.push([1, 14, 15, 16]), r = s(e), o = r.next(), u.label = 2; case 2: return o.done ? [3, 13] : (i = o.value, n ? (n = !1, [3, 5]) : [3, 3]); case 3: return [4, t]; case 4: u.sent(), u.label = 5; case 5: u.trys.push([5, 10, 11, 12]), d = void 0, a = s(i), c = a.next(), u.label = 6; case 6: return c.done ? [3, 9] : [4, c.value]; case 7: u.sent(), u.label = 8; case 8: return c = a.next(), [3, 6]; case 9: return [3, 12]; case 10: return l = u.sent(), d = { error: l }, [3, 12]; case 11: try { c && !c.done && (m = a.return) && m.call(a) } finally { if (d) throw d.error } return [7]; case 12: return o = r.next(), [3, 2]; case 13: return [3, 16]; case 14: return p = u.sent(), f = { error: p }, [3, 16]; case 15: try { o && !o.done && (h = r.return) && h.call(r) } finally { if (f) throw f.error } return [7]; case 16: return [2] } })) }(e.map((function (e) { return fe(e, t) })), new V)) }; !function (e) { e[e.Single = 1] = "Single", e[e.Fragments = 2] = "Fragments" }(se || (se = {})); var de = function () { function e(e) { this.tagBuilderSettings = [new Map, new Map], this.delimiters = null != e ? e : ue, this.lexer = pe(this.delimiters), this.grammar = ee.Grammar.fromCompiled(ie(this.lexer)) } return e.prototype.update = function (e) { this.delimiters = Object.assign({}, ue, e), this.lexer = pe(this.delimiters), this.grammar = ee.Grammar.fromCompiled(ie(this.lexer)) }, e.prototype.parse = function (e) { return re.push(this.tagBuilderSettings, this.delimiters), he(e, this.grammar) }, e.prototype.rawParse = function (e) { return re.push(this.tagBuilderSettings, this.delimiters), fe(e, this.grammar) }, e }(), me = function () { function e(e, t, n) { this.textFragments = e, this.parser = new de(n), this.nodes = null != t ? t : this.parser.parse(e) } return e.make = function (t, n) { return new e([t], null, n) }, e.makeFromFragments = function (t, n) { return new e(t, null, n) }, e.prototype.render = function (e, t) { var n = this; void 0 === t && (t = function () { }); for (var r = { template: this, parser: this.parser }, o = this.nodes, i = !1, u = function (t) { var u = { iteration: t }, s = e.makeAccessor(r, u); o = o.flatMap((function (e, t) { return e.evaluate(n.parser, s, [t]) })), i = o.reduce(q, !0), e.finishIteration(r, u) }, a = 0; a < 50 && !i; a++)u(a); var c = function (e) { var t, n, r = []; if (0 === e.length) return r; var o = ""; try { for (var i = s(e), u = i.next(); !u.done; u = i.next()) { var a = u.value.toString(); "string" == typeof a ? o += a : (r.push(o), o = "") } } catch (e) { t = { error: e } } finally { try { u && !u.done && (n = i.return) && n.call(i) } finally { if (t) throw t.error } } return r.push(o), r }(o); return t(c), e.finishRun(r, { result: c }), c }, e }(), ve = function (e, t, n) { return e < 0 ? n + e + 1 : t + e }, ye = function (e, t) { var n; if ("string" == typeof e) return e; var r = null; switch (e.nodeType) { case Node.TEXT_NODE: return null !== (n = e.textContent) && void 0 !== n ? n : ""; case Node.ELEMENT_NODE: return r = e, t ? r.outerHTML : r.innerHTML; case xe.CHILD_NODE_SPAN: return e.spanAsStrings().join(""); default: return "" } }, ge = { type: "index", value: 0 }, be = { type: "index", value: -1 }, xe = function () { function e(t, n, r) { var o, i, u, s; void 0 === n && (n = ge), void 0 === r && (r = be), this.nodeType = e.CHILD_NODE_SPAN, this.parentElement = t, this.childNodes = Array.from(this.parentElement.childNodes), this.max = t.childNodes.length - 1; var a = this.getFromMethod(n.type), c = this.getToMethod(r.type); this._fromIndex = a.call(this, n.value, null !== (o = n.startAtIndex) && void 0 !== o ? o : 0, null !== (i = n.exclusive) && void 0 !== i && i), this._toIndex = c.call(this, r.value, Math.max(null !== (u = r.startAtIndex) && void 0 !== u ? u : 0, this.from), null !== (s = r.exclusive) && void 0 !== s && s) } return Object.defineProperty(e.prototype, "from", { get: function () { return this._fromIndex }, enumerable: !1, configurable: !0 }), Object.defineProperty(e.prototype, "to", { get: function () { return this._toIndex }, enumerable: !1, configurable: !0 }), e.prototype.getFromMethod = function (e) { return "index" === e ? this.fromIndex : "node" === e ? this.fromNode : this.fromPredicate }, e.prototype.getToMethod = function (e) { return "index" === e ? this.toIndex : "node" === e ? this.toNode : this.toPredicate }, e.prototype.fromSafe = function (e, t) { return e < t || e > this.max ? this.max : e }, e.prototype.fromIndex = function (e, t, n) { var r = ve(e, t, this.max) + (n ? 1 : 0); return this.fromSafe(r, t) }, e.prototype.fromNode = function (e, t, n) { var r = this.childNodes.slice(t).findIndex((function (t) { return t === e })) + t + (n ? 1 : 0); return this.fromSafe(r, t) }, e.prototype.fromPredicate = function (e, t, n) { var r = this.childNodes.slice(t).findIndex(e) + t + (n ? 1 : 0); return this.fromSafe(r, t) }, e.prototype.toSafe = function (e, t) { return e < t || e > this.max ? 0 : e }, e.prototype.toIndex = function (e, t, n) { var r = ve(e, t, this.max) - (n ? 1 : 0); return this.toSafe(r, t) }, e.prototype.toNode = function (e, t, n) { var r = this.childNodes.slice(t).findIndex((function (t) { return t === e })) + t - (n ? 1 : 0); return this.toSafe(r, t) }, e.prototype.toPredicate = function (e, t, n) { var r = this.childNodes.slice(t).findIndex(e) + t - (n ? 1 : 0); return this.toSafe(r, t) }, Object.defineProperty(e.prototype, "valid", { get: function () { return this._fromIndex <= this._toIndex }, enumerable: !1, configurable: !0 }), Object.defineProperty(e.prototype, "length", { get: function () { return Math.max(0, this._toIndex - this._fromIndex + 1) }, enumerable: !1, configurable: !0 }), e.prototype.getRootNode = function () { return this.parentElement.getRootNode() }, e.prototype.span = function () { return this.valid ? this.childNodes.slice(this._fromIndex, this._toIndex + 1) : [] }, e.prototype.spanAsStrings = function () { return this.span().map((function (e) { return ye(e, !0) })) }, e.prototype.replaceSpan = function (e) { var t, n; if (this.valid) { var r = document.createElement("div"), o = this.parentElement.childNodes.length; this.parentElement.insertBefore(r, this.parentElement.childNodes[this._fromIndex]); try { for (var i = s(this.span()), u = i.next(); !u.done; u = i.next()) { var a = u.value; a.parentElement === this.parentElement && this.parentElement.removeChild(a) } } catch (e) { t = { error: e } } finally { try { u && !u.done && (n = i.return) && n.call(i) } finally { if (t) throw t.error } } r.outerHTML = e, this.childNodes = Array.from(this.parentElement.childNodes), this.max = this.childNodes.length, this._toIndex = this._toIndex + (this.max - o) } }, e.CHILD_NODE_SPAN = 3353, e }(), Ae = function (e, t) { return void 0 === t && (t = 0), [{ type: "predicate", value: e.value, startAtIndex: t, exclusive: !1 }, { type: "predicate", value: function (t) { return !e.value(t) }, startAtIndex: t, exclusive: !0 }] }, we = function (e, t) { for (var n = [], r = new (xe.bind.apply(xe, c([void 0, e], Ae(t)))); r.valid;)n.push(r), r = new (xe.bind.apply(xe, c([void 0, e], Ae(t, r.to + 1)))); return n }, ke = function (e, t) { var n = document.createElement("style"); return n.type = "text/css", n.id = "closet-" + e, n.textContent = t, n }, Ee = function (e, t) { return Boolean(e.getElementById("closet-" + t)) }, Fe = function (e, t) { Ee(document, e) || document.head.appendChild(ke(e, t)) }, Se = function (e) { function t(t, n, r, o) { var i = e.call(this, t, n, o) || this; return i.inputs = r, i } return o(t, e), t.makeFromNode = function (e, n) { return t.makeFromNodes([e], n) }, t.makeFromNodes = function (e, n) { return new t(e.map((function (e) { return ye(e, !1) })), null, e, n) }, t.prototype.renderToNodes = function (t) { var n = this; e.prototype.render.call(this, t, (function (e) { return e.forEach((function (e, t) { return function (e, t) { if ("string" != typeof e) { var n = null, r = null; switch (e.nodeType) { case Node.TEXT_NODE: n = e, r = document.createElement("div"), n.parentElement && (n.parentElement.insertBefore(r, n), n.parentElement.removeChild(n)), r.outerHTML = t; break; case Node.ELEMENT_NODE: e.innerHTML = t; break; case xe.CHILD_NODE_SPAN: e.replaceSpan(t) } } }(n.inputs[t], e) })) })) }, t.prototype.appendDocumentStyle = function (e, t) { Fe(e, t) }, t.prototype.appendStyle = function (e, t, n) { !function (e, t, n) { var r = e.getRootNode(); r instanceof Document ? Fe(t, n) : Ee(r, t) || r.insertBefore(ke(t, n), r.firstElementChild) }(e, t, n) }, t.prototype.appendStyleAll = function (e, t) { var n, r; try { for (var o = s(this.inputs), i = o.next(); !i.done; i = o.next()) { var u = i.value; "string" != typeof u && this.appendStyle(u, e, t) } } catch (e) { n = { error: e } } finally { try { i && !i.done && (r = o.return) && r.call(o) } finally { if (n) throw n.error } } }, t }(me), Ce = function () { var e, t; try { for (var n = s(Array.from(document.getElementsByClassName("closet-delay"))), r = n.next(); !r.done; r = n.next()) { r.value.style.visibility = "visible" } } catch (t) { e = { error: t } } finally { try { r && !r.done && (t = n.return) && t.call(n) } finally { if (e) throw e.error } } }, Be = Object.freeze({ __proto__: null, BrowserTemplate: Se, cleanup: Ce, interspliceChildNodes: we }), Ne = ae.compile({ text: { match: /[a-zA-Z_]+/u }, numDigits: { match: /\d+(?=:|$)/u }, digits: { match: /\d+/u }, slash: { match: "/" }, escapeseq: { match: /%\w/u }, multiplierSeq: { match: /\*n\+/u }, allStar: { match: /^\*$/u }, numStar: { match: /\*(?=:|$)/u }, star: { match: "*" }, groupOpen: { match: "{" }, groupAlternative: { match: "," }, rangeSpecifier: { match: /-/u }, numGroupClose: { match: /\}(?=:|$)/u }, groupClose: { match: "}" }, occurSep: { match: ":" } }); function De(e) { return e[0] } var Oe, _e = { Lexer: Ne, ParserRules: [{ name: "start", symbols: ["allStar"], postprocess: De }, { name: "start", symbols: ["pattern"], postprocess: De }, { name: "allStar", symbols: [Ne.has("allStar") ? { type: "allStar" } : allStar], postprocess: function () { return function () { return !0 } } }, { name: "pattern", symbols: ["key", "num", "occur"], postprocess: function (e) { var t = a(e, 3), n = t[0], r = t[1], o = t[2]; return function (e, t, i) { if (void 0 === t) { var u = e.match(te); if (!u) return !1; var s = u[1], a = Number(u[2]), c = Number.isNaN(a) ? null : a; return n.test(s) && r(c) && o(i) } return n.test(e) && r(t) && o(i) } } }, { name: "key$ebnf$1$subexpression$1", symbols: ["keyComps"] }, { name: "key$ebnf$1", symbols: ["key$ebnf$1$subexpression$1"] }, { name: "key$ebnf$1$subexpression$2", symbols: ["keyComps"] }, { name: "key$ebnf$1", symbols: ["key$ebnf$1", "key$ebnf$1$subexpression$2"], postprocess: function (e) { return e[0].concat([e[1]]) } }, { name: "key", symbols: ["key$ebnf$1"], postprocess: function (e) { var t = a(e, 1)[0]; return new RegExp("^" + t.join("") + "$", "u") } }, { name: "keyComps", symbols: ["mixedText"], postprocess: De }, { name: "keyComps", symbols: ["escapeseq"], postprocess: De }, { name: "keyComps", symbols: ["keyGroup"], postprocess: De }, { name: "mixedText", symbols: [Ne.has("text") ? { type: "text" } : text], postprocess: function (e) { return a(e, 1)[0].value } }, { name: "mixedText", symbols: [Ne.has("star") ? { type: "star" } : star], postprocess: function () { return ".*" } }, { name: "mixedText", symbols: [Ne.has("slash") ? { type: "slash" } : slash], postprocess: function () { return "\\\\" } }, { name: "escapeseq", symbols: [Ne.has("escapeseq") ? { type: "escapeseq" } : escapeseq], postprocess: function (e) { return a(e, 1)[0].value } }, { name: "keyGroup", symbols: [Ne.has("groupOpen") ? { type: "groupOpen" } : groupOpen, "keyGroupInner", Ne.has("groupClose") ? { type: "groupClose" } : groupClose], postprocess: function (e) { return a(e, 2)[1].join("") } }, { name: "keyGroupInner$ebnf$1", symbols: [] }, { name: "keyGroupInner$ebnf$1$subexpression$1", symbols: [Ne.has("groupAlternative") ? { type: "groupAlternative" } : groupAlternative, "keyGroupItem"] }, { name: "keyGroupInner$ebnf$1", symbols: ["keyGroupInner$ebnf$1", "keyGroupInner$ebnf$1$subexpression$1"], postprocess: function (e) { return e[0].concat([e[1]]) } }, { name: "keyGroupInner", symbols: ["keyGroupItem", "keyGroupInner$ebnf$1"], postprocess: function (e) { var t = a(e, 2); return c([t[0]], t[1].map((function (e) { return e.value }))) } }, { name: "keyGroupItem$ebnf$1", symbols: [] }, { name: "keyGroupItem$ebnf$1$subexpression$1", symbols: ["mixedText"] }, { name: "keyGroupItem$ebnf$1", symbols: ["keyGroupItem$ebnf$1", "keyGroupItem$ebnf$1$subexpression$1"], postprocess: function (e) { return e[0].concat([e[1]]) } }, { name: "keyGroupItem", symbols: ["keyGroupItem$ebnf$1"], postprocess: function (e) { return a(e, 1)[0].join("") } }, { name: "num$ebnf$1$subexpression$1", symbols: ["numPredicate"] }, { name: "num$ebnf$1", symbols: ["num$ebnf$1$subexpression$1"], postprocess: De }, { name: "num$ebnf$1", symbols: [], postprocess: function () { return null } }, { name: "num", symbols: ["num$ebnf$1"], postprocess: function (e) { var t = a(e, 1)[0]; return t ? t[0] : function (e) { return null === e } } }, { name: "numPredicate", symbols: ["numDigits"], postprocess: De }, { name: "numPredicate", symbols: ["numStar"], postprocess: De }, { name: "numPredicate", symbols: ["numGroup"], postprocess: De }, { name: "numDigits", symbols: [Ne.has("numDigits") ? { type: "numDigits" } : numDigits], postprocess: function (e) { var t = a(e, 1)[0]; return function (e) { return e === Number(t.value) } } }, { name: "numStar", symbols: [Ne.has("numStar") ? { type: "numStar" } : numStar], postprocess: function () { return function (e) { return !0 } } }, { name: "numGroup", symbols: [Ne.has("groupOpen") ? { type: "groupOpen" } : groupOpen, "numGroupInner", Ne.has("numGroupClose") ? { type: "numGroupClose" } : numGroupClose], postprocess: function (e) { var t = a(e, 2)[1]; return function (e) { return t.reduce((function (t, n) { return t || n(e) }), !1) } } }, { name: "numGroupInner$ebnf$1", symbols: [] }, { name: "numGroupInner$ebnf$1$subexpression$1", symbols: [Ne.has("groupAlternative") ? { type: "groupAlternative" } : groupAlternative, "numGroupItem"] }, { name: "numGroupInner$ebnf$1", symbols: ["numGroupInner$ebnf$1", "numGroupInner$ebnf$1$subexpression$1"], postprocess: function (e) { return e[0].concat([e[1]]) } }, { name: "numGroupInner", symbols: ["numGroupItem", "numGroupInner$ebnf$1"], postprocess: function (e) { var t = a(e, 2); return c([t[0]], t[1].map((function (e) { return a(e, 2)[1] }))) } }, { name: "numGroupItem$ebnf$1$subexpression$1", symbols: ["numGroupPredicate"] }, { name: "numGroupItem$ebnf$1", symbols: ["numGroupItem$ebnf$1$subexpression$1"], postprocess: De }, { name: "numGroupItem$ebnf$1", symbols: [], postprocess: function () { return null } }, { name: "numGroupItem", symbols: ["numGroupItem$ebnf$1"], postprocess: function (e) { var t = a(e, 1)[0]; return t ? t[0] : function (e) { return null === e } } }, { name: "numGroupPredicate", symbols: ["digits"], postprocess: De }, { name: "numGroupPredicate", symbols: ["range"], postprocess: De }, { name: "numGroupPredicate", symbols: ["multiple"], postprocess: De }, { name: "digits", symbols: [Ne.has("digits") ? { type: "digits" } : digits], postprocess: function (e) { var t = a(e, 1)[0]; return function (e) { return e === Number(t.value) } } }, { name: "range", symbols: ["bounded"], postprocess: De }, { name: "range", symbols: ["leftBounded"], postprocess: De }, { name: "range", symbols: ["rightBounded"], postprocess: De }, { name: "bounded", symbols: [Ne.has("digits") ? { type: "digits" } : digits, Ne.has("rangeSpecifier") ? { type: "rangeSpecifier" } : rangeSpecifier, Ne.has("digits") ? { type: "digits" } : digits], postprocess: function (e) { var t = a(e, 3), n = t[0], r = t[2]; return function (e) { return "number" == typeof e && Number(n.value) <= e && e <= Number(r.value) } } }, { name: "leftBounded", symbols: [Ne.has("digits") ? { type: "digits" } : digits, Ne.has("rangeSpecifier") ? { type: "rangeSpecifier" } : rangeSpecifier], postprocess: function (e) { var t = a(e, 1)[0]; return function (e) { return "number" == typeof e && Number(t.value) <= e } } }, { name: "rightBounded", symbols: [Ne.has("rangeSpecifier") ? { type: "rangeSpecifier" } : rangeSpecifier, Ne.has("digits") ? { type: "digits" } : digits], postprocess: function (e) { var t = a(e, 2)[1]; return function (e) { return "number" == typeof e && e <= Number(t.value) } } }, { name: "multiple", symbols: [Ne.has("digits") ? { type: "digits" } : digits, Ne.has("multiplierSeq") ? { type: "multiplierSeq" } : multiplierSeq, Ne.has("digits") ? { type: "digits" } : digits], postprocess: function (e) { var t = a(e, 3), n = t[0], r = t[2]; return function (e) { return (e - r) % n == 0 } } }, { name: "occur$ebnf$1$subexpression$1", symbols: [Ne.has("occurSep") ? { type: "occurSep" } : occurSep, "occurPredicate"] }, { name: "occur$ebnf$1", symbols: ["occur$ebnf$1$subexpression$1"], postprocess: De }, { name: "occur$ebnf$1", symbols: [], postprocess: function () { return null } }, { name: "occur", symbols: ["occur$ebnf$1"], postprocess: function (e) { var t = a(e, 1)[0]; return t ? function (e) { return null === e || t[1](e) } : function () { return !0 } } }, { name: "occurPredicate", symbols: ["numDigits"], postprocess: De }, { name: "occurPredicate", symbols: ["numStar"], postprocess: De }, { name: "occurPredicate", symbols: ["occurGroup"], postprocess: De }, { name: "occurGroup", symbols: [Ne.has("groupOpen") ? { type: "groupOpen" } : groupOpen, "occurGroupInner", Ne.has("groupClose") ? { type: "groupClose" } : groupClose], postprocess: function (e) { var t = a(e, 2)[1]; return function (e) { return t.reduce((function (t, n) { return n || t(e) }), !1) } } }, { name: "occurGroupInner$ebnf$1", symbols: [] }, { name: "occurGroupInner$ebnf$1$subexpression$1", symbols: [Ne.has("groupAlternative") ? { type: "groupAlternative" } : groupAlternative, "numGroupPredicate"] }, { name: "occurGroupInner$ebnf$1", symbols: ["occurGroupInner$ebnf$1", "occurGroupInner$ebnf$1$subexpression$1"], postprocess: function (e) { return e[0].concat([e[1]]) } }, { name: "occurGroupInner", symbols: ["numGroupPredicate", "occurGroupInner$ebnf$1"], postprocess: function (e) { var t = a(e, 2); return c([t[0]], t[1].map((function (e) { return a(e, 2)[1] }))) } }], ParserStart: "start" }, $e = ee.Grammar.fromCompiled(_e), Te = function () { function e(e) { this.predicate = function (e) { var t = new ee.Parser($e); try { var n = t.feed(e).results; return n.length > 1 && console.log("Tag selector is ambiguous: ", e, n), n[0] } catch (e) { return console.log("Tag selector failed to parse: ", e), function () { return !1 } } }(e) } return e.make = function (t) { return new e(t) }, e.prototype.check = function (e, t, n) { return void 0 === n && (n = null), this.predicate(e, t, n) }, e.prototype.checkFullKey = function (e, t) { return void 0 === t && (t = null), this.predicate(e, void 0, t) }, e.prototype.checkTagIdentifier = function (e) { var t = e.match(te); if (!t) return !1; var n = t[1], r = t[2].length > 0 ? Number(t[2]) : null, o = t[3] ? Number(t[3]) : null; return this.predicate(n, r, o) }, e }(), Ie = function () { for (var e = [], t = 0; t < arguments.length; t++)e[t] = arguments[t]; if (!globalThis.closetDebug) return !1; var n = Object.assign(document.createElement("div"), { className: "closet-log", innerText: e.map(String).join("\n") }), r = document.getElementById("qa"); return !!r && (r.appendChild(n), !0) }, je = "github.com/hgiesel/closet", Pe = function () { function e() { this._isAvailable = !1; try { "object" == typeof globalThis.sessionStorage && (this._isAvailable = !0) } catch (e) { } } return e.prototype.clear = function () { for (var e = 0; e < sessionStorage.length; e++) { var t = sessionStorage.key(e); t && 0 === t.indexOf(je) && (sessionStorage.removeItem(t), e--) } }, e.prototype.setItem = function (e, t) { sessionStorage.setItem(je + e, JSON.stringify(t)) }, e.prototype.getItem = function (e) { var t, n = null !== (t = sessionStorage.getItem(je + e)) && void 0 !== t ? t : "null"; return JSON.parse(n) }, e.prototype.removeItem = function (e) { sessionStorage.removeItem(je + e) }, e.prototype.isAvailable = function () { return this._isAvailable }, e }(), ze = function () { function e(e) { this._isAvailable = !1, this.obj = globalThis[e], "object" == typeof this.obj && (this._isAvailable = !0, void 0 === this.obj[je] && this.clear()) } return e.prototype.clear = function () { this.obj[je] = {} }, e.prototype.setItem = function (e, t) { this.obj[je][e] = t }, e.prototype.getItem = function (e) { return null == this.obj[je][e] ? null : this.obj[je][e] }, e.prototype.removeItem = function (e) { delete this.obj[je][e] }, e.prototype.isAvailable = function () { return this._isAvailable }, e }(), Me = function () { var e; if ((e = new Pe).isAvailable()) return Ie("Used Persistence sessionStorage implementation"), e; if ((e = new ze("py")).isAvailable()) return Ie("Used Persistence windowKey py implementation"), e; var t = location.toString().indexOf("title"), n = location.toString().indexOf("main", t); return t > 0 && n > 0 && n - t < 10 ? (Ie("Used Persistence windowKey qt implementation"), new ze("qt")) : (Ie("Defaulted to Persistence windowKey implementation"), e) }(); !function (e) { e[e.ClosetImmediately = 1] = "ClosetImmediately", e[e.DesktopShownHook = 2] = "DesktopShownHook", e[e.MobileMathJax = 3] = "MobileMathJax", e[e.DesktopMobileLate = 4] = "DesktopMobileLate", e[e.Web = 5] = "Web" }(Oe || (Oe = {})); var Le, Ge, Re, qe, He = function (e) { switch (e) { case Oe.ClosetImmediately: return "You have set the variable globalThis.closetImmediately."; case Oe.DesktopShownHook: return "Closet executes while MathJax is still rendering. You are on AnkiDesktop. The action will be pushed onto onShownHook."; case Oe.MobileMathJax: return "Closet executes while MathJax is still rendering. You are on AnkiMobile. The action will be enqueued on MathJax.Hub.Queue."; case Oe.DesktopMobileLate: return "Closet executes after MathJax finished rendering. The action will be executed immediatley."; case Oe.Web: return "You are on AnkiWeb, which does not have MathJax. Alternatively, you are on platform with MathJax 3. The action will be executed immediatley."; default: return "No Description found. This should never happen." } }, Je = { raw: function (e) { return e() }, viaShownHook: function (e) { return globalThis.onShownHook.push(e) }, viaMathJaxQueue: function (e) { return globalThis.MathJax.Hub.Queue(e) } }, Ke = function () { var e = globalThis.MathJax.Hub.queue, t = Object.assign({}, e); return t.queue = t.queue.map(String), t }, Ye = function (e) { var t = a(function () { if (globalThis.closetImmediately) return [Je.raw, Oe.ClosetImmediately, []]; try { var e = globalThis._updatingQA, t = Ke(); if (e) { var n = globalThis.onShownHook; return n ? [Je.viaShownHook, Oe.DesktopShownHook, [JSON.stringify(n.map(String)), JSON.stringify(t)]] : [Je.viaMathJaxQueue, Oe.MobileMathJax, [JSON.stringify(t)]] } return [Je.raw, Oe.DesktopMobileLate, [JSON.stringify(t)]] } catch (e) { return [Je.raw, Oe.Web, [e]] } }(), 3), n = t[0], r = t[1], o = t[2]; Ie.apply(void 0, c(["Init mode " + r, He(r)], o)), n(e) }, We = function (e, t, n, r) { var o = window.performance.now(); return Se.makeFromNodes(e, null == r ? void 0 : r.delimiters).renderToNodes(n), t.writeBack(), window.performance.now() - o }, Xe = function (e, t, n, r, o) { var i, u, s = function (e, t, n) { return { card: e, cardNumber: (r = e, Number(r.match(/[0-9]*$/))), tagsFull: t, tags: 0 === t.length ? [] : t.split(" "), side: n }; var r }(n, r, o), a = function (e, t) { return function (n) { var r, o = null !== (r = globalThis.closetCardHash) && void 0 !== r ? r : null, i = function () { if (!Me.isAvailable()) return new Map; if ("front" === e) { var r = function (e) { var t, n = 0; for (t = 0; t < e.length; t++)n = (n << 5) - n + e.charCodeAt(t), n |= 0; return n }(t); if (r !== o) return globalThis.closetCardHash = r, new Map } var i = Me.getItem(n); return new Map(i) }(); return { key: n, map: i, writeBack: function () { if (Me.isAvailable()) { var e = Array.from(i.entries()); Me.setItem(n, e) } } } } }(o, null !== (u = null === (i = document.getElementById("qa")) || void 0 === i ? void 0 : i.innerHTML) && void 0 !== u ? u : ""); return t(e, s, a).map((function (e) { return We.apply(void 0, c(e)) })) }, Ue = function (e) { return function (e) { return e.nodeType === Node.ELEMENT_NODE }(e) && (function (e) { return "STYLE" === e.tagName }(e) || function (e) { return function (e) { return "SCRIPT" === e.tagName }(e) && (0 === e.type.length || e.type.endsWith("javascript")) }(e) || function (e) { return "anki-am" === e.id }(e)) }, Ve = Object.freeze({ __proto__: null, init: Xe, initialize: function (e, t, n, r, o) { return Ye((function () { return function (e, t, n, r, o) { try { var i = Xe(e, t, n, r, o); console.log("Closet executed in " + i.map((function (e) { return e.toFixed(3) })) + "ms.") } catch (e) { console.log("An error occured while executing Closet:", e), Ie("An error occured while executing Closet", e) } finally { Ce() } }(e, t, n, r, o) })), e }, ankiLog: Ie, getQaChildNodes: function () { if (!globalThis.document) return null; var e = globalThis.document.getElementById("qa"); return e ? we(e, { type: "predicate", value: function (e) { return !Ue(e) } }) : null } }), Qe = Object.freeze({ __proto__: null, Template: me, BrowserTemplate: Se, Parser: de, TagSelector: Te, browser: Be, anki: Ve, optics: R }), Ze = I({ sep: ";" }), et = I({ sep: "=", trim: !0, max: 2 }), tt = function () { function e(e) { this.defaultValue = e, this.selectors = [] } return e.prototype.set = function (e, t) { this.selectors.unshift([Te.make(e), t]) }, e.prototype.get = function (e, t, n) { var r = this.selectors.find((function (r) { return a(r, 1)[0].check(e, t, n) })); return r ? r[1] : this.defaultValue }, e }(), nt = function (e) { return function (t, n, r) { return function (o, i) { var u = i.cache; return o.values.forEach((function (o) { u.over(t, r.apply(void 0, c(o)), new e(n)) })), { ready: !0 } } } }, rt = nt(function (e) { function t() { return null !== e && e.apply(this, arguments) || this } return o(t, e), t.prototype.setNumber = function (e, t) { this.set(e, Number.isNaN(t) ? 0 : t) }, t }(tt)), ot = nt(function (e) { function t() { return null !== e && e.apply(this, arguments) || this } return o(t, e), t.prototype.on = function (e) { this.set(e, !0) }, t.prototype.off = function (e) { this.set(e, !1) }, t }(tt)), it = function (e) { return { get: function () { return e } } }, ut = function (e) { function t() { return null !== e && e.apply(this, arguments) || this } return o(t, e), t.prototype.setList = function (e, t) { this.set(e, t) }, t.prototype.overwriteList = function (e, t, n) { void 0 === n && (n = 0); for (var r = this.getList(e), o = 0; o < t.length; o++)r[n + o] = t[o]; this.setList(e, r) }, t.prototype.getList = function (e) { return this.get(e, []) }, t }(function (e) { function t() { return null !== e && e.apply(this, arguments) || this } return o(t, e), t }(g)), st = (Le = ut, function (e, t) { return function (n, r) { var o; return { ready: !0, result: null !== (o = r.cache.over(e, t(n, r), new Le(new Map))) && void 0 !== o ? o : "" } } }), at = function (e, t) { return function (e, t) { return "" } }, ct = (Ge = function (e, t, n) { return function (r, o) { return null === r.num ? e.setList(t, n) : e.overwriteList(t, n, r.num), "" } }, function (e) { var t = void 0 === e ? {} : e, n = t.tagname, r = void 0 === n ? "setl" : n, o = t.storeId, i = void 0 === o ? "lists" : o, u = t.postprocess, s = void 0 === u ? at : u, c = t.optics, l = void 0 === c ? [I({ sep: "::" }), function (e, t) { return n = t, function (e) { return e.map(n) }; var n }, I({ sep: "||" })] : c; return function (e) { return e.register(r, st(i, function (e, t) { return function (n, r) { return function (o) { var i = a(2 === n.values.length ? n.values : [["default"], n.values[0]], 2), u = i[0], s = i[1], c = u[0], l = o.getList(c), p = e(o, c, s)(n, r); return t(p, l)(n, r) } } }(Ge, s)), { optics: l }) } }), lt = function (e, t, n, r) { var o, i, s; return void 0 === n && (n = []), void 0 === r && (r = function () { return !1 }), u(this, (function (u) { switch (u.label) { case 0: o = [], i = 0, u.label = 1; case 1: return !r(n) && i < 1e3 ? (s = e(), n.includes(s) || o.includes(s) ? [3, 3] : (t && o.push(s), [4, s])) : [3, 4]; case 2: u.sent(), u.label = 3; case 3: return i++, [3, 1]; case 4: return [2, o] } })) }, pt = function (e, t) { return function () { return e + Math.floor(Math.random() * (t - e)) } }, ft = function (e, t) { return function (t, n) { return e } }, ht = function (e) { return function (t) { var n = void 0 === t ? {} : t, r = n.tagname, o = void 0 === r ? "pick" : r, i = n.storeId, u = void 0 === i ? "lists" : i, s = n.postprocess, a = void 0 === s ? ft : s; return function (t) { return t.register(o, st(u, function (e, t) { return function (n, r) { return function (o) { var i = n.values || "default", u = o.getList(i), s = e(u, i)(n, r); return t(s, u)(n, r) } } }(e, a))) } } }, dt = ht((function (e, t) { return function (n, r) { var o = r.memory, i = "pick:picked:" + n.fullKey + ":" + n.occur, u = o.lazy(i, (function () { var r, i, u = pt(0, e.length), s = (r = 0, i = e.length, function (e) { return e.length >= i - r }), a = Boolean(n.num), c = "pick:uniqList:" + n.fullKey, l = a ? o.over(c, (function (e) { return Object.prototype.hasOwnProperty.call(e, t) || (e[t] = []), e }), {})[t] : [], p = Array.from(e).reduce((function (e, t, n) { return void 0 === t && e.push(n), e }), []), f = l.concat(p), h = lt(u, a, f, s).next(); if (!h.done) return l.push(h.value), h.value })); return Number.isInteger(u) ? e[u] : "" } })), mt = ht((function (e, t) { return function (t, n) { return e[Number(t.num) || 0] } })), vt = ht((function (e, t) { return function (t, n) { var r, o = e[n.preset.cardNumber]; return null !== (r = null != o ? o : e[0]) && void 0 !== r ? r : "" } })), yt = function () { function e(e, t, n) { this.separator = e, this.mapper = t, this.processor = n } return e.make = function (t) { var n = void 0 === t ? {} : t, r = n.separator, o = void 0 === r ? "" : r, i = n.mapper, u = void 0 === i ? B : i, s = n.processor; return new e(o, u, void 0 === s ? B : s) }, e.prototype.toStylizer = function (t) { var n = void 0 === t ? {} : t, r = n.separator, o = void 0 === r ? this.separator : r, i = n.mapper, u = void 0 === i ? this.mapper : i, s = n.processor; return new e(o, u, void 0 === s ? this.processor : s) }, e.prototype.stylize = function (e) { for (var t = this, n = [], r = 1; r < arguments.length; r++)n[r - 1] = arguments[r]; return this.processor(e.flatMap((function (e, r, o) { return t.mapper.apply(t, c([e, r], n.map((function (e) { return e[r] })), [o])) })).join(this.separator)) }, e.prototype.stylizeFull = function (e, t, n) { var r = this; return void 0 === t && (t = []), void 0 === n && (n = []), this.processor.apply(this, c([e.flatMap((function (e, n, o) { return r.mapper.apply(r, c([e, n], t.map((function (e) { return e[n] })), [o])) })).join(this.separator)], n)) }, e }(), gt = function (e) { return function (t) { var n = (void 0 === t ? {} : t).tagname, r = void 0 === n ? "s" : n; return function (t) { t.register(r, e) } } }, bt = function (e) { return function (t) { return e(t.values) } }, xt = function (e) { return function (t, n) { return function (r, o) { var i = a(e(r, o), 2), u = i[0], s = i[1]; return o.cache.over("sequences", (function (e) { e.includes(s) || e.push(s) }), []), function (e, t, n, r) { return function (o, i) { var u = e + ":apply", s = e + ":length", a = t + ":waitingSet", c = t + ":shuffle"; if (i.cache.get(u, !1)) { if (i.cache.get(a, new Set).size > 0) return; for (var l = [], p = i.cache.get(c, []), f = i.cache.get(s, 0), h = 0; h < f; h++) { var d = p.shift(); d && l.push(d) } return l } if (i.ready) { var m = r(o, i); if (0 === m.length) return m; i.cache.fold(c, (function (e) { return e.concat(m) }), []), i.cache.set(s, m.length), i.deferred.registerIfNotExists(u, (function () { i.cache.set(u, !0), i.cache.over(a, (function (t) { return t.delete(e) }), new Set) }), { priority: 65 }), i.deferred.registerIfNotExists(c, (function () { i.cache.over(a, (function (e) { return e.size > 0 }), new Set) || i.cache.fold(c, (function (e) { var t = i.memory.fold(c, (function (e) { return n(e, i.cache.get(c, []).length) }), []); return O(e, t) }), []) })) } else i.cache.over(a, (function (t) { return t.add(e) }), new Set) } }(u, s, n, t)(r, o) } } }, At = xt((function (e, t) { var n = e.fullKey, r = e.fullOccur; return [n + ":" + r, n + ":" + r] })), wt = xt((function (e, t) { var n = e.fullKey; return [n + ":" + e.fullOccur, n] })), kt = xt((function (e, t) { var n = e.fullKey, r = e.fullOccur; return [n + ":" + r, e.num ? n : n + ":" + r] })), Et = Object.freeze({ __proto__: null, withinTag: At, acrossTag: wt, acrossNumberedTag: kt, withinCustom: function (e) { return xt(function (e) { return function (t, n) { var r = t.fullKey, o = t.fullOccur, i = t.num; return [r + ":" + o, "" + e + i + ":" + o] } }(e)) }, acrossCustom: function (e) { return xt(function (e) { return function (t, n) { var r = t.fullKey, o = t.fullOccur, i = t.num; return [r + ":" + o, "" + e + i] } }(e)) }, acrossNumberedCustom: function (e) { return xt(function (e) { return function (t, n) { var r = t.fullKey, o = t.fullOccur, i = t.num, u = "" + e + i; return [r + ":" + o, i ? u : u + ":" + o] } }(e)) } }), Ft = function (e, t) { if (e.length >= t) return e; var n = function (e) { for (var t = e.slice(0), n = e.length; 0 !== n;) { var r = Math.floor(Math.random() * n), o = t[n -= 1]; t[n] = t[r], t[r] = o } return t }(Array.from(new Array(t - e.length), (function (t, n) { return n + e.length }))); return c(e, n) }, St = Object.freeze({ __proto__: null, topUp: Ft, mixIn: function (e, t) { if (e.length >= t) return e; var n = Array.from(new Array(t - e.length), (function (t, n) { return n + e.length })), r = c(e); return n.forEach((function (e) { return r.splice(Math.floor(Math.random() * (r.length + 1)), 0, e) })), r } }), Ct = yt.make({ processor: function (e) { return '' + e + "" }, mapper: function (e) { return '' + e + "" }, separator: '' }), Bt = function (e, t) { return function () { return e.stylize(t) } }, Nt = [I({ sep: "||" })], Dt = { priority: 35, persistent: !0 }, Ot = [I("::"), function (e, t) { return n = t, function (e) { return e.map(n) }; var n }, I(",")], _t = [I({ sep: "," })], $t = function (e, t, n) { return function (r) { var o = void 0 === r ? {} : r, i = o.tagname, u = void 0 === i ? "gen" : i, s = o.uniqueConstraintId, c = void 0 === s ? "uniq" : s, l = o.optics, p = void 0 === l ? _t : l; return function (r) { var o = "gen:" + c; r.register(u, (function (r, i) { var s = r.values, c = r.fullOccur, l = r.num, p = i.memory, f = a(1 === s.length ? [1, Number(s[0]), n] : s.map(Number), 3), h = f[0], d = void 0 === h ? 1 : h, m = f[1], v = void 0 === m ? 100 : m, y = f[2], g = void 0 === y ? n : y, b = Number.isInteger(l) ? o + ":" + l : o, x = "gen:" + u + ":" + c; return { ready: !0, result: p.lazy(x, (function () { var n = lt(e(d, v), !0, Number.isInteger(l) ? p.get(b, []) : []).next(); return n.done ? "" : t(n.value, g) })) } }), { optics: p }) } } }, Tt = $t(pt, (function (e, t) { return String(e * t) }), 1), It = $t((function (e, t) { return function () { return e + Math.random() * (t - e) } }), (function (e, t) { return e.toFixed(t) }), 2), jt = /%(.)/gu, Pt = { optics: [I({ sep: "::", max: 2 })], capture: !0 }, zt = { inlineOptics: [I({ sep: "::", max: 2 })], capture: !0 }, Mt = Object.freeze({ __proto__: null, apply: function (e) { var t = void 0 === e ? {} : e, n = t.tagname, r = void 0 === n ? "s" : n, o = t.apply, i = void 0 === o ? bt(B) : o, u = t.optics, s = void 0 === u ? [] : u; return function (e) { e.register(r, i, { optics: s }) } }, process: function (e) { var t = void 0 === e ? {} : e, n = t.tagname, r = void 0 === n ? "s" : n, o = t.processor, i = void 0 === o ? B : o, u = t.optics, s = void 0 === u ? [] : u; return function (e) { e.register(r, bt(i), { optics: s }) } }, style: function (e) { var t = void 0 === e ? {} : e, n = t.tagname, r = void 0 === n ? "s" : n, o = t.stylizer, i = void 0 === o ? yt.make() : o, u = t.optics, s = void 0 === u ? [I("::")] : u; return function (e) { e.register(r, bt(i.stylize.bind(i)), { optics: s }) } }, shuffle: function (e) { var t = void 0 === e ? {} : e, n = t.tagname, r = void 0 === n ? "mix" : n, o = t.stylizer, i = void 0 === o ? Ct : o, u = t.evaluate, s = void 0 === u ? Bt : u, a = t.sortInStrategy, c = void 0 === a ? Ft : a, l = t.sequencer, p = void 0 === l ? kt : l, f = t.optics, h = void 0 === f ? Nt : f; return function (e) { e.register(r, (function (e, t) { var n = p((function (e) { var t = e.values; return null != t ? t : [] }), c), r = n(e, t); if (r) return s(i, r)(e, t) }), { optics: h }) } }, order: function (e) { var t = void 0 === e ? {} : e, n = t.tagname, r = void 0 === n ? "ord" : n, o = t.optics, i = void 0 === o ? Ot : o, u = t.sortInStrategy, c = void 0 === u ? Ft : u; return function (e) { e.register(r, (function (e, t) { var n, r, o = t.deferred, i = t.cache, u = t.memory, l = new Set(i.get("sequences", [])), p = function (t, n) { var r = n.map(Te.make), p = e.key + ":" + e.fullOccur + ":ord:" + t; o.register(p, (function () { var e, t, n = Array.from(l).filter((function (e) { return !!r.some((function (t) { return t.checkTagIdentifier(e) })) && (l.delete(e), !0) })), f = n.map((function (e) { return e + ":shuffle" })), h = function (e, t) { var n = f[e], r = t + ":waitingSet"; if (o.block(n), 0 !== i.get(r, new Set).size) return "continue"; var s = f.reduce((function (e, t) { var n = u.get(t, []); return e.length < n.length ? n : e }), []), a = i.get(n, []), l = c(i.get(p, s), a.length); i.fold(n, (function (e) { return O(e, l) }), []), i.set(p, l), u.set(n, l) }; try { for (var d = (e = void 0, s(n.entries())), m = d.next(); !m.done; m = d.next()) { var v = a(m.value, 2); h(v[0], v[1]) } } catch (t) { e = { error: t } } finally { try { m && !m.done && (t = d.return) && t.call(d) } finally { if (e) throw e.error } } 0 === l.size && o.unregister(p) }), Dt) }; try { for (var f = s(e.values.entries()), h = f.next(); !h.done; h = f.next()) { var d = a(h.value, 2); p(d[0], d[1]) } } catch (e) { n = { error: e } } finally { try { h && !h.done && (r = f.return) && r.call(f) } finally { if (n) throw n.error } } return { ready: !0 } }), { optics: i }) } }, generateInteger: Tt, generateReal: It, debug: function () { return function (e) { e.register("tagpath", (function (e, t) { return t.path.join(":") })), e.register("never", (function () { })), e.register("empty", (function () { return "" })), e.register("key", (function (e) { return e.key })), e.register("stopIteration", (function (e, t) { var n = e.values, r = t.filters, o = Number(n), i = r.getOrDefault("base"); return r.register("base", (function (e, t) { var n; return t.iteration >= o ? null !== (n = e.text) && void 0 !== n ? n : "" : i(e, t) })), { ready: !0 } })), e.register("memorytest", (function (e, t) { var n = t.memory; return String(n.fold("base:memorytest", (function (e) { return ++e }), 0)) })) } }, define: function (e) { return void 0 === e && (e = {}), function (t) { var n = e.tagname, r = void 0 === n ? "def" : n, o = { optics: [I({ sep: "::" })] }; t.register(r, (function (e) { var n = a(e.values, 2), r = n[0], i = n[1]; return t.register(r, (function (e) { var t = i.replace(jt, function (e) { return function (t, n) { var r, o = null; switch (n) { case "%": return n; case "n": return "number" == typeof e.num ? String(e.num) : ""; case "k": return e.key; case "f": return e.fullKey; default: return o = Number(n), Number.isNaN(o) ? t : null !== (r = e.values[o]) && void 0 !== r ? r : "" } } }(e)); return { result: t, parse: !0 } }), o), { ready: !0 } }), Pt) } }, delimiter: function (e) { return void 0 === e && (e = {}), function (t) { var n = e.tagname, r = void 0 === n ? "delim" : n; t.register(r, (function (e, t) { var n = t.template; if (t.isCapture) { var r = a(e.inlineValues, 2), o = r[0], i = r[1]; return n.parser.update({ open: o, close: i }), { result: e.blockText, parse: !0 } } return n.parser.update(), { result: e.innerNodes } }), zt) } }, setNumber: function (e) { var t = void 0 === e ? {} : e, n = t.tagname, r = void 0 === n ? "set" : n, o = t.storeId, i = void 0 === o ? "numerical" : o, u = t.separator, s = void 0 === u ? Ze : u, a = t.assignmentSeparator, c = void 0 === a ? et : a; return function (e) { return e.register(r, rt(i, 0, (function (e, t) { return function (n) { return n.setNumber(e, Number(t)) } })), { optics: [s, function (e, t) { return n = t, function (e) { return e.map(n) }; var n }, c] }) } }, activate: function (e) { var t = void 0 === e ? {} : e, n = t.tagname, r = void 0 === n ? "on" : n, o = t.storeId, i = void 0 === o ? "active" : o, u = t.optic, s = void 0 === u ? Ze : u; return function (e) { return e.register(r, ot(i, !1, (function (e) { return function (t) { t.on(e) } })), { separators: [s, function (e, t) { return n = t, function (e) { return e.map(n) }; var n }, et] }) } }, deactivate: function (e) { var t = void 0 === e ? {} : e, n = t.tagname, r = void 0 === n ? "off" : n, o = t.storeId, i = void 0 === o ? "active" : o, u = t.optic, s = void 0 === u ? Ze : u; return function (e) { return e.register(r, ot(i, !1, (function (e) { return function (t) { return t.off(e) } })), { separators: [s, function (e, t) { return n = t, function (e) { return e.map(n) }; var n }, et] }) } }, constantGet: it, setList: ct, pick: dt, pickIndex: mt, pickCardNumber: vt }), Lt = function (e, t, n, r) { return e - n <= t && t <= e + r }, Gt = function (e, t) { var n = e.key, r = e.num, o = e.fullOccur, i = t.preset, u = t.cache, s = it(0), a = null, c = null; switch (r) { case null: return !1; case 0: return !0; default: return "number" == typeof i.cardNumber && (a = u.get("flashcardActiveBottom", s).get(n, i.cardNumber, o), c = u.get("flashcardActiveTop", s).get(n, i.cardNumber, o), Lt(i.cardNumber, r, a, c)) } }, Rt = function (e, t) { return t.cache.get("flashcardActive", it(!1)).get(e.key, e.num, e.fullOccur) }, qt = function (e, t) { return Rt(e, t) || Gt(e, t) }, Ht = function (e, t) { return "back" === t.preset.side }, Jt = Ht, Kt = Object.freeze({ __proto__: null, isActiveWithinRange: Lt, isActive: function (e, t) { var n = e.num, r = t.preset; switch (n) { case null: return !1; case 0: return !0; default: return !!Object.prototype.hasOwnProperty.call(r, "cardNumber") && n === r.cardNumber } }, isActiveGetRange: Gt, isActiveOverwritten: Rt, isActiveAll: qt, isBack: Ht, isBackAll: Jt }), Yt = function (e) { return Object.prototype.hasOwnProperty.call(e, "tagname") ? [e.tagname] : [] }, Wt = function (e, t) { return i(i({}, e), { tagname: t[0] }) }, Xt = function (e) { return function (t, n) { var r = void 0 === n ? { wrapId: "wrapped", getTagnames: Yt, setTagnames: Wt } : n, o = r.wrapId, i = r.getTagnames, u = r.setTagnames; return function (n) { return void 0 === n && (n = {}), function (r) { var a, c, l = i(n), p = function (e) { return e + ":" + o + ":internal" }, f = new Map, h = l.map((function (e) { var t = p(e); return f.set(e, t), t })); t(u(n, h))(r); var d = function (t, n) { var r = f.get(t.key); return e(Object.assign({}, t, { keyInternal: r }), n), n.filters.getOrDefault(r)(t, n) }; try { for (var m = s(l), v = m.next(); !v.done; v = m.next()) { var y = v.value; r.register(y, d, r.getOptions(p(y))) } } catch (e) { a = { error: e } } finally { try { v && !v.done && (c = m.return) && c.call(m) } finally { if (a) throw a.error } } } } } }, Ut = function (e) { return function (t, n, r, o) { return void 0 === r && (r = {}), void 0 === o && (o = { wrapId: "wrapped", getTagnames: Yt, setTagnames: Wt }), Xt((function (t, o) { e(o).registerIfNotExists(t.keyInternal, n, r) }))(t, o) } }, Vt = Ut((function (e) { return e.deferred })), Qt = Ut((function (e) { return e.aftermath })), Zt = function (e, t, n, r) { var o = void 0 === r ? { wrapId: "sum", getTagnames: Yt, setTagnames: Wt } : r, i = o.wrapId, u = o.setTagnames; return function (r) { var o = void 0 === r ? {} : r, s = o.tagname, a = void 0 === s ? "sum" : s, c = o.optionsFalse, l = void 0 === c ? {} : c, p = o.optionsTrue, f = void 0 === p ? {} : p; return function (r) { var o = a + ":" + i + ":false", s = a + ":" + i + ":true"; e(u(l, [o]))(r), t(u(f, [s]))(r); r.register(a, (function (e, t) { return n(e, t) ? t.filters.getOrDefault(s)(e, t) : t.filters.getOrDefault(o)(e, t) }), r.getOptions(s)) } } }, en = function (e, t, n, r, o, i, u) { var s = void 0 === u ? { wrapId: "sumFour", getTagnames: Yt, setTagnames: Wt } : u, a = s.wrapId, c = s.setTagnames; return function (u) { var s = void 0 === u ? {} : u, l = s.tagname, p = void 0 === l ? "sum" : l, f = s.optionsZero, h = void 0 === f ? {} : f, d = s.optionsOne, m = void 0 === d ? {} : d, v = s.optionsTwo, y = void 0 === v ? {} : v, g = s.optionsThree, b = void 0 === g ? {} : g; return function (u) { var s = p + ":" + a + ":zero", l = p + ":" + a + ":two"; Zt(e, t, o)({ tagname: s, optionsFalse: c(h, [s]), optionsTrue: c(m, [s]) })(u), Zt(n, r, o)({ tagname: l, optionsFalse: c(y, [l]), optionsTrue: c(b, [l]) })(u); u.register(p, (function (e, t) { return i(e, t) ? t.filters.getOrDefault(l)(e, t) : t.filters.getOrDefault(s)(e, t) })) } } }, tn = function (e) { return function (t) { return function (n) { var r, o, i = null != t ? t : {}; try { for (var u = s(e), c = u.next(); !c.done; c = u.next()) { var l = a(c.value, 2), p = l[0], f = l[1], h = void 0 === f ? {} : f, d = h.setTagnames, m = void 0 === d ? Wt : d, v = h.getTagnames; p(m(i, (void 0 === v ? Yt : v)(i)))(n) } } catch (e) { r = { error: e } } finally { try { c && !c.done && (o = u.return) && o.call(u) } finally { if (r) throw r.error } } } } }, nn = it(!1), rn = it(0), on = function (e, t) { var n = t.cache, r = t.preset; if (!r.cardNumber) return [0, 0, 0, 0]; return [n.get("flashcardShowBottom", rn).get(e.key, r.cardNumber, e.fullOccur), n.get("flashcardShowTop", rn).get(e.key, r.cardNumber, e.fullOccur), n.get("flashcardHideBottom", rn).get(e.key, r.cardNumber, e.fullOccur), n.get("flashcardHideTop", rn).get(e.key, r.cardNumber, e.fullOccur)] }, un = function (e) { return function (t) { return function (n, r) { return function (o, i) { return i.cache.get("flashcardShow", nn).get(o.key, o.num, o.fullOccur) ? n(o, i) : i.cache.get("flashcardHide", nn).get(o.key, o.num, o.fullOccur) ? r(o, i) : e(t)(n, r)(o, i) } } } }((Re = function (e) { return function (t, n) { return function (r, o) { return e(t, n)(r, o) } } }, function (e) { return function (t, n) { return function (r, o) { if (!Object.prototype.hasOwnProperty.call(o.preset, "cardNumber")) return Re(e)(t, n)(r, o); var i = a(on(r, o), 4), u = i[0], s = i[1], c = i[2], l = i[3], p = o.preset.cardNumber; return p && r.num ? p - u <= r.num && r.num <= p + s ? t(r, o) : p - c <= r.num && r.num <= p + l ? n(r, o) : Re(e)(t, n)(r, o) : e(t, n)(r, o) } } })), sn = function (e, t, n) { return void 0 === e && (e = qt), void 0 === t && (t = Jt), void 0 === n && (n = un), function (r, o) { return function (i, u, s, a, c, l) { return void 0 === l && (l = {}), function (p) { var f = i + ":internal"; en(gt(n(r)(a, c)), gt(u), gt(n(o)(a, c)), gt(s), e, t)({ tagname: f })(p); p.register(i, (function (e, t) { return t.filters.getOrDefault(f)(e, t) }), l) } } } }; !function (e) { e.Show = "show", e.Hide = "hide", e.Reveal = "reveal" }(qe || (qe = {})); var an = function (e) { var t = e(B, B), n = e(N, N), r = e(N, B), o = a([[qe.Show, "s"], [qe.Hide, "h"], [qe.Reveal, "r"]].map((function (e) { var t = a(e, 2), n = t[0], r = t[1]; return { getTagnames: function (e) { var t, o; return [(null !== (t = e.defaultBehavior) && void 0 !== t ? t : qe.Show) === n ? e.tagname : null !== (o = e.tagnameShow) && void 0 !== o ? o : "" + e.tagname + r] } } })), 3), i = o[0], u = o[1], s = o[2], c = tn([[n, u], [t, i], [r, s]]); return c.show = t, c.hide = n, c.reveal = r, c }, cn = function (e) { return Array.isArray(e[0]) ? e[0].map((function (t, n) { return e.map((function (e) { return e[n] })) })) : [e] }, ln = function (e, t) { return function (n, r) { return r.ready ? e.stylize.apply(e, c(cn(t(n, r)))) : { ready: !1 } } }, pn = function (e, t) { return function (n, r) { if (!r.ready) return { ready: !1 }; var o = t(n, r); return o ? e.stylize.apply(e, c(cn(o))) : { ready: !1 } } }, fn = D(''), hn = function (e) { return function (t) { return '' + t + "" } }, dn = yt.make({ processor: hn("front") }), mn = yt.make({ processor: hn("back") }), vn = function (e, t) { var n; return ['' + (null !== (n = e.values[1]) && void 0 !== n ? n : "") + ""] }, yn = function (e) { return '' + e.values[0] + "" }, gn = function (e, t) { return { ready: t.ready, result: '' + yn(e) + "" } }, bn = function (e) { return [yn(e)] }, xn = an((function (e, t) { return function (n) { void 0 === n && (n = {}); var r = n.tagname, o = void 0 === r ? "c" : r, i = n.inactiveEllipser, u = void 0 === i ? fn : i, s = n.frontStylizer, a = void 0 === s ? dn : s, c = n.frontEllipser, l = void 0 === c ? vn : c, p = n.backStylizer, f = void 0 === p ? mn : p, h = n.backEllipser, d = void 0 === h ? bn : h, m = n.optics, v = void 0 === m ? [I({ sep: "::", max: 2 })] : m, y = n.flashcardTemplate, g = void 0 === y ? sn() : y, b = ln(a, l), x = ln(f, d), A = { optics: v }; return g(e, t)(o, b, x, gn, u, A) } })), An = D(''), wn = function (e) { return e.values ? e.values.flatMap((function (e, t) { return e.map((function (e) { return [e, t] })) })) : [] }, kn = function (e) { return e.values[0] }, En = '', Fn = yt.make({ processor: function (e) { return '' + e + "" }, mapper: function (e) { return '' + e + "" }, separator: En }), Sn = function (e) { return function (t) { return '' + t + "" } }, Cn = yt.make({ processor: Sn("front"), mapper: function (e) { return '' + e + "" }, separator: En }), Bn = yt.make({ processor: Sn("back"), mapper: function (e, t, n) { return '' + e + "" }, separator: En }), Nn = [I({ sep: "::" }), function (e, t) { return n = t, function (e) { return e.map(n) }; var n }, I({ sep: "||", keepEmpty: !1 })], Dn = an((function (e, t) { return function (n) { void 0 === n && (n = {}); var r = n.tagname, o = void 0 === r ? "mc" : r, i = n.frontStylizer, u = void 0 === i ? Cn : i, s = n.backStylizer, a = void 0 === s ? Bn : s, c = n.inactiveStylizer, l = void 0 === c ? Fn : c, p = n.contexter, f = void 0 === p ? kn : p, h = n.ellipser, d = void 0 === h ? An : h, m = n.getValues, v = void 0 === m ? wn : m, y = n.sequence, g = void 0 === y ? wt : y, b = n.sortInStrategy, x = void 0 === b ? Ft : b, A = n.optics, w = void 0 === A ? Nn : A, k = n.flashcardTemplate, E = void 0 === k ? sn() : k, F = g(v, x), S = pn(u, F), C = pn(a, F); return E(e, t)(o, S, C, ln(l, f), d, { optics: w }) } })), On = function (e) { return { result: e.values, parse: !0 } }, _n = function () { return "" }, $n = an((function (e, t) { return function (n) { void 0 === n && (n = {}); var r = n.tagname, o = void 0 === r ? "spec" : r, i = n.flashcardTemplate; return (void 0 === i ? sn() : i)(e, t)(o, On, On, On, _n, { capture: !0 }) } })), Tn = function (e, t) { return e.values }, In = function (e) { return function (t) { return '' + t + "" } }, jn = function (e) { return '' }, Pn = function (e) { return e.values ? e.values : [] }, zn = function (e, t) { return ln(e, Tn) }, Mn = [I({ sep: "::" })], Ln = function (e, t, n) { return function (r, o) { return function (i) { void 0 === i && (i = {}); var u, s = i.tagname, a = void 0 === s ? "shuf" : s, c = i.activeStylizer, l = void 0 === c ? (u = e, yt.make({ processor: function (e) { return '' + e + "" }, mapper: In(u), separator: jn(u) })) : c, p = i.inactiveStylizer, f = void 0 === p ? function (e) { return yt.make({ processor: function (t) { return '' + t + "" }, mapper: In(e), separator: jn(e) }) }(e) : p, h = i.contexter, d = void 0 === h ? Pn : h, m = i.ellipser, v = void 0 === m ? function (e) { return function () { return '' } }(e) : m, y = i.sequence, g = void 0 === y ? wt : y, b = i.sortInStrategy, x = void 0 === b ? Ft : b, A = i.optics, w = void 0 === A ? Mn : A, k = i.flashcardTemplate, E = void 0 === k ? sn() : k, F = { optics: w }, S = g(d, x), C = t(l, S), B = n(l, S), N = ln(f, d); return E(r, o)(a, C, B, N, v, F) } } }, Gn = { cloze: xn, multipleChoice: Dn, specification: $n, mingle: an(Ln("mingle", pn, pn)), sort: an(Ln("sort", pn, zn)), jumble: an(Ln("jumble", zn, pn)) }, Rn = Object.freeze({ __proto__: null, recipes: Gn, deciders: Kt, get behaviors() { return qe } }), qn = "A-Za-z\\xAA\\xB5\\xBA\\xC0-\\xD6\\xD8-\\xF6\\xF8-\\u02C1\\u02C6-\\u02D1\\u02E0-\\u02E4\\u02EC\\u02EE\\u0370-\\u0374\\u0376\\u0377\\u037A-\\u037D\\u037F\\u0386\\u0388-\\u038A\\u038C\\u038E-\\u03A1\\u03A3-\\u03F5\\u03F7-\\u0481\\u048A-\\u052F\\u0531-\\u0556\\u0559\\u0561-\\u0587\\u05D0-\\u05EA\\u05F0-\\u05F2\\u0620-\\u064A\\u066E\\u066F\\u0671-\\u06D3\\u06D5\\u06E5\\u06E6\\u06EE\\u06EF\\u06FA-\\u06FC\\u06FF\\u0710\\u0712-\\u072F\\u074D-\\u07A5\\u07B1\\u07CA-\\u07EA\\u07F4\\u07F5\\u07FA\\u0800-\\u0815\\u081A\\u0824\\u0828\\u0840-\\u0858\\u08A0-\\u08B4\\u08B6-\\u08BD\\u0904-\\u0939\\u093D\\u0950\\u0958-\\u0961\\u0971-\\u0980\\u0985-\\u098C\\u098F\\u0990\\u0993-\\u09A8\\u09AA-\\u09B0\\u09B2\\u09B6-\\u09B9\\u09BD\\u09CE\\u09DC\\u09DD\\u09DF-\\u09E1\\u09F0\\u09F1\\u0A05-\\u0A0A\\u0A0F\\u0A10\\u0A13-\\u0A28\\u0A2A-\\u0A30\\u0A32\\u0A33\\u0A35\\u0A36\\u0A38\\u0A39\\u0A59-\\u0A5C\\u0A5E\\u0A72-\\u0A74\\u0A85-\\u0A8D\\u0A8F-\\u0A91\\u0A93-\\u0AA8\\u0AAA-\\u0AB0\\u0AB2\\u0AB3\\u0AB5-\\u0AB9\\u0ABD\\u0AD0\\u0AE0\\u0AE1\\u0AF9\\u0B05-\\u0B0C\\u0B0F\\u0B10\\u0B13-\\u0B28\\u0B2A-\\u0B30\\u0B32\\u0B33\\u0B35-\\u0B39\\u0B3D\\u0B5C\\u0B5D\\u0B5F-\\u0B61\\u0B71\\u0B83\\u0B85-\\u0B8A\\u0B8E-\\u0B90\\u0B92-\\u0B95\\u0B99\\u0B9A\\u0B9C\\u0B9E\\u0B9F\\u0BA3\\u0BA4\\u0BA8-\\u0BAA\\u0BAE-\\u0BB9\\u0BD0\\u0C05-\\u0C0C\\u0C0E-\\u0C10\\u0C12-\\u0C28\\u0C2A-\\u0C39\\u0C3D\\u0C58-\\u0C5A\\u0C60\\u0C61\\u0C80\\u0C85-\\u0C8C\\u0C8E-\\u0C90\\u0C92-\\u0CA8\\u0CAA-\\u0CB3\\u0CB5-\\u0CB9\\u0CBD\\u0CDE\\u0CE0\\u0CE1\\u0CF1\\u0CF2\\u0D05-\\u0D0C\\u0D0E-\\u0D10\\u0D12-\\u0D3A\\u0D3D\\u0D4E\\u0D54-\\u0D56\\u0D5F-\\u0D61\\u0D7A-\\u0D7F\\u0D85-\\u0D96\\u0D9A-\\u0DB1\\u0DB3-\\u0DBB\\u0DBD\\u0DC0-\\u0DC6\\u0E01-\\u0E30\\u0E32\\u0E33\\u0E40-\\u0E46\\u0E81\\u0E82\\u0E84\\u0E87\\u0E88\\u0E8A\\u0E8D\\u0E94-\\u0E97\\u0E99-\\u0E9F\\u0EA1-\\u0EA3\\u0EA5\\u0EA7\\u0EAA\\u0EAB\\u0EAD-\\u0EB0\\u0EB2\\u0EB3\\u0EBD\\u0EC0-\\u0EC4\\u0EC6\\u0EDC-\\u0EDF\\u0F00\\u0F40-\\u0F47\\u0F49-\\u0F6C\\u0F88-\\u0F8C\\u1000-\\u102A\\u103F\\u1050-\\u1055\\u105A-\\u105D\\u1061\\u1065\\u1066\\u106E-\\u1070\\u1075-\\u1081\\u108E\\u10A0-\\u10C5\\u10C7\\u10CD\\u10D0-\\u10FA\\u10FC-\\u1248\\u124A-\\u124D\\u1250-\\u1256\\u1258\\u125A-\\u125D\\u1260-\\u1288\\u128A-\\u128D\\u1290-\\u12B0\\u12B2-\\u12B5\\u12B8-\\u12BE\\u12C0\\u12C2-\\u12C5\\u12C8-\\u12D6\\u12D8-\\u1310\\u1312-\\u1315\\u1318-\\u135A\\u1380-\\u138F\\u13A0-\\u13F5\\u13F8-\\u13FD\\u1401-\\u166C\\u166F-\\u167F\\u1681-\\u169A\\u16A0-\\u16EA\\u16F1-\\u16F8\\u1700-\\u170C\\u170E-\\u1711\\u1720-\\u1731\\u1740-\\u1751\\u1760-\\u176C\\u176E-\\u1770\\u1780-\\u17B3\\u17D7\\u17DC\\u1820-\\u1877\\u1880-\\u1884\\u1887-\\u18A8\\u18AA\\u18B0-\\u18F5\\u1900-\\u191E\\u1950-\\u196D\\u1970-\\u1974\\u1980-\\u19AB\\u19B0-\\u19C9\\u1A00-\\u1A16\\u1A20-\\u1A54\\u1AA7\\u1B05-\\u1B33\\u1B45-\\u1B4B\\u1B83-\\u1BA0\\u1BAE\\u1BAF\\u1BBA-\\u1BE5\\u1C00-\\u1C23\\u1C4D-\\u1C4F\\u1C5A-\\u1C7D\\u1C80-\\u1C88\\u1CE9-\\u1CEC\\u1CEE-\\u1CF1\\u1CF5\\u1CF6\\u1D00-\\u1DBF\\u1E00-\\u1F15\\u1F18-\\u1F1D\\u1F20-\\u1F45\\u1F48-\\u1F4D\\u1F50-\\u1F57\\u1F59\\u1F5B\\u1F5D\\u1F5F-\\u1F7D\\u1F80-\\u1FB4\\u1FB6-\\u1FBC\\u1FBE\\u1FC2-\\u1FC4\\u1FC6-\\u1FCC\\u1FD0-\\u1FD3\\u1FD6-\\u1FDB\\u1FE0-\\u1FEC\\u1FF2-\\u1FF4\\u1FF6-\\u1FFC\\u2071\\u207F\\u2090-\\u209C\\u2102\\u2107\\u210A-\\u2113\\u2115\\u2119-\\u211D\\u2124\\u2126\\u2128\\u212A-\\u212D\\u212F-\\u2139\\u213C-\\u213F\\u2145-\\u2149\\u214E\\u2183\\u2184\\u2C00-\\u2C2E\\u2C30-\\u2C5E\\u2C60-\\u2CE4\\u2CEB-\\u2CEE\\u2CF2\\u2CF3\\u2D00-\\u2D25\\u2D27\\u2D2D\\u2D30-\\u2D67\\u2D6F\\u2D80-\\u2D96\\u2DA0-\\u2DA6\\u2DA8-\\u2DAE\\u2DB0-\\u2DB6\\u2DB8-\\u2DBE\\u2DC0-\\u2DC6\\u2DC8-\\u2DCE\\u2DD0-\\u2DD6\\u2DD8-\\u2DDE\\u2E2F\\u3005\\u3006\\u3031-\\u3035\\u303B\\u303C\\u3041-\\u3096\\u309D-\\u309F\\u30A1-\\u30FA\\u30FC-\\u30FF\\u3105-\\u312D\\u3131-\\u318E\\u31A0-\\u31BA\\u31F0-\\u31FF\\u3400-\\u4DB5\\u4E00-\\u9FD5\\uA000-\\uA48C\\uA4D0-\\uA4FD\\uA500-\\uA60C\\uA610-\\uA61F\\uA62A\\uA62B\\uA640-\\uA66E\\uA67F-\\uA69D\\uA6A0-\\uA6E5\\uA717-\\uA71F\\uA722-\\uA788\\uA78B-\\uA7AE\\uA7B0-\\uA7B7\\uA7F7-\\uA801\\uA803-\\uA805\\uA807-\\uA80A\\uA80C-\\uA822\\uA840-\\uA873\\uA882-\\uA8B3\\uA8F2-\\uA8F7\\uA8FB\\uA8FD\\uA90A-\\uA925\\uA930-\\uA946\\uA960-\\uA97C\\uA984-\\uA9B2\\uA9CF\\uA9E0-\\uA9E4\\uA9E6-\\uA9EF\\uA9FA-\\uA9FE\\uAA00-\\uAA28\\uAA40-\\uAA42\\uAA44-\\uAA4B\\uAA60-\\uAA76\\uAA7A\\uAA7E-\\uAAAF\\uAAB1\\uAAB5\\uAAB6\\uAAB9-\\uAABD\\uAAC0\\uAAC2\\uAADB-\\uAADD\\uAAE0-\\uAAEA\\uAAF2-\\uAAF4\\uAB01-\\uAB06\\uAB09-\\uAB0E\\uAB11-\\uAB16\\uAB20-\\uAB26\\uAB28-\\uAB2E\\uAB30-\\uAB5A\\uAB5C-\\uAB65\\uAB70-\\uABE2\\uAC00-\\uD7A3\\uD7B0-\\uD7C6\\uD7CB-\\uD7FB\\uF900-\\uFA6D\\uFA70-\\uFAD9\\uFB00-\\uFB06\\uFB13-\\uFB17\\uFB1D\\uFB1F-\\uFB28\\uFB2A-\\uFB36\\uFB38-\\uFB3C\\uFB3E\\uFB40\\uFB41\\uFB43\\uFB44\\uFB46-\\uFBB1\\uFBD3-\\uFD3D\\uFD50-\\uFD8F\\uFD92-\\uFDC7\\uFDF0-\\uFDFB\\uFE70-\\uFE74\\uFE76-\\uFEFC\\uFF21-\\uFF3A\\uFF41-\\uFF5A\\uFF66-\\uFFBE\\uFFC2-\\uFFC7\\uFFCA-\\uFFCF\\uFFD2-\\uFFD7\\uFFDA-\\uFFDC", Hn = "0-9\\xB2\\xB3\\xB9\\xBC-\\xBE\\u0660-\\u0669\\u06F0-\\u06F9\\u07C0-\\u07C9\\u0966-\\u096F\\u09E6-\\u09EF\\u09F4-\\u09F9\\u0A66-\\u0A6F\\u0AE6-\\u0AEF\\u0B66-\\u0B6F\\u0B72-\\u0B77\\u0BE6-\\u0BF2\\u0C66-\\u0C6F\\u0C78-\\u0C7E\\u0CE6-\\u0CEF\\u0D58-\\u0D5E\\u0D66-\\u0D78\\u0DE6-\\u0DEF\\u0E50-\\u0E59\\u0ED0-\\u0ED9\\u0F20-\\u0F33\\u1040-\\u1049\\u1090-\\u1099\\u1369-\\u137C\\u16EE-\\u16F0\\u17E0-\\u17E9\\u17F0-\\u17F9\\u1810-\\u1819\\u1946-\\u194F\\u19D0-\\u19DA\\u1A80-\\u1A89\\u1A90-\\u1A99\\u1B50-\\u1B59\\u1BB0-\\u1BB9\\u1C40-\\u1C49\\u1C50-\\u1C59\\u2070\\u2074-\\u2079\\u2080-\\u2089\\u2150-\\u2182\\u2185-\\u2189\\u2460-\\u249B\\u24EA-\\u24FF\\u2776-\\u2793\\u2CFD\\u3007\\u3021-\\u3029\\u3038-\\u303A\\u3192-\\u3195\\u3220-\\u3229\\u3248-\\u324F\\u3251-\\u325F\\u3280-\\u3289\\u32B1-\\u32BF\\uA620-\\uA629\\uA6E6-\\uA6EF\\uA830-\\uA835\\uA8D0-\\uA8D9\\uA900-\\uA909\\uA9D0-\\uA9D9\\uA9F0-\\uA9F9\\uAA50-\\uAA59\\uABF0-\\uABF9\\uFF10-\\uFF19", Jn = new RegExp("[" + qn + "]", "gu"), Kn = new RegExp("[" + Hn + "]", "gu"), Yn = new RegExp("[" + qn + Hn + "]", "gu"), Wn = Object.freeze({ __proto__: null, unicodeLetterPattern: Jn, unicodeNumberPattern: Kn, unicodeAlphanumericPattern: Yn, key: ce, keySeparation: te }), Xn = /]*?src="(.+?)"[^>]*>/g, Un = function (e) { return e.textFragments.map((function (t, n) { return [t, e.inputs[n]] })).flatMap((function (e) { var t = a(e, 2), n = t[0], r = t[1]; return "string" == typeof r ? [] : function (e, t) { var n, r = []; do { (n = Xn.exec(e)) && r.push([n[1], t]) } while (n); return r }(n, r.getRootNode()) })) }, Vn = function (e) { if (navigator.userAgent.search("Chrome") >= 0) return [e.offsetX, e.offsetY]; var t = e.currentTarget, n = t.getBoundingClientRect(), r = t.parentElement.getBoundingClientRect(); return [e.layerX - (r.left - n.left), e.layerY - (r.top - n.top)] }, Qn = function (e, t, n) { var r = t.querySelector(e); r && (r.complete ? n({ target: r }) : r.addEventListener("load", n)) }, Zn = "\nimg {\n max-width: 100% !important;\n}\n\n.closet-occlusion-container {\n display: inline-block;\n position: relative;\n}\n\n.closet-occlusion-container > * {\n display: block;\n\n margin-left: auto;\n margin-right: auto;\n}\n\n.closet-occlusion-container > svg {\n position: absolute;\n top: 0;\n}\n\n.closet-shape > text {\n text-anchor: middle;\n dominant-baseline: central;\n pointer-events: none;\n}", er = "http://www.w3.org/2000/svg", tr = function () { function e(e, t, n) { var r = this; this.scaleFactorX = 1, this.scaleFactorY = 1, this.maxOcclusions = 500, this.elements = [], this.container = e, this.image = t, this.svg = n, this.resizer = new globalThis.ResizeObserver((function () { return r.resize() })), this.resizer.observe(t), this.setScaleFactors() } return e.prototype.setScaleFactors = function () { this.scaleFactorX = this.image.width / this.image.naturalWidth, this.scaleFactorY = this.image.height / this.image.naturalHeight }, e.wrapImage = function (t) { var n = document.createElement("div"); t.parentNode && t.parentNode.replaceChild(n, t), n.appendChild(t); var r = document.createElementNS(er, "svg"); r.setAttributeNS(null, "width", "100%"), r.setAttributeNS(null, "height", "100%"); var o = window.getComputedStyle(t); return r.style.zoom = o.getPropertyValue("zoom"), r.style.transform = o.getPropertyValue("transform"), n.appendChild(r), n.classList.add("closet-occlusion-container"), new e(n, t, r) }, Object.defineProperty(e.prototype, "scaleFactors", { get: function () { return [this.scaleFactorX, this.scaleFactorY] }, enumerable: !1, configurable: !0 }), e.prototype.resize = function () { var e, t; if (this.setScaleFactors(), this.scaleFactors.includes(0)) this.resizer.disconnect(); else try { for (var n = s(this.elements), r = n.next(); !r.done; r = n.next()) { r.value.resize(this) } } catch (t) { e = { error: t } } finally { try { r && !r.done && (t = n.return) && t.call(n) } finally { if (e) throw e.error } } }, e.prototype.append = function (e) { var t, n; this.elements.push(e); try { for (var r = s(e.getElements()), o = r.next(); !o.done; o = r.next()) { var i = o.value; this.svg.appendChild(i) } } catch (e) { t = { error: e } } finally { try { o && !o.done && (n = r.return) && n.call(r) } finally { if (t) throw t.error } } }, e.prototype.getElements = function () { return this.elements }, e.prototype.getLabels = function () { return this.getElements().map((function (e) { return e.getLabel() })) }, e.prototype.remove = function (e) { var t = this; this.elements.find((function (n, r) { var o, i; if (n.getAnchorElement() === e.getAnchorElement()) { try { for (var u = s(e.getElements()), a = u.next(); !a.done; a = u.next()) { a.value.remove() } } catch (e) { o = { error: e } } finally { try { a && !a.done && (i = u.return) && i.call(u) } finally { if (o) throw o.error } } return t.elements.splice(r, 1), !0 } })) }, e.prototype.setMaxOcclusions = function (e) { this.maxOcclusions = e }, e.prototype.getNextNum = function (e) { var t = e.altKey ? 0 : 1, n = Math.max(1, function (e) { var t, n, r = 0; try { for (var o = s(e), i = o.next(); !i.done; i = o.next()) { var u = i.value.match(te); if (u) { var a = Number(u[2]); Number.isNaN(a) || (r = Math.max(r, a)) } } } catch (e) { t = { error: e } } finally { try { i && !i.done && (n = o.return) && n.call(o) } finally { if (t) throw t.error } } return r }(this.getLabels()) + t); return n > this.maxOcclusions ? 0 : n }, e.prototype.cleanup = function () { var e = this.container.removeChild(this.image); this.container.replaceWith(e) }, e }(), nr = function () { function e(e, t, n, r, o) { this.scaleFactorX = 1, this.scaleFactorY = 1, this.container = e, this.rect = t, this.label = n, this.setScaleFactors(r, o) } return e.make = function (t) { var n = document.createElementNS(er, "svg"), r = document.createElementNS(er, "rect"), o = document.createElementNS(er, "text"); n.appendChild(r), n.appendChild(o), n.classList.add("closet-rect"), n.classList.add("closet-shape"), n.tabIndex = -1; var i = a(t ? t.scaleFactors : [1, 1], 2); return new e(n, r, o, i[0], i[1]) }, e.wrap = function (t, n) { var r = a(n ? n.scaleFactors : [1, 1], 2), o = r[0], i = r[1]; return new e(t.parentElement, t, t.nextSibling, o, i) }, e.prototype.remove = function () { this.container.remove() }, e.prototype.getElements = function () { return [this.container] }, e.prototype.getAnchorElement = function () { return this.rect }, e.prototype.getLabel = function () { return this.labelText }, e.prototype.setScaleFactors = function (e, t) { this.scaleFactorX = e, this.scaleFactorY = t }, e.prototype.readjust = function (e) { var t = e ? e.scaleFactors : [1, 1], n = this.scaleFactorX, r = this.scaleFactorY; return this.setScaleFactors(t[0], t[1]), [n, r] }, e.prototype.resize = function (e) { var t = this.pos; this.readjust(e), this.pos = t }, e.prototype.toDefinition = function () { return ["rect", void 0, this.labelText, this.x, this.y, this.width, this.height, {}] }, e.prototype.toText = function (e) { return "" + e.open + this.labelText + e.sep + this.x.toFixed() + "," + this.y.toFixed() + "," + this.width.toFixed() + "," + this.height.toFixed() + e.close }, e.prototype.fontSizeUpdate = function () { var e = a(this.readjust(), 2), t = e[0], n = e[1], r = Math.max(.1, Math.min(32, .4 * ((this.width + this.height) / 2 - 2 * this.strokeWidth))); this.fontSize = r, this.setScaleFactors(t, n) }, Object.defineProperty(e.prototype, "pos", { get: function () { return [this.x, this.y, this.width, this.height] }, set: function (e) { var t = a(e, 4), n = t[0], r = t[1], o = t[2], i = t[3]; this.width = o, this.height = i, this.x = n, this.y = r }, enumerable: !1, configurable: !0 }), Object.defineProperty(e.prototype, "scaled", { get: function () { return [Number(this.rect.getAttributeNS(null, "x")), Number(this.rect.getAttributeNS(null, "y")), Number(this.rect.getAttributeNS(null, "width")), Number(this.rect.getAttributeNS(null, "height"))] }, enumerable: !1, configurable: !0 }), Object.defineProperty(e.prototype, "x", { get: function () { return Number(this.rect.getAttributeNS(null, "x")) / this.scaleFactorX }, set: function (e) { var t = e * this.scaleFactorX, n = (e + this.width / 2) * this.scaleFactorX; this.rect.setAttributeNS(null, "x", String(t)), this.label.setAttributeNS(null, "x", String(n)) }, enumerable: !1, configurable: !0 }), Object.defineProperty(e.prototype, "y", { get: function () { return Number(this.rect.getAttributeNS(null, "y")) / this.scaleFactorY }, set: function (e) { var t = e * this.scaleFactorY, n = (e + this.height / 2) * this.scaleFactorY; this.rect.setAttributeNS(null, "y", String(t)), this.label.setAttributeNS(null, "y", String(n)) }, enumerable: !1, configurable: !0 }), Object.defineProperty(e.prototype, "width", { get: function () { return Number(this.rect.getAttributeNS(null, "width")) / this.scaleFactorX }, set: function (e) { var t = String(Math.max(10, e) * this.scaleFactorX); this.rect.setAttributeNS(null, "width", t), this.label.setAttributeNS(null, "width", t), this.fontSizeUpdate() }, enumerable: !1, configurable: !0 }), Object.defineProperty(e.prototype, "height", { get: function () { return Number(this.rect.getAttributeNS(null, "height")) / this.scaleFactorY }, set: function (e) { var t = String(Math.max(10, e) * this.scaleFactorY); this.rect.setAttributeNS(null, "height", t), this.label.setAttributeNS(null, "height", t), this.fontSizeUpdate() }, enumerable: !1, configurable: !0 }), e.prototype.props = function (e) { for (var t in e) { var n = t; this.prop(n, e[n]) } }, e.prototype.prop = function (e, t) { this[e] = t }, Object.defineProperty(e.prototype, "containerClasses", { get: function () { return Array.from(this.container.className).join(" ") }, set: function (e) { var t = this; e.split(" ").forEach((function (e) { return t.container.classList.add(e) })) }, enumerable: !1, configurable: !0 }), Object.defineProperty(e.prototype, "rx", { get: function () { return Number(this.rect.getAttributeNS(null, "rx")) }, set: function (e) { this.rect.setAttributeNS(null, "rx", String(e)) }, enumerable: !1, configurable: !0 }), Object.defineProperty(e.prototype, "ry", { get: function () { return Number(this.rect.getAttributeNS(null, "ry")) }, set: function (e) { this.rect.setAttributeNS(null, "ry", String(e)) }, enumerable: !1, configurable: !0 }), Object.defineProperty(e.prototype, "fill", { get: function () { var e; return null !== (e = this.rect.getAttributeNS(null, "fill")) && void 0 !== e ? e : "" }, set: function (e) { this.rect.setAttributeNS(null, "fill", e) }, enumerable: !1, configurable: !0 }), Object.defineProperty(e.prototype, "fillOpacity", { get: function () { return Number(this.rect.getAttributeNS(null, "fill-opacity")) }, set: function (e) { this.rect.setAttributeNS(null, "fill-opacity", String(e)) }, enumerable: !1, configurable: !0 }), Object.defineProperty(e.prototype, "stroke", { get: function () { var e; return null !== (e = this.rect.getAttributeNS(null, "stroke")) && void 0 !== e ? e : "" }, set: function (e) { this.rect.setAttributeNS(null, "stroke", e) }, enumerable: !1, configurable: !0 }), Object.defineProperty(e.prototype, "strokeOpacity", { get: function () { return Number(this.rect.getAttributeNS(null, "stroke-opacity")) }, set: function (e) { this.rect.setAttributeNS(null, "stroke-opacity", String(e)) }, enumerable: !1, configurable: !0 }), Object.defineProperty(e.prototype, "strokeWidth", { get: function () { return Number(this.rect.getAttributeNS(null, "stroke-width")) }, set: function (e) { this.rect.setAttributeNS(null, "stroke-width", String(e)) }, enumerable: !1, configurable: !0 }), Object.defineProperty(e.prototype, "classes", { get: function () { return Array.from(this.rect.classList).join(" ") }, set: function (e) { var t = this; e.split(" ").forEach((function (e) { return t.rect.classList.add(e) })) }, enumerable: !1, configurable: !0 }), Object.defineProperty(e.prototype, "labelText", { get: function () { return this.label.innerHTML }, set: function (e) { this.label.innerHTML = e }, enumerable: !1, configurable: !0 }), Object.defineProperty(e.prototype, "fontSize", { get: function () { return Number(this.label.getAttributeNS(null, "font-size")) }, set: function (e) { this.label.setAttributeNS(null, "font-size", String(e)) }, enumerable: !1, configurable: !0 }), Object.defineProperty(e.prototype, "labelClasses", { get: function () { return Array.from(this.label.classList).join(" ") }, set: function (e) { var t = this; e.split(" ").forEach((function (e) { return t.label.classList.add(e) })) }, enumerable: !1, configurable: !0 }), e }(), rr = function (e, t, n, r, o, i, u, s) { return function (c) { var l = a(e(Vn(c)), 2), p = l[0], f = l[1]; n && p < u ? (t.x = p, t.width = u - p) : r && (t.x = u, t.width = p - u), o && f < s ? (t.y = f, t.height = s - f) : i && (t.y = s, t.height = f - s) } }, or = function (e, t, n, r, o, i, u, s, c) { return function (l, p, f) { var h = a(l.scaled, 4), d = h[0], m = h[1], v = d + h[2] - p, y = f - m, g = m + h[3] - f; return p - d <= 5 ? y <= 5 ? e(l) : g <= 5 ? t(l) : n(l) : v <= 5 ? y <= 5 ? r(l) : g <= 5 ? o(l) : i(l) : y <= 5 ? u(l) : g <= 5 ? s(l) : c(l) } }, ir = or((function (e) { return [!0, !1, !0, !1, e.x + e.width, e.y + e.height] }), (function (e) { return [!0, !1, !1, !0, e.x + e.width, e.y] }), (function (e) { return [!0, !1, !1, !1, e.x + e.width, 0] }), (function (e) { return [!1, !0, !0, !1, e.x, e.y + e.height] }), (function (e) { return [!1, !0, !1, !0, e.x, e.y] }), (function (e) { return [!1, !0, !1, !1, e.x, 0] }), (function (e) { return [!1, !1, !0, !1, 0, e.y + e.height] }), (function (e) { return [!1, !1, !1, !0, 0, e.y] }), (function () { return [!1, !1, !1, !1, 0, 0] })), ur = or((function () { return "nwse-resize" }), (function () { return "nesw-resize" }), (function () { return "ew-resize" }), (function () { return "nesw-resize" }), (function () { return "nwse-resize" }), (function () { return "ew-resize" }), (function () { return "ns-resize" }), (function () { return "ns-resize" }), (function () { return "move" })), sr = function (e) { return function (t) { var n = a(t, 2), r = n[0], o = n[1], i = function (e) { return function (t) { var n = a(t, 2), r = n[0], o = n[1], i = e.getPropertyValue("zoom"), u = Number(i); return Number.isNaN(u) || u <= 0 ? [r, o] : [r / u, o / u] } }(e); return i([r, o]) } }, ar = function (e) { var t = document.createElement("li"), n = document.createElement("a"); return e.html ? n.innerHTML = e.label : n.innerText = e.label, n.id = e.itemId, n.classList.add("context-menu-item"), e.clickEvent && n.addEventListener("click", e.clickEvent), t.appendChild(n), e.sub && e.sub.length > 0 && t.appendChild(cr(e.sub)), t }, cr = function (e) { var t, n, r = document.createElement("ul"), o = e.map(ar); try { for (var i = s(o), u = i.next(); !u.done; u = i.next()) { var a = u.value; r.appendChild(a) } } catch (e) { t = { error: e } } finally { try { u && !u.done && (n = i.return) && n.call(i) } finally { if (t) throw t.error } } return r }, lr = function (e) { return function () { e.classList.remove("context-menu--active") } }, pr = function (e, t) { var n = lr(e); t.addEventListener("contextmenu", (function (t) { t.preventDefault(), t.stopPropagation(), document.querySelectorAll(".context-menu--active").forEach((function (e) { return e.classList.remove("context-menu--active") })), e.classList.add("context-menu--active"), window.addEventListener("click", n, { once: !0 }), function (e, t, n) { e.style.left = t + "px", e.style.top = n + "px" }(e, t.pageX, t.pageY) })) }, fr = function (e, t) { var n, r, o, i = (n = e, r = t, (o = document.createElement("nav")).classList.add("context-menu"), o.id = n, o.appendChild(cr(r)), o); return document.body.insertAdjacentElement("beforeend", i), function (e) { e.addEventListener("click", (function (e) { e.stopPropagation() })), e.querySelectorAll("li").forEach((function (t) { t.addEventListener("mouseenter", (function () { t.classList.add("context-menu--hover") })), t.addEventListener("mouseleave", (function () { t.classList.remove("context-menu--hover") })), t.addEventListener("click", (function () { lr(e)() })) })) }(i), i }, hr = function (e, t) { var n = t.template, r = t.cache, o = Un(n), i = r.get(e.keyword, []), u = o[0]; u && Qn('img[src="' + u[0] + '"]', u[1], (function (e) { var t, r; n.appendStyle(e.target, "occlusionSvgCss", Zn); var o = tr.wrapImage(e.target); try { for (var u = s(i), c = u.next(); !c.done; c = u.next()) { var l = a(c.value, 8), p = l[1], f = l[3], h = l[4], d = l[5], m = l[6], v = l[7]; if (p) { var y = nr.make(o); for (var g in y.pos = [f, h, d, m], v) { var b = g; y.prop(b, v[b]) } o.append(y) } } } catch (e) { t = { error: e } } finally { try { c && !c.done && (r = u.return) && r.call(u) } finally { if (t) throw t.error } } })) }, dr = function (e, t) { void 0 === t && (t = {}); var n = {}; return e.map((function (e) { return e.split("=") })).forEach((function (e) { var t = a(e, 2), r = t[0], o = t[1]; return n[r] = o })), Object.assign(n, t) }, mr = function (e) { return function (t, n) { var r = a(t.values), o = r[0], i = void 0 === o ? 0 : o, u = r[1], s = void 0 === u ? 0 : u, c = r[2], l = void 0 === c ? 50 : c, p = r[3], f = void 0 === p ? l : p, h = r.slice(4), d = "function" == typeof e ? e(t, n) : e; return n.cache.over("occlusionRenderRect", (function (e) { return e.push(["rect", !0, t.fullKey, Number(i), Number(s), Number(l), Number(f), dr(h, d)]) }), []), n.aftermath.registerIfNotExists("occlusionRenderRect", hr), { ready: !0 } } }, vr = { classes: "closet-rect__rect", labelClasses: "closet-rect__label" }, yr = { classes: "closet-rect__ellipsis", labelClasses: "closet-rect__ellipsis" }, gr = an((function (e, t) { return function (n) { void 0 === n && (n = {}); var r = n.tagname, o = void 0 === r ? "rect" : r, u = n.front, s = void 0 === u ? mr : u, a = n.back, c = void 0 === a ? mr : a, l = n.optics, p = void 0 === l ? [I({ sep: "," })] : l, f = n.flashcardTemplate, h = void 0 === f ? sn() : f, d = n.frontProperties, m = void 0 === d ? i(i({}, vr), { containerClasses: "is-active is-front" }) : d, v = n.backProperties, y = void 0 === v ? i(i({}, vr), { containerClasses: "is-active is-back" }) : v, g = n.ellipseProperties, b = void 0 === g ? i(i({}, yr), { containerClasses: "is-inactive" }) : g, x = n.contextProperties, A = void 0 === x ? i(i({}, vr), { containerClasses: "is-inactive" }) : x, w = { optics: p }; return h(e, t)(o, s(m), c(y), mr(b), mr(A), w) } })), br = function (e, t) { var n = nr.wrap(t.target); if (t.shiftKey) e.remove(n); else { var r = sr(window.getComputedStyle(e.image)), o = a(r(Vn(t)), 2), i = o[0], u = o[1], s = ir(n, i, u), l = s.includes(!0) ? rr.apply(void 0, c([r, n], s)) : function (e, t, n, r, o, i) { return function (u) { var s = a(e(Vn(u)), 2), c = s[0], l = s[1], p = n + (c - o), f = r + (l - i); t.x = p, t.y = f } }(r, n, n.x, n.y, i, u); e.svg.addEventListener("mousemove", l), e.svg.addEventListener("mouseup", (function (t) { t.preventDefault(), e.svg.removeEventListener("mousemove", l) }), { once: !0 }) } }, xr = function (e, t) { var n = sr(window.getComputedStyle(e.image)); t.rect.addEventListener("mousemove", function (e, t) { return function (n) { if (n.shiftKey) t.rect.style.cursor = "no-drop"; else { var r = a(e(Vn(n)), 2), o = r[0], i = r[1], u = ur(t, o, i); t.rect.style.cursor = u } } }(n, t)); var r = fr("occlusion-shape-menu", [{ label: "Change Label", itemId: "change-label", clickEvent: function () { var e = prompt("Specify the new label", t.labelText); "string" == typeof e && (t.labelText = e) } }, { label: "Close Menu", itemId: "close-occlusion-menu" }]); pr(r, t.rect), e.append(t) }, Ar = function (e, t, n, r, o) { var i = nr.make(); return i.pos = [e, t, n, r], i.labelText = o, i.classes = "closet-rect__rect", i.labelClasses = "closet-rect__label", i }, wr = function (e, t) { var n = sr(window.getComputedStyle(e.image)), r = a(n(Vn(t)), 2), o = r[0], i = r[1], u = e.getNextNum(t), s = Ar(o, i, 0, 0, "rect" + u); xr(e, s); var c = rr(n, s, !0, !0, !0, !0, o, i); e.svg.addEventListener("mousemove", c), e.svg.addEventListener("mouseup", (function () { e.svg.removeEventListener("mousemove", c), s.readjust(e) })) }, kr = function (e, t) { e.svg.addEventListener("mousedown", (function (t) { 0 === t.button && function (e, t) { t.preventDefault(), ("svg" !== t.target.nodeName ? br : wr)(e, t) }(e, t) })), pr(t, e.svg) }, Er = function (e, t) { return function (e) { navigator.clipboard.writeText(e.map((function (e) { return e.toText(t.template.parser.delimiters) })).join("\n")).catch((function () { return console.log("Window needs active focus for copying to clipboard") })) } }, Fr = function () { return function (e, t) { return t.cleanup() } }, Sr = { occlusionEditor: function (e) { return void 0 === e && (e = {}), function (t) { var n = "makeOcclusions", r = new EventTarget, o = e.tagname, i = void 0 === o ? n : o, u = e.maxOcclusions, l = void 0 === u ? 500 : u, p = e.acceptHandler, f = void 0 === p ? Er : p, h = e.rejectHandler, d = void 0 === h ? Fr : h, m = e.setupOcclusionMenu, v = void 0 === m ? B : m, y = e.existingShapesFilter, g = void 0 === y ? function () { return B } : y, b = e.shapeKeywords, x = void 0 === b ? ["occlusionRenderRect"] : b; return t.register(i, (function (e, t) { var o = t.template, i = Un(o); return t.aftermath.registerIfNotExists(n, (function (e, t) { var n, o, u, p, h = []; try { for (var m = s(x), y = m.next(); !y.done; y = m.next()) { var b = y.value, A = t.cache.get(b, []); h.push.apply(h, c(A)), t.aftermath.block(b) } } catch (e) { n = { error: e } } finally { try { y && !y.done && (o = m.return) && o.call(m) } finally { if (n) throw n.error } } var w = function (n) { var o = n.target, i = t.template; i.appendDocumentStyle("occlusionMakerCss", "\n:root {\n --cm-niceblue: #3f8cf1;\n --cm-nicegray: #444;\n --cm-backgray: #ececec;\n --cm-boxgray: #cfcfcf;\n}\n\nnav.context-menu {\n position: absolute;\n z-index: 9999;\n display: none;\n}\n\nnav.context-menu--active {\n display: block;\n}\n\n/******************** UL */\n\nnav.context-menu ul {\n position: absolute;\n\n padding: 0.2rem 0;\n width: 10rem;\n\n white-space: nowrap;\n list-style: none;\n\n background-color: var(--cm-backgray);\n border: solid 1px var(--cm-boxgray);\n box-shadow: 1px 1px 2px var(--cm-boxgray);\n}\n\n/******************** LI */\n\nnav.context-menu li {\n margin-bottom: 2px;\n}\n\nnav.context-menu li.context-menu--hover {\n background-color: var(--cm-niceblue);\n}\n\nnav.context-menu li:last-child {\n margin-bottom: 0;\n}\n\nnav.context-menu li ul {\n display: none;\n margin-left: 2rem;\n}\n\nnav.context-menu li.context-menu--hover ul {\n display: block;\n}\n\nnav.context-menu li li ul {\n display: none;\n margin-left: 4rem;\n}\n\nnav.context-menu li li.context-menu--hover ul {\n display: block;\n}\n\n/******************** A */\n\nnav.context-menu a {\n display: block;\n padding: 0.3rem 1rem;\n user-select: none;\n\n color: var(--cm-nicegray);\n}\n\nnav.context-menu li.context-menu--hover a {\n color: white;\n}\n"), i.appendStyle(o, "occlusionMakerCss", "\n.closet-occlusion-container {\n outline: 3px dotted hotpink;\n outline-offset: -3px;\n}"), i.appendStyle(o, "occlusionSvgCss", Zn); var u = tr.wrapImage(o); u.setMaxOcclusions(l), g(e, t)(h, u).forEach((function (e) { return function (e, t) { var n, r = null, o = null, i = null, u = null, s = null, c = null, l = a(t), p = l[0], f = l.slice(2); "rect" === p && (r = (n = a(f, 5))[0], o = n[1], i = n[2], u = n[3], s = n[4], c = Ar(o, i, u, s, r), xr(e, c)) }(u, e) })); r.addEventListener("accept", (function () { f(e, t)(u.getElements(), u) })), r.addEventListener("reject", (function () { d(e, t)(u.getElements(), u) })); var s = function (e, t) { var n = t([{ label: "Accept occlusions", itemId: "occlusion-accept", clickEvent: function (t) { t.preventDefault(), e.dispatchEvent(new Event("accept")) } }, { label: "Close Menu", itemId: "close-occlusion-menu" }]); return fr("occlusion-menu", n) }(r, v); kr(u, s) }; try { for (var k = s(i), E = k.next(); !E.done; E = k.next()) { var F = a(E.value, 2), S = F[0], C = F[1]; Qn('img[src="' + S + '"]', C, w) } } catch (e) { u = { error: e } } finally { try { E && !E.done && (p = k.return) && p.call(k) } finally { if (u) throw u.error } } }), { priority: 100 }), { ready: !0 } })), r } }, rect: gr }, Cr = Object.freeze({ __proto__: null, recipes: Sr }), Br = Object.freeze({ __proto__: null, wrap: Xt, deferred: Vt, aftermath: Qt, sum: Zt, sumFour: en, product: function (e, t, n, r) { void 0 === n && (n = function () { return function () { return { ready: !0 } } }); var o = void 0 === r ? { wrapId: "product", getTagnames: Yt, setTagnames: Wt } : r, i = o.wrapId, u = o.setTagnames; return function (r) { var o = void 0 === r ? {} : r, s = o.tagname, a = void 0 === s ? "prod" : s, c = o.optionsFirst, l = void 0 === c ? {} : c, p = o.optionsSecond, f = void 0 === p ? {} : p; return function (r) { var o = a + ":" + i + ":fst", s = a + ":" + i + ":snd"; e(u(l, [o]))(r), t(u(f, [s]))(r); r.register(a, (function (e, t) { return n(t.filters.getOrDefault(o)(e, t), t.filters.getOrDefault(s)(e, t))(e, t) }), r.getOptions(o)) } } }, collection: tn }), Nr = Object.freeze({ __proto__: null, template: Qe, recipes: Mt, flashcard: Rn, browser: Cr, Stylizer: yt, wrappers: Br, sequencers: Et, sortInStrategies: St, patterns: Wn, versionInfo: e, prereleaseInfo: t, version: n, FilterManager: C }); export { Nr as closet }; diff --git a/src/anking_notetypes/web/editor.css b/src/anking_notetypes/web/editor.css new file mode 100644 index 0000000..681d9f9 --- /dev/null +++ b/src/anking_notetypes/web/editor.css @@ -0,0 +1,36 @@ +img { + max-width: 100% !important; + max-height: var(--closet-max-height); +} + +.closet-select-mode { + /* slightly below top */ + vertical-align: 20%; + /* night-mode fix */ + background-color: var(--frame-bg); + border-color: var(--border); + color: var(--text-fg); +} + +.closet-rect__rect { + fill: moccasin; + stroke: olive; +} +.is-active .closet-rect__rect { + fill: salmon; + stroke: yellow; +} +.is-back.is-active .closet-rect__rect { + fill: transparent; + stroke: transparent; +} + +.closet-rect__ellipsis { + fill: transparent; + stroke: transparent; +} + +.closet-rect__label { + stroke: black; + stroke-width: 0.5; +} diff --git a/src/anking_notetypes/web/editor.js b/src/anking_notetypes/web/editor.js new file mode 100644 index 0000000..cebfe43 --- /dev/null +++ b/src/anking_notetypes/web/editor.js @@ -0,0 +1,325 @@ +/** tweening **/ +var rushInOut = (x) => { + return 2.388 * x - 4.166 * Math.pow(x, 2) + 2.77 * Math.pow(x, 3); +}; + +/** Python functions moved to JS for async operations **/ +var escapeJSText = (text) => { + return text.replace("\\", "\\\\").replace('"', '\\"').replace("'", "\\'"); +}; + +var getFocusedFieldIndex = () => { + if (document.activeElement.classList.contains("rich-text-editable")) { + return [...document.querySelector(".fields").children].indexOf( + document.activeElement.closest(".editor-field").parentNode + ); + } else return 0; +}; + +var replaceOrPrefixOldOcclusionText = (oldHTML, newText) => { + const occlusionBlockRegex = /\[#!occlusions.*?#\]/; + + const newHTML = newText.split(/\r?\n/).join("
"); + replacement = `[#!occlusions ${newHTML} #]`; + + /** imitate re.subn **/ + [subbed, numberOfSubs] = ((count = 0) => { + const subbed = oldHTML.replace(occlusionBlockRegex, () => { + ++count; + return replacement; + }); + return [subbed, count]; + })(); + + if (numberOfSubs > 0) { + return subbed; + } else if (["", "
"].includes(oldHTML)) { + return replacement; + } else { + return `${replacement}
${oldHTML}`; + } +}; + +const NoteEditor = require("anki/NoteEditor"); +const EditorField = require("anki/EditorField"); +const { get } = require("svelte/store"); + +const occlusionCss = ` +img { + max-width: 100% !important; + max-height: var(--closet-max-height); +} + +.closet-rect__rect { + fill: moccasin; + stroke: olive; +} + +.is-active .closet-rect__rect { + fill: salmon; + stroke: yellow; +} + +.closet-rect__ellipsis { + fill: transparent; + stroke: transparent; +} + +.closet-rect__label { + stroke: black; + stroke-width: 0.5; +}`; + +EditorField.lifecycle.onMount((field) => { + (async () => { + const fieldElement = await field.element; + + if (!fieldElement.hasAttribute("has-occlusion-style")) { + const style = document.createElement("style"); + style.id = "closet-occlusion-style"; + style.rel = "stylesheet"; + style.textContent = occlusionCss; + const richTextEditable = await get( + field.editingArea.editingInputs + ).find((input) => input.name === "rich-text").element; + richTextEditable.getRootNode().prepend(style); + + fieldElement.setAttribute("has-occlusion-style", ""); + } + })(); +}); + +var EditorCloset = { + imageSrcPattern: /^https?:\/\/(?:localhost|127.0.0.1):\d+\/(.*)$/u, + + focusIndex: 0, + occlusionMode: false, + occlusionField: null, + occlusionEditorTarget: null, + getOcclusionButton: () => document.getElementById("closetOcclude"), + + setActive: (target) => { + EditorCloset.occlusionEditorTarget = target; + EditorCloset.occlusionMode = true; + EditorCloset.getOcclusionButton().classList.add("highlighted"); + bridgeCommand("occlusionEditorActive"); + }, + + setInactive: () => { + EditorCloset.occlusionEditorTarget = null; + EditorCloset.occlusionMode = false; + EditorCloset.getOcclusionButton().classList.remove("highlighted"); + bridgeCommand("occlusionEditorInactive"); + }, + + getFieldHTML: async (index) => { + const richTextEditable = await EditorCloset.getRichTextEditable(index); + return richTextEditable.innerHTML; + }, + + setFieldHTML: async (index, html) => { + const richTextEditable = await EditorCloset.getRichTextEditable(index); + richTextEditable.innerHTML = html; + }, + + getRichTextEditable: async (index) => { + return await get( + NoteEditor.instances[0].fields[index].editingArea.editingInputs + ).find((input) => input.name === "rich-text").element; + }, + + setupOcclusionEditor: async (closet, maxOcclusions) => { + const elements = ["[[makeOcclusions]]"]; + let fieldFound = false; + + for (const field of NoteEditor.instances[0].fields) { + const richTextInputAPI = get(field.editingArea.editingInputs).find( + (input) => input.name === "rich-text" + ); + + const richTextEditable = await richTextInputAPI.element; + + if (!fieldFound) { + let images = richTextEditable.querySelectorAll("img"); + + if (images.length && !fieldFound) { + if (images.length > 1) { + bridgeCommand("closetMultipleImages"); + return; + } + EditorCloset.occlusionField = { + editingArea: field.editingArea, + callback: richTextInputAPI.preventResubscription(), + }; + fieldFound = true; + field.editingArea.refocus(); + } + } + elements.push(richTextEditable); + } + + const acceptHandler = (_entry, internals) => (shapes, draw) => { + const imageSrc = draw.image.src.match( + EditorCloset.imageSrcPattern + )[1]; + + const newIndices = [ + ...new Set( + shapes + .map((shape) => shape.labelText) + .map((label) => + label.match(closet.patterns.keySeparation) + ) + .filter((match) => match) + .map((match) => Number(match[2])) + .filter((maybeNumber) => !Number.isNaN(maybeNumber)) + ), + ]; + + bridgeCommand(`newOcclusions:${imageSrc}:${newIndices.join(",")}`); + + const shapeText = shapes + .map((shape) => + shape.toText(internals.template.parser.delimiters) + ) + .join("\n"); + + bridgeCommand(`occlusionText:${shapeText}`); + + EditorCloset.clearOcclusionMode(); + }; + + const setupOcclusionMenu = (menu) => { + menu.splice(1, 0, { + label: ``, + html: true, + }); + + return menu; + }; + + const existingShapesFilter = () => (shapeDefs, draw) => { + const indices = [ + ...new Set( + shapeDefs + .map((shape) => shape[2]) + .map((label) => + label.match(closet.patterns.keySeparation) + ) + .filter((match) => match) + .map((match) => Number(match[2])) + .filter((maybeNumber) => !Number.isNaN(maybeNumber)) + ), + ]; + + const imageSrc = draw.image.src.match( + EditorCloset.imageSrcPattern + )[1]; + bridgeCommand(`oldOcclusions:${imageSrc}:${indices.join(",")}`); + + return shapeDefs; + }; + + const editorOcclusion = closet.browser.recipes.occlusionEditor({ + maxOcclusions, + acceptHandler, + setupOcclusionMenu, + existingShapesFilter, + }); + + const filterManager = closet.FilterManager.make(); + const target = editorOcclusion(filterManager.registrar); + + filterManager.install( + ...["rect", "recth", "rectr"].map((tagname) => + closet.browser.recipes.rect.hide({ tagname }) + ) + ); + + closet.template.BrowserTemplate.makeFromNodes(elements).render( + filterManager + ); + + EditorCloset.setActive(target); + }, + + clearOcclusionMode: async () => { + if (EditorCloset.occlusionMode) { + EditorCloset.occlusionEditorTarget.dispatchEvent( + new Event("reject") + ); + + EditorCloset.occlusionField.callback.call(); + EditorCloset.focusIndex = getFocusedFieldIndex(); + + EditorCloset.hadOcclusionEditor = true; + EditorCloset.setInactive(); + } + }, + + maybeRefocus: () => { + if (EditorCloset.hadOcclusionEditor) { + bridgeCommand("closetRefocus"); + EditorCloset.hadOcclusionEditor = false; + } + }, + + refocus: () => { + if (EditorCloset.occlusionField) { + EditorCloset.occlusionField.editingArea.refocus(); + } + }, + + // is what is called from the UI + toggleOcclusionMode: (jsPath, maxOcclusions) => { + if (EditorCloset.occlusionMode) { + EditorCloset.clearOcclusionMode(); + } else { + import(`/${jsPath}`).then( + (closet) => + EditorCloset.setupOcclusionEditor( + closet, + maxOcclusions + ), + (error) => console.log("Could not load Closet:", error) + ); + } + }, + + insertIntoZeroIndexed: async (newText, index) => { + const oldHTML = await EditorCloset.getFieldHTML(index); + const text = replaceOrPrefixOldOcclusionText(oldHTML, newText); + + const escaped = escapeJSText(text); + + pycmd(`key:${index}:${getNoteId()}:${escaped}`); + EditorCloset.setFieldHTML(index, escaped); + }, + + /**************** MAX HEIGHT *****************/ + handleMaxHeightChange: (event) => { + EditorCloset.setMaxHeightPercent(Number(event.currentTarget.value)); + }, + + maxHeightPercent: 0, + setMaxHeightPercent: (value /* 1 <= x <= 100 */) => { + const maxMaxHeight = globalThis.screen.height; + const factor = rushInOut(value / 100); + + EditorCloset.maxHeightPercent = value; + EditorCloset.setMaxHeight(factor * maxMaxHeight); + }, + + setMaxHeight: (value /* > 0 */) => { + document.documentElement.style.setProperty( + "--closet-max-height", + `${value}px` + ); + }, +}; diff --git a/ts/.eslintrc.js b/ts/.eslintrc.js new file mode 100644 index 0000000..9bce487 --- /dev/null +++ b/ts/.eslintrc.js @@ -0,0 +1,21 @@ +module.exports = { + extends: [ + "eslint:recommended", + "plugin:@typescript-eslint/eslint-recommended", + "plugin:@typescript-eslint/recommended", + "plugin:compat/recommended", + ], + parser: "@typescript-eslint/parser", + plugins: ["@typescript-eslint"], + rules: { + "prefer-const": "warn", + "no-nested-ternary": "warn", + "@typescript-eslint/ban-ts-comment": "warn", + "@typescript-eslint/no-non-null-assertion": "off", + "@typescript-eslint/no-unused-vars": [ + "warn", + { argsIgnorePattern: "^_", varsIgnorePattern: "^_" }, + ], + }, + env: { browser: true }, +}; diff --git a/ts/.prettierrc b/ts/.prettierrc new file mode 100644 index 0000000..4f93aae --- /dev/null +++ b/ts/.prettierrc @@ -0,0 +1,6 @@ +{ + "trailingComma": "all", + "printWidth": 88, + "tabWidth": 4, + "semi": true +} diff --git a/ts/.version b/ts/.version new file mode 100644 index 0000000..b616048 --- /dev/null +++ b/ts/.version @@ -0,0 +1 @@ +0.6.2 diff --git a/ts/build.mjs b/ts/build.mjs new file mode 100644 index 0000000..df7264e --- /dev/null +++ b/ts/build.mjs @@ -0,0 +1,38 @@ +import { env } from "process"; +import * as esbuild from "esbuild"; +import fs from "fs"; + +function getVersion() { + return fs.readFileSync(".version", { encoding: "utf8", flag: "r" }); +} + +const production = env.NODE_ENV === "production"; +const development = env.NODE_ENV === "development"; + +const entryPoints = ["src/index.ts"]; + +const options = { + entryPoints, + outfile: `../src/anking_notetypes/resources/__ankingio-${getVersion()}.js`, + format: "esm", + target: "es6", + bundle: true, + minify: production, + treeShaking: production, + sourcemap: !production, + pure: production ? ["console.log", "console.time", "console.timeEnd"] : [], + loader: { + ".png": "dataurl", + ".svg": "text", + }, +}; + +const context = await esbuild.context(options); + +if (development) { + console.log("Watching for changes..."); + await context.watch(); +} else { + await context.rebuild(); + context.dispose(); +} diff --git a/ts/package.json b/ts/package.json new file mode 100644 index 0000000..ba04b9b --- /dev/null +++ b/ts/package.json @@ -0,0 +1,44 @@ +{ + "name": "anking-io", + "version": "0.1.0", + "private": true, + "scripts": { + "pack": "cross-env NODE_ENV=production node build.mjs", + "dev": "cross-env NODE_ENV=development node build.mjs", + "eslint": "eslint --ext ts -c .eslintrc.js src", + "prettier": "prettier --write *.{js,json} .prettierrc .eslintrc.js src", + "lint": "run-s eslint prettier", + "compile:parser": "scripts\\compile-parser.sh", + "compile:style": "node scripts\\compile-style.js", + "compile": "run-p compile:parser compile:style", + "watch": "run-s compile:parser dev", + "test": "jest", + "build": "run-s compile pack" + }, + "devDependencies": { + "@typescript-eslint/eslint-plugin": "^6.7.2", + "@typescript-eslint/parser": "^6.7.2", + "cross-env": "^7.0.3", + "esbuild": "^0.19.3", + "eslint": "^8.49.0", + "eslint-plugin-compat": "^4.2.0", + "prettier": "^3.0.3", + "typescript": "^5.2.2" + }, + "browserslist": [ + "defaults", + "not IE 11", + "not op_mini all", + "Chrome 77", + "iOS 12" + ], + "dependencies": { + "npm-run-all": "^4.1.5", + "@types/jest": "^27.0.1", + "@types/mocha": "^9.0.0", + "moo": "^0.5.1", + "nearley": "^2.19.6", + "sass": "^1.67.0", + "terser": "^5.3.2" + } +} diff --git a/ts/scripts/build.sh b/ts/scripts/build.sh new file mode 100644 index 0000000..866f497 --- /dev/null +++ b/ts/scripts/build.sh @@ -0,0 +1,13 @@ +#!/usr/bin/env bash +declare DIR="$(cd "$(dirname "$0")/.." && pwd -P)" +set -e + +git clean -fdx "$DIR/dist" + +# Typescript +yarn --cwd "$DIR/ts" && yarn --cwd "$DIR/ts" build + +# Python +rsync -rai "$DIR/src" "$DIR/dist" --filter=":- $DIR/.gitignore" --delete-after + +echo 'Was successfully built!' diff --git a/ts/scripts/compile-parser.sh b/ts/scripts/compile-parser.sh new file mode 100644 index 0000000..b507834 --- /dev/null +++ b/ts/scripts/compile-parser.sh @@ -0,0 +1,61 @@ +#!/usr/bin/env bash +DIR="$(cd "$(dirname "$0")/../src" && pwd -P)" + +if ! type nearleyc &> /dev/null; then + echo 'No nearleyc executable found in $PATH' + exit +fi + +################ TEMPLATE + +template="$DIR/template/parser/grammar" +template_source="${template}.ne" +template_target="${template}.ts" + +nearleyc "$template_source" > "$template_target" + +################ TEMPLATE ADAPTIONS + +remove_import="import { templateTokenizer } from './tokenizer'" + +sed -i.bak -e "s#$remove_import##" "$template_target" + +open_grammar='const grammar: Grammar = {' +new_open_grammar='const makeGrammar = (tokenizer: NearleyLexer): Grammar => {' + +sed -i.bak -e "s/$open_grammar/$new_open_grammar\n$open_grammar/" "$template_target" + +close_grammar='ParserStart: "start",' +new_close_grammar='return grammar' + +sed -i.bak -e "s/$close_grammar/$close_grammar\n};\n$new_close_grammar/" "$template_target" + +old_export='export default grammar;' +new_export='export default makeGrammar;' + +sed -i.bak -e "s/$old_export/$new_export/" "$template_target" + +old_lexer='interface NearleyLexer {' +export_lexer='export interface NearleyLexer {' + +sed -i.bak -e "s/$old_lexer/$export_lexer/" "$template_target" + +################ TAG SELECTOR + +tagSelector="$DIR/template/tagSelector/grammar" +tagSelector_source="${tagSelector}.ne" +tagSelector_target="${tagSelector}.ts" + +nearleyc "$tagSelector_source" > "$tagSelector_target" + +################ TAG SELECTOR ADAPTIONS + +type_invalid_lexer='Lexer: tagSelectorTokenizer,' +type_forced_lexer='Lexer: tagSelectorTokenizer as unknown as NearleyLexer,' + +sed -i.bak -e "s/$type_invalid_lexer/$type_forced_lexer/" "$tagSelector_target" + +################ CLEANUP + +rm -f "$template_target.bak" +rm -f "$tagSelector_target.bak" diff --git a/ts/scripts/compile-style.js b/ts/scripts/compile-style.js new file mode 100644 index 0000000..6cf1df7 --- /dev/null +++ b/ts/scripts/compile-style.js @@ -0,0 +1,40 @@ +const fs = require("fs"); +const path = require("path"); +const sass = require("sass"); + +const __basedir = `${__dirname}/..`; + +const getVersion = () => { + return fs.readFileSync(`${__basedir}/.version`, { encoding: 'utf8', flag: 'r' }); +} +const renderFile = (input, output) => { + sass.render( + { + file: input, + }, + (error, result) => { + if (error) { + console.error(error); + return; + } + + fs.writeFile(output, result.css, (error) => { + if (error) { + return console.log(error); + } + + console.log(`SCSS was successfully compiled to ${output}`); + }); + }, + ); +}; + +renderFile( + path.join(__basedir, "style", "editor.scss"), + `${__basedir}/../src/anking_notetypes/web/editor.css`, +); + +renderFile( + path.join(__basedir, "style", "base.scss"), + `${__basedir}/../src/anking_notetypes/resources/__ankingio-${getVersion()}.css`, +); diff --git a/ts/scripts/dev.sh b/ts/scripts/dev.sh new file mode 100644 index 0000000..96c71d8 --- /dev/null +++ b/ts/scripts/dev.sh @@ -0,0 +1,19 @@ +#!/usr/bin/env bash +declare DIR="$(cd "$(dirname "$0")/.." && pwd -P)" +set -e + +"$DIR/tools/build.sh" & + +cd "$DIR" +git submodule init + +cd "$DIR/anki" +git submodule update +( + # Start Anki + ANKI_BASE="$DIR/ankidata" "$DIR/anki/tools/ts-run" & + # Watch for changes in Anki TypeScript code + "$DIR/anki/tools/ts-watch" & + # Watch for changes in Add-on TypeScript code + yarn --cwd "$DIR/ts" dev +) diff --git a/ts/scripts/run.sh b/ts/scripts/run.sh new file mode 100644 index 0000000..3d2a366 --- /dev/null +++ b/ts/scripts/run.sh @@ -0,0 +1,12 @@ +#!/usr/bin/env bash +declare DIR="$(cd "$(dirname "$0")/.." && pwd -P)" +set -e + +"$DIR/tools/build.sh" + +cd "$DIR"; +git submodule init + +cd "$DIR/anki"; +git submodule update +ANKI_BASE="$DIR/ankidata" "$DIR/anki/tools/ts-run" diff --git a/ts/src/browser/index.ts b/ts/src/browser/index.ts new file mode 100644 index 0000000..f6e9ba2 --- /dev/null +++ b/ts/src/browser/index.ts @@ -0,0 +1,7 @@ +import { occlusionMakerRecipe as occlusionEditor } from "./occlusionEditor"; +import { rectRecipes as rect } from "./rect"; + +export const recipes = { + occlusionEditor, + rect, +}; diff --git a/ts/src/browser/menu.ts b/ts/src/browser/menu.ts new file mode 100644 index 0000000..a99cfde --- /dev/null +++ b/ts/src/browser/menu.ts @@ -0,0 +1,146 @@ +import type { MenuItem } from "./menuConstruction"; +import { constructMenu } from "./menuConstruction"; + +export const menuCss = ` +:root { + --cm-niceblue: #3f8cf1; + --cm-nicegray: #444; + --cm-backgray: #ececec; + --cm-boxgray: #cfcfcf; +} + +nav.context-menu { + position: absolute; + z-index: 9999; + display: none; +} + +nav.context-menu--active { + display: block; +} + +/******************** UL */ + +nav.context-menu ul { + position: absolute; + + padding: 0.2rem 0; + width: 10rem; + + white-space: nowrap; + list-style: none; + + background-color: var(--cm-backgray); + border: solid 1px var(--cm-boxgray); + box-shadow: 1px 1px 2px var(--cm-boxgray); +} + +/******************** LI */ + +nav.context-menu li { + margin-bottom: 2px; +} + +nav.context-menu li.context-menu--hover { + background-color: var(--cm-niceblue); +} + +nav.context-menu li:last-child { + margin-bottom: 0; +} + +nav.context-menu li ul { + display: none; + margin-left: 2rem; +} + +nav.context-menu li.context-menu--hover ul { + display: block; +} + +nav.context-menu li li ul { + display: none; + margin-left: 4rem; +} + +nav.context-menu li li.context-menu--hover ul { + display: block; +} + +/******************** A */ + +nav.context-menu a { + display: block; + padding: 0.3rem 1rem; + user-select: none; + + color: var(--cm-nicegray); +} + +nav.context-menu li.context-menu--hover a { + color: white; +} +`; + +const turnOffMenu = (menu: HTMLElement) => () => { + menu.classList.remove("context-menu--active"); +}; + +const positionMenu = (menu: HTMLElement, x: number, y: number) => { + menu.style.left = `${x}px`; + menu.style.top = `${y}px`; +}; + +/////////////////// + +const initializeMenu = (menu: HTMLElement): void => { + menu.addEventListener("click", (event: MouseEvent) => { + event.stopPropagation(); + }); + + menu.querySelectorAll("li").forEach((liElement: HTMLLIElement): void => { + liElement.addEventListener("mouseenter", () => { + liElement.classList.add("context-menu--hover"); + }); + + liElement.addEventListener("mouseleave", () => { + liElement.classList.remove("context-menu--hover"); + }); + + liElement.addEventListener("click", () => { + turnOffMenu(menu)(); + }); + }); +}; + +export const enableAsMenuTrigger = (menu: HTMLElement, where: EventTarget): void => { + const turnOffTheMenu = turnOffMenu(menu); + + where.addEventListener("contextmenu", (event: any /* MouseEvent */) => { + event.preventDefault(); + event.stopPropagation(); + + document + .querySelectorAll(".context-menu--active") + .forEach((element) => + element.classList.remove("context-menu--active"), + ); + + menu.classList.add("context-menu--active"); + + window.addEventListener("click", turnOffTheMenu, { once: true }); + positionMenu(menu, event.pageX, event.pageY); + }); +}; + +export const setupMenu = ( + menuName: string, + menuItems: MenuItem[], +): HTMLElement => { + const menu = constructMenu(menuName, menuItems); + + document.body.insertAdjacentElement("beforeend", menu); + initializeMenu(menu); + + return menu; +}; diff --git a/ts/src/browser/menuConstruction.ts b/ts/src/browser/menuConstruction.ts new file mode 100644 index 0000000..c6dfbd4 --- /dev/null +++ b/ts/src/browser/menuConstruction.ts @@ -0,0 +1,56 @@ +export interface MenuItem { + label: string; + itemId: string; + html?: boolean; + clickEvent?: (event: MouseEvent) => void; + sub?: MenuItem[]; +} + +const processMenuItem = (item: MenuItem) => { + const liElement = document.createElement("li"); + const aElement = document.createElement("a"); + + if (item.html) { + aElement.innerHTML = item.label; + } else { + aElement.innerText = item.label; + } + + aElement.id = item.itemId; + aElement.classList.add("context-menu-item"); + + if (item.clickEvent) { + aElement.addEventListener("click", item.clickEvent); + } + + liElement.appendChild(aElement); + + if (item.sub && item.sub.length > 0) { + liElement.appendChild(processMenuItems(item.sub)); + } + + return liElement; +}; + +const processMenuItems = (items: MenuItem[]) => { + const ulElement = document.createElement("ul"); + const liElements = items.map(processMenuItem); + + for (const li of liElements) { + ulElement.appendChild(li); + } + + return ulElement; +}; + +export const constructMenu = ( + menuId: string, + items: MenuItem[], +): HTMLElement => { + const navElement = document.createElement("nav"); + navElement.classList.add("context-menu"); + navElement.id = menuId; + navElement.appendChild(processMenuItems(items)); + + return navElement; +}; diff --git a/ts/src/browser/moveResize.ts b/ts/src/browser/moveResize.ts new file mode 100644 index 0000000..a4b6fe4 --- /dev/null +++ b/ts/src/browser/moveResize.ts @@ -0,0 +1,137 @@ +import { Rect } from "./svgClasses"; +import { getOffsets } from "./utils"; +import type { Reverser } from "./scaleZoom"; + +export const onMouseMoveResize = ( + reverser: Reverser, + currentShape: Rect, + left: boolean, + right: boolean, + top: boolean, + bottom: boolean, + downX: number, + downY: number, +) => (event: MouseEvent): void => { + const [moveX, moveY] = reverser(getOffsets(event)); + + if (left && moveX < downX) { + currentShape.x = moveX; + currentShape.width = downX - moveX; + } else if (right) { + currentShape.x = downX; + currentShape.width = moveX - downX; + } + + if (top && moveY < downY) { + currentShape.y = moveY; + currentShape.height = downY - moveY; + } else if (bottom) { + currentShape.y = downY; + currentShape.height = moveY - downY; + } +}; + +export const onMouseMoveMove = ( + reverser: Reverser, + currentShape: Rect, + startX: number, + startY: number, + downX: number, + downY: number, +) => (event: MouseEvent): void => { + const [moveX, moveY] = reverser(getOffsets(event)); + + const newX = startX + (moveX - downX); + const newY = startY + (moveY - downY); + + currentShape.x = newX; + currentShape.y = newY; +}; + +const perDirection = ( + leftTop: (shape: Rect) => T, + leftBottom: (shape: Rect) => T, + left: (shape: Rect) => T, + rightTop: (shape: Rect) => T, + rightBottom: (shape: Rect) => T, + right: (shape: Rect) => T, + top: (shape: Rect) => T, + bottom: (shape: Rect) => T, + none: (shape: Rect) => T, +) => (rect: Rect, downX: number, downY: number): T => { + const borderOffset = 5; + const [x, y, width, height] = rect.scaled; + + const offsetLeft = downX - x; + const offsetRight = x + width - downX; + + const offsetTop = downY - y; + const offsetBottom = y + height - downY; + + const result = + offsetLeft <= borderOffset + ? offsetTop <= borderOffset + ? leftTop(rect) + : offsetBottom <= borderOffset + ? leftBottom(rect) + : left(rect) + : offsetRight <= borderOffset + ? offsetTop <= borderOffset + ? rightTop(rect) + : offsetBottom <= borderOffset + ? rightBottom(rect) + : right(rect) + : offsetTop <= borderOffset + ? top(rect) + : offsetBottom <= borderOffset + ? bottom(rect) + : none(rect); + + return result; +}; + +export const getResizeParameters = perDirection< + [boolean, boolean, boolean, boolean, number, number] +>( + (rect: Rect) => [ + true, + false, + true, + false, + rect.x + rect.width, + rect.y + rect.height, + ], + (rect: Rect) => [true, false, false, true, rect.x + rect.width, rect.y], + (rect: Rect) => [true, false, false, false, rect.x + rect.width, 0], + (rect: Rect) => [false, true, true, false, rect.x, rect.y + rect.height], + (rect: Rect) => [false, true, false, true, rect.x, rect.y], + (rect: Rect) => [false, true, false, false, rect.x, 0], + (rect: Rect) => [false, false, true, false, 0, rect.y + rect.height], + (rect: Rect) => [false, false, false, true, 0, rect.y], + () => [false, false, false, false, 0, 0], +); + +const getCursor = perDirection( + () => "nwse-resize", + () => "nesw-resize", + () => "ew-resize", + () => "nesw-resize", + () => "nwse-resize", + () => "ew-resize", + () => "ns-resize", + () => "ns-resize", + () => "move", +); + +export const adaptCursor = (reverser: Reverser, currentShape: Rect) => ( + event: MouseEvent, +): void => { + if (event.shiftKey) { + currentShape.rect.style.cursor = "no-drop"; + } else { + const [downX, downY] = reverser(getOffsets(event)); + const cursor = getCursor(currentShape, downX, downY); + + currentShape.rect.style.cursor = cursor; + } +}; diff --git a/ts/src/browser/occlusionEditor.ts b/ts/src/browser/occlusionEditor.ts new file mode 100644 index 0000000..f654bad --- /dev/null +++ b/ts/src/browser/occlusionEditor.ts @@ -0,0 +1,346 @@ +import type { + Registrar, + TagNode, + Internals, + AftermathEntry, + AftermathInternals, +} from "../types"; +import type { MenuItem } from "./menuConstruction"; +import type { BrowserTemplate } from "../template/browser"; +import { id } from "../utils"; + +import { SVG, Shape, ShapeDefinition, Rect } from "./svgClasses"; +import { + adaptCursor, + getResizeParameters, + onMouseMoveResize, + onMouseMoveMove, +} from "./moveResize"; +import { reverseEffects } from "./scaleZoom"; +import { + getImagesFromTemplate, + getOffsets, + imageLoadCallback, + svgKeyword, + svgCss, +} from "./utils"; + +import { setupMenu, enableAsMenuTrigger, menuCss } from "./menu"; +import { rectKeyword } from "./rect"; + +const clickInsideShape = (draw: SVG, event: MouseEvent) => { + /* assumes its rect */ + const rect = Rect.wrap(event.target as SVGRectElement); + + if (event.shiftKey) { + draw.remove(rect); + return; + } + + const reverser = reverseEffects(window.getComputedStyle(draw.image)); + const [downX, downY] = reverser(getOffsets(event)); + + const resizeParameters = getResizeParameters(rect, downX, downY); + + const action = resizeParameters.includes(true) + ? onMouseMoveResize(reverser, rect, ...resizeParameters) + : onMouseMoveMove(reverser, rect, rect.x, rect.y, downX, downY); + + draw.svg.addEventListener("mousemove", action); + draw.svg.addEventListener( + "mouseup", + (innerEvent: MouseEvent) => { + innerEvent.preventDefault(); + draw.svg.removeEventListener("mousemove", action); + }, + { once: true }, + ); +}; + +const makeInteractive = (draw: SVG, newRect: Rect): void => { + const reverser = reverseEffects(window.getComputedStyle(draw.image)); + newRect.rect.addEventListener("mousemove", adaptCursor(reverser, newRect)); + + const shapeMenu = setupMenu("occlusion-shape-menu", [ + { + label: "Change Label", + itemId: "change-label", + clickEvent: () => { + const newLabel = prompt( + "Specify the new label", + newRect.labelText, + ); + if (typeof newLabel === "string") { + newRect.labelText = newLabel; + } + }, + }, + { + label: "Close Menu", + itemId: "close-occlusion-menu", + }, + ]); + enableAsMenuTrigger(shapeMenu, newRect.rect); + + draw.append(newRect); +}; + +const initRect = ( + x: number, + y: number, + width: number, + height: number, + labelText: string, +) => { + const result = Rect.make(); + result.pos = [x, y, width, height]; + result.labelText = labelText; + result.classes = "closet-rect__rect"; + result.labelClasses = "closet-rect__label"; + + return result; +}; + +const initShape = (draw: SVG, shape: ShapeDefinition): void => { + let labelTxt = null, + x = null, + y = null, + width = null, + height = null, + newRect = null; + + const [shapeType /* active */, , ...rest] = shape; + + switch (shapeType) { + case "rect": + [labelTxt, x, y, width, height] = rest; + + newRect = initRect(x, y, width, height, labelTxt); + makeInteractive(draw, newRect); + break; + + default: + // no other shapes yet + } +}; + +const clickOutsideShape = (draw: SVG, event: MouseEvent) => { + const reverser = reverseEffects(window.getComputedStyle(draw.image)); + const [downX, downY] = reverser(getOffsets(event)); + + const nextNum = draw.getNextNum(event); + const newRect = initRect(downX, downY, 0, 0, `rect${nextNum}`); + makeInteractive(draw, newRect); + + const resizer = onMouseMoveResize( + reverser, + newRect, + true, + true, + true, + true, + downX, + downY, + ); + + draw.svg.addEventListener("mousemove", resizer); + draw.svg.addEventListener("mouseup", () => { + draw.svg.removeEventListener("mousemove", resizer); + newRect.readjust(draw); + }); +}; + +const occlusionLeftClick = (draw: SVG, event: MouseEvent) => { + event.preventDefault(); + + const click = + (event.target as Element).nodeName !== "svg" + ? clickInsideShape + : clickOutsideShape; + + click(draw, event); +}; + +type MenuHandler = (menu: MenuItem[]) => MenuItem[]; + +type PartialShapeHandler = (shapes: Shape[], draw: SVG) => void; +type ShapeHandler = >( + entry: AftermathEntry, + internals: AftermathInternals, +) => PartialShapeHandler; + +type PartialShapeFilter = ( + shapes: ShapeDefinition[], + draw: SVG, +) => ShapeDefinition[]; +type ShapeFilter = >( + entry: AftermathEntry, + internals: AftermathInternals, +) => PartialShapeFilter; + +const makeOcclusionMenu = ( + target: EventTarget, + setupOcclusionMenu: MenuHandler, +): HTMLElement => { + const acceptEventHandler = (event: MouseEvent) => { + event.preventDefault(); + target.dispatchEvent(new Event("accept")); + }; + + const occlusionMenu = setupOcclusionMenu([ + { + label: "Accept occlusions", + itemId: "occlusion-accept", + clickEvent: acceptEventHandler, + }, + { + label: "Close Menu", + itemId: "close-occlusion-menu", + }, + ]); + + return setupMenu("occlusion-menu", occlusionMenu); +}; + +const wrapForOcclusion = (draw: SVG, menu: HTMLElement): void => { + const occlusionClick = (event: MouseEvent) => { + if (event.button === 0 /* left mouse button */) { + occlusionLeftClick(draw, event); + } + }; + + draw.svg.addEventListener("mousedown", occlusionClick); + + enableAsMenuTrigger(menu, draw.svg); +}; + +const defaultAcceptHandler: ShapeHandler = (_entry, internals) => (shapes) => { + // window needs active focus for thi + navigator.clipboard + .writeText( + shapes + .map((shape) => + shape.toText(internals.template.parser.delimiters), + ) + .join("\n"), + ) + .catch(() => + console.log("Window needs active focus for copying to clipboard"), + ); +}; + +const defaultRejectHandler: ShapeHandler = () => ( + _shapes: Shape[], + draw: SVG, +) => draw.cleanup(); + +const occlusionCss = ` +.closet-occlusion-container { + outline: 3px dotted hotpink; + outline-offset: -3px; +}`; + +const occlusionMakerCssKeyword = "occlusionMakerCss"; + +export const occlusionMakerRecipe = >( + options: { + tagname?: string; + maxOcclusions?: number; + acceptHandler?: ShapeHandler; + rejectHandler?: ShapeHandler; + setupOcclusionMenu?: MenuHandler; + existingShapesFilter?: ShapeFilter; + shapeKeywords?: string[]; + } = {}, +) => (registrar: Registrar) => { + const keyword = "makeOcclusions"; + const target = new EventTarget(); + + const { + tagname = keyword, + maxOcclusions = 500, + acceptHandler = defaultAcceptHandler, + rejectHandler = defaultRejectHandler, + setupOcclusionMenu = id, + existingShapesFilter = () => id, + shapeKeywords = [rectKeyword], + } = options; + + const occlusionMakerFilter = (_tag: TagNode, internals: Internals) => { + const template = internals.template as BrowserTemplate; + const images = getImagesFromTemplate(template); + + internals.aftermath.registerIfNotExists( + keyword, + (entry, internals) => { + const existingShapes: any[] = []; + for (const kw of shapeKeywords) { + // get shapes for editing + const otherShapes = internals.cache.get(kw, []); + existingShapes.push(...otherShapes); + + // block aftermath render action + internals.aftermath.block(kw); + } + + const callback = (event: Event): void => { + const image = event.target as HTMLImageElement; + const template = internals.template as BrowserTemplate; + + template.appendDocumentStyle( + occlusionMakerCssKeyword, + menuCss, + ); + template.appendStyle( + image, + occlusionMakerCssKeyword, + occlusionCss, + ); + template.appendStyle(image, svgKeyword, svgCss); + + const draw = SVG.wrapImage(image); + draw.setMaxOcclusions(maxOcclusions); + + existingShapesFilter(entry as any, internals)( + existingShapes, + draw, + ).forEach((definition: ShapeDefinition) => + initShape(draw, definition), + ); + + const acceptEvent = () => { + acceptHandler(entry as any, internals)( + draw.getElements(), + draw, + ); + }; + + const rejectEvent = () => { + rejectHandler(entry as any, internals)( + draw.getElements(), + draw, + ); + }; + + target.addEventListener("accept", acceptEvent); + target.addEventListener("reject", rejectEvent); + + const menu = makeOcclusionMenu(target, setupOcclusionMenu); + wrapForOcclusion(draw, menu); + }; + + for (const [srcUrl, root] of images) { + imageLoadCallback(`img[src="${srcUrl}"]`, root, callback); + } + }, + { + priority: 100 /* before any other occlusion aftermath */, + }, + ); + + return { ready: true }; + }; + + registrar.register(tagname, occlusionMakerFilter); + return target; +}; diff --git a/ts/src/browser/rect.ts b/ts/src/browser/rect.ts new file mode 100644 index 0000000..5e43614 --- /dev/null +++ b/ts/src/browser/rect.ts @@ -0,0 +1,201 @@ +import type { + TagNode, + Internals, + AftermathEntry, + AftermathInternals, + Un, + Recipe, + WeakFilter, + InactiveBehavior, +} from "../types"; +import type { FlashcardPreset } from "../flashcard"; +import type { Optic } from "../template/optics"; +import type { BrowserTemplate } from "../template/browser"; + +import { + FlashcardTemplate, + makeFlashcardTemplate, + generateFlashcardRecipes, +} from "../flashcard/flashcardTemplate"; + +import type { + RectProperty, + RectProperties, + RectPropertyGetter, + RectDefinition, +} from "./svgClasses"; +import { SVG, Rect } from "./svgClasses"; + +import { + getImagesFromTemplate, + imageLoadCallback, + svgKeyword, + svgCss, +} from "./utils"; +import { separated } from "../template/optics"; + +export const rectKeyword = "occlusionRenderRect"; + +const renderRects = ( + entry: AftermathEntry, + { template, cache }: AftermathInternals, +): void => { + const images = getImagesFromTemplate(template as BrowserTemplate); + const rects = cache.get(entry.keyword, []); + + const firstImage = images[0]; + + if (!firstImage) { + return; + } + + imageLoadCallback( + `img[src="${firstImage[0]}"]`, + firstImage[1], + (event: Event): void => { + const browserTemplate = template as BrowserTemplate; + browserTemplate.appendStyle( + event.target as HTMLImageElement, + svgKeyword, + svgCss, + ); + + const draw = SVG.wrapImage(event.target as HTMLImageElement); + + for (const [ + , + /* type */ active /* fullKey */, + , + x, + y, + width, + height, + options, + ] of rects) { + if (!active) { + continue; + } + + const svgRect = Rect.make(draw); + console.log(`x=${x}, y=${y}, width=${width}, height=${height}`); + svgRect.pos = [x, y, width, height]; + + for (const prop in options) { + const rectProperty = prop as RectProperty; + svgRect.prop(rectProperty, options[rectProperty] as string); + } + + draw.append(svgRect); + } + }, + ); +}; + +const processProps = (rest: string[], overwriteProps = {}): RectProperties => { + const propObject: RectProperties = {}; + + rest.map( + (propString: string) => propString.split("=") as [RectProperty, string], + ).forEach( + ([name, value]: [RectProperty, string]) => (propObject[name] = value), + ); + + return Object.assign(propObject, overwriteProps); +}; + +const makeRects = (props: RectProperties | RectPropertyGetter) => < + T extends Un +>( + tag: TagNode, + internals: Internals, +) => { + const [x = 0, y = 0, width = 50, height = width, ...rest] = tag.values; + + const theProps: RectProperties = typeof props === "function" ? props(tag, internals) : props; + + internals.cache.over( + rectKeyword, + (rectList: RectDefinition[]) => + rectList.push([ + "rect", + true, + tag.fullKey, + Number(x), + Number(y), + Number(width), + Number(height), + processProps(rest, theProps), + ]), + [], + ); + + internals.aftermath.registerIfNotExists(rectKeyword, renderRects as any); + + return { ready: true }; +}; + +const props: RectProperties = { + classes: "closet-rect__rect", + labelClasses: "closet-rect__label", +}; + +const ellipseProps: RectProperties = { + classes: "closet-rect__ellipsis", + labelClasses: "closet-rect__ellipsis", +}; + +const classesForFront = "is-active is-front"; +const classesForBack = "is-active is-back"; +const classesForInactive = "is-inactive"; + +const rectPublicApi = ( + frontInactive: InactiveBehavior, + backInactive: InactiveBehavior, +): Recipe => ( + options: { + tagname?: string; + + front?: (props: RectProperties | RectPropertyGetter) => WeakFilter; + back?: (props: RectProperties | RectPropertyGetter) => WeakFilter; + + optics?: Optic[]; + flashcardTemplate?: FlashcardTemplate; + + frontProperties?: RectProperties | RectPropertyGetter; + backProperties?: RectProperties | RectPropertyGetter; + ellipseProperties?: RectProperties | RectPropertyGetter; + contextProperties?: RectProperties | RectPropertyGetter; + } = {}, +) => { + const { + tagname = "rect", + + front = makeRects, + back = makeRects, + + optics = [separated({ sep: "," })], + flashcardTemplate = makeFlashcardTemplate(), + + frontProperties = { ...props, containerClasses: classesForFront }, + backProperties = { ...props, containerClasses: classesForBack }, + ellipseProperties = { + ...ellipseProps, + containerClasses: classesForInactive, + }, + contextProperties = { ...props, containerClasses: classesForInactive }, + } = options; + + const rectOptics = { optics }; + const rectRecipe = flashcardTemplate(frontInactive, backInactive); + + return rectRecipe( + tagname, + front(frontProperties), + back(backProperties), + makeRects(ellipseProperties), + makeRects(contextProperties), + rectOptics, + ); +}; + +export const rectRecipes = generateFlashcardRecipes(rectPublicApi); diff --git a/ts/src/browser/scaleZoom.ts b/ts/src/browser/scaleZoom.ts new file mode 100644 index 0000000..fd825b3 --- /dev/null +++ b/ts/src/browser/scaleZoom.ts @@ -0,0 +1,28 @@ +/** + * functions for reversing css properties zoom and transform + */ + +export type Reverser = ([x, y]: [number, number]) => [number, number]; + +const reverseZoom = (style: CSSStyleDeclaration): Reverser => ([origX, origY]: [ + number, + number, +]): [number, number] => { + const zoomString = style.getPropertyValue("zoom"); + const zoomValue = Number(zoomString); + + if (Number.isNaN(zoomValue) || zoomValue <= 0) { + return [origX, origY]; + } + + return [origX / zoomValue, origY / zoomValue]; +}; + +export const reverseEffects = (style: CSSStyleDeclaration): Reverser => ([ + x, + y, +]: [number, number]) => { + const zoom = reverseZoom(style); + + return zoom([x, y]); +}; diff --git a/ts/src/browser/svgClasses.ts b/ts/src/browser/svgClasses.ts new file mode 100644 index 0000000..6188a18 --- /dev/null +++ b/ts/src/browser/svgClasses.ts @@ -0,0 +1,500 @@ +import type { Delimiters } from "../template/delimiters"; + +import type { + TagNode, + Internals, + Un, +} from "../types"; + +import { getHighestNum } from "./utils"; + +const ns = "http://www.w3.org/2000/svg"; + +export type RectProperty = + | "classes" + | "labelClasses" + | "containerClasses" + | "rx" + | "ry" + | "fill" + | "fillOpacity" + | "stroke" + | "strokeOpacity" + | "strokeWidth"; + +export type RectProperties = Partial>; +export type RectPropertyGetter = (tag: TagNode, internals: Internals) => RectProperties; + +export type RectDefinition = [ + "rect", + boolean | undefined, + string, + number, + number, + number, + number, + RectProperties, +]; + +export type ShapeDefinition = RectDefinition; + +export interface Shape { + getElements(): Element[]; + /** + * all elements that need to be put into svg container + */ + + getAnchorElement(): Element; + /** + * the "anchor" element should be the one used in the "wrap" command + * can be used to locate all other svgs related to that shape + */ + + getLabel(): string; + + resize(forSVG: SVG): void; + readjust(forSVG: SVG): void; + + toDefinition(): ShapeDefinition; + toText(delimiters: Delimiters): string; +} + +export class SVG { + readonly container: HTMLDivElement; + readonly image: HTMLImageElement; + readonly svg: SVGElement; + + protected scaleFactorX = 1; + protected scaleFactorY = 1; + protected maxOcclusions = 500; + + protected elements: Shape[] = []; + protected resizer: any; /* ResizeObserver */ + + protected constructor( + container: HTMLDivElement, + image: HTMLImageElement, + svg: SVGElement, + ) { + this.container = container; + this.image = image; + this.svg = svg; + + this.resizer = new (globalThis as any).ResizeObserver(() => + this.resize(), + ); + this.resizer.observe(image); + + this.setScaleFactors(); + } + + protected setScaleFactors(): void { + this.scaleFactorX = this.image.width / this.image.naturalWidth; + this.scaleFactorY = this.image.height / this.image.naturalHeight; + } + + static wrapImage(image: HTMLImageElement): SVG { + const container = document.createElement("div"); + + image.parentNode && image.parentNode.replaceChild(container, image); + container.appendChild(image); + + const svg = document.createElementNS(ns, "svg"); + svg.setAttributeNS(null, "width", "100%"); + svg.setAttributeNS(null, "height", "100%"); + + // Adopt images zoom and transform attributes + // both can be used to scale + const imageStyle = window.getComputedStyle(image); + svg.style.zoom = imageStyle.getPropertyValue("zoom"); + svg.style.transform = imageStyle.getPropertyValue("transform"); + + container.appendChild(svg); + container.classList.add("closet-occlusion-container"); + + return new SVG(container, image, svg); + } + + get scaleFactors(): [number, number] { + return [this.scaleFactorX, this.scaleFactorY]; + } + + resize(): void { + this.setScaleFactors(); + + if (this.scaleFactors.includes(0)) { + // image is not displayed anymore + this.resizer.disconnect(); + return; + } + + for (const elem of this.elements) { + elem.resize(this); + } + } + + append(element: Shape): void { + this.elements.push(element); + + for (const elem of element.getElements()) { + this.svg.appendChild(elem); + } + } + + getElements(): Shape[] { + return this.elements; + } + + getLabels(): string[] { + return this.getElements().map((element) => element.getLabel()); + } + + remove(shape: Shape): void { + this.elements.find((elem, index) => { + if (elem.getAnchorElement() === shape.getAnchorElement()) { + for (const elem of shape.getElements()) { + elem.remove(); + } + + this.elements.splice(index, 1); + return true; + } + }); + } + + setMaxOcclusions(maxOcclusions: number): void { + this.maxOcclusions = maxOcclusions; + } + + getNextNum(event: MouseEvent): number { + const increment = event.altKey ? 0 : 1; + + const maybeCurrentNum = Math.max( + 1, + getHighestNum(this.getLabels()) + increment, + ); + + const currentNum = + maybeCurrentNum > this.maxOcclusions ? 0 : maybeCurrentNum; + + return currentNum; + } + + cleanup(): void { + const image = this.container.removeChild(this.image); + this.container.replaceWith(image); + } +} + +export class Rect implements Shape { + readonly container: SVGElement; + readonly rect: SVGRectElement; + readonly label: SVGTextElement; + + protected scaleFactorX = 1; + protected scaleFactorY = 1; + + protected constructor( + container: SVGElement, + rect: SVGRectElement, + label: SVGTextElement, + scaleFactorX: number, + scaleFactorY: number, + ) { + this.container = container; + this.rect = rect; + this.label = label; + + this.setScaleFactors(scaleFactorX, scaleFactorY); + } + + static make(forSVG?: SVG): Rect { + const container = document.createElementNS(ns, "svg"); + const rect = document.createElementNS(ns, "rect"); + const label = document.createElementNS(ns, "text"); + + container.appendChild(rect); + container.appendChild(label); + container.classList.add("closet-rect"); + container.classList.add("closet-shape"); + + container.tabIndex = -1; + + const [scaleFactorX, scaleFactorY] = forSVG + ? forSVG.scaleFactors + : [1, 1]; + + const theRect = new Rect( + container, + rect, + label, + scaleFactorX, + scaleFactorY, + ); + + return theRect; + } + + static wrap(rect: SVGRectElement, forSVG?: SVG): Rect { + const [scaleFactorX, scaleFactorY] = forSVG + ? forSVG.scaleFactors + : [1, 1]; + + return new Rect( + (rect.parentElement as unknown) as SVGElement, + rect, + rect.nextSibling as SVGTextElement, + scaleFactorX, + scaleFactorY, + ); + } + + remove(): void { + this.container.remove(); + } + + getElements(): Element[] { + return [this.container]; + } + + getAnchorElement(): Element { + return this.rect; + } + + getLabel(): string { + return this.labelText; + } + + setScaleFactors(x: number, y: number): void { + this.scaleFactorX = x; + this.scaleFactorY = y; + } + + readjust(forSVG?: SVG): [number, number] { + const scaleFactors = forSVG ? forSVG.scaleFactors : [1, 1]; + + const saveX = this.scaleFactorX; + const saveY = this.scaleFactorY; + + this.setScaleFactors(scaleFactors[0], scaleFactors[1]); + + return [saveX, saveY]; + } + + resize(forSVG?: SVG): void { + const savePos = this.pos; + this.readjust(forSVG); + this.pos = savePos; + } + + toDefinition(): RectDefinition { + return [ + "rect", + undefined, + this.labelText, + this.x, + this.y, + this.width, + this.height, + {}, + ]; + } + + toText(delimiters: Delimiters): string { + return `${delimiters.open}${this.labelText}${ + delimiters.sep + }${this.x.toFixed()},${this.y.toFixed()},${this.width.toFixed()},${this.height.toFixed()}${ + delimiters.close + }`; + } + + fontSizeUpdate(): void { + const [saveX, saveY] = this.readjust(); + + const fontSizeEstimate = Math.max( + 0.1, + Math.min( + 32, + 0.4 * ((this.width + this.height) / 2 - 2 * this.strokeWidth), + ), + ); + + this.fontSize = fontSizeEstimate; + this.setScaleFactors(saveX, saveY); + } + + /////////////////// on both + + get pos(): [number, number, number, number] { + return [this.x, this.y, this.width, this.height]; + } + set pos([x, y, width, height]: [number, number, number, number]) { + // NOTE: width and height need to be set first, because x/y are dependent on them + this.width = width; + this.height = height; + this.x = x; + this.y = y; + } + + get scaled(): [number, number, number, number] { + return [ + Number(this.rect.getAttributeNS(null, "x")), + Number(this.rect.getAttributeNS(null, "y")), + Number(this.rect.getAttributeNS(null, "width")), + Number(this.rect.getAttributeNS(null, "height")), + ]; + } + + /////////////////// internal functions, prefer using pos() / scaled() + + get x(): number { + return Number(this.rect.getAttributeNS(null, "x")) / this.scaleFactorX; + } + set x(i: number) { + const scaledX = i * this.scaleFactorX; + const scaledForLabelX = (i + this.width / 2) * this.scaleFactorX; + + this.rect.setAttributeNS(null, "x", String(scaledX)); + this.label.setAttributeNS(null, "x", String(scaledForLabelX)); + } + + get y(): number { + return Number(this.rect.getAttributeNS(null, "y")) / this.scaleFactorY; + } + set y(i: number) { + const scaledY = i * this.scaleFactorY; + const scaledForLabelY = (i + this.height / 2) * this.scaleFactorY; + + this.rect.setAttributeNS(null, "y", String(scaledY)); + this.label.setAttributeNS(null, "y", String(scaledForLabelY)); + } + + get width(): number { + return ( + Number(this.rect.getAttributeNS(null, "width")) / this.scaleFactorX + ); + } + set width(i: number) { + const stringified = String(Math.max(10, i) * this.scaleFactorX); + this.rect.setAttributeNS(null, "width", stringified); + this.label.setAttributeNS(null, "width", stringified); + this.fontSizeUpdate(); + } + + get height(): number { + return ( + Number(this.rect.getAttributeNS(null, "height")) / this.scaleFactorY + ); + } + set height(i: number) { + const stringified = String(Math.max(10, i) * this.scaleFactorY); + this.rect.setAttributeNS(null, "height", stringified); + this.label.setAttributeNS(null, "height", stringified); + this.fontSizeUpdate(); + } + + /////////////////// styling + + props(attrs: RectProperties): void { + for (const key in attrs) { + const propName = key as RectProperty; + this.prop(propName, attrs[propName] as string); + } + } + + prop(attr: RectProperty, value: string): void { + (this[attr] as any) = value; + } + + /////////////////// on container + + set containerClasses(txt: string) { + txt.split(" ").forEach((cls: string) => + this.container.classList.add(cls), + ); + } + get containerClasses(): string { + return Array.from(this.container.className).join(" "); + } + + /////////////////// on rect + + set rx(i: number) { + this.rect.setAttributeNS(null, "rx", String(i)); + } + get rx(): number { + return Number(this.rect.getAttributeNS(null, "rx")); + } + + set ry(i: number) { + this.rect.setAttributeNS(null, "ry", String(i)); + } + get ry(): number { + return Number(this.rect.getAttributeNS(null, "ry")); + } + + set fill(color: string) { + this.rect.setAttributeNS(null, "fill", color); + } + get fill(): string { + return this.rect.getAttributeNS(null, "fill") ?? ""; + } + + set fillOpacity(i: number) { + this.rect.setAttributeNS(null, "fill-opacity", String(i)); + } + get fillOpacity(): number { + return Number(this.rect.getAttributeNS(null, "fill-opacity")); + } + + set stroke(color: string) { + this.rect.setAttributeNS(null, "stroke", color); + } + get stroke(): string { + return this.rect.getAttributeNS(null, "stroke") ?? ""; + } + + set strokeOpacity(i: number) { + this.rect.setAttributeNS(null, "stroke-opacity", String(i)); + } + get strokeOpacity(): number { + return Number(this.rect.getAttributeNS(null, "stroke-opacity")); + } + + set strokeWidth(i: number) { + this.rect.setAttributeNS(null, "stroke-width", String(i)); + } + get strokeWidth(): number { + return Number(this.rect.getAttributeNS(null, "stroke-width")); + } + + set classes(txt: string) { + txt.split(" ").forEach((cls: string) => this.rect.classList.add(cls)); + } + get classes(): string { + return Array.from(this.rect.classList).join(" "); + } + + /////////////////// on label + + set labelText(txt: string) { + this.label.innerHTML = txt; + } + get labelText(): string { + return this.label.innerHTML; + } + + set fontSize(i: number) { + this.label.setAttributeNS(null, "font-size", String(i)); + } + get fontSize(): number { + return Number(this.label.getAttributeNS(null, "font-size")); + } + + set labelClasses(txt: string) { + txt.split(" ").forEach((cls: string) => this.label.classList.add(cls)); + } + get labelClasses(): string { + return Array.from(this.label.classList).join(" "); + } +} diff --git a/ts/src/browser/utils.ts b/ts/src/browser/utils.ts new file mode 100644 index 0000000..2536f68 --- /dev/null +++ b/ts/src/browser/utils.ts @@ -0,0 +1,124 @@ +import type { BrowserTemplate, BrowserTemplateNode } from "../template/browser"; + +import { keySeparation } from "../patterns"; + +const imageSrcPattern = /]*?src="(.+?)"[^>]*>/g; +const getImages = (txt: string, root: Node): [string, Node][] => { + const result = []; + let match: RegExpExecArray | null; + + do { + match = imageSrcPattern.exec(txt); + if (match) { + result.push([match[1], root]); + } + } while (match); + + return result as [string, Node][]; +}; + +export const getImagesFromTemplate = ( + template: BrowserTemplate, +): [string, Node][] => { + const applyGetImages = ([fragment, input]: [string, BrowserTemplateNode]): [ + string, + Node, + ][] => + typeof input === "string" + ? [] + : getImages(fragment, input.getRootNode()); + + return template.textFragments + .map((fragment: string, index: number): [ + string, + BrowserTemplateNode, + ] => [fragment, template.inputs[index]]) + .flatMap(applyGetImages); +}; + +export const getOffsets = (event: MouseEvent): [number, number] => { + if (navigator.userAgent.search("Chrome") >= 0) { + return [event.offsetX, event.offsetY]; + } /* Firefox support */ else { + // layerX/Y are deprecated, however offsetX/Y give wrong values on Firefox + // this does not work when using transform + const target = event.currentTarget as Element; + + const boundingRect = target.getBoundingClientRect(); + const parentRect = (target.parentElement as Element).getBoundingClientRect(); + + const offsetX = + (event as any).layerX - (parentRect.left - boundingRect.left); + const offsetY = + (event as any).layerY - (parentRect.top - boundingRect.top); + + return [offsetX, offsetY]; + } +}; + +export const imageLoadCallback = ( + query: string, + root: Node, + callback: (event: Event) => void, +): void => { + const maybeElement = (root as any).querySelector(query) as HTMLImageElement; + + if (maybeElement) { + if (maybeElement.complete) { + callback({ target: maybeElement } as any); + } else { + maybeElement.addEventListener("load", callback); + } + } +}; + +export const getHighestNum = (labels: string[]): number => { + let result = 0; + + for (const label of labels) { + const match = label.match(keySeparation); + + if (!match) { + continue; + } + + const labelNum = Number(match[2]); + + if (Number.isNaN(labelNum)) { + continue; + } + + result = Math.max(result, labelNum); + } + + return result; +}; + +export const svgKeyword = "occlusionSvgCss"; +export const svgCss = ` +img { + max-width: 100% !important; +} + +.closet-occlusion-container { + display: inline-block; + position: relative; +} + +.closet-occlusion-container > * { + display: block; + + margin-left: auto; + margin-right: auto; +} + +.closet-occlusion-container > svg { + position: absolute; + top: 0; +} + +.closet-shape > text { + text-anchor: middle; + dominant-baseline: central; + pointer-events: none; +}`; diff --git a/ts/src/filterManager.ts b/ts/src/filterManager.ts new file mode 100644 index 0000000..8ffc8a7 --- /dev/null +++ b/ts/src/filterManager.ts @@ -0,0 +1,121 @@ +import type { Un } from "./types" + +import { MetaFilterManager } from "./filterManager/index"; +import { FilterApi } from "./filterManager/filters"; +import { Storage, StorageType } from "./filterManager/storage"; +import { DeferredApi } from "./filterManager/deferred"; +import { Status } from "./template/types"; + +import type { FilterResult } from "./filterManager/filters"; +import type { + TagRenderer, + TemplateInfo, + IterationInfo, + ResultInfo, +} from "./template"; +import type { TagNode } from "./template/nodes"; +import type { + TagAccessor, + TagProcessor, + RoundInfo, + DataOptions, + ProcessorOutput, +} from "./template/types"; + +const filterResultToProcessorOutput = ( + filterResult: FilterResult, +): ProcessorOutput => ({ + status: filterResult.parse + ? filterResult.ready + ? Status.ContinueTags + : Status.ContainsTags + : filterResult.ready + ? Status.Ready + : Status.NotReady, + result: filterResult.result, +}); + +const fillDataOptions = (partial: Partial): DataOptions => { + const optics = partial.optics ?? []; + + return { + inlineOptics: partial.inlineOptics ?? optics, + blockOptics: partial.blockOptics ?? optics, + capture: partial.capture ?? false, + }; +}; + +const closetEnvironmentName = "_closetEnvironment"; +interface ClosetEnvironment { + [closetEnvironmentName]: StorageType; +} + +export class FilterManager

+ extends MetaFilterManager< + TagNode, + TemplateInfo, + IterationInfo, + RoundInfo, + ResultInfo, + DataOptions, + P + > + implements TagRenderer { + static make

( + preset: P = {} as P, + memory: StorageType = new Map(), + ): FilterManager

{ + const environment = !Object.prototype.hasOwnProperty.call( + globalThis, + closetEnvironmentName, + ) + ? ((globalThis as typeof globalThis & Partial)[ + closetEnvironmentName + ] = new Map()) + : (globalThis as typeof globalThis & Partial)[ + closetEnvironmentName + ]; + + return new FilterManager( + preset, + new FilterApi() as any, + new Storage(new Map()), + new DeferredApi() as any, + new DeferredApi() as any, + new Storage(new Map()), + new Storage(memory), + new Storage(environment as StorageType), + ); + } + + makeAccessor( + template: TemplateInfo, + iteration: IterationInfo, + ): TagAccessor { + const accessor = this.filterAccessor(template, iteration); + + return (name: string): TagProcessor => { + const processor = accessor.getProcessor(name); + + return { + execute: (data: TagNode, round: RoundInfo): ProcessorOutput => + filterResultToProcessorOutput( + processor.execute( + data, + super.getInternals(template, iteration, round), + ), + ), + getOptions: () => fillDataOptions(processor.getOptions()), + }; + }; + } + + finishIteration(template: TemplateInfo, iteration: IterationInfo) { + this.executeDeferred(template, iteration); + } + + finishRun(template: TemplateInfo, result: ResultInfo): void { + this.executeAftermath(template, result); + this.reset(); + } +} diff --git a/ts/src/filterManager/deferred.ts b/ts/src/filterManager/deferred.ts new file mode 100644 index 0000000..6fa4646 --- /dev/null +++ b/ts/src/filterManager/deferred.ts @@ -0,0 +1,103 @@ +import { PriorityQueue } from "./priorityQueue"; + +export type Deferred = (entry: DeferredEntry, internals: T) => void; + +export interface DeferredEntry { + keyword: string; + procedure: Deferred; + priority: number; + persistent: boolean; +} + +export interface DeferredOptions { + priority: number; + persistent: boolean; +} + +const defaultDeferredOptions: DeferredOptions = { + priority: 0, + persistent: false, +}; + +// bigger number means higher priority, and higher priority means executed first +const deferredComparator = ( + x: DeferredEntry, + y: DeferredEntry, +): boolean => x.priority > y.priority; + +export class DeferredApi { + private readonly _deferred: Map>; + private readonly _blocked: Set; + + constructor() { + this._deferred = new Map(); + this._blocked = new Set(); + } + + register( + keyword: string, + procedure: Deferred, + options: Partial = defaultDeferredOptions, + ): void { + this._deferred.set(keyword, { + keyword: keyword, + procedure: procedure, + priority: options.priority ?? defaultDeferredOptions.priority, + persistent: options.persistent ?? defaultDeferredOptions.persistent, + }); + } + + registerIfNotExists( + keyword: string, + procedure: Deferred, + options: Partial = defaultDeferredOptions, + ): void { + if (!this.isRegistered(keyword)) { + this.register(keyword, procedure, options); + } + } + + unregister(keyword: string): void { + this._deferred.delete(keyword); + } + + isRegistered(keyword: string): boolean { + return this._deferred.has(keyword); + } + + block(keyword: string) { + this._blocked.add(keyword); + } + + unblock(keyword: string) { + this._blocked.delete(keyword); + } + + isBlocked(keyword: string): boolean { + return this._blocked.has(keyword); + } + + clear(): void { + this._deferred.clear(); + this._blocked.clear(); + } + + executeEach(internals: T): void { + const prioQueue = new PriorityQueue>( + deferredComparator, + ); + prioQueue.push(...this._deferred.values()); + + for (const def of prioQueue.generate()) { + if (!this.isBlocked(def.keyword)) { + def.procedure(def, internals); + } + + if (!def.persistent) { + this.unregister(def.keyword); + } + } + + this._blocked.clear(); + } +} diff --git a/ts/src/filterManager/filters.ts b/ts/src/filterManager/filters.ts new file mode 100644 index 0000000..6eb7d3f --- /dev/null +++ b/ts/src/filterManager/filters.ts @@ -0,0 +1,100 @@ +import type { ASTNode } from "../template/nodes"; + +type ResultType = string | ASTNode[]; + +export interface FilterResult { + ready: boolean; + result: ResultType; + parse: boolean; +} + +export interface Filterable { + toReprString(): string; +} + +export type Filter = (t: F, i: T) => FilterResult; + +export type WeakFilterResult = Partial | string | void; +export type WeakFilter = (tag: F, internals: T) => WeakFilterResult; + +const wrapWithBool = (result: ResultType, bool: boolean): FilterResult => ({ + ready: bool, + result: result, + parse: false, +}); + +const wrapWithReady = (result: ResultType): FilterResult => + wrapWithBool(result, true); +const wrapWithReadyBubbled = ( + result: ResultType, + ready: boolean, +): FilterResult => wrapWithBool(result, ready); + +const defaultFilter = (t: Filterable, i: T) => + wrapWithReadyBubbled(t.toReprString(), i.ready); + +const nullFilterResult: FilterResult = { + ready: false, + result: [], + parse: false, +}; + +const standardizeFilterResult = (weak: WeakFilterResult): FilterResult => { + switch (typeof weak) { + case "string": + return wrapWithReady(weak); + + case "object": + // includes null + return { + ready: weak.ready ?? true, + result: weak.result ?? [], + parse: weak.parse ?? false, + }; + + default: + // undefined + // marks as not ready + return nullFilterResult; + } +}; + +export interface Readiable { + ready: boolean; +} + +export class FilterApi< + F extends Filterable, + T extends Readiable /* Internals with dependency on RoundInfo */ +> { + private filters: Map> = new Map(); + + register(name: string, filter: WeakFilter): void { + this.filters.set(name, filter); + } + + has(name: string): boolean { + return this.filters.has(name); + } + + get(name: string): WeakFilter | null { + return this.filters.get(name) ?? null; + } + + getOrDefault(name: string): WeakFilter { + return this.get(name) ?? defaultFilter; + } + + unregisterFilter(name: string): void { + this.filters.delete(name); + } + + clearFilters(): void { + this.filters.clear(); + } + + execute(name: string, data: F, internals: T): FilterResult { + const filter = this.getOrDefault(name); + return standardizeFilterResult(filter(data, internals)); + } +} diff --git a/ts/src/filterManager/index.ts b/ts/src/filterManager/index.ts new file mode 100644 index 0000000..dc0794c --- /dev/null +++ b/ts/src/filterManager/index.ts @@ -0,0 +1,209 @@ +import { Storage } from "./storage"; + +import type { Filterable, FilterResult, Readiable } from "./filters"; + +import { FilterApi } from "./filters"; + +import { DeferredApi } from "./deferred"; + +import { RegistrarApi } from "./registrar"; + +export interface ManagerInfo< + F extends Filterable, + T, + I, + R extends Readiable, + X, + D extends Record, + P extends Record +> { + filters: FilterApi & T & I & R>; + options: Storage>; + registrar: RegistrarApi & T & I & R, D>; + + cache: Storage; + memory: Storage; + environment: Storage; + + deferred: DeferredApi & T & I>; + aftermath: DeferredApi & T & X>; + + preset: P; +} + +interface FilterAccessor { + getProcessor: (name: string) => FilterProcessor; +} + +interface FilterProcessor { + execute: (data: F, r: T) => FilterResult; + getOptions: () => Partial; +} + +export class MetaFilterManager< + F extends Filterable, + T, + I, + R extends Readiable, + X extends Record, + D extends Record, + P extends Record +> { + protected readonly filters: FilterApi< + F, + ManagerInfo & T & I & R + >; + protected readonly options: Storage>; + + readonly registrar: RegistrarApi< + F, + ManagerInfo & T & I & R, + D + >; + + protected readonly deferred: DeferredApi< + ManagerInfo & T & I + >; + protected readonly aftermath: DeferredApi< + ManagerInfo & T & X + >; + + protected readonly cache: Storage; + protected readonly memory: Storage; + protected readonly environment: Storage; + + protected preset: P; + + protected constructor( + preset: P, + filters: FilterApi & T & I & R>, + options: Storage>, + deferred: DeferredApi & T & I>, + aftermath: DeferredApi & T & X>, + cache: Storage, + memory: Storage, + environment: Storage, + ) { + this.preset = preset; + + this.filters = filters; + this.options = options; + + this.registrar = new RegistrarApi(filters, options); + + this.deferred = deferred; + this.aftermath = aftermath; + + this.cache = cache; + this.memory = memory; + this.environment = environment; + } + + protected getBaseInternals(t: T): ManagerInfo & T { + return Object.assign( + {}, + { + filters: this.filters, + options: this.options, + registrar: this.registrar, + + cache: this.cache, + memory: this.memory, + environment: this.environment, + + deferred: this.deferred, + aftermath: this.aftermath, + + preset: this.preset, + }, + t, + ); + } + + protected getAftermathInternals( + t: T, + x: X, + ): ManagerInfo & T & X { + return Object.assign(this.getBaseInternals(t), x); + } + + protected getDeferredInternals( + t: T, + i: I, + ): ManagerInfo & T & I { + return Object.assign(this.getBaseInternals(t), i); + } + + protected getInternals( + t: T, + i: I, + r: R, + ): ManagerInfo & T & I & R { + return Object.assign(this.getDeferredInternals(t, i), r); + } + + filterAccessor( + t: T, + i: I, + ): FilterAccessor & T & I & R, D> { + return { + getProcessor: ( + name: string, + ): FilterProcessor< + F, + ManagerInfo & T & I & R, + D + > => { + return { + execute: (data: F, r: R): FilterResult => + this.filters.execute( + name, + data, + this.getInternals(t, i, r), + ), + getOptions: () => this.options.get(name, {}), + }; + }, + }; + } + + executeDeferred(t: T, i: I) { + const deferredInternals = this.getDeferredInternals(t, i); + this.deferred.executeEach(deferredInternals); + } + + executeAftermath(t: T, x: X) { + const aftermathInternals = this.getAftermathInternals(t, x); + this.aftermath.executeEach(aftermathInternals); + } + + switchPreset(preset: P) { + this.preset = preset; + } + + clear() { + this.memory.clear(); + this.aftermath.clear(); + } + + reset() { + this.cache.clear(); + this.deferred.clear(); + } + + install( + ...recipes: Array< + ( + registrar: RegistrarApi< + F, + ManagerInfo & T & I & R, + D + >, + ) => void + > + ): void { + for (const recipe of recipes) { + recipe(this.registrar); + } + } +} diff --git a/ts/src/filterManager/priorityQueue.ts b/ts/src/filterManager/priorityQueue.ts new file mode 100644 index 0000000..d76946e --- /dev/null +++ b/ts/src/filterManager/priorityQueue.ts @@ -0,0 +1,95 @@ +export type Comparator = (a: T, b: T) => boolean; + +const top = 0; +const parent = (i: number): number => ((i + 1) >>> 1) - 1; +const left = (i: number): number => (i << 1) + 1; +const right = (i: number): number => (i + 1) << 1; + +export class PriorityQueue { + private readonly _heap: T[]; + private readonly _comparator: Comparator; + + constructor(comparator: Comparator) { + this._heap = []; + this._comparator = comparator; + } + + private greater(i: number, j: number): boolean { + return this._comparator(this._heap[i], this._heap[j]); + } + + private swap(i: number, j: number): void { + [this._heap[i], this._heap[j]] = [this._heap[j], this._heap[i]]; + } + + private siftUp(): void { + let node = this.size() - 1; + while (node > top && this.greater(node, parent(node))) { + this.swap(node, parent(node)); + node = parent(node); + } + } + + private siftDown(): void { + let node = top; + while ( + (left(node) < this.size() && this.greater(left(node), node)) || + (right(node) < this.size() && this.greater(right(node), node)) + ) { + const maxChild = + right(node) < this.size() && + this.greater(right(node), left(node)) + ? right(node) + : left(node); + + this.swap(node, maxChild); + node = maxChild; + } + } + + size(): number { + return this._heap.length; + } + + isEmpty(): boolean { + return this.size() === 0; + } + + peek(): T { + return this._heap[top]; + } + + push(...values: T[]): number { + values.forEach((value) => { + this._heap.push(value); + this.siftUp(); + }); + + return this.size(); + } + + pop(): T { + const poppedValue = this.peek(); + const bottom = this.size() - 1; + + if (bottom > top) { + this.swap(top, bottom); + } + + this._heap.pop(); + this.siftDown(); + + return poppedValue; + } + + *generate(): Generator { + let nextItem = this.pop(); + + while (nextItem /* falsy! */) { + yield nextItem; + nextItem = this.pop(); + } + + return null; + } +} diff --git a/ts/src/filterManager/registrar.ts b/ts/src/filterManager/registrar.ts new file mode 100644 index 0000000..b64c52c --- /dev/null +++ b/ts/src/filterManager/registrar.ts @@ -0,0 +1,29 @@ +import { Filterable, FilterApi, Readiable, WeakFilter } from "./filters"; +import { Storage } from "./storage"; + +export class RegistrarApi< + F extends Filterable, + T extends Readiable, + D extends Record +> { + private filters: FilterApi; + private options: Storage>; + + constructor(filters: FilterApi, options: Storage>) { + this.filters = filters; + this.options = options; + } + + register( + name: string, + filter: WeakFilter, + options: Partial = {}, + ): void { + this.filters.register(name, filter); + this.options.set(name, options); + } + + getOptions(name: string): Partial { + return this.options.get(name, {}); + } +} diff --git a/ts/src/filterManager/storage.ts b/ts/src/filterManager/storage.ts new file mode 100644 index 0000000..a37405c --- /dev/null +++ b/ts/src/filterManager/storage.ts @@ -0,0 +1,80 @@ +export interface StorageType { + has(storageTypeHasKey: string): boolean; + get(storageTypeKey: string): D | undefined; + set(storageTypeKey: string, storageTypeValue: D): void; + delete(storageTypeDeleteKey: string): void; + clear(): void; +} + +export class Storage { + /** + * @param D + * is useful for restriciting values available by options storage + * however is otherwise set to unknown (top type) + */ + protected readonly storage: StorageType; + + constructor(v: StorageType) { + this.storage = v; + } + + set(name: string, value: T): void { + this.storage.set(name, value); + } + + get(name: string, defaultValue: T): T { + return (this.storage.get(name) as T) ?? defaultValue; + } + + has(name: string): boolean { + return this.storage.has(name); + } + + fold( + name: string, + foldFunc: (foldParam: T) => T, + mempty: T, + ): T { + const result = foldFunc(this.get(name, mempty)); + this.set(name, result); + + return result; + } + + post( + name: string, + postFunc: (postParam: T) => T, + mempty: T, + ): T { + const result = this.get(name, mempty); + this.set(name, postFunc(result)); + + return result; + } + + lazy(name: string, lazyFunc: () => T): T { + return ( + this.get(name, (null as unknown) as T) ?? + this.fold(name, lazyFunc, (null as unknown) as T) + ); + } + + over( + name: string, + overFunc: (overParam: T) => X, + mempty: T, + ): X { + const value = this.get(name, mempty); + this.set(name, value); + + return overFunc(value); + } + + delete(name: string): void { + this.storage.delete(name); + } + + clear(): void { + this.storage.clear(); + } +} diff --git a/ts/src/flashcard/cloze.ts b/ts/src/flashcard/cloze.ts new file mode 100644 index 0000000..f790adc --- /dev/null +++ b/ts/src/flashcard/cloze.ts @@ -0,0 +1,100 @@ +import type { + Un, + TagNode, + Internals, + Eval, + Optic, + Recipe, + WeakFilter, + InactiveBehavior, +} from "../types"; +import type { StyleList } from "../styleList"; + +import type { FlashcardTemplate, FlashcardPreset } from "./flashcardTemplate"; + +import { separated } from "../template/optics"; + +import { listStylize } from "../styleList"; +import { Stylizer } from "../stylizer"; +import { constant } from "../utils"; + +import { + makeFlashcardTemplate, + generateFlashcardRecipes, +} from "./flashcardTemplate"; + +const inactive = constant( + '', +); + +const active = (side: string) => (s: string) => + `${s}`; +const activeFront: Stylizer = Stylizer.make({ processor: active("front") }); +const activeBack = Stylizer.make({ processor: active("back") }); + +const hint = (tag: TagNode, _internals: Internals) => { + return [`${tag.values[1] ?? ""}`]; +}; + +const answer = (tag: TagNode) => + `${tag.values[0]}`; +const answerBubbleReady = ( + tag: TagNode, + { ready }: Internals, +) => ({ + ready: ready, + result: `${answer(tag)}`, +}); +const answerAsList = (tag: TagNode) => [answer(tag)]; + +const clozePublicApi = ( + frontInactive: InactiveBehavior, + backInactive: InactiveBehavior, +): Recipe => ( + options: { + tagname?: string; + + frontStylizer?: Stylizer; + frontEllipser?: Eval; + + backStylizer?: Stylizer; + backEllipser?: Eval; + + inactiveEllipser?: WeakFilter; + + optics?: Optic[]; + flashcardTemplate?: FlashcardTemplate; + } = {}, +) => { + const { + tagname = "c", + + inactiveEllipser = inactive, + + frontStylizer = activeFront, + frontEllipser = hint, + + backStylizer = activeBack, + backEllipser = answerAsList, + + optics = [separated({ sep: "::", max: 2 })], + flashcardTemplate = makeFlashcardTemplate(), + } = options; + + const front = listStylize(frontStylizer, frontEllipser); + const back = listStylize(backStylizer, backEllipser); + + const clozeOptics = { optics }; + const clozeRecipe = flashcardTemplate(frontInactive, backInactive); + + return clozeRecipe( + tagname, + front, + back, + answerBubbleReady, + inactiveEllipser, + clozeOptics, + ); +}; + +export const clozeRecipes = generateFlashcardRecipes(clozePublicApi); diff --git a/ts/src/flashcard/deciders.ts b/ts/src/flashcard/deciders.ts new file mode 100644 index 0000000..543da36 --- /dev/null +++ b/ts/src/flashcard/deciders.ts @@ -0,0 +1,100 @@ +import type { StoreGetter } from "../recipes/preferenceStore"; +import type { TagNode, Internals } from "../types"; +import type { CardPreset, SidePreset } from "./flashcardTemplate"; + +import { constantGet } from "../recipes/preferenceStore"; + +export const isActiveWithinRange = ( + cardNumber: number, + num: number, + bottomRange: number, + topRange: number, +): boolean => { + return cardNumber - bottomRange <= num && num <= cardNumber + topRange; +}; + +export const isActive = ( + { num }: TagNode, + { preset }: Internals, +): boolean => { + switch (num) { + case null: + return false; + case 0: + return true; + default: + if (!Object.prototype.hasOwnProperty.call(preset, "cardNumber")) { + return false; + } + + return num === preset.cardNumber; + } +}; + +export const isActiveGetRange = ( + { key, num, fullOccur }: TagNode, + { preset, cache }: Internals, +): boolean => { + const bottomRangeKeyword = "flashcardActiveBottom"; + const topRangeKeyword = "flashcardActiveTop"; + const constantZero = constantGet(0); + + let bottomRange = null; + let topRange = null; + + switch (num) { + case null: + return false; + case 0: + return true; + default: + if (typeof preset.cardNumber !== "number") { + return false; + } + + bottomRange = cache + .get>(bottomRangeKeyword, constantZero) + .get(key, preset.cardNumber, fullOccur); + + topRange = cache + .get>(topRangeKeyword, constantZero) + .get(key, preset.cardNumber, fullOccur); + + return isActiveWithinRange( + preset.cardNumber, + num, + bottomRange, + topRange, + ); + } +}; + +export const isActiveOverwritten = ( + tag: TagNode, + internals: Internals, +): boolean => { + const activeKeyword = "flashcardActive"; + + const activeOverwrite = internals.cache + .get>(activeKeyword, constantGet(false)) + .get(tag.key, tag.num, tag.fullOccur); + + return activeOverwrite; +}; + +export const isActiveAll = ( + tag: TagNode, + internals: Internals, +): boolean => + isActiveOverwritten(tag, internals) || isActiveGetRange(tag, internals); + +////////////////////////////////////////// + +export const isBack = ( + _t: TagNode, + { preset }: Internals, +) => { + return preset.side === "back"; +}; + +export const isBackAll = isBack; diff --git a/ts/src/flashcard/flashcardTemplate.ts b/ts/src/flashcard/flashcardTemplate.ts new file mode 100644 index 0000000..d7dccf3 --- /dev/null +++ b/ts/src/flashcard/flashcardTemplate.ts @@ -0,0 +1,142 @@ +import type { + TagNode, + Internals, + Registrar, + Recipe, + Eval, + InactiveBehavior, + InactiveAdapter, + DataOptions, + WeakFilter, + RecipeOptions, +} from "../types"; + +import { id, id2, constant } from "../utils"; +import { simpleRecipe as simple } from "../recipes/simple"; +import { sumFour } from "../wrappers/sum"; +import { collection } from "../wrappers/collection"; + +import { isActiveAll, isBackAll } from "./deciders"; +import { inactiveAdapterAll } from "./inactiveAdapter"; + +export type FlashcardTemplate = ( + f2: InactiveBehavior, + b2: InactiveBehavior, +) => ( + tagname: string, + front: WeakFilter, + back: WeakFilter, + contexter: WeakFilter, + inactive: WeakFilter, + dataOptions: Partial, +) => (registrar: Registrar) => void; + +export interface CardPreset { + cardNumber: number; + [v: string]: unknown; +} + +export interface SidePreset { + side: "front" | "back"; + [v: string]: unknown; +} + +export type FlashcardPreset = CardPreset & SidePreset; + +export const makeFlashcardTemplate = ( + isActive: Eval = isActiveAll, + isBack: Eval = isBackAll, + inactiveAdapter: InactiveAdapter = inactiveAdapterAll, +): FlashcardTemplate => ( + frontInactiveBehavior: InactiveBehavior, + backInactiveBehavior: InactiveBehavior, +) => ( + tagname: string, + // highlight it, show the question (e.g. hint for cloze; answer choices for multiple choice) + front: WeakFilter, + // highlight it, show the answer (e.g. unobscure occlusions) + back: WeakFilter, + // provide context for the rest of the question (e.g. show the answer of a cloze; unobscure occlusion) + contexter: WeakFilter, + // render it inactive, avoid giving away too much context for answering (e.g. an ellipsis) + inactive: WeakFilter, + dataOptions: Partial = {}, +) => (registrar: Registrar) => { + const internalFilter = `${tagname}:internal`; + + const flashcardRecipe = sumFour( + // isInactive isFront behavior + simple( + inactiveAdapter(frontInactiveBehavior)(contexter, inactive), + ), + // isActive isFront behavior + simple(front), + // isInctive isBack behavior + simple( + inactiveAdapter(backInactiveBehavior)(contexter, inactive), + ), + // isActive isBack behavior + simple(back), + isActive, + isBack, + ); + + flashcardRecipe({ tagname: internalFilter })(registrar); + + const flashcardFilter = (tag: TagNode, inter: Internals) => { + return inter.filters.getOrDefault(internalFilter)(tag, inter); + }; + + registrar.register(tagname, flashcardFilter, dataOptions); +}; + +export const ellipsis = constant("[...]"); + +export interface FlashcardRecipe> + extends Recipe { + (options?: Record): (filters: Registrar) => void; + show: Recipe; + hide: Recipe; + reveal: Recipe; +} + +export enum FlashcardBehavior { + Show = "show", + Hide = "hide", + Reveal = "reveal", +} + +export const generateFlashcardRecipes = ( + publicApi: ( + front: InactiveBehavior, + back: InactiveBehavior, + ) => Recipe, +): FlashcardRecipe => { + const show = publicApi(id, id); + const hide = publicApi(id2, id2); + const reveal = publicApi(id2, id); + + const [showOptions, hideOptions, revealOptions] = [ + [FlashcardBehavior.Show, "s"], + [FlashcardBehavior.Hide, "h"], + [FlashcardBehavior.Reveal, "r"], + ].map(([behavior, suffix]) => ({ + getTagnames: (options: RecipeOptions): string[] => [ + (options.defaultBehavior ?? FlashcardBehavior.Show) === behavior + ? options.tagname + : options.tagnameShow ?? `${options.tagname}${suffix}`, + ], + })); + + const col = collection([ + [hide, hideOptions], + [show, showOptions], + [reveal, revealOptions], + ]) as FlashcardRecipe; + + col.show = show; + col.hide = hide; + col.reveal = reveal; + + return col; +}; diff --git a/ts/src/flashcard/inactiveAdapter.ts b/ts/src/flashcard/inactiveAdapter.ts new file mode 100644 index 0000000..96b5b70 --- /dev/null +++ b/ts/src/flashcard/inactiveAdapter.ts @@ -0,0 +1,119 @@ +import type { + InactiveAdapter, + InactiveBehavior, + TagNode, + Internals, + WeakFilter, + WeakFilterResult, +} from "../types"; +import type { StoreGetter } from "../recipes/preferenceStore"; +import type { CardPreset } from "./flashcardTemplate"; + +import { constantGet } from "../recipes/preferenceStore"; + +const constantFalse = constantGet(false); +const constantZero = constantGet(0); + +export const inactiveAdapter = ( + behavior: InactiveBehavior, +) => (contexter: WeakFilter, ellipser: WeakFilter) => ( + tag: TagNode, + internals: Internals, +) => behavior(contexter, ellipser)(tag, internals); + +export const inactiveAdapterOverwritten = ( + adapter: InactiveAdapter, +): InactiveAdapter => (behavior: InactiveBehavior) => ( + contexter: WeakFilter, + ellipser: WeakFilter, +) => (tag: TagNode, internals: Internals): WeakFilterResult => { + const showKeyword = "flashcardShow"; + const hideKeyword = "flashcardHide"; + + const showOverwrite = internals.cache + .get>(showKeyword, constantFalse) + .get(tag.key, tag.num, tag.fullOccur); + + if (showOverwrite) { + return contexter(tag, internals); + } + + const hideOverwrite = internals.cache + .get>(hideKeyword, constantFalse) + .get(tag.key, tag.num, tag.fullOccur); + + if (hideOverwrite) { + return ellipser(tag, internals); + } + + return adapter(behavior)(contexter, ellipser)(tag, internals); +}; + +export const inactiveAdapterWithinRange = ( + adapter: InactiveAdapter, +): InactiveAdapter => (behavior: InactiveBehavior) => ( + contexter: WeakFilter, + ellipser: WeakFilter, +) => (tag: TagNode, internals: Internals): WeakFilterResult => { + if (!Object.prototype.hasOwnProperty.call(internals.preset, "cardNumber")) { + return adapter(behavior)(contexter, ellipser)(tag, internals); + } + + const [showBottom, showTop, hideBottom, hideTop] = inactiveAdapterGetRange( + tag, + internals, + ); + + const cardNumber = internals.preset.cardNumber; + + if (!cardNumber || !tag.num) { + return behavior(contexter, ellipser)(tag, internals); + } + + if (cardNumber - showBottom <= tag.num && tag.num <= cardNumber + showTop) { + return contexter(tag, internals); + } + + if (cardNumber - hideBottom <= tag.num && tag.num <= cardNumber + hideTop) { + return ellipser(tag, internals); + } + + return adapter(behavior)(contexter, ellipser)(tag, internals); +}; + +export const inactiveAdapterGetRange = ( + tag: TagNode, + { cache, preset }: Internals, +): [number, number, number, number] => { + const showBottomKeyword = "flashcardShowBottom"; + const showTopKeyword = "flashcardShowTop"; + + if (!preset.cardNumber) { + return [0, 0, 0, 0]; + } + + const showBottom = cache + .get>(showBottomKeyword, constantZero) + .get(tag.key, preset.cardNumber, tag.fullOccur); + + const showTop = cache + .get>(showTopKeyword, constantZero) + .get(tag.key, preset.cardNumber, tag.fullOccur); + + const hideBottomKeyword = "flashcardHideBottom"; + const hideTopKeyword = "flashcardHideTop"; + + const hideBottom = cache + .get>(hideBottomKeyword, constantZero) + .get(tag.key, preset.cardNumber, tag.fullOccur); + + const hideTop = cache + .get>(hideTopKeyword, constantZero) + .get(tag.key, preset.cardNumber, tag.fullOccur); + + return [showBottom, showTop, hideBottom, hideTop]; +}; + +export const inactiveAdapterAll = inactiveAdapterOverwritten( + inactiveAdapterWithinRange(inactiveAdapter), +); diff --git a/ts/src/flashcard/index.ts b/ts/src/flashcard/index.ts new file mode 100644 index 0000000..b1d5301 --- /dev/null +++ b/ts/src/flashcard/index.ts @@ -0,0 +1,31 @@ +import type { Recipe } from "../types"; +import type { FlashcardPreset } from "./flashcardTemplate"; + +export type { FlashcardPreset } from "./flashcardTemplate"; +export interface FlashcardRecipes { + show: Recipe; + hide: Recipe; + reveal: Recipe; +} + +export * as deciders from "./deciders"; +export { FlashcardBehavior as behaviors } from "./flashcardTemplate"; + +import { clozeRecipes as cloze } from "./cloze"; +import { multipleChoiceRecipes as multipleChoice } from "./multipleChoice"; +import { specRecipes as specification } from "./spec"; + +import { + mingleRecipes as mingle, + sortRecipes as sort, + jumbleRecipes as jumble, +} from "./shuffleQuestion"; + +export const recipes = { + cloze, + multipleChoice, + specification, + mingle, + sort, + jumble, +}; diff --git a/ts/src/flashcard/multipleChoice.ts b/ts/src/flashcard/multipleChoice.ts new file mode 100644 index 0000000..f164e1b --- /dev/null +++ b/ts/src/flashcard/multipleChoice.ts @@ -0,0 +1,151 @@ +import type { + TagNode, + Recipe, + Eval, + InactiveBehavior, + WeakFilter, +} from "../types"; +import type { SortInStrategy } from "../sortInStrategies"; +import type { StyleList } from "../styleList"; +import type { FlashcardPreset, FlashcardTemplate } from "./flashcardTemplate"; +import type { Optic } from "../template/optics"; + +import { separated, mapped } from "../template/optics"; + +import { constant } from "../utils"; +import { Stylizer } from "../stylizer"; +import { acrossTag } from "../sequencers"; +import { listStylize, listStylizeMaybe } from "../styleList"; +import { topUp } from "../sortInStrategies"; + +import { + makeFlashcardTemplate, + generateFlashcardRecipes, +} from "./flashcardTemplate"; + +type ValuePlusCategory = [string, number]; + +const ellipsis = constant( + '', +); + +const valuesWithIndex = (tag: TagNode): ValuePlusCategory[] => + tag.values + ? tag.values.flatMap((v: string[], i: number) => + v.map((w: string) => [w, i]), + ) + : []; + +const firstCategory = (tag: TagNode): string[] => tag.values[0]; + +const separator = ``; + +const inactive = Stylizer.make({ + processor: (s: string) => + `${s}`, + mapper: (s: string) => + `${s}`, + separator, +}); + +const active = (side: string) => (s: string) => + `${s}`; +const activeFront = Stylizer.make({ + processor: active("front"), + mapper: (s: string) => + `${s}`, + separator, +}); + +const activeBack = Stylizer.make({ + processor: active("back"), + mapper: (v: string, _i, t: number) => + `${v}`, + separator, +}); + +const multipleChoiceOptics = [ + separated({ + sep: "::", + }), + mapped(), + separated({ + sep: "||", + keepEmpty: false, + }), +]; + +const multipleChoicePublicApi = ( + frontInactive: InactiveBehavior, + backInactive: InactiveBehavior, +): Recipe => ( + options: { + tagname?: string; + + frontStylizer?: Stylizer; + backStylizer?: Stylizer; + + inactiveStylizer?: Stylizer; + contexter?: Eval; + ellipser?: WeakFilter; + + getValues?: Eval; + sequence?: ( + getValues: Eval, + sortIn: SortInStrategy, + ) => Eval; + + sortInStrategy?: SortInStrategy; + optics?: Optic[]; + + flashcardTemplate?: FlashcardTemplate; + } = {}, +) => { + const { + tagname = "mc", + + frontStylizer = activeFront, + backStylizer = activeBack, + + inactiveStylizer = inactive, + contexter = firstCategory, + ellipser = ellipsis, + + getValues = valuesWithIndex, + + sequence = acrossTag, + sortInStrategy = topUp, + + optics = multipleChoiceOptics, + + flashcardTemplate = makeFlashcardTemplate(), + } = options; + + const shuffler: Eval = sequence( + getValues as any, + sortInStrategy, + ) as Eval; + + const front = listStylizeMaybe(frontStylizer, shuffler); + const back = listStylizeMaybe(backStylizer, shuffler); + + const multipleChoiceRecipe = flashcardTemplate(frontInactive, backInactive); + + const trueContexter = listStylize(inactiveStylizer, contexter); + const dataOptions = { optics }; + + return multipleChoiceRecipe( + tagname, + front, + back, + trueContexter, + ellipser, + dataOptions, + ); +}; + +export const multipleChoiceRecipes = generateFlashcardRecipes( + multipleChoicePublicApi, +); diff --git a/ts/src/flashcard/shuffleQuestion.ts b/ts/src/flashcard/shuffleQuestion.ts new file mode 100644 index 0000000..314d5cc --- /dev/null +++ b/ts/src/flashcard/shuffleQuestion.ts @@ -0,0 +1,146 @@ +import type { + Un, + TagNode, + Internals, + Eval, + Recipe, + InactiveBehavior, + WeakFilter, +} from "../types"; +import type { SortInStrategy } from "../sortInStrategies"; +import type { StyleList } from "../styleList"; +import type { FlashcardTemplate, FlashcardPreset } from "./flashcardTemplate"; +import type { Optic } from "../template/optics"; + +import { listStylize, listStylizeMaybe } from "../styleList"; +import { + makeFlashcardTemplate, + generateFlashcardRecipes, +} from "./flashcardTemplate"; + +import { Stylizer } from "../stylizer"; +import { acrossTag } from "../sequencers"; +import { topUp } from "../sortInStrategies"; +import { separated } from "../template/optics"; + +const justValues = (tag: TagNode, _internals: Internals) => + tag.values; + +const mapper = (name: string) => (s: string) => + `${s}`; +const separator = (name: string) => + ``; + +const ellipsis = (name: string) => () => + ``; + +const inactive = (name: string): Stylizer => + Stylizer.make({ + processor: (s: string) => + `${s}`, + mapper: mapper(name), + separator: separator(name), + }); + +const active = (name: string): Stylizer => + Stylizer.make({ + processor: (s: string) => + `${s}`, + mapper: mapper(name), + separator: separator(name), + }); + +const valuesInOrder = (tag: TagNode): StyleList => + tag.values ? tag.values : []; + +const simplyShow = ( + stylizer: Stylizer, + _shuffler: Eval, +) => listStylize(stylizer, justValues); + +const clozeOptics = [separated({ sep: "::" })]; + +const oneSidedShufflePublicApi = < + T extends FlashcardPreset, + V extends StyleList +>( + internalName: string, + frontActive: ( + stylizer: Stylizer, + shuffler: Eval, + ) => WeakFilter, + backActive: ( + stylizer: Stylizer, + shuffler: Eval, + ) => WeakFilter, +) => ( + frontInactive: InactiveBehavior, + backInactive: InactiveBehavior, +): Recipe => ( + options: { + tagname?: string; + + activeStylizer?: Stylizer; + inactiveStylizer?: Stylizer; + + contexter?: Eval; + ellipser?: WeakFilter; + + sequence?: ( + getValues: Eval, + sortIn: SortInStrategy, + ) => Eval; + + sortInStrategy?: SortInStrategy; + optics?: Optic[]; + flashcardTemplate?: FlashcardTemplate; + } = {}, +) => { + const { + tagname = "shuf", + + activeStylizer = active(internalName), + inactiveStylizer = inactive(internalName), + + contexter = valuesInOrder, + ellipser = ellipsis(internalName), + + sequence = acrossTag, + sortInStrategy = topUp, + + optics = clozeOptics, + flashcardTemplate = makeFlashcardTemplate(), + } = options; + + const clozeOptions = { optics }; + + const shuffler: Eval = sequence( + contexter as any, + sortInStrategy, + ) as Eval; + + const front = frontActive(activeStylizer, shuffler); + const back = backActive(activeStylizer, shuffler); + + const trueContexter = listStylize(inactiveStylizer, contexter); + + const clozeRecipe = flashcardTemplate(frontInactive, backInactive); + return clozeRecipe( + tagname, + front, + back, + trueContexter, + ellipser, + clozeOptions, + ); +}; + +export const mingleRecipes = generateFlashcardRecipes( + oneSidedShufflePublicApi("mingle", listStylizeMaybe, listStylizeMaybe), +); +export const sortRecipes = generateFlashcardRecipes( + oneSidedShufflePublicApi("sort", listStylizeMaybe, simplyShow), +); +export const jumbleRecipes = generateFlashcardRecipes( + oneSidedShufflePublicApi("jumble", simplyShow, listStylizeMaybe), +); diff --git a/ts/src/flashcard/spec.ts b/ts/src/flashcard/spec.ts new file mode 100644 index 0000000..008d4fb --- /dev/null +++ b/ts/src/flashcard/spec.ts @@ -0,0 +1,32 @@ +import type { TagNode, Recipe, InactiveBehavior } from "../types"; +import type { FlashcardTemplate, FlashcardPreset } from "./flashcardTemplate"; + +import { + makeFlashcardTemplate, + generateFlashcardRecipes, +} from "./flashcardTemplate"; + +const insert = (tag: TagNode) => ({ result: tag.values, parse: true }); +const noinsert = () => ""; + +const specPublicApi = ( + frontInactive: InactiveBehavior, + backInactive: InactiveBehavior, +): Recipe => ( + options: { + tagname?: string; + flashcardTemplate?: FlashcardTemplate; + } = {}, +) => { + const { + tagname = "spec", + flashcardTemplate = makeFlashcardTemplate(), + } = options; + + const specOptions = { capture: true }; + const specRecipe = flashcardTemplate(frontInactive, backInactive); + + return specRecipe(tagname, insert, insert, insert, noinsert, specOptions); +}; + +export const specRecipes = generateFlashcardRecipes(specPublicApi); diff --git a/ts/src/generator.ts b/ts/src/generator.ts new file mode 100644 index 0000000..40dcd7b --- /dev/null +++ b/ts/src/generator.ts @@ -0,0 +1,49 @@ +export type NumberGenAlgorithm = () => number; +// type NumberGen = (min: number, max: number, extra: number, banDomain: string[], filter: boolean) => Generator + +const maxTries = 1_000; + +export const numberGenerator = function* ( + gen: NumberGenAlgorithm, + filter: boolean, + banDomain: number[] = [], + prematureStop: (banDomain: number[]) => boolean = () => false, +): Generator { + const filteredValues: number[] = []; + let tries = 0; + + while (!prematureStop(banDomain) && tries < maxTries) { + const randomValue = gen(); + + if ( + !banDomain.includes(randomValue) && + !filteredValues.includes(randomValue) + ) { + if (filter) { + filteredValues.push(randomValue); + } + + yield randomValue; + } + + tries++; + } + + return filteredValues; +}; + +export const intAlgorithm = ( + min: number, + max: number, +): NumberGenAlgorithm => () => min + Math.floor(Math.random() * (max - min)); +export const intOutput = (value: number, extra: number) => + String(value * extra); +export const intStop = (min: number, max: number) => (banDomain: number[]) => + banDomain.length >= max - min; + +export const realAlgorithm = ( + min: number, + max: number, +): NumberGenAlgorithm => () => min + Math.random() * (max - min); +export const realOutput = (value: number, extra: number) => + value.toFixed(extra); diff --git a/ts/src/index.ts b/ts/src/index.ts new file mode 100644 index 0000000..b580142 --- /dev/null +++ b/ts/src/index.ts @@ -0,0 +1,12 @@ +export * from "./filterManager"; +export * as template from "./template"; + +export * as recipes from "./recipes"; +export * as flashcard from "./flashcard"; +export * as browser from "./browser"; + +export { Stylizer } from "./stylizer"; +export * as wrappers from "./wrappers"; +export * as sequencers from "./sequencers"; +export * as sortInStrategies from "./sortInStrategies"; +export * as patterns from "./patterns"; diff --git a/ts/src/patterns.ts b/ts/src/patterns.ts new file mode 100644 index 0000000..8f3689e --- /dev/null +++ b/ts/src/patterns.ts @@ -0,0 +1,37 @@ +export { keyPattern as key } from "./template/parser/tokenizer"; +export { keySeparationPattern as keySeparation } from "./template/parser/tagBuilder"; + +// from http://xregexp.com/v/3.2.0/xregexp-all.js +/* The MIT License + +Copyright (c) 2007-present Steven Levithan + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. */ +const unicodeLetters = + "A-Za-z\\xAA\\xB5\\xBA\\xC0-\\xD6\\xD8-\\xF6\\xF8-\\u02C1\\u02C6-\\u02D1\\u02E0-\\u02E4\\u02EC\\u02EE\\u0370-\\u0374\\u0376\\u0377\\u037A-\\u037D\\u037F\\u0386\\u0388-\\u038A\\u038C\\u038E-\\u03A1\\u03A3-\\u03F5\\u03F7-\\u0481\\u048A-\\u052F\\u0531-\\u0556\\u0559\\u0561-\\u0587\\u05D0-\\u05EA\\u05F0-\\u05F2\\u0620-\\u064A\\u066E\\u066F\\u0671-\\u06D3\\u06D5\\u06E5\\u06E6\\u06EE\\u06EF\\u06FA-\\u06FC\\u06FF\\u0710\\u0712-\\u072F\\u074D-\\u07A5\\u07B1\\u07CA-\\u07EA\\u07F4\\u07F5\\u07FA\\u0800-\\u0815\\u081A\\u0824\\u0828\\u0840-\\u0858\\u08A0-\\u08B4\\u08B6-\\u08BD\\u0904-\\u0939\\u093D\\u0950\\u0958-\\u0961\\u0971-\\u0980\\u0985-\\u098C\\u098F\\u0990\\u0993-\\u09A8\\u09AA-\\u09B0\\u09B2\\u09B6-\\u09B9\\u09BD\\u09CE\\u09DC\\u09DD\\u09DF-\\u09E1\\u09F0\\u09F1\\u0A05-\\u0A0A\\u0A0F\\u0A10\\u0A13-\\u0A28\\u0A2A-\\u0A30\\u0A32\\u0A33\\u0A35\\u0A36\\u0A38\\u0A39\\u0A59-\\u0A5C\\u0A5E\\u0A72-\\u0A74\\u0A85-\\u0A8D\\u0A8F-\\u0A91\\u0A93-\\u0AA8\\u0AAA-\\u0AB0\\u0AB2\\u0AB3\\u0AB5-\\u0AB9\\u0ABD\\u0AD0\\u0AE0\\u0AE1\\u0AF9\\u0B05-\\u0B0C\\u0B0F\\u0B10\\u0B13-\\u0B28\\u0B2A-\\u0B30\\u0B32\\u0B33\\u0B35-\\u0B39\\u0B3D\\u0B5C\\u0B5D\\u0B5F-\\u0B61\\u0B71\\u0B83\\u0B85-\\u0B8A\\u0B8E-\\u0B90\\u0B92-\\u0B95\\u0B99\\u0B9A\\u0B9C\\u0B9E\\u0B9F\\u0BA3\\u0BA4\\u0BA8-\\u0BAA\\u0BAE-\\u0BB9\\u0BD0\\u0C05-\\u0C0C\\u0C0E-\\u0C10\\u0C12-\\u0C28\\u0C2A-\\u0C39\\u0C3D\\u0C58-\\u0C5A\\u0C60\\u0C61\\u0C80\\u0C85-\\u0C8C\\u0C8E-\\u0C90\\u0C92-\\u0CA8\\u0CAA-\\u0CB3\\u0CB5-\\u0CB9\\u0CBD\\u0CDE\\u0CE0\\u0CE1\\u0CF1\\u0CF2\\u0D05-\\u0D0C\\u0D0E-\\u0D10\\u0D12-\\u0D3A\\u0D3D\\u0D4E\\u0D54-\\u0D56\\u0D5F-\\u0D61\\u0D7A-\\u0D7F\\u0D85-\\u0D96\\u0D9A-\\u0DB1\\u0DB3-\\u0DBB\\u0DBD\\u0DC0-\\u0DC6\\u0E01-\\u0E30\\u0E32\\u0E33\\u0E40-\\u0E46\\u0E81\\u0E82\\u0E84\\u0E87\\u0E88\\u0E8A\\u0E8D\\u0E94-\\u0E97\\u0E99-\\u0E9F\\u0EA1-\\u0EA3\\u0EA5\\u0EA7\\u0EAA\\u0EAB\\u0EAD-\\u0EB0\\u0EB2\\u0EB3\\u0EBD\\u0EC0-\\u0EC4\\u0EC6\\u0EDC-\\u0EDF\\u0F00\\u0F40-\\u0F47\\u0F49-\\u0F6C\\u0F88-\\u0F8C\\u1000-\\u102A\\u103F\\u1050-\\u1055\\u105A-\\u105D\\u1061\\u1065\\u1066\\u106E-\\u1070\\u1075-\\u1081\\u108E\\u10A0-\\u10C5\\u10C7\\u10CD\\u10D0-\\u10FA\\u10FC-\\u1248\\u124A-\\u124D\\u1250-\\u1256\\u1258\\u125A-\\u125D\\u1260-\\u1288\\u128A-\\u128D\\u1290-\\u12B0\\u12B2-\\u12B5\\u12B8-\\u12BE\\u12C0\\u12C2-\\u12C5\\u12C8-\\u12D6\\u12D8-\\u1310\\u1312-\\u1315\\u1318-\\u135A\\u1380-\\u138F\\u13A0-\\u13F5\\u13F8-\\u13FD\\u1401-\\u166C\\u166F-\\u167F\\u1681-\\u169A\\u16A0-\\u16EA\\u16F1-\\u16F8\\u1700-\\u170C\\u170E-\\u1711\\u1720-\\u1731\\u1740-\\u1751\\u1760-\\u176C\\u176E-\\u1770\\u1780-\\u17B3\\u17D7\\u17DC\\u1820-\\u1877\\u1880-\\u1884\\u1887-\\u18A8\\u18AA\\u18B0-\\u18F5\\u1900-\\u191E\\u1950-\\u196D\\u1970-\\u1974\\u1980-\\u19AB\\u19B0-\\u19C9\\u1A00-\\u1A16\\u1A20-\\u1A54\\u1AA7\\u1B05-\\u1B33\\u1B45-\\u1B4B\\u1B83-\\u1BA0\\u1BAE\\u1BAF\\u1BBA-\\u1BE5\\u1C00-\\u1C23\\u1C4D-\\u1C4F\\u1C5A-\\u1C7D\\u1C80-\\u1C88\\u1CE9-\\u1CEC\\u1CEE-\\u1CF1\\u1CF5\\u1CF6\\u1D00-\\u1DBF\\u1E00-\\u1F15\\u1F18-\\u1F1D\\u1F20-\\u1F45\\u1F48-\\u1F4D\\u1F50-\\u1F57\\u1F59\\u1F5B\\u1F5D\\u1F5F-\\u1F7D\\u1F80-\\u1FB4\\u1FB6-\\u1FBC\\u1FBE\\u1FC2-\\u1FC4\\u1FC6-\\u1FCC\\u1FD0-\\u1FD3\\u1FD6-\\u1FDB\\u1FE0-\\u1FEC\\u1FF2-\\u1FF4\\u1FF6-\\u1FFC\\u2071\\u207F\\u2090-\\u209C\\u2102\\u2107\\u210A-\\u2113\\u2115\\u2119-\\u211D\\u2124\\u2126\\u2128\\u212A-\\u212D\\u212F-\\u2139\\u213C-\\u213F\\u2145-\\u2149\\u214E\\u2183\\u2184\\u2C00-\\u2C2E\\u2C30-\\u2C5E\\u2C60-\\u2CE4\\u2CEB-\\u2CEE\\u2CF2\\u2CF3\\u2D00-\\u2D25\\u2D27\\u2D2D\\u2D30-\\u2D67\\u2D6F\\u2D80-\\u2D96\\u2DA0-\\u2DA6\\u2DA8-\\u2DAE\\u2DB0-\\u2DB6\\u2DB8-\\u2DBE\\u2DC0-\\u2DC6\\u2DC8-\\u2DCE\\u2DD0-\\u2DD6\\u2DD8-\\u2DDE\\u2E2F\\u3005\\u3006\\u3031-\\u3035\\u303B\\u303C\\u3041-\\u3096\\u309D-\\u309F\\u30A1-\\u30FA\\u30FC-\\u30FF\\u3105-\\u312D\\u3131-\\u318E\\u31A0-\\u31BA\\u31F0-\\u31FF\\u3400-\\u4DB5\\u4E00-\\u9FD5\\uA000-\\uA48C\\uA4D0-\\uA4FD\\uA500-\\uA60C\\uA610-\\uA61F\\uA62A\\uA62B\\uA640-\\uA66E\\uA67F-\\uA69D\\uA6A0-\\uA6E5\\uA717-\\uA71F\\uA722-\\uA788\\uA78B-\\uA7AE\\uA7B0-\\uA7B7\\uA7F7-\\uA801\\uA803-\\uA805\\uA807-\\uA80A\\uA80C-\\uA822\\uA840-\\uA873\\uA882-\\uA8B3\\uA8F2-\\uA8F7\\uA8FB\\uA8FD\\uA90A-\\uA925\\uA930-\\uA946\\uA960-\\uA97C\\uA984-\\uA9B2\\uA9CF\\uA9E0-\\uA9E4\\uA9E6-\\uA9EF\\uA9FA-\\uA9FE\\uAA00-\\uAA28\\uAA40-\\uAA42\\uAA44-\\uAA4B\\uAA60-\\uAA76\\uAA7A\\uAA7E-\\uAAAF\\uAAB1\\uAAB5\\uAAB6\\uAAB9-\\uAABD\\uAAC0\\uAAC2\\uAADB-\\uAADD\\uAAE0-\\uAAEA\\uAAF2-\\uAAF4\\uAB01-\\uAB06\\uAB09-\\uAB0E\\uAB11-\\uAB16\\uAB20-\\uAB26\\uAB28-\\uAB2E\\uAB30-\\uAB5A\\uAB5C-\\uAB65\\uAB70-\\uABE2\\uAC00-\\uD7A3\\uD7B0-\\uD7C6\\uD7CB-\\uD7FB\\uF900-\\uFA6D\\uFA70-\\uFAD9\\uFB00-\\uFB06\\uFB13-\\uFB17\\uFB1D\\uFB1F-\\uFB28\\uFB2A-\\uFB36\\uFB38-\\uFB3C\\uFB3E\\uFB40\\uFB41\\uFB43\\uFB44\\uFB46-\\uFBB1\\uFBD3-\\uFD3D\\uFD50-\\uFD8F\\uFD92-\\uFDC7\\uFDF0-\\uFDFB\\uFE70-\\uFE74\\uFE76-\\uFEFC\\uFF21-\\uFF3A\\uFF41-\\uFF5A\\uFF66-\\uFFBE\\uFFC2-\\uFFC7\\uFFCA-\\uFFCF\\uFFD2-\\uFFD7\\uFFDA-\\uFFDC"; +const unicodeNumbers = + "0-9\\xB2\\xB3\\xB9\\xBC-\\xBE\\u0660-\\u0669\\u06F0-\\u06F9\\u07C0-\\u07C9\\u0966-\\u096F\\u09E6-\\u09EF\\u09F4-\\u09F9\\u0A66-\\u0A6F\\u0AE6-\\u0AEF\\u0B66-\\u0B6F\\u0B72-\\u0B77\\u0BE6-\\u0BF2\\u0C66-\\u0C6F\\u0C78-\\u0C7E\\u0CE6-\\u0CEF\\u0D58-\\u0D5E\\u0D66-\\u0D78\\u0DE6-\\u0DEF\\u0E50-\\u0E59\\u0ED0-\\u0ED9\\u0F20-\\u0F33\\u1040-\\u1049\\u1090-\\u1099\\u1369-\\u137C\\u16EE-\\u16F0\\u17E0-\\u17E9\\u17F0-\\u17F9\\u1810-\\u1819\\u1946-\\u194F\\u19D0-\\u19DA\\u1A80-\\u1A89\\u1A90-\\u1A99\\u1B50-\\u1B59\\u1BB0-\\u1BB9\\u1C40-\\u1C49\\u1C50-\\u1C59\\u2070\\u2074-\\u2079\\u2080-\\u2089\\u2150-\\u2182\\u2185-\\u2189\\u2460-\\u249B\\u24EA-\\u24FF\\u2776-\\u2793\\u2CFD\\u3007\\u3021-\\u3029\\u3038-\\u303A\\u3192-\\u3195\\u3220-\\u3229\\u3248-\\u324F\\u3251-\\u325F\\u3280-\\u3289\\u32B1-\\u32BF\\uA620-\\uA629\\uA6E6-\\uA6EF\\uA830-\\uA835\\uA8D0-\\uA8D9\\uA900-\\uA909\\uA9D0-\\uA9D9\\uA9F0-\\uA9F9\\uAA50-\\uAA59\\uABF0-\\uABF9\\uFF10-\\uFF19"; +// end of notice + +export const unicodeLetterPattern = new RegExp(`[${unicodeLetters}]`, "gu"); +export const unicodeNumberPattern = new RegExp(`[${unicodeNumbers}]`, "gu"); +export const unicodeAlphanumericPattern = new RegExp( + `[${unicodeLetters}${unicodeNumbers}]`, + "gu", +); diff --git a/ts/src/recipes/debug.ts b/ts/src/recipes/debug.ts new file mode 100644 index 0000000..6994e2d --- /dev/null +++ b/ts/src/recipes/debug.ts @@ -0,0 +1,38 @@ +import type { Registrar, TagNode, Internals } from "../types"; + +export const debugRecipe = () => >( + registrar: Registrar, +) => { + const pathFilter = (_t: TagNode, { path }: Internals) => path.join(":"); + + registrar.register("tagpath", pathFilter); + registrar.register("never", () => { + /* nothing */ + }); + registrar.register("empty", () => ""); + registrar.register("key", ({ key }: TagNode) => key); + + registrar.register( + "stopIteration", + ({ values }: TagNode, { filters }: Internals) => { + const endAtIteration = Number(values); + const savedBase = filters.getOrDefault("base"); + + filters.register( + "base", + (tag: TagNode, internals: Internals) => { + return internals.iteration >= endAtIteration + ? tag.text ?? "" + : savedBase(tag, internals); + }, + ); + + return { ready: true }; + }, + ); + + registrar.register("memorytest", (_tag, { memory }: Internals) => { + const memoryTestKey = "base:memorytest"; + return String(memory.fold(memoryTestKey, (v: number) => ++v, 0)); + }); +}; diff --git a/ts/src/recipes/delim.ts b/ts/src/recipes/delim.ts new file mode 100644 index 0000000..5b08afd --- /dev/null +++ b/ts/src/recipes/delim.ts @@ -0,0 +1,41 @@ +import type { Registrar, TagNode, Internals, WeakFilterResult } from "../types"; + +import { separated } from "../template/optics"; + +const delimOptions = { + inlineOptics: [separated({ sep: "::", max: 2 })], + capture: true, +}; + +export const delimRecipe = ( + options: { + tagname?: string; + } = {}, +) => >(registrar: Registrar) => { + const { tagname = "delim" } = options; + + const delimFilter = ( + tag: TagNode, + { template, isCapture }: Internals, + ): WeakFilterResult => { + if (isCapture) { + const [open, close] = tag.inlineValues; + + template.parser.update({ open, close }); + + return { + result: tag.blockText, + parse: true, + }; + } + + // Reset delimiters + template.parser.update(); + + return { + result: tag.innerNodes, + }; + }; + + registrar.register(tagname, delimFilter, delimOptions); +}; diff --git a/ts/src/recipes/generating.ts b/ts/src/recipes/generating.ts new file mode 100644 index 0000000..8607445 --- /dev/null +++ b/ts/src/recipes/generating.ts @@ -0,0 +1,63 @@ +import type { Registrar, TagNode, Internals } from "../types"; +import type { NumberGenAlgorithm } from "../generator"; + +import { + numberGenerator, + intAlgorithm, + intOutput, + realAlgorithm, + realOutput, +} from "../generator"; + +import { separated } from "../template/optics"; + +const generateOptics = [separated({ sep: "," })]; + +const generateTemplate = ( + algorithm: (min: number, max: number) => NumberGenAlgorithm, + outputAlgorithm: (value: number, extra: number) => string, + defaultExtra: number, +) => ({ + tagname = "gen", + uniqueConstraintId = "uniq", + optics = generateOptics, +} = {}) => >(registrar: Registrar) => { + const uniqConstraintPrefix = `gen:${uniqueConstraintId}`; + + const generateFilter = ( + { values, fullOccur, num }: TagNode, + { memory }: Internals, + ) => { + const [min = 1, max = 100, extra = defaultExtra] = + values.length === 1 + ? [1, Number(values[0]), defaultExtra] + : values.map(Number); + + const uniqueConstraintId = Number.isInteger(num) + ? `${uniqConstraintPrefix}:${num}` + : uniqConstraintPrefix; + + const generateId = `gen:${tagname}:${fullOccur}`; + + const result = memory.lazy(generateId, (): string => { + const gen = numberGenerator( + algorithm(min, max), + true, + Number.isInteger(num) ? memory.get(uniqueConstraintId, []) : [], + ); + + const generated = gen.next(); + + return generated.done + ? "" + : outputAlgorithm(generated.value as number, extra); + }); + + return { ready: true, result: result }; + }; + + registrar.register(tagname, generateFilter, { optics }); +}; + +export const generateInteger = generateTemplate(intAlgorithm, intOutput, 1); +export const generateReal = generateTemplate(realAlgorithm, realOutput, 2); diff --git a/ts/src/recipes/index.ts b/ts/src/recipes/index.ts new file mode 100644 index 0000000..707357d --- /dev/null +++ b/ts/src/recipes/index.ts @@ -0,0 +1,10 @@ +export * from "./preferenceStore"; +export * from "./sharedStore"; + +export { applyRecipe as apply, processRecipe as process, styleRecipe as style } from "./simple"; +export { shufflingRecipe as shuffle } from "./shuffling"; +export { orderingRecipe as order } from "./ordering"; +export { generateInteger, generateReal } from "./generating"; +export { debugRecipe as debug } from "./debug"; +export { defRecipe as define } from "./meta"; +export { delimRecipe as delimiter } from "./delim"; diff --git a/ts/src/recipes/meta.ts b/ts/src/recipes/meta.ts new file mode 100644 index 0000000..64e061f --- /dev/null +++ b/ts/src/recipes/meta.ts @@ -0,0 +1,62 @@ +import type { Registrar, TagNode, WeakFilterResult } from "../types"; + +import { separated } from "../template/optics"; + +const paramPattern = /%(.)/gu; +const defOptions = { + optics: [separated({ sep: "::", max: 2 })], + capture: true, +}; + +const matcher = (tag: TagNode) => (match: string, p1: string) => { + let num = null; + + switch (p1) { + case "%": + return p1; + + case "n": + return typeof tag.num === "number" ? String(tag.num) : ""; + + case "k": + return tag.key; + case "f": + return tag.fullKey; + + default: + num = Number(p1); + + return Number.isNaN(num) ? match : tag.values[num] ?? ""; + } +}; + +export const defRecipe = ( + options: { + tagname?: string; + } = {}, +) => >(registrar: Registrar) => { + const { tagname = "def" } = options; + + const innerOptions = { optics: [separated({ sep: "::" })] }; + + const defFilter = (tag: TagNode): WeakFilterResult => { + const [definedTag, template] = tag.values; + + const innerFilter = (tag: TagNode) => { + const result = template.replace(paramPattern, matcher(tag)); + + return { + result: result, + parse: true, + }; + }; + + registrar.register(definedTag, innerFilter, innerOptions); + + return { + ready: true, + }; + }; + + registrar.register(tagname, defFilter, defOptions); +}; diff --git a/ts/src/recipes/ordering.ts b/ts/src/recipes/ordering.ts new file mode 100644 index 0000000..29473b9 --- /dev/null +++ b/ts/src/recipes/ordering.ts @@ -0,0 +1,114 @@ +import type { TagNode, Registrar, Internals } from "../types"; + +import { sortWithIndices } from "../utils"; +import { topUp } from "../sortInStrategies"; +import { TagSelector } from "../template"; + +import { separated, mapped } from "../template/optics"; + +const adjustOptions = { + priority: 35 /* must be higher than sequencers 15 */, + persistent: true, +}; + +const orderingOptics = [separated("::"), mapped(), separated(",")]; + +export const orderingRecipe = ({ + tagname = "ord", + optics = orderingOptics, + sortInStrategy = topUp, +} = {}) => >(registrar: Registrar) => { + const ordFilter = ( + tag: TagNode, + { deferred, cache, memory }: Internals, + ) => { + /** + * @param tag.values must contain lists of *tag selectors* for sequence ids + */ + + const sequencesKey = "sequences"; + const sequences = new Set(cache.get(sequencesKey, [])); + + for (const [idx, cmd] of tag.values.entries()) { + const selectors = cmd.map(TagSelector.make); + const ordKey = `${tag.key}:${tag.fullOccur}:ord:${idx}`; + + const adjustOrder = () => { + const remainingSequences = Array.from(sequences); + const activeSequences = remainingSequences.filter( + (sequenceId: string) => { + if ( + selectors.some((selector: TagSelector) => + selector.checkTagIdentifier(sequenceId), + ) + ) { + sequences.delete(sequenceId); + return true; + } + + return false; + }, + ); + + const activeShuffleKeys = activeSequences.map( + (sequenceId: string) => `${sequenceId}:shuffle`, + ); + + for (const [index, sequenceId] of activeSequences.entries()) { + const shuffleKey = activeShuffleKeys[index]; + const waitingSetKey = `${sequenceId}:waitingSet`; + + deferred.block(shuffleKey); + + const waitingSet = cache.get>( + waitingSetKey, + new Set(), + ); + + if (waitingSet.size !== 0) { + continue; + } + + const presetShuffle = activeShuffleKeys.reduce( + (accu: number[], sk: string) => { + const nextShuffleOrder = memory.get(sk, []); + return accu.length < nextShuffleOrder.length + ? nextShuffleOrder + : accu; + }, + [], + ); + + const shuffleItems = cache.get(shuffleKey, []); + + const toppedUpIndices = sortInStrategy( + cache.get(ordKey, presetShuffle), + shuffleItems.length, + ); + + // order mix items + cache.fold( + shuffleKey, + (vs: string[]) => sortWithIndices(vs, toppedUpIndices), + [], + ); + + // possibly update with longer sort order + cache.set(ordKey, toppedUpIndices); + memory.set(shuffleKey, toppedUpIndices); + } + + // need to remove ord deferred because it is persistent + if (sequences.size === 0) { + deferred.unregister(ordKey); + } + }; + + deferred.register(ordKey, adjustOrder, adjustOptions); + } + + return { ready: true }; + }; + + registrar.register(tagname, ordFilter, { optics }); +}; diff --git a/ts/src/recipes/preferenceStore/boolStore.ts b/ts/src/recipes/preferenceStore/boolStore.ts new file mode 100644 index 0000000..7e0ba2a --- /dev/null +++ b/ts/src/recipes/preferenceStore/boolStore.ts @@ -0,0 +1,52 @@ +import type { Registrar } from "../../types"; + +import { + PreferenceStore, + storeTemplate, + defaultSeparated, + innerSeparated, +} from "./storeTemplate"; + +import { mapped } from "../../template/optics"; + +class BoolStore extends PreferenceStore { + on(selector: string): void { + this.set(selector, true); + } + + off(selector: string): void { + this.set(selector, false); + } +} + +const boolStoreFilterTemplate = storeTemplate(BoolStore); + +export const activateRecipe = ({ + tagname = "on", + storeId = "active", + optic = defaultSeparated, +} = {}) => (registrar: Registrar>) => + registrar.register( + tagname, + boolStoreFilterTemplate(storeId, false, (selector) => (activateMap) => { + activateMap.on(selector); + }), + { + separators: [optic, mapped(), innerSeparated], + }, + ); + +export const deactivateRecipe = >({ + tagname = "off", + storeId = "active", + optic = defaultSeparated, +} = {}) => (registrar: Registrar) => + registrar.register( + tagname, + boolStoreFilterTemplate(storeId, false, (selector) => (activateMap) => + activateMap.off(selector), + ), + { + separators: [optic, mapped(), innerSeparated], + }, + ); diff --git a/ts/src/recipes/preferenceStore/index.ts b/ts/src/recipes/preferenceStore/index.ts new file mode 100644 index 0000000..0a0aa9c --- /dev/null +++ b/ts/src/recipes/preferenceStore/index.ts @@ -0,0 +1,11 @@ +export type StoreGetter = { + get: (key: string, num: number | null | undefined, occur: number) => T; +}; + +export const constantGet = (v: T): StoreGetter => ({ get: () => v }); + +export { setNumberRecipe as setNumber } from "./numberStore"; +export { + activateRecipe as activate, + deactivateRecipe as deactivate, +} from "./boolStore"; diff --git a/ts/src/recipes/preferenceStore/numberStore.ts b/ts/src/recipes/preferenceStore/numberStore.ts new file mode 100644 index 0000000..55541fe --- /dev/null +++ b/ts/src/recipes/preferenceStore/numberStore.ts @@ -0,0 +1,34 @@ +import type { Registrar } from "../../types"; + +import { + PreferenceStore, + storeTemplate, + defaultSeparated, + innerSeparated, +} from "./storeTemplate"; + +import { mapped } from "../../template/optics"; + +class NumberStore extends PreferenceStore { + setNumber(selector: string, value: number): void { + this.set(selector, Number.isNaN(value) ? 0 : value); + } +} + +const numStoreFilterTemplate = storeTemplate(NumberStore); + +export const setNumberRecipe = >({ + tagname = "set", + storeId = "numerical", + separator = defaultSeparated, + assignmentSeparator = innerSeparated, +} = {}) => (registrar: Registrar) => + registrar.register( + tagname, + numStoreFilterTemplate(storeId, 0, (selector, val) => (numberMap) => + numberMap.setNumber(selector, Number(val)), + ), + { + optics: [separator, mapped(), assignmentSeparator], + }, + ); diff --git a/ts/src/recipes/preferenceStore/storeTemplate.ts b/ts/src/recipes/preferenceStore/storeTemplate.ts new file mode 100644 index 0000000..97b5c5b --- /dev/null +++ b/ts/src/recipes/preferenceStore/storeTemplate.ts @@ -0,0 +1,60 @@ +import type { TagNode, Internals } from "../../types"; + +import { TagSelector } from "../../template/"; + +import { separated } from "../../template/optics"; + +export const defaultSeparated = separated({ sep: ";" }); +export const innerSeparated = separated({ sep: "=", trim: true, max: 2 }); + +export class PreferenceStore { + /** + * Values can be stored in a preference store + * Tags can inquire against value stores if they provide + * 1. the storeId, and + * 2. their identification, typically (key, num, fullOccur) + * + * Values cannot be updated, only overwritten + * You would save settings regarding a specific tag in here, + * not make a shared value, for that see `SharedStore` (TODO) + */ + + selectors: [TagSelector, T][]; + defaultValue: T; + + constructor(defaultValue: T) { + this.defaultValue = defaultValue; + this.selectors = []; + } + + protected set(selector: string, value: T): void { + this.selectors.unshift([TagSelector.make(selector), value]); + } + + get(key: string, num: number, fullOccur: number): T { + const found = this.selectors.find(([selector]) => + selector.check(key, num, fullOccur), + ); + + return found ? found[1] : this.defaultValue; + } +} + +export const storeTemplate = , U>( + Store: new (u: U) => Store, +) => ( + storeId: string, + defaultValue: U, + operation: (...vals: string[]) => (a: Store) => void, +) => >( + tag: TagNode, + { cache }: Internals, +) => { + const commands = tag.values; + + commands.forEach((cmd: string) => { + cache.over(storeId, operation(...cmd), new Store(defaultValue)); + }); + + return { ready: true }; +}; diff --git a/ts/src/recipes/sharedStore/index.ts b/ts/src/recipes/sharedStore/index.ts new file mode 100644 index 0000000..c179e7f --- /dev/null +++ b/ts/src/recipes/sharedStore/index.ts @@ -0,0 +1,7 @@ +export { setListRecipe as setList } from "./setList"; + +export { + pickRandomRecipe as pick, + pickIndexRecipe as pickIndex, + pickCardNumberRecipe as pickCardNumber, +} from "./pickers"; diff --git a/ts/src/recipes/sharedStore/listStore.ts b/ts/src/recipes/sharedStore/listStore.ts new file mode 100644 index 0000000..cd1dc46 --- /dev/null +++ b/ts/src/recipes/sharedStore/listStore.ts @@ -0,0 +1,23 @@ +import { SharedStore, storeTemplate } from "./storeTemplate"; + +export class ListStore extends SharedStore { + setList(storeKey: string, list: string[]): void { + this.set(storeKey, list); + } + + overwriteList(storeKey: string, newList: string[], fromIndex = 0): void { + const list = this.getList(storeKey); + + for (let i = 0; i < newList.length; i++) { + list[fromIndex + i] = newList[i]; + } + + this.setList(storeKey, list); + } + + getList(storeKey: string): string[] { + return this.get(storeKey, []); + } +} + +export const listStoreTemplate = storeTemplate(ListStore); diff --git a/ts/src/recipes/sharedStore/pickers.ts b/ts/src/recipes/sharedStore/pickers.ts new file mode 100644 index 0000000..cf83433 --- /dev/null +++ b/ts/src/recipes/sharedStore/pickers.ts @@ -0,0 +1,128 @@ +import type { Registrar, TagNode, Internals, Eval } from "../../types"; +import type { CardPreset } from "../../flashcard/flashcardTemplate"; +import type { ListStore } from "./listStore"; + +import { numberGenerator, intAlgorithm, intStop } from "../../generator"; + +import { listStoreTemplate } from "./listStore"; + +const defaultPostprocess = >( + value: string, + _valueList: string[], +): Eval => (_tag: TagNode, _internals: Internals): string => + value; + +const pickIndex = >( + picker: (valueList: string[], key: string) => Eval, + postprocess: (value: string, valueList: string[]) => Eval, +) => (tag: TagNode, internals: Internals) => ( + listStore: ListStore, +): string => { + const key = tag.values || /* in case it is '' */ "default"; + + const valueList = listStore.getList(key); + const value = picker(valueList, key)(tag, internals); + + return postprocess(value, valueList)(tag, internals as any); +}; + +const pickIndexTemplate = >( + picker: (list: string[], key: string) => Eval, +) => ({ + tagname = "pick", + storeId = "lists", + postprocess = defaultPostprocess, +} = {}) => (registrar: Registrar) => + registrar.register( + tagname, + listStoreTemplate( + storeId, + pickIndex(picker as any, postprocess) as any, + ), + ); + +const pickRandom = (list: string[], key: string) => < + T extends Record +>( + tag: TagNode, + { memory }: Internals, +) => { + const pickedKey = `pick:picked:${tag.fullKey}:${tag.occur}`; + + const index = memory.lazy(pickedKey, () => { + const algorithm = intAlgorithm(0, list.length); + const stopper = intStop(0, list.length); + + // tag.num is used to decide uniqList + const filter = Boolean(tag.num /* 0 is falsy */); + const filterKey = `pick:uniqList:${tag.fullKey}`; + + const uniqList: number[] = filter + ? memory.over( + filterKey, + ( + uniqHash: Record, + ): Record => { + if ( + !Object.prototype.hasOwnProperty.call(uniqHash, key) + ) { + uniqHash[key] = []; + } + + return uniqHash; + }, + {}, + )[key] + : []; + + const emptyIndices = Array.from(list).reduce( + ( + accu: number[], + value: string | undefined, + index: number, + ): number[] => { + if (value === undefined) { + accu.push(index); + } + + return accu; + }, + [], + ); + + const banList = uniqList.concat(emptyIndices); + + const generator = numberGenerator(algorithm, filter, banList, stopper); + + const picked = generator.next(); + + if (!picked.done) { + uniqList.push(picked.value); + return picked.value; + } + }); + + return Number.isInteger(index) ? list[index as number] : ""; +}; + +const pickNum = (list: string[], _key: string) => < + T extends Record +>( + tag: TagNode, + _internals: Internals, +): string => list[Number(tag.num) || 0]; + +const pickCardNumber = (list: string[], _key: string) => ( + _tag: TagNode, + internals: Internals, +): string => { + const indexedValue: string | undefined = + list[internals.preset.cardNumber as number]; + const result: string = indexedValue ?? list[0] ?? ""; + + return result; +}; + +export const pickRandomRecipe = pickIndexTemplate(pickRandom); +export const pickIndexRecipe = pickIndexTemplate(pickNum); +export const pickCardNumberRecipe = pickIndexTemplate(pickCardNumber); diff --git a/ts/src/recipes/sharedStore/setList.ts b/ts/src/recipes/sharedStore/setList.ts new file mode 100644 index 0000000..2b3d7fa --- /dev/null +++ b/ts/src/recipes/sharedStore/setList.ts @@ -0,0 +1,67 @@ +import type { Registrar, TagNode, Internals, Eval } from "../../types"; +import type { CardPreset } from "../../flashcard/flashcardTemplate"; +import type { ListStore } from "./listStore"; + +import { listStoreTemplate } from "./listStore"; +import { separated, mapped } from "../../template/optics"; + +const discard = >( + _value: string, + _valueList: string[], +): Eval => (_tag: TagNode, _internals: Internals): string => ""; + +const setList = >( + setter: ( + listStore: ListStore, + key: string, + values: string[], + ) => Eval, + postprocess: (value: string, valueList: string[]) => Eval, +) => (tag: TagNode, internals: Internals) => ( + listStore: ListStore, +): string => { + const [key, values] = + tag.values.length === 2 ? tag.values : [["default"], tag.values[0]]; + + const theKey = key[0]; + + const valueList = listStore.getList(theKey); + const value = setter(listStore, theKey, values)(tag, internals); + + return postprocess(value, valueList)(tag, internals as any); +}; + +const setListTemplate = >( + picker: ( + listStore: ListStore, + key: string, + values: string[], + ) => Eval, +) => ({ + tagname = "setl", + storeId = "lists", + postprocess = discard, + optics = [separated({ sep: "::" }), mapped(), separated({ sep: "||" })], +} = {}) => (registrar: Registrar) => + registrar.register( + tagname, + listStoreTemplate(storeId, setList(picker as any, postprocess) as any), + { optics }, + ); + +const setListTo = (listStore: ListStore, key: string, values: string[]) => < + T extends CardPreset +>( + tag: TagNode, + _internals: Internals, +): string => { + if (tag.num === null) { + listStore.setList(key, values); + } else { + listStore.overwriteList(key, values, tag.num); + } + + return ""; +}; + +export const setListRecipe = setListTemplate(setListTo); diff --git a/ts/src/recipes/sharedStore/storeTemplate.ts b/ts/src/recipes/sharedStore/storeTemplate.ts new file mode 100644 index 0000000..530f43a --- /dev/null +++ b/ts/src/recipes/sharedStore/storeTemplate.ts @@ -0,0 +1,36 @@ +import type { TagNode, Internals } from "../../types"; + +import { Storage } from "../../filterManager/storage"; + +export class SharedStore extends Storage { + /** + * Values in here are accessed with: + * 1. the storeId, and + * 2. a secondary storeKey + * + * Values can be updated + * You would save values here that are shared among tags in here, + * for configuring behavior of tags, see `PreferenceStore` + */ +} + +export const storeTemplate = , U>( + Store: new (map: Map) => Store, +) => ( + storeId: string, + operation: >( + tag: TagNode, + internals: Internals, + ) => (store: Store) => string | void, +) => >( + tag: TagNode, + internals: Internals, +) => { + const result = (internals.cache.over( + storeId, + operation(tag, internals), + new Store(new Map()), + ) ?? "") as string; + + return { ready: true, result: result }; +}; diff --git a/ts/src/recipes/shuffling.ts b/ts/src/recipes/shuffling.ts new file mode 100644 index 0000000..6ab132b --- /dev/null +++ b/ts/src/recipes/shuffling.ts @@ -0,0 +1,46 @@ +import type { TagNode, Registrar, Internals, Eval, Un } from "../types"; + +import { Stylizer } from "../stylizer"; +import { acrossNumberedTag } from "../sequencers"; +import { topUp } from "../sortInStrategies"; + +import { separated } from "../template/optics"; + +const defaultStylizer = Stylizer.make({ + processor: (s: string) => `${s}`, + mapper: (s: string) => `${s}`, + separator: '', +}); + +const defaultEval = ( + stylizer: Stylizer, + mixedValues: string[], +): Eval => (): string => stylizer.stylize(mixedValues); + +const defaultOptics = [separated({ sep: "||" })]; + +export const shufflingRecipe = ({ + tagname = "mix", + stylizer = defaultStylizer, + evaluate = defaultEval, + sortInStrategy = topUp, + sequencer = acrossNumberedTag, + optics = defaultOptics, +} = {}) => (registrar: Registrar) => { + const shuffleFilter = (tag: TagNode, internals: Internals) => { + const getValues: Eval = ({ values }: TagNode): string[] => + values ?? []; + + const shuffler: Eval = sequencer( + getValues as any, + sortInStrategy, + ) as Eval; + + const maybeValues = shuffler(tag, internals); + if (maybeValues) { + return evaluate(stylizer, maybeValues)(tag, internals as any); + } + }; + + registrar.register(tagname, shuffleFilter, { optics }); +}; diff --git a/ts/src/recipes/simple.ts b/ts/src/recipes/simple.ts new file mode 100644 index 0000000..e3a87fd --- /dev/null +++ b/ts/src/recipes/simple.ts @@ -0,0 +1,43 @@ +import type { TagNode, WeakFilter, Recipe, Registrar, Un } from "../types"; + +import { Stylizer } from "../stylizer"; +import { id } from "../utils"; +import { separated } from "../template/optics"; + + +// Used for internals, for externals applyRecipe should be preferred +export const simpleRecipe = ( + weakFilter: WeakFilter, +): Recipe => ({ + tagname = "s", +}: { + tagname?: string, +} = {}) => (registrar: Registrar) => { + registrar.register(tagname, weakFilter); +}; + +const applyToValues = (f: (v: V) => V): WeakFilter => (tag: TagNode): V => f(tag.values) + +export const applyRecipe = ({ + tagname = "s", + apply = applyToValues(id), + optics = [], +} = {}) => (registrar: Registrar) => { + registrar.register(tagname, apply, { optics }); +}; + +export const processRecipe = ({ + tagname = "s", + processor = id, + optics = [], +} = {}) => (registrar: Registrar) => { + registrar.register(tagname, applyToValues(processor), { optics }); +}; + +export const styleRecipe = ({ + tagname = "s", + stylizer = Stylizer.make(), + optics = [separated("::")], +} = {}) => (registrar: Registrar) => { + registrar.register(tagname, applyToValues(stylizer.stylize.bind(stylizer)), { optics }); +}; diff --git a/ts/src/sequencers.ts b/ts/src/sequencers.ts new file mode 100644 index 0000000..107a3b5 --- /dev/null +++ b/ts/src/sequencers.ts @@ -0,0 +1,210 @@ +import type { TagNode, Internals, Eval, Un } from "./types"; +import type { SortInStrategy } from "./sortInStrategies"; + +import { sortWithIndices } from "./utils"; + +const sequencer = , V>( + // identifies each unit (tag) receiving shuffled items + unitId: string, + // identifies each collection of items being shuffled + sequenceId: string, + strategy: (indices: number[], toLength: number) => number[], + getValues: Eval, +): Eval => ( + tag: TagNode, + internals: Internals, +): V[] | void => { + const applyKey = `${unitId}:apply`; + // in cache: boolean whether ready for application + // in deferred: sets apply key true, deletes unitId from waitingSet + + const lengthKey = `${unitId}:length`; + // in cache: number of values the unit contributed to the sequence + + const waitingSetKey = `${sequenceId}:waitingSet`; + // in cache: Set with all tags (contains unitId) who wait for their inner sets to be ready + + const shuffleKey = `${sequenceId}:shuffle`; + // in cache: holds all string values for one fullKey; will be empty after it's done + // in memory: hold sorting indices, which are used as basis for shuffling + // in deferred: tries to shuffle cache[shuffleKey], stops if waitingSet is not empty + + /////////// APPLY LOGIC + if (internals.cache.get(applyKey, false)) { + const waitingSet = internals.cache.get(waitingSetKey, new Set()) as Set< + string + >; + if (waitingSet.size > 0) { + // continue waiting for other tags with same fullKey + return; + } + + const popped: V[] = []; + + const possibleValues = internals.cache.get(shuffleKey, []) as V[]; + const valueLength = internals.cache.get(lengthKey, 0); + + for (let x = 0; x < valueLength; x++) { + // pop off start, so the result is the same as in program logic + const shiftedValue = possibleValues.shift(); + + if (shiftedValue) { + popped.push(shiftedValue); + } + } + + return popped; + } + + /////////// ADD TO SHUFFLE KEY LOGIC + if (!internals.ready) { + // add to waitingSet + internals.cache.over( + waitingSetKey, + (s: Set) => s.add(unitId), + new Set(), + ); + return; + } + + // each unit should only continue past this point once + const values = getValues(tag, internals); + + if (values.length === 0) { + // no need to shuffle an empty unit + return values; + } + + internals.cache.fold(shuffleKey, (v: V[]) => v.concat(values), []); + internals.cache.set(lengthKey, values.length); + + /////////// TRY TO SHUFFLE FROM DEFERRED LOGIC + // needs to be executed per individual tag, because of applyKey + internals.deferred.registerIfNotExists( + applyKey, + () => { + internals.cache.set(applyKey, true); + internals.cache.over( + waitingSetKey, + (set: Set) => set.delete(unitId), + new Set(), + ); + }, + { + priority: 65 /* is higher than ordering 35 */, + }, + ); + + // needs to be executed once per fullKey + internals.deferred.registerIfNotExists(shuffleKey, () => { + if ( + internals.cache.over( + waitingSetKey, + (waitingSet) => waitingSet.size > 0, + new Set(), + ) + ) { + return; + } + // will only go go beyong this point for the last set that becomes ready + // because shuffling only needs to be done once + + internals.cache.fold( + shuffleKey, + (vs: V[]) => { + const sortingIndices = internals.memory.fold( + shuffleKey, + (vs: number[]) => { + return strategy( + vs, + internals.cache.get(shuffleKey, []).length, + ); + }, + [], + ); + + return sortWithIndices(vs, sortingIndices); + }, + [], + ); + }); +}; + +const sequenceTemplate = >( + makeKeywords: Eval, +) => ( + getValues: Eval, + sortIn: SortInStrategy, +): Eval => ( + tag: TagNode, + internals: Internals, +): V[] | void => { + const sequencesKey = "sequences"; + // in cache: list of all sequences + + const [uid, sequenceId] = makeKeywords(tag, internals); + + internals.cache.over( + sequencesKey, + (sequences: string[]) => { + if (!sequences.includes(sequenceId)) { + sequences.push(sequenceId); + } + }, + [], + ); + + return sequencer(uid, sequenceId, sortIn, getValues)(tag, internals); +}; + +const within = ( + { fullKey, fullOccur }: TagNode, + _internals: Internals, +): [string, string] => [`${fullKey}:${fullOccur}`, `${fullKey}:${fullOccur}`]; + +const across = ( + { fullKey, fullOccur }: TagNode, + _internals: Internals, +): [string, string] => [`${fullKey}:${fullOccur}`, fullKey]; + +const acrossNumbered = ( + { fullKey, fullOccur, num }: TagNode, + _internals: Internals, +): [string, string] => [ + `${fullKey}:${fullOccur}`, + num ? fullKey : `${fullKey}:${fullOccur}`, +]; + +const customWithin = (custom: string) => ( + { fullKey, fullOccur, num }: TagNode, + _internals: Internals, +): [string, string] => [ + `${fullKey}:${fullOccur}`, + `${custom}${num}:${fullOccur}`, +]; + +const customAcross = (custom: string) => ( + { fullKey, fullOccur, num }: TagNode, + _internals: Internals, +): [string, string] => [`${fullKey}:${fullOccur}`, `${custom}${num}`]; + +const customAcrossNumbered = (custom: string) => ( + { fullKey, fullOccur, num }: TagNode, + _internals: Internals, +): [string, string] => { + const customKey = `${custom}${num}`; + return [ + `${fullKey}:${fullOccur}`, + num ? customKey : `${customKey}:${fullOccur}`, + ]; +}; + +export const withinTag = sequenceTemplate(within); +export const acrossTag = sequenceTemplate(across); +export const acrossNumberedTag = sequenceTemplate(acrossNumbered); +export const withinCustom = (custom: string) => + sequenceTemplate(customWithin(custom)); +export const acrossCustom = (custom: string) => + sequenceTemplate(customAcross(custom)); +export const acrossNumberedCustom = (custom: string) => + sequenceTemplate(customAcrossNumbered(custom)); diff --git a/ts/src/sortInStrategies.ts b/ts/src/sortInStrategies.ts new file mode 100644 index 0000000..110c4a0 --- /dev/null +++ b/ts/src/sortInStrategies.ts @@ -0,0 +1,60 @@ +import { shuffle } from "./utils"; + +export type SortInStrategy = (indices: number[], toLength: number) => number[]; + +export const topUp: SortInStrategy = ( + indices: number[], + toLength: number, +): number[] => { + /** + * topUpSortingIndices([0,1], 4) => [0,1,2,3] or [0,1,3,2] + */ + + if (indices.length >= toLength) { + // indices already have sufficient length + return indices; + } + + const newIndices = shuffle( + Array.from( + new Array(toLength - indices.length), + (_x: undefined, i: number) => i + indices.length, + ), + ); + + const result = [...indices, ...newIndices]; + + return result; +}; + +export const mixIn: SortInStrategy = ( + indices: number[], + toLength: number, +): number[] => { + /** + * topUpSortingIndices([0,1], 4) => [2,3,0,1], [2,0,3,1], [0,1,2,3], [0,1,3,2], etc. + */ + + if (indices.length >= toLength) { + // indices already have sufficient length + return indices; + } + + const newIndices = Array.from( + new Array(toLength - indices.length), + (_x: undefined, i: number) => i + indices.length, + ); + + const result = [...indices]; + newIndices.forEach((newIndex: number) => + result.splice( + Math.floor( + Math.random() * (result.length + 1), + ) /* possible insertion positions */, + 0, + newIndex, + ), + ); + + return result; +}; diff --git a/ts/src/styleList.ts b/ts/src/styleList.ts new file mode 100644 index 0000000..49ef89e --- /dev/null +++ b/ts/src/styleList.ts @@ -0,0 +1,37 @@ +import type { Stylizer, Eval, TagNode, Internals, WeakFilter } from "./types"; + +const transpose = (array: (T | T[])[]): T[][] => + Array.isArray(array[0]) + ? array[0].map((_, index) => + array.map((value: any /* T[] */) => value[index]), + ) + : [array]; + +export type StyleList = (string | [string, ...any[]])[]; + +export const listStylize = >( + stylizer: Stylizer, + toList: Eval, +): WeakFilter => (tag: TagNode, internals: Internals) => + internals.ready + ? stylizer.stylize( + ...(transpose(toList(tag, internals)) as [any, ...any[]]), + ) + : { ready: false }; + +export const listStylizeMaybe = >( + stylizer: Stylizer, + toListMaybe: Eval, +): WeakFilter => (tag: TagNode, internals: Internals) => { + if (!internals.ready) { + return { ready: false }; + } + + const maybeValues = toListMaybe(tag, internals); + + if (!maybeValues) { + return { ready: false }; + } + + return stylizer.stylize(...(transpose(maybeValues) as [any, ...any[]])); +}; diff --git a/ts/src/stylizer.ts b/ts/src/stylizer.ts new file mode 100644 index 0000000..12dd69e --- /dev/null +++ b/ts/src/stylizer.ts @@ -0,0 +1,62 @@ +import { id } from "./utils"; + +type StringFunction = (v: string, ...a: any[]) => string; +type StringPlusFunction = (v: string, i: number, ...a: any[]) => string; + +export class Stylizer { + protected readonly separator: string; + protected readonly mapper: StringPlusFunction; + protected readonly processor: StringFunction; + + protected constructor( + separator: string, + mapper: StringPlusFunction, + processor: StringFunction, + ) { + this.separator = separator; + this.mapper = mapper; + this.processor = processor; + } + + static make({ + separator = "", + mapper = id as StringPlusFunction, + processor = id as StringFunction, + } = {}) { + return new Stylizer(separator, mapper, processor); + } + + toStylizer({ + separator = this.separator, + mapper = this.mapper, + processor = this.processor, + } = {}): Stylizer { + return new Stylizer(separator, mapper, processor); + } + + stylize(input: string[], ...args: unknown[][]): string { + return this.processor( + input + .flatMap((v: string, i: number, l: string[]) => + this.mapper(v, i, ...args.map((arg) => arg[i]), l), + ) + .join(this.separator), + ); + } + + /* improved version of stylize */ + stylizeFull( + input: string[], + mapArgs: unknown[][] = [], + processArgs: unknown[] = [], + ): string { + return this.processor( + input + .flatMap((v: string, i: number, l: string[]) => + this.mapper(v, i, ...mapArgs.map((arg) => arg[i]), l), + ) + .join(this.separator), + ...processArgs, + ); + } +} diff --git a/ts/src/template/anki/delay.ts b/ts/src/template/anki/delay.ts new file mode 100644 index 0000000..c27a302 --- /dev/null +++ b/ts/src/template/anki/delay.ts @@ -0,0 +1,153 @@ +import { ankiLog } from "./utils"; + +interface AnkiBuiltins { + MathJax: any; + _updatingQA: boolean; + closetImmediately: boolean; + onShownHook: Array<() => void>; +} + +enum InitDescription { + ClosetImmediately = 1, + DesktopShownHook, + MobileMathJax, + DesktopMobileLate, + Web, +} + +const stringifyDescription = (description: InitDescription): string => { + switch (description) { + case InitDescription.ClosetImmediately: + return "You have set the variable globalThis.closetImmediately."; + case InitDescription.DesktopShownHook: + return ( + "Closet executes while MathJax is still rendering. " + + "You are on AnkiDesktop. " + + "The action will be pushed onto onShownHook." + ); + case InitDescription.MobileMathJax: + return ( + "Closet executes while MathJax is still rendering. " + + "You are on AnkiMobile. " + + "The action will be enqueued on MathJax.Hub.Queue." + ); + case InitDescription.DesktopMobileLate: + return ( + "Closet executes after MathJax finished rendering. " + + "The action will be executed immediatley." + ); + case InitDescription.Web: + return ( + "You are on AnkiWeb, which does not have MathJax. " + + "Alternatively, you are on platform with MathJax 3. " + + "The action will be executed immediatley." + ); + default: + return "No Description found. " + "This should never happen."; + } +}; + +type InitMode = (cb: () => void) => void; + +const initModes: Record = { + raw: (callback) => callback(), + // Only works on Desktop as far as I know + viaShownHook: (callback) => + (globalThis as typeof globalThis & AnkiBuiltins).onShownHook.push( + callback, + ), + // Works when MathJax is available in globalThis, desktop, or mobile + viaMathJaxQueue: (callback) => + (globalThis as typeof globalThis & AnkiBuiltins).MathJax.Hub.Queue( + callback, + ), +}; + +const getMathJaxQueue = (): Record => { + return (globalThis as typeof globalThis & AnkiBuiltins).MathJax.Hub.queue; +}; + +const getMathJaxQueueForPrinting = (): Record => { + const mathJaxQueue = getMathJaxQueue(); + const mathJaxPrintable = Object.assign({}, mathJaxQueue); + mathJaxPrintable.queue = mathJaxPrintable.queue.map(String); + + return mathJaxPrintable; +}; + +const delayChoice = (): [InitMode, InitDescription, unknown[]] => { + if ((globalThis as typeof globalThis & AnkiBuiltins).closetImmediately) { + /** + * This is interesting, if you do not care for MathJax whatsoever + */ + return [initModes.raw, InitDescription.ClosetImmediately, []]; + } else { + try { + /** + * This check is important for Desktop + * Pushing to shownHook only makes sense, if it has not been cleared yet + * After the clearing _updatingQA is set to false + * Additionally _updatingQA exists on Mobile, however not shown hook + */ + + const mathJaxIsCurrentlyExecuting = (globalThis as typeof globalThis & + AnkiBuiltins)._updatingQA; + const mathJaxQueue = getMathJaxQueueForPrinting(); + + if (mathJaxIsCurrentlyExecuting) { + const maybeOnShownHook = (globalThis as typeof globalThis & + AnkiBuiltins).onShownHook; + + if (maybeOnShownHook) { + /** + * On desktop, if MathJax takes long + */ + return [ + initModes.viaShownHook, + InitDescription.DesktopShownHook, + [ + JSON.stringify(maybeOnShownHook.map(String)), + JSON.stringify(mathJaxQueue), + ], + ]; + } else { + /** + * On mobile, as there is no onShownHook + */ + return [ + initModes.viaMathJaxQueue, + InitDescription.MobileMathJax, + [JSON.stringify(mathJaxQueue)], + ]; + } + } else { + /** + * On desktop, or mobile, if MathJax finishes quickly + */ + return [ + initModes.raw, + InitDescription.DesktopMobileLate, + [JSON.stringify(mathJaxQueue)], + ]; + } + } catch (error) { + /** + * On web, as there is neither _updatingQA nor MathJax + * Or on a platform with MathJax 3, as MathJax does not have queue property + */ + return [initModes.raw, InitDescription.Web, [error]]; + } + } +}; + +export const delayAction = (callback: () => void): void => { + const [choice, description, logs] = delayChoice(); + + ankiLog( + `Init mode ${description}`, + stringifyDescription(description), + ...logs, + ); + + choice(callback); +}; diff --git a/ts/src/template/anki/index.ts b/ts/src/template/anki/index.ts new file mode 100644 index 0000000..6b7c0e1 --- /dev/null +++ b/ts/src/template/anki/index.ts @@ -0,0 +1,3 @@ +export { init, initialize } from "./initialize"; +export { ankiLog } from "./utils"; +export { getQaChildNodes } from "./qaNodes"; diff --git a/ts/src/template/anki/initialize.ts b/ts/src/template/anki/initialize.ts new file mode 100644 index 0000000..1c916db --- /dev/null +++ b/ts/src/template/anki/initialize.ts @@ -0,0 +1,120 @@ +import type { TagRenderer } from ".."; +import type { Delimiters } from "../delimiters"; +import type { MemoryMap } from "./persistence"; + +import { BrowserTemplate, cleanup } from "../browser"; +import { persistenceInterface } from "./persistence"; + +import { ankiLog } from "./utils"; +import { delayAction } from "./delay"; + +type CardSide = "front" | "back"; + +interface DefaultPreset { + card: string; + cardNumber: number; + tagsFull: string; + tags: string[]; + side: CardSide; + [key: string]: unknown; +} + +const getCardNumber = (textNum: string): number => + Number(textNum.match(/[0-9]*$/)); + +const preset = ( + cardType: string, + tagsFull: string, + side: CardSide, +): DefaultPreset => ({ + card: cardType, + cardNumber: getCardNumber(cardType), + tagsFull: tagsFull, + tags: tagsFull.length === 0 ? [] : tagsFull.split(" "), + side: side, +}); + +/////////////////////////////////////// LOAD + +interface SetupOptions { + delimiters: Delimiters; +} + +const load = ( + elements: Element[], + memoryMap: MemoryMap, + filterManager: TagRenderer, + options?: SetupOptions, +): number => { + const before = window.performance.now(); + BrowserTemplate.makeFromNodes(elements, options?.delimiters).renderToNodes( + filterManager, + ); + + memoryMap.writeBack(); + const after = window.performance.now(); + + return after - before; +}; + +/////////////////////////////////////// INIT + +type SetupOutput = [Element[], MemoryMap, TagRenderer, SetupOptions?]; + +type UserLogic> = ( + closet: Record, + preset: T, + chooseMemory: (memoryKey: string) => MemoryMap, +) => SetupOutput[]; + +// Export for legacy support +export const init = ( + closet: Record, + logic: UserLogic, + cardType: string, + tagsFull: string, + side: CardSide, +): number[] => { + const userPreset = preset(cardType, tagsFull, side); + const chooseMemory = persistenceInterface( + side, + document.getElementById("qa")?.innerHTML ?? "", + ); + + return logic(closet, userPreset, chooseMemory).map((value: SetupOutput) => + load(...value), + ); +}; + +/////////////////////////////////////// INITIALIZE + +const logInit = ( + closet: Record, + logic: UserLogic, + cardType: string, + tagsFull: string, + side: CardSide, +): void => { + try { + const times = init(closet, logic, cardType, tagsFull, side); + console.log( + `Closet executed in ${times.map((t: number) => t.toFixed(3))}ms.`, + ); + } catch (error) { + console.log("An error occured while executing Closet:", error); + ankiLog("An error occured while executing Closet", error); + } finally { + cleanup(); + } +}; + +export const initialize = ( + closet: Record, + logic: UserLogic, + cardType: string, + tagsFull: string, + side: CardSide, +): Record => { + delayAction(() => logInit(closet, logic, cardType, tagsFull, side)); + return closet; +}; diff --git a/ts/src/template/anki/persistence.ts b/ts/src/template/anki/persistence.ts new file mode 100644 index 0000000..3ea02e4 --- /dev/null +++ b/ts/src/template/anki/persistence.ts @@ -0,0 +1,237 @@ +import { ankiLog } from "./utils"; + +// This code is based on +// https://github.com/SimonLammer/anki-persistence +// +// changes: +// - rewrite to TS +// - drop support for _default key + +interface AnkiPersistence { + clear: () => void; + getItem(key: string): JSON; + setItem(key: string, value: JSON): void; + removeItem(key: string): void; + isAvailable(): boolean; +} + +type JSON = + | null + | boolean + | number + | string + | JSON[] + | { [prop: string]: JSON }; + +interface ClosetSideHash { + closetCardHash: number; +} + +export interface MemoryMap { + key: string; + map: Map; + writeBack: () => void; +} + +const _persistenceKey = "github.com/hgiesel/closet"; + +// used in android, iOS, web +class Persistence_sessionStorage implements AnkiPersistence { + _isAvailable = false; + + constructor() { + try { + if (typeof globalThis.sessionStorage === "object") { + this._isAvailable = true; + } + } catch { + // sessionStorage is disabled inside of 'data:' URLs + } + } + + clear() { + for (let i = 0; i < sessionStorage.length; i++) { + const k = sessionStorage.key(i); + + if (k && k.indexOf(_persistenceKey) === 0) { + sessionStorage.removeItem(k as string); + i--; + } + } + } + + setItem(key: string, value: JSON) { + sessionStorage.setItem(_persistenceKey + key, JSON.stringify(value)); + } + + getItem(key: string): JSON { + const item = sessionStorage.getItem(_persistenceKey + key) ?? "null"; + return JSON.parse(item); + } + + removeItem(key: string) { + sessionStorage.removeItem(_persistenceKey + key); + } + + isAvailable() { + return this._isAvailable; + } +} + +// used in windows, linux, mac +class Persistence_windowKey implements AnkiPersistence { + _isAvailable = false; + obj: Record; + + constructor(persistentKey: string) { + this.obj = (globalThis as any)[persistentKey]; + + if (typeof this.obj !== "object") { + return; + } + + this._isAvailable = true; + + if (this.obj[_persistenceKey] === undefined) { + this.clear(); + } + } + + clear() { + this.obj[_persistenceKey] = {}; + } + + setItem(key: string, value: JSON) { + this.obj[_persistenceKey][key] = value; + } + + getItem(key: string) { + return this.obj[_persistenceKey][key] == undefined + ? null + : this.obj[_persistenceKey][key]; + } + + removeItem(key: string) { + delete this.obj[_persistenceKey][key]; + } + + isAvailable() { + return this._isAvailable; + } +} + +const initPersistence = (): AnkiPersistence => { + let persistence; + /** + * client | sessionStorage | persistentKey | useful location | + * ----------|----------------|---------------|-----------------| + * web | YES | - | NO | + * windows | NO | py | NO | + * android | YES | - | NO | + * linux 2.0 | NO | qt | YES | + * linux 2.1 | NO | qt | YES | + * mac 2.0 | NO | py | NO | + * mac 2.1 | NO | qt | YES | + * iOS | YES | - | NO | + */ + + // android, iOS, web + persistence = new Persistence_sessionStorage(); + if (persistence.isAvailable()) { + ankiLog("Used Persistence sessionStorage implementation"); + return persistence; + } + + // windows, mac (2.0) + persistence = new Persistence_windowKey("py"); + if (persistence.isAvailable()) { + ankiLog("Used Persistence windowKey py implementation"); + return persistence; + } + + // if titleStartIndex > 0, window.location is useful + const titleStartIndex = location.toString().indexOf("title"); + const titleContentIndex = location + .toString() + .indexOf("main", titleStartIndex); + + if ( + titleStartIndex > 0 && + titleContentIndex > 0 && + titleContentIndex - titleStartIndex < 10 + ) { + // linux, mac (2.1) + ankiLog("Used Persistence windowKey qt implementation"); + return new Persistence_windowKey("qt"); + } + + ankiLog("Defaulted to Persistence windowKey implementation"); + return persistence; +}; + +const hashCode = (plain: string): number => { + let hash = 0, + i, + chr; + for (i = 0; i < plain.length; i++) { + chr = plain.charCodeAt(i); + hash = (hash << 5) - hash + chr; + hash |= 0; // Convert to 32bit integer + } + return hash; +}; + +const persistence = initPersistence(); + +export const persistenceInterface = (side: "front" | "back", label: string) => ( + memoryKey: string, +): MemoryMap => { + /** + * @param label: should identify the context where persistence is used + */ + + const currentHash = + (globalThis as typeof globalThis & Partial) + .closetCardHash ?? null; + + const getPersistentMap = (): Map => { + if (!persistence.isAvailable()) { + // Persistence is not available, fallback to no memory + return new Map(); + } + + if (side === "front") { + const hash = hashCode(label); + + if (hash !== currentHash) { + // This is a new displayed card, reset memory + (globalThis as typeof globalThis & + Partial).closetCardHash = hash; + return new Map(); + } + } + + const maybeMap = persistence.getItem(memoryKey) as Iterable< + readonly [string, unknown] + >; + return new Map(maybeMap); + }; + + const map = getPersistentMap(); + + const setPersistentMap = (): void => { + if (persistence.isAvailable()) { + const persistentData = Array.from(map.entries()); + + persistence.setItem(memoryKey, persistentData as JSON); + } + }; + + const memoryMap = { + key: memoryKey, + map: map, + writeBack: setPersistentMap, + }; + + return memoryMap; +}; diff --git a/ts/src/template/anki/qaNodes.ts b/ts/src/template/anki/qaNodes.ts new file mode 100644 index 0000000..1617cb7 --- /dev/null +++ b/ts/src/template/anki/qaNodes.ts @@ -0,0 +1,43 @@ +import type { ChildNodeSpan, BrowserTemplateNode } from "../browser"; +import { interspliceChildNodes } from "../browser"; + +const isElement = (tag: Node): tag is Element => + tag.nodeType === Node.ELEMENT_NODE; + +const isStyleElement = (tag: Element): tag is HTMLStyleElement => + tag.tagName === "STYLE"; + +const isScriptElement = (tag: Element): tag is HTMLScriptElement => + tag.tagName === "SCRIPT"; + +const isJavaScriptElement = (tag: Element): tag is HTMLScriptElement => + isScriptElement(tag) && + (tag.type.length === 0 || tag.type.endsWith("javascript")); + +// Anki Asset Manager support +const isAnkiAssetManagerElement = (tag: Element): tag is HTMLDivElement => + tag.id === "anki-am"; + +const isTemplateableElement = (tag: Node): boolean => + isElement(tag) && + (isStyleElement(tag) || + isJavaScriptElement(tag) || + isAnkiAssetManagerElement(tag)); + +export const getQaChildNodes = (): ChildNodeSpan[] | null => { + if (!globalThis.document) { + return null; + } + + const qa = globalThis.document.getElementById("qa"); + + if (!qa) { + return null; + } + + return interspliceChildNodes(qa, { + type: "predicate", + value: (tag: BrowserTemplateNode): boolean => + !isTemplateableElement(tag as Node), + }); +}; diff --git a/ts/src/template/anki/utils.ts b/ts/src/template/anki/utils.ts new file mode 100644 index 0000000..b4f1c63 --- /dev/null +++ b/ts/src/template/anki/utils.ts @@ -0,0 +1,23 @@ +interface DebugMode { + closetDebug: boolean; +} + +export const ankiLog = (...objs: unknown[]): boolean => { + if (!(globalThis as typeof globalThis & DebugMode).closetDebug) { + return false; + } + + const logDiv = Object.assign(document.createElement("div"), { + className: "closet-log", + innerText: objs.map(String).join("\n"), + }); + + const qa = document.getElementById("qa"); + + if (!qa) { + return false; + } + + qa.appendChild(logDiv); + return true; +}; diff --git a/ts/src/template/browser/childNodes.ts b/ts/src/template/browser/childNodes.ts new file mode 100644 index 0000000..e42ec11 --- /dev/null +++ b/ts/src/template/browser/childNodes.ts @@ -0,0 +1,282 @@ +export type BrowserTemplateNode = Element | Text | ChildNodeSpan | string; + +// negative result implies invalid idx +const parseIndexArgument = (idx: number, min: number, max: number): number => { + return idx < 0 ? max + idx + 1 : min + idx; +}; + +export const getText = ( + input: BrowserTemplateNode, + takeOuter: boolean, +): string => { + if (typeof input === "string") { + return input; + } + + let textNode = null, + elementNode = null, + span = null; + + switch (input.nodeType) { + case Node.TEXT_NODE: + textNode = input as Text; + return textNode.textContent ?? ""; + + case Node.ELEMENT_NODE: + elementNode = input as Element; + return takeOuter ? elementNode.outerHTML : elementNode.innerHTML; + + case ChildNodeSpan.CHILD_NODE_SPAN: + span = input as ChildNodeSpan; + return span.spanAsStrings().join(""); + + default: + return ""; + } +}; + +export const setText = (input: BrowserTemplateNode, newText: string): void => { + if (typeof input === "string") { + return; + } + + let textNode = null, + placeholderNode = null, + elementNode = null, + span = null; + + switch (input.nodeType) { + case Node.TEXT_NODE: + textNode = input as Text; + placeholderNode = document.createElement("div"); + + if (textNode.parentElement) { + textNode.parentElement.insertBefore(placeholderNode, textNode); + textNode.parentElement.removeChild(textNode); + } + + placeholderNode.outerHTML = newText; + break; + + case Node.ELEMENT_NODE: + elementNode = input as Element; + elementNode.innerHTML = newText; + break; + + case ChildNodeSpan.CHILD_NODE_SPAN: + span = input as ChildNodeSpan; + span.replaceSpan(newText); + break; + } +}; + +interface ChildNodeIndex { + type: "index"; + value: number; + exclusive?: boolean; + startAtIndex?: number; +} + +interface ChildNodeNode { + type: "node"; + value: BrowserTemplateNode; + exclusive?: boolean; + startAtIndex?: number; +} + +export interface ChildNodePredicate { + type: "predicate"; + value: (v: BrowserTemplateNode) => boolean; + exclusive?: boolean; + startAtIndex?: number; +} + +type ChildNodePosition = ChildNodeIndex | ChildNodeNode | ChildNodePredicate; + +const defaultFromValue: ChildNodeIndex = { + type: "index", + value: 0, +}; + +const defaultToValue: ChildNodeIndex = { + type: "index", + value: -1, +}; + +export class ChildNodeSpan { + static readonly CHILD_NODE_SPAN = 3353; /* this number is arbitrary */ + readonly nodeType = ChildNodeSpan.CHILD_NODE_SPAN; + + private readonly parentElement: Element; + private childNodes: ChildNode[]; + private max: number; + + private _fromIndex: number; + private _toIndex: number; + + constructor( + parentElement: Element, + fromValue: ChildNodePosition = defaultFromValue, + toValue: ChildNodePosition = defaultToValue, + ) { + this.parentElement = parentElement; + this.childNodes = Array.from(this.parentElement.childNodes); + + this.max = parentElement.childNodes.length - 1; + + const fromFunc = this.getFromMethod(fromValue.type) as any; + const toFunc = this.getToMethod(toValue.type) as any; + + this._fromIndex = fromFunc.call( + this as ChildNodeSpan, + fromValue.value as any, + fromValue.startAtIndex ?? 0, + fromValue.exclusive ?? false, + ); + + this._toIndex = toFunc.call( + this as ChildNodeSpan, + toValue.value as any, + Math.max(toValue.startAtIndex ?? 0, this.from), + toValue.exclusive ?? false, + ); + } + + get from(): number { + return this._fromIndex; + } + + get to(): number { + return this._toIndex; + } + + private getFromMethod(name: string) { + return name === "index" + ? this.fromIndex + : name === "node" + ? this.fromNode + : this.fromPredicate; + } + + private getToMethod(name: string) { + return name === "index" + ? this.toIndex + : name === "node" + ? this.toNode + : this.toPredicate; + } + + private fromSafe(i: number, min: number): number { + return i < min || i > this.max ? this.max : i; + } + + private fromIndex(i: number, min: number, exclusive: boolean): number { + const parsed = + parseIndexArgument(i, min, this.max) + (exclusive ? 1 : 0); + return this.fromSafe(parsed, min); + } + + private fromNode(node: Node, min: number, exclusive: boolean): number { + const found = + this.childNodes + .slice(min) + .findIndex((v: ChildNode): boolean => v === node) + + min + + (exclusive ? 1 : 0); + return this.fromSafe(found, min); + } + + private fromPredicate( + pred: (v: Node) => boolean, + min: number, + exclusive: boolean, + ): number { + const found = + this.childNodes.slice(min).findIndex(pred) + + min + + (exclusive ? 1 : 0); + return this.fromSafe(found, min); + } + + private toSafe(i: number, min: number): number { + return i < min || i > this.max ? 0 : i; + } + + private toIndex(i: number, min: number, exclusive: boolean): number { + const parsed = + parseIndexArgument(i, min, this.max) - (exclusive ? 1 : 0); + return this.toSafe(parsed, min); + } + + private toNode(node: Node, min: number, exclusive: boolean): number { + const found = + this.childNodes + .slice(min) + .findIndex((v: ChildNode): boolean => v === node) + + min - + (exclusive ? 1 : 0); + return this.toSafe(found, min); + } + + private toPredicate( + pred: (v: Node) => boolean, + min: number, + exclusive: boolean, + ): number { + const found = + this.childNodes.slice(min).findIndex(pred) + + min - + (exclusive ? 1 : 0); + return this.toSafe(found, min); + } + + get valid(): boolean { + return this._fromIndex <= this._toIndex; + } + + get length(): number { + return Math.max(0, this._toIndex - this._fromIndex + 1); + } + + getRootNode(): Node { + return this.parentElement.getRootNode(); + } + + span(): ChildNode[] { + return this.valid + ? this.childNodes.slice(this._fromIndex, this._toIndex + 1) + : []; + } + + spanAsStrings(): string[] { + return this.span().map((elem) => getText(elem as Element, true)); + } + + replaceSpan(newText: string): void { + if (!this.valid) { + return; + } + + const placeholderNode = document.createElement("div"); + const oldLength = this.parentElement.childNodes.length; + + this.parentElement.insertBefore( + placeholderNode, + this.parentElement.childNodes[this._fromIndex], + ); + for (const node of this.span()) { + if (node.parentElement === this.parentElement) { + this.parentElement.removeChild(node); + } + } + + // might turn the original div into multiple nodes including text nodes + placeholderNode.outerHTML = newText; + + // reset childNode information + this.childNodes = Array.from(this.parentElement.childNodes); + this.max = this.childNodes.length; + + this._toIndex = this._toIndex + (this.max - oldLength); + } +} diff --git a/ts/src/template/browser/index.ts b/ts/src/template/browser/index.ts new file mode 100644 index 0000000..0dcfb5b --- /dev/null +++ b/ts/src/template/browser/index.ts @@ -0,0 +1,115 @@ +import type { TagRenderer } from "../template"; +import type { ASTNode } from "../nodes"; +import type { Delimiters } from "../delimiters"; +import type { BrowserTemplateNode } from "./childNodes"; + +import { Template } from "../template"; +import { getText, setText } from "./childNodes"; + +export type { ChildNodeSpan, BrowserTemplateNode } from "./childNodes"; + +export { interspliceChildNodes } from "./intersplice"; + +const createStyleTag = (keyword: string, input: string): HTMLStyleElement => { + const styleSheet = document.createElement("style"); + styleSheet.type = "text/css"; + styleSheet.id = `closet-${keyword}`; + styleSheet.textContent = input; + + return styleSheet; +}; + +const hasStyleTag = (root: Document | ShadowRoot, keyword: string): boolean => { + return Boolean(root.getElementById(`closet-${keyword}`)); +}; + +const appendDocumentStyle = (keyword: string, css: string): void => { + if (!hasStyleTag(document, keyword)) { + document.head.appendChild(createStyleTag(keyword, css)); + } +}; + +const appendStyle = ( + anchor: Exclude, + keyword: string, + css: string, +): void => { + const root = anchor.getRootNode() as Document | ShadowRoot; + + if (root instanceof Document) { + appendDocumentStyle(keyword, css); + } else if (!hasStyleTag(root, keyword)) { + root.insertBefore(createStyleTag(keyword, css), root.firstElementChild); + } +}; + +export class BrowserTemplate extends Template { + inputs: BrowserTemplateNode[]; + + protected constructor( + text: string[], + preparsed: ASTNode[] | null, + inputs: BrowserTemplateNode[], + delimiters?: Delimiters, + ) { + super(text, preparsed, delimiters); + this.inputs = inputs; + } + + static makeFromNode( + input: BrowserTemplateNode, + delimiters?: Delimiters, + ): BrowserTemplate { + return BrowserTemplate.makeFromNodes([input], delimiters); + } + + static makeFromNodes( + inputs: BrowserTemplateNode[], + delimiters?: Delimiters, + ): BrowserTemplate { + return new BrowserTemplate( + inputs.map((input) => getText(input, false)), + null, + inputs, + delimiters, + ); + } + + renderToNodes(tagRenderer: TagRenderer): void { + super.render(tagRenderer, (outputs: string[]) => + outputs.forEach((text: string, index: number) => + setText(this.inputs[index], text), + ), + ); + } + + appendDocumentStyle(keyword: string, css: string): void { + appendDocumentStyle(keyword, css); + } + + appendStyle( + input: Exclude, + keyword: string, + css: string, + ): void { + appendStyle(input, keyword, css); + } + + appendStyleAll(keyword: string, css: string): void { + for (const input of this.inputs) { + if (typeof input !== "string") { + this.appendStyle(input, keyword, css); + } + } + } +} + +const delayKeyword = "closet-delay"; + +export const cleanup = (): void => { + for (const element of Array.from( + document.getElementsByClassName(delayKeyword), + )) { + (element as HTMLElement).style.visibility = "visible"; + } +}; diff --git a/ts/src/template/browser/intersplice.ts b/ts/src/template/browser/intersplice.ts new file mode 100644 index 0000000..5b48b72 --- /dev/null +++ b/ts/src/template/browser/intersplice.ts @@ -0,0 +1,42 @@ +import type { ChildNodePredicate, BrowserTemplateNode } from "./childNodes"; + +import { ChildNodeSpan } from "./childNodes"; + +const makePositions = ( + template: ChildNodePredicate, + currentIndex = 0, +): [ChildNodePredicate, ChildNodePredicate] => { + const fromSkip: ChildNodePredicate = { + type: "predicate", + value: template.value, + startAtIndex: currentIndex, + exclusive: false, + }; + const toSkip: ChildNodePredicate = { + type: "predicate", + value: (v: BrowserTemplateNode): boolean => !template.value(v), + startAtIndex: currentIndex, + exclusive: true, + }; + + return [fromSkip, toSkip]; +}; + +export const interspliceChildNodes = ( + parent: Element, + skip: ChildNodePredicate, +): ChildNodeSpan[] => { + const result: ChildNodeSpan[] = []; + let currentSpan = new ChildNodeSpan(parent, ...makePositions(skip)); + + while (currentSpan.valid) { + result.push(currentSpan); + + currentSpan = new ChildNodeSpan( + parent, + ...makePositions(skip, currentSpan.to + 1), + ); + } + + return result; +}; diff --git a/ts/src/template/delimiters.ts b/ts/src/template/delimiters.ts new file mode 100644 index 0000000..2fe98f2 --- /dev/null +++ b/ts/src/template/delimiters.ts @@ -0,0 +1,11 @@ +export interface Delimiters { + open: string; + sep: string; + close: string; +} + +export const defaultDelimiters = { + open: "[[", + sep: "::", + close: "]]", +}; diff --git a/ts/src/template/index.ts b/ts/src/template/index.ts new file mode 100644 index 0000000..c303389 --- /dev/null +++ b/ts/src/template/index.ts @@ -0,0 +1,14 @@ +export { + Template, + TagRenderer, + TemplateInfo, + IterationInfo, + ResultInfo, +} from "./template"; +export { BrowserTemplate } from "./browser"; +export { Parser } from "./parser"; +export { TagSelector } from "./tagSelector"; + +export * as browser from "./browser"; +export * as anki from "./anki"; +export * as optics from "./optics"; diff --git a/ts/src/template/nodes.ts b/ts/src/template/nodes.ts new file mode 100644 index 0000000..a863ac8 --- /dev/null +++ b/ts/src/template/nodes.ts @@ -0,0 +1,345 @@ +import type { Filterable } from "../filterManager/filters"; +import type { Parser } from "./parser"; +import type { TagAccessor, TagPath, RoundInfo } from "./types"; +import type { Delimiters } from "./delimiters"; +import type { Optic } from "./optics"; + +import { id } from "../utils"; +import { run, dictFunction, dictForget } from "./optics"; +import { Status } from "./types"; + +export interface ASTNode extends Filterable { + toString(): string | null; + isReady(): boolean; + evaluate( + parser: Parser, + tagAccessor: TagAccessor, + tagPath: TagPath, + ): ASTNode[]; +} + +export const nodesAreReady = (accu: boolean, node: ASTNode): boolean => + accu && node.isReady(); + +const joinNodes = (nodes: ASTNode[]) => + nodes.map((node) => node.toString()).join(""); +const joinRepr = (nodes: ASTNode[]) => + nodes.map((node) => node.toReprString()).join(""); + +const leadingTrailingNewlineOrBr = /^(?:|\n)|(?:|\n)$/gu; +const stripLineBreaks = (text: string): string => + text.replace(leadingTrailingNewlineOrBr, ""); + +export class TagNode implements ASTNode { + readonly fullKey: string; + readonly key: string; + readonly num: number | null; + readonly fullOccur: number; + readonly occur: number; + + readonly delimiters: Delimiters; + + protected _inlineNodes: ASTNode[]; + readonly hasInline: boolean; + protected _blockNodes: ASTNode[]; + readonly hasBlock: boolean; + + protected _inlineOptics: Optic[] = []; + protected _inlineGetter: (i: any) => any = id; + + protected _blockOptics: Optic[] = []; + protected _blockGetter: (i: any) => any = id; + + constructor( + fullKey: string, + key: string, + num: number | null, + fullOccur: number, + occur: number, + delimiters: Delimiters, + inlineNodes: ASTNode[], + hasInline: boolean, + blockNodes: ASTNode[], + hasBlock: boolean, + ) { + this.fullKey = fullKey; + this.key = key; + this.num = num; + this.fullOccur = fullOccur; + this.occur = occur; + this.delimiters = delimiters; + + this._inlineNodes = inlineNodes; + this.hasInline = hasInline; + this._blockNodes = blockNodes; + this.hasBlock = hasBlock; + } + + /******************** NODES ********************/ + get inlineNodes(): ASTNode[] { + return this._inlineNodes; + } + + get blockNodes(): ASTNode[] { + return this._blockNodes; + } + + get innerNodes(): ASTNode[] { + return this.hasBlock ? this.blockNodes : this.inlineNodes; + } + + protected set internalNodes(nodes: ASTNode[]) { + if (this.hasBlock) { + this._blockNodes = nodes; + } else { + this._inlineNodes = nodes; + } + } + + /******************** TEXT ********************/ + get inlineText(): string { + return joinNodes(this.inlineNodes); + } + + get blockText(): string { + return stripLineBreaks(joinNodes(this.blockNodes)); + } + + get text(): string { + return this.hasBlock ? this.blockText : this.inlineText; + } + + /******************** OPTICS ********************/ + set inlineOptics(op: Optic[]) { + this._inlineOptics = op; + this._inlineGetter = run(op, dictForget, id); + } + + get inlineOptics(): Optic[] { + return this._inlineOptics; + } + + set blockOptics(op: Optic[]) { + this._blockOptics = op; + this._blockGetter = run(op, dictForget, id); + } + + get blockOptics(): Optic[] { + return this._blockOptics; + } + + set optics(op: Optic[]) { + if (this.hasBlock) { + this.blockOptics = op; + } else { + this.inlineOptics = op; + } + } + + get optics(): Optic[] { + return this.hasBlock ? this.blockOptics : this.inlineOptics; + } + + get inlineGetter(): (i: any) => any { + return this._inlineGetter; + } + + get blockGetter(): (i: any) => any { + return this._blockGetter; + } + + get getter(): (i: any) => any { + return this.hasBlock ? this.blockGetter : this.inlineGetter; + } + + /******************** VALUES ********************/ + get inlineValues() { + return this.inlineGetter(this.inlineText); + } + + get blockValues() { + return this.blockGetter(this.blockText); + } + + get values() { + return this.hasBlock ? this.blockValues : this.inlineValues; + } + + /******************** TRAVERSE ********************/ + inlineTraverse(f: (x: unknown) => unknown) { + return run(this.inlineOptics, dictFunction, f)(this.inlineText); + } + + blockTraverse(f: (x: unknown) => unknown) { + return run(this.blockOptics, dictFunction, f)(this.blockText); + } + + traverse(f: (x: unknown) => unknown) { + return this.hasBlock ? this.blockTraverse(f) : this.inlineTraverse(f); + } + + /******************** STRINGIFY ********************/ + // implementation detail + protected wrapWithDelimiters(infix: string, wrapped?: string): string { + return `${this.delimiters.open}${infix}${this.fullKey}${ + wrapped ? this.delimiters.sep + wrapped : "" + }${this.delimiters.close}`; + } + + protected wrapBlock(block: string, inline?: string): string { + return `${this.wrapWithDelimiters( + "#", + inline, + )}${block}${this.wrapWithDelimiters("/")}`; + } + + protected baseToString( + getInline: () => string, + getBlock: () => string, + ): string { + return this.hasInline + ? this.hasBlock + ? this.wrapBlock(getBlock(), getInline()) + : this.wrapWithDelimiters("", getInline()) + : this.hasBlock + ? this.wrapBlock(getBlock()) + : this.wrapWithDelimiters(""); + } + + toString(): string { + return this.baseToString( + () => this.inlineText, + () => this.blockText, + ); + } + + toReprString(): string { + return this.baseToString( + () => joinRepr(this.inlineNodes), + () => joinRepr(this.blockNodes), + ); + } + + isReady(): boolean { + return false; + } + + evaluate( + parser: Parser, + tagAccessor: TagAccessor, + tagPath: TagPath, + afterCapture = false, + ): ASTNode[] { + const tagProcessor = tagAccessor(this.key); + const depth = tagPath.length - 1; + + const useCapture = tagProcessor.getOptions().capture; + + const innerEvaluate = (node: ASTNode, index: number) => + node.evaluate(parser, tagAccessor, [...tagPath, index]); + + const shouldEvaluateInnerNodes = !useCapture && !afterCapture; + const operatesAsCapture = useCapture && !afterCapture; + + if (shouldEvaluateInnerNodes) { + this.innerNodes.splice( + 0, + this.innerNodes.length, + ...this.innerNodes.flatMap(innerEvaluate), + ); + } + + const allReady = this.innerNodes.reduce(nodesAreReady, true); + + this.inlineOptics = tagProcessor.getOptions().inlineOptics; + this.blockOptics = tagProcessor.getOptions().blockOptics; + + const roundInfo: RoundInfo = { + path: tagPath, + depth: depth, + ready: allReady, + isCapture: operatesAsCapture, + }; + + const filterOutput = tagProcessor.execute(this, roundInfo); + let result; + + switch (filterOutput.status) { + case Status.Ready: + result = + typeof filterOutput.result === "string" + ? [new TextNode(filterOutput.result as string)] + : ((filterOutput.result as unknown) as ASTNode[]); + break; + case Status.NotReady: + result = operatesAsCapture ? this.innerNodes : [this]; + break; + case Status.ContinueTags: + result = parser + .rawParse(filterOutput.result as string) + .flatMap(innerEvaluate); + break; + case Status.ContainsTags: + result = parser.rawParse(filterOutput.result as string); + break; + } + + if (operatesAsCapture) { + this.internalNodes = result; + return this.evaluate(parser, tagAccessor, tagPath, true); + } + + return result; + } +} + +class SimpleNode implements ASTNode { + isReady(): boolean { + // should never happen + return true; + } + + evaluate(): ASTNode[] { + return [this]; + } + + toReprString(): string { + return this.toString() ?? ""; + } +} + +export class TextNode extends SimpleNode { + readonly text: string; + + constructor(text: string) { + super(); + this.text = text; + } + + toString(): string | null { + return this.text; + } +} + +export class EscapedNode extends SimpleNode { + readonly escaped: string; + + constructor(escaped: string) { + super(); + this.escaped = escaped; + } + + toString(): string | null { + return this.escaped.length === 1 ? this.escaped : this.escaped.slice(1); + } + + toReprString(): string { + return this.escaped; + } +} + +export class DocSeparatorNode extends SimpleNode { + toString(): string | null { + return null; + } +} diff --git a/ts/src/template/optics/circumfix.ts b/ts/src/template/optics/circumfix.ts new file mode 100644 index 0000000..d4cdeb4 --- /dev/null +++ b/ts/src/template/optics/circumfix.ts @@ -0,0 +1,17 @@ +export interface Circumfix { + before: string; + after: string; +} + +export type WeakCircumfix = Partial | string; + +export const weakCircumfixToCircumfix = (ws: WeakCircumfix): Circumfix => + typeof ws === "string" + ? { + before: ws, + after: ws, + } + : { + before: ws.before ?? "", + after: ws.after ?? "", + }; diff --git a/ts/src/template/optics/consumers.ts b/ts/src/template/optics/consumers.ts new file mode 100644 index 0000000..c01d8d6 --- /dev/null +++ b/ts/src/template/optics/consumers.ts @@ -0,0 +1,5 @@ +import type { ProfunctorDict } from "./profunctors"; + +export const run = (zooms: any, dict: ProfunctorDict, f: (a: A) => B) => { + return zooms.reverse().reduce((accu: any, z: any) => z(dict, accu), f); +}; diff --git a/ts/src/template/optics/index.ts b/ts/src/template/optics/index.ts new file mode 100644 index 0000000..bbdc839 --- /dev/null +++ b/ts/src/template/optics/index.ts @@ -0,0 +1,11 @@ +export type { Optic } from "./utils"; +export type { WeakCircumfix } from "./circumfix"; +export type { WeakSeparator } from "./separated"; + +export { run } from "./consumers"; +export { dictFunction, dictForget } from "./profunctors"; + +export { separated } from "./separated"; +export { templated, templatedRegex } from "./templated"; +export { stripped, strippedRegex } from "./stripped"; +export { mapped } from "./mapped"; diff --git a/ts/src/template/optics/mapped.ts b/ts/src/template/optics/mapped.ts new file mode 100644 index 0000000..08bee6a --- /dev/null +++ b/ts/src/template/optics/mapped.ts @@ -0,0 +1,12 @@ +import type { ProfunctorDict } from "./profunctors"; +import type { Optic } from "./utils"; + +const fmap = (f: (a: A) => B) => (xs: A[]): B[] => { + return xs.map(f); +}; + +export const mapped = (): Optic => { + return (_dict: ProfunctorDict, f0: (a: A) => B) => { + return fmap(f0); + }; +}; diff --git a/ts/src/template/optics/profunctors.ts b/ts/src/template/optics/profunctors.ts new file mode 100644 index 0000000..5692be1 --- /dev/null +++ b/ts/src/template/optics/profunctors.ts @@ -0,0 +1,93 @@ +// Heavily inspired by: +// - https://oleg.fi/gists/posts/2017-04-18-glassery.html#instances-forget +// - Optika JS library + +enum ProfunctorInstance { + Strong, + Choice, + Traversing, + Bicontravariant, +} + +export interface ProfunctorDict { + classes: ProfunctorInstance[]; + dimap: ( + l: (a: A) => B, + r: (c: C) => D, + f: (b: B) => C, + ) => (a: A) => unknown; + + first: (f: (a: A) => B) => ([a, c]: [A, C]) => unknown; + + right: ( + f: (a: A) => B, + ) => ([isRight, x]: [false, C] | [true, A]) => unknown; +} + +/////////////////////////////////////// Profunctor (->) +const dimap = ( + l: (a: A) => B, + r: (c: C) => D, + f: (b: B) => C, +): ((a: A) => D) => (a: A): D => r(f(l(a))); + +// Strong profunctor +// (a, b) == [a, b] +const first = (f: (a: A) => B) => ([a, c]: [A, C]): [B, C] => [ + f(a), + c, +]; + +// Choice profunctor +// Either l r == [boolean, L | R] == [false, L] | [true | R] +const right = (f: (a: A) => B) => ([isRight, x]: + | [false, C] + | [true, A]): [false, C] | [true, B] => + isRight + ? ([isRight, f(x as A)] as [true, B]) + : ([isRight, x] as [false, C]); + +export const dictFunction: ProfunctorDict = { + classes: [ + ProfunctorInstance.Choice, + ProfunctorInstance.Strong, + ProfunctorInstance.Traversing, + ], + dimap, + first, + right: right as any, +}; + +/////////////////////////////////////// Profunctor (Forget r) == Star (Const r) +const lmap = (l: (a: A) => B, f: (b: B) => C): ((a: A) => C) => ( + a: A, +): C => f(l(a)); + +const dimapForget = ( + l: (a: A) => B, + _r: (c: C) => D, + f: (b: B) => C, +): ((a: A) => C) => lmap(l, f); + +const firstForget = (f: (a: A) => B) => ([a]: [A, C]): B => f(a); + +const rightForget = (f: (a: A) => B) => ([isRight, x]: + | [false, C] + | [true, A]): [] | B => + isRight + ? (f(x as A) as B) + : [ + /* mempty */ + ]; + +export const dictForget: ProfunctorDict = { + classes: [ + ProfunctorInstance.Bicontravariant, + ProfunctorInstance.Choice, + ProfunctorInstance.Strong, + ProfunctorInstance.Traversing, + ], + dimap: dimapForget, + first: firstForget, + right: rightForget as any, +}; diff --git a/ts/src/template/optics/separated.ts b/ts/src/template/optics/separated.ts new file mode 100644 index 0000000..bfa857e --- /dev/null +++ b/ts/src/template/optics/separated.ts @@ -0,0 +1,72 @@ +import type { ProfunctorDict } from "./profunctors"; +import type { Optic } from "./utils"; + +export interface Separator { + sep: string; + max: number; + trim: boolean; + keepEmpty: boolean; +} + +export type WeakSeparator = Partial | string; + +export const weakSeparatorToSeparator = (ws: WeakSeparator): Separator => + typeof ws === "string" + ? { + sep: ws, + max: Infinity, + trim: false, + keepEmpty: true, + } + : { + sep: ws.sep ?? "::", + max: ws.max ?? Infinity, + trim: ws.trim ?? false, + keepEmpty: ws.keepEmpty ?? true, + }; + +export const separated = (weakSep: WeakSeparator): Optic => { + const { sep, max, trim, keepEmpty } = weakSeparatorToSeparator(weakSep); + + const getter = ( + text: string, + ): [string[] /* no value passed to setter */] => { + const splits = []; + let textSplit = text; + let quit = false; + + while (!quit) { + const pos = textSplit.indexOf(sep); + + const [currentSplit, rest, innerQuit]: [string, string, boolean] = + pos < 0 || splits.length + 1 === max + ? [textSplit, "", true] + : [ + textSplit.slice(0, pos), + textSplit.slice(pos + sep.length), + false, + ]; + + const trimmed: string = trim ? currentSplit.trim() : currentSplit; + + if (keepEmpty || trimmed.length >= 1) { + splits.push(trimmed); + } + + textSplit = rest; + quit = innerQuit; + } + + return [splits]; + }; + + const setter = ([val]: [string]): string => { + return val; + }; + + return (dict: ProfunctorDict, f0: (s: string[]) => string) => { + const f1 = dict.first(f0); + const f2 = dict.dimap(getter, setter, f1 as any); + return f2; + }; +}; diff --git a/ts/src/template/optics/stripped.ts b/ts/src/template/optics/stripped.ts new file mode 100644 index 0000000..022940b --- /dev/null +++ b/ts/src/template/optics/stripped.ts @@ -0,0 +1,48 @@ +import type { ProfunctorDict } from "./profunctors"; +import type { Optic } from "./utils"; +import { WeakCircumfix, weakCircumfixToCircumfix } from "./circumfix"; +import { escapeRegExp, regExpString } from "./utils"; + +export const strippedRegex = (wc: WeakCircumfix): Optic => { + const { before, after } = weakCircumfixToCircumfix(wc); + + const getter = (text: string): [string, null] => { + const regex = new RegExp( + `^${regExpString(before)}(.*?)${regExpString(after)}$`, + "su", + ); + let match = text; + + text.replace(regex, (_match: string, inner: string): string => { + match = inner; + return ""; + }); + + return [match, null]; + }; + + const setter = ([repl]: [string, null]): string => { + return repl; + }; + + return ( + dict: ProfunctorDict, + f0: (s: string) => string, + ): ((s: string) => string) => { + const f1 = dict.first(f0); + const f2 = dict.dimap(getter, setter, f1 as any); + return f2 as any; + }; +}; + +export const stripped = ({ + before, + after, +}: { + before: string; + after: string; +}): Optic => + strippedRegex({ + before: `^${escapeRegExp(before)}`, + after: `${escapeRegExp(after)}$`, + }); diff --git a/ts/src/template/optics/templated.ts b/ts/src/template/optics/templated.ts new file mode 100644 index 0000000..eca1ade --- /dev/null +++ b/ts/src/template/optics/templated.ts @@ -0,0 +1,84 @@ +import type { ProfunctorDict } from "./profunctors"; +import type { Optic } from "./utils"; +import { WeakCircumfix, weakCircumfixToCircumfix } from "./circumfix"; +import { escapeRegExp } from "./utils"; + +const temp = (strings: string[], keys: number[]) => { + return (values: string[]): string => { + const dict = values[values.length - 1] || {}; + + const result = [strings[0]]; + keys.forEach((key, index) => { + const value = Number.isInteger(key) + ? values[key] + : (dict as any)[key]; + result.push(value, strings[index + 1]); + }); + + return result.join(""); + }; +}; + +// Setter +export const templatedRegex = (wc: WeakCircumfix): Optic => { + type TemplatedResult = [string[], (repls: string[]) => string]; + + const { before, after } = weakCircumfixToCircumfix(wc); + + const getter = (text: string): TemplatedResult => { + //[string[], => { + const regex = new RegExp(`(${before})(.*?)(${after})`, "gsu"); + + const parts: string[] = []; + const matches: string[] = []; + let lastOffset = 0; + + text.replace( + regex, + ( + match: string, + before: string, + inner: string, + after: string, + offset: number, + ): string => { + const part = text.substring(lastOffset, offset + before.length); + + parts.push(part); + matches.push(inner); + lastOffset = offset + match.length - after.length; + + return ""; + }, + ); + + parts.push(text.substring(lastOffset)); + + const indices = [...Array(matches.length).keys()]; + const templ = temp(parts, indices); + + return [matches, templ]; + }; + + const setter = ([repls, templ]: TemplatedResult): string => { + return templ(repls); + }; + + return (dict: ProfunctorDict, f0: (s: string[]) => string[]) => { + const f1 = dict.first(f0); + const f2 = dict.dimap(getter, setter, f1 as any); + return f2; + }; +}; + +export const templated = ({ + before, + after, +}: { + before: string; + after: string; +}): Optic => + templatedRegex({ + before: escapeRegExp(before), + after: escapeRegExp(after), + }); diff --git a/ts/src/template/optics/utils.ts b/ts/src/template/optics/utils.ts new file mode 100644 index 0000000..756a61f --- /dev/null +++ b/ts/src/template/optics/utils.ts @@ -0,0 +1,15 @@ +import { ProfunctorDict } from "./profunctors"; + +// to hard to type in TS +export type Optic = ( + dict: ProfunctorDict, + f: (a: any) => any, +) => (x: any) => any; + +export const escapeRegExp = (s: string): string => { + return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); +}; + +export const regExpString = (re: string | RegExp): string => { + return re instanceof RegExp ? re.source : re; +}; diff --git a/ts/src/template/parser/.gitignore b/ts/src/template/parser/.gitignore new file mode 100644 index 0000000..a5c0e28 --- /dev/null +++ b/ts/src/template/parser/.gitignore @@ -0,0 +1 @@ +grammar.ts diff --git a/ts/src/template/parser/grammar.ne b/ts/src/template/parser/grammar.ne new file mode 100644 index 0000000..6fe92d7 --- /dev/null +++ b/ts/src/template/parser/grammar.ne @@ -0,0 +1,48 @@ +@{% +import { TextNode, EscapedNode } from '../nodes' + +import { tagBuilder } from './tagBuilder' +%} + +@preprocessor typescript +@lexer tokenizer + +################################# + +start -> content {% id %} + +content -> node:* {% id %} +node -> text {% id %} + | inlinetag {% id %} + | blocktag {% id %} + +text -> %text {% ([match]) => new TextNode(match.value) %} + | %escapeseq {% ([match]) => new EscapedNode(match.value) %} + +inlinetag -> %inlineopen inline {% + ([/* open */, [name, inlineNodes, hasInline]]) => tagBuilder.build( + name.value, + inlineNodes, + hasInline, + ) +%} + +blocktag -> %blockopen inline content blockclose {% + ([/* open */, [name, inlineNodes, hasInline], blockNodes, closename], _location, reject) => + !closename || name.value === closename.value + ? tagBuilder.build( + name.value, + inlineNodes, + hasInline, + blockNodes, + true, + ) + : reject +%} + +inline -> %keyname (%sep content):? %close {% ([name, match]) => match + ? [name, match[1], true] + : [name, [], false] +%} + +blockclose -> %blockclose %keyname:? %close {% ([,name]) => name %} diff --git a/ts/src/template/parser/index.ts b/ts/src/template/parser/index.ts new file mode 100644 index 0000000..ee8b5a9 --- /dev/null +++ b/ts/src/template/parser/index.ts @@ -0,0 +1,90 @@ +import type { Delimiters } from "../delimiters"; +import type { TagBuilderSettings } from "./tagBuilder"; +import type { ASTNode } from "../nodes"; +import type { NearleyLexer } from "./grammar"; +import type { Lexer } from "moo"; + +import { Parser as NearleyParser, Grammar } from "nearley"; +import makeGrammar from "./grammar"; + +import { TextNode, DocSeparatorNode } from "../nodes"; +import { defaultDelimiters } from "../delimiters"; +import { intersperse2d } from "../utils"; +import { tagBuilder } from "./tagBuilder"; +import { makeLexer } from "./tokenizer"; + +const coreParse = (text: string, grammar: Grammar): ASTNode[][] => { + const parser = new NearleyParser(grammar); + + try { + const result = parser.feed(text).results; + return result; + } catch (e) { + console.log( + `Will default to Trivial Base Tag, due to error parsing text:`, + e, + ); + return [[new TextNode(text)]]; + } +}; + +const mainParse = (text: string, grammar: Grammar): ASTNode[] => { + const parsed = coreParse(text, grammar); + + if (parsed.length > 1) { + console.log("Ambiguous template grammar"); + } + + return parsed[0]; +}; + +const fragmentsParse = ( + textFragments: string[], + grammar: Grammar, +): ASTNode[] => [ + ...intersperse2d( + textFragments.map((fragment: string) => mainParse(fragment, grammar)), + new DocSeparatorNode(), + ), +]; + +export enum BaseDepth { + Single = 1, + Fragments = 2, +} + +export class Parser { + public delimiters: Delimiters; + public lexer: Lexer; + public grammar: Grammar; + + protected tagBuilderSettings: TagBuilderSettings = [new Map(), new Map()]; + + constructor(delimiters?: Delimiters) { + this.delimiters = delimiters ?? defaultDelimiters; + this.lexer = makeLexer(this.delimiters); + // Force silence moo/nearley inconsistency + this.grammar = Grammar.fromCompiled( + makeGrammar((this.lexer as unknown) as NearleyLexer), + ); + } + + update(delimiters?: Partial) { + this.delimiters = Object.assign({}, defaultDelimiters, delimiters); + this.lexer = makeLexer(this.delimiters); + // Force silence moo/nearley inconsistency + this.grammar = Grammar.fromCompiled( + makeGrammar((this.lexer as unknown) as NearleyLexer), + ); + } + + parse(texts: string[]): ASTNode[] { + tagBuilder.push(this.tagBuilderSettings, this.delimiters); + return fragmentsParse(texts, this.grammar); + } + + rawParse(text: string): ASTNode[] { + tagBuilder.push(this.tagBuilderSettings, this.delimiters); + return mainParse(text, this.grammar); + } +} diff --git a/ts/src/template/parser/tagBuilder.ts b/ts/src/template/parser/tagBuilder.ts new file mode 100644 index 0000000..60db525 --- /dev/null +++ b/ts/src/template/parser/tagBuilder.ts @@ -0,0 +1,74 @@ +import type { ASTNode } from "../nodes"; +import type { Delimiters } from "../delimiters"; + +import { TagNode } from "../nodes"; + +/** + * works with keys, but also with tag identifier + */ +export const keySeparationPattern = /^((?:[a-zA-Z_/]|%\w)+)(\d*)(?::(\d+))?$/u; + +const getAndInc = (map: Map, key: string): number => { + const result = (map.get(key) ?? -1) + 1; + + map.set(key, result); + return result; +}; + +export type TagBuilderSettings = [Map, Map]; + +class TagBuilder { + /** These settings being non-null must be enforced during usage */ + protected tagCounter: Map | null = null; + protected tagCounterFull: Map | null = null; + protected delimiters: Delimiters | null = null; + + protected increment(fullKey: string, key: string): [number, number] { + return [ + getAndInc(this.tagCounterFull as Map, fullKey), + getAndInc(this.tagCounter as Map, key), + ]; + } + + build( + fullKey: string, + inlineNodes: ASTNode[], + hasInline: boolean, + blockNodes: ASTNode[] = [], + hasBlock = false, + ): TagNode { + const match = fullKey.match(keySeparationPattern); + + if (!match) { + throw new Error("Could not match key. This should never happen."); + } + + const key = match[1]; + const num = match[2].length > 0 ? Number(match[2]) : null; + + const [fullOccur, occur] = this.increment(fullKey, key); + + return new TagNode( + fullKey, + key, + num, + fullOccur, + occur, + this.delimiters as Delimiters, + inlineNodes, + hasInline, + blockNodes, + hasBlock, + ); + } + + push(settings: TagBuilderSettings, delimiters: Delimiters): void { + this.tagCounterFull = settings[0]; + this.tagCounter = settings[1]; + + this.delimiters = delimiters; + } +} + +// singleton +export const tagBuilder = new TagBuilder(); diff --git a/ts/src/template/parser/tokenizer.ts b/ts/src/template/parser/tokenizer.ts new file mode 100644 index 0000000..f4605eb --- /dev/null +++ b/ts/src/template/parser/tokenizer.ts @@ -0,0 +1,127 @@ +import type { Delimiters } from "../delimiters"; +import type { Lexer } from "moo"; + +import { states } from "moo"; + +export const keyPattern = /(?:[a-zA-Z_]|%\w)+\d*/u; + +// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#Escaping +const escapeRegExp = (str: string): string => + str.replace(/[.*+\-?^${}()|[\]\\]/g, "\\$&"); + +export const makeLexer = (delimiters: Delimiters): Lexer => { + const escapedOpen = escapeRegExp(delimiters.open); + const escapedClose = escapeRegExp(delimiters.close); + + const outerTextPattern = new RegExp( + `[\\s\\S]+?(?=\\\\|${escapedOpen}|$)`, + "u", + ); + const innerTextPattern = new RegExp( + `[\\s\\S]+?(?=\\\\|${escapedOpen}|${escapedClose})`, + "u", + ); + const escapedSequence = new RegExp( + `\\\\(?:${escapedOpen}|${escapedClose})?`, + "u", + ); + + const inlineopen = { + match: delimiters.open, + push: "inlinekey", + }; + + const blockopen = { + match: `${delimiters.open}#`, + push: "blockkey", + }; + + const blockclose = { + match: `${delimiters.open}/`, + push: "blockclose", + }; + + const closepop = { + match: delimiters.close, + pop: 1, + }; + + const closenext = { + match: delimiters.close, + next: "blockmain", + }; + + const keyname = { + match: keyPattern, + }; + + const maintext = { + match: outerTextPattern, + lineBreaks: true, + }; + + const text = { + match: innerTextPattern, + lineBreaks: true, + }; + + const escapeseq = { + match: escapedSequence, + }; + + const sep = (state: string) => ({ + match: delimiters.sep, + next: state, + }); + + // img tags are parsed via HTML (!) + return states({ + main: { + blockopen, + inlineopen, + escapeseq, + text: maintext, + }, + + blockkey: { + keyname, + sep: sep("blocktag"), + close: closenext, + }, + + inlinekey: { + keyname, + sep: sep("inlinetag"), + close: closepop, + }, + + blocktag: { + blockopen, + inlineopen, + escapeseq, + close: closenext, + text, + }, + + inlinetag: { + blockopen, + inlineopen, + escapeseq, + close: closepop, + text, + }, + + blockmain: { + blockopen, + blockclose, + inlineopen, + escapeseq, + text: maintext, + }, + + blockclose: { + keyname, + close: closepop, + }, + }) as Lexer; +}; diff --git a/ts/src/template/tagSelector/.gitignore b/ts/src/template/tagSelector/.gitignore new file mode 100644 index 0000000..a5c0e28 --- /dev/null +++ b/ts/src/template/tagSelector/.gitignore @@ -0,0 +1 @@ +grammar.ts diff --git a/ts/src/template/tagSelector/grammar.ne b/ts/src/template/tagSelector/grammar.ne new file mode 100644 index 0000000..877853c --- /dev/null +++ b/ts/src/template/tagSelector/grammar.ne @@ -0,0 +1,146 @@ +@{% +import { tagSelectorTokenizer } from './tokenizer' +import { keySeparationPattern } from '../parser/tagBuilder' +%} + +@preprocessor typescript +@lexer tagSelectorTokenizer + +################################# + +start -> allStar {% id %} + | pattern {% id %} + +allStar -> %allStar {% () => () => true %} + +pattern -> key num occur {% + ([keyPattern, numPred, occurPred]) => (key: string, num: number | null | undefined, fullOccur: number | null) => { + if (typeof num === 'undefined') { + const match = key.match(keySeparationPattern) + + if (!match) { + return false + } + + const actualKey = match[1] + + const maybeNum = Number(match[2]) + const actualNum = Number.isNaN(maybeNum) + ? null + : maybeNum + + return keyPattern.test(actualKey) && numPred(actualNum) && occurPred(fullOccur) + } + else { + return keyPattern.test(key) && numPred(num) && occurPred(fullOccur) + } + } +%} + +key -> (keyComps):+ {% ([vals]) => new RegExp(`^${vals.join('')}$`, 'u') %} + +keyComps -> mixedText {% id %} + | escapeseq {% id %} + | keyGroup {% id %} + +mixedText -> %text {% ([val]) => val.value %} + | %star {% () => '.*' %} + | %slash {% () => '\\\\' %} + +escapeseq -> %escapeseq {% ([val]) => val.value %} + +keyGroup -> %groupOpen keyGroupInner %groupClose {% + ([,inner]) => inner.join('') +%} + +keyGroupInner -> keyGroupItem (%groupAlternative keyGroupItem):* {% + ([val, alts]) => [val, ...alts.map((alt: any) => alt.value)] +%} + +keyGroupItem -> (mixedText):* {% + ([vals]) => vals.join('') +%} + +################################# + +num -> (numPredicate):? {% + ([pred]) => pred ? pred[0] : (num: number | null) => num === null +%} + +numPredicate -> numDigits {% id %} + | numStar {% id %} + | numGroup {% id %} + +numDigits -> %numDigits {% ([val]) => (num: number | null) => num === Number(val.value) %} + +numStar -> %numStar {% () => (num: number | null) => true %} + +numGroup -> %groupOpen numGroupInner %numGroupClose {% + ([,preds]) => (num: number | null) => preds.reduce((accu: boolean, pred: any) => accu || pred(num), false) +%} + +numGroupInner -> numGroupItem (%groupAlternative numGroupItem):* {% + ([first,rest]) => [ + first, + ...rest.map(([,item]: any) => item), + ] +%} + +numGroupItem -> (numGroupPredicate):? {% + ([pred]) => pred ? pred[0] : (num: number | null) => num === null +%} + +numGroupPredicate -> digits {% id %} + | range {% id %} + | multiple {% id %} + +digits -> %digits {% ([val]) => (num: number | null) => num === Number(val.value) %} + +range -> bounded {% id %} + | leftBounded {% id %} + | rightBounded {% id %} + +bounded -> %digits %rangeSpecifier %digits {% + ([leftVal,,rightVal]) => (num: number | null) => ( + typeof num === 'number' && + Number(leftVal.value) <= num && + num <= Number(rightVal.value) + ) +%} + +leftBounded -> %digits %rangeSpecifier {% + ([val]) => (num: number) => typeof num === 'number' && Number(val.value) <= num +%} + +rightBounded -> %rangeSpecifier %digits {% + ([,val]) => (num: number) => typeof num === 'number' && num <= Number(val.value) +%} + +multiple -> %digits %multiplierSeq %digits {% + ([mult,,base]) => (num: number) => (num - base) % mult === 0 +%} + +################################# + +occur -> (%occurSep occurPredicate):? {% + ([pred]) => pred + ? (occur: number | null) => occur === null ? true : pred[1](occur) + : () => true +%} + +occurPredicate -> numDigits {% id %} + | numStar {% id %} + | occurGroup {% id %} + +occurGroup -> %groupOpen occurGroupInner %groupClose {% + ([,preds]) => (num: number) => preds.reduce((pred: any, accu: boolean) => accu || pred(num), false) +%} + +# no empty predicate possible for occur (can't be null) +# so we can skip numGroupItem +occurGroupInner -> numGroupPredicate (%groupAlternative numGroupPredicate):* {% + ([first,rest]) => [ + first, + ...rest.map(([,item]: any) => item), + ] +%} diff --git a/ts/src/template/tagSelector/index.ts b/ts/src/template/tagSelector/index.ts new file mode 100644 index 0000000..430df19 --- /dev/null +++ b/ts/src/template/tagSelector/index.ts @@ -0,0 +1,77 @@ +import { Grammar, Parser } from "nearley"; +import grammar from "./grammar"; + +import { keySeparationPattern } from "../parser/tagBuilder"; + +type TagPredicate = ( + key: string, + num: number | null | undefined, + occur: number | null, +) => boolean; + +const tagPredicateGrammar = Grammar.fromCompiled(grammar); + +const parseTagSelector = (selector: string): TagPredicate => { + /** + * `TagPredicate` can be used in three ways: + * 1. pred(key, num, fullOccur) + * 2. pred(fullKey, undefined, occur) + * 3. pred(fullKey, undefined, fullOccur) + * + * all of the following combinations uniquely identifies tags: + * 1. (key, num, fullOccur) + * 2. (fullKey, fullOccur) + * 3. (fullKey, occur) + * + * passing in null for `occur` means to ignore the occurrence numbers (always true) + */ + + const parser = new Parser(tagPredicateGrammar); + + try { + const parsed = parser.feed(selector).results; + + if (parsed.length > 1) { + console.log("Tag selector is ambiguous: ", selector, parsed); + } + + return parsed[0]; + } catch (e) { + console.log("Tag selector failed to parse: ", e); + return () => false; + } +}; + +export class TagSelector { + predicate: TagPredicate; + + protected constructor(selector: string) { + this.predicate = parseTagSelector(selector); + } + + static make(selector: string) { + return new TagSelector(selector); + } + + check(key: string, num: number | null, occur: number | null = null) { + return this.predicate(key, num, occur); + } + + checkFullKey(key: string, occur: number | null = null) { + return this.predicate(key, undefined, occur); + } + + checkTagIdentifier(identifier: string) { + const match = identifier.match(keySeparationPattern); + + if (!match) { + return false; + } + + const key = match[1]; + const num = match[2].length > 0 ? Number(match[2]) : null; + const occur = match[3] ? Number(match[3]) : null; + + return this.predicate(key, num, occur); + } +} diff --git a/ts/src/template/tagSelector/tokenizer.ts b/ts/src/template/tagSelector/tokenizer.ts new file mode 100644 index 0000000..f997814 --- /dev/null +++ b/ts/src/template/tagSelector/tokenizer.ts @@ -0,0 +1,66 @@ +import type { Lexer } from "moo"; + +import { compile } from "moo"; + +export const tagSelectorTokenizer: Lexer = compile({ + text: { + match: /[a-zA-Z_]+/u, + }, + + numDigits: { + match: /\d+(?=:|$)/u, + }, + + digits: { + match: /\d+/u, + }, + + slash: { + match: "/", + }, + + escapeseq: { + match: /%\w/u, + }, + + // has to come before star + multiplierSeq: { + match: /\*n\+/u, + }, + + allStar: { + match: /^\*$/u, + }, + + numStar: { + match: /\*(?=:|$)/u, + }, + + star: { + match: "*", + }, + + groupOpen: { + match: "{", + }, + + groupAlternative: { + match: ",", + }, + + rangeSpecifier: { + match: /-/u, + }, + + numGroupClose: { + match: /\}(?=:|$)/u, + }, + + groupClose: { + match: "}", + }, + + occurSep: { + match: ":", + }, +}); diff --git a/ts/src/template/template.ts b/ts/src/template/template.ts new file mode 100644 index 0000000..912955a --- /dev/null +++ b/ts/src/template/template.ts @@ -0,0 +1,119 @@ +import type { ASTNode } from "./nodes"; +import type { TagAccessor } from "./types"; +import type { Delimiters } from "./delimiters"; + +import { nodesAreReady } from "./nodes"; +import { Parser } from "./parser"; + +export interface TemplateInfo { + template: Template; + parser: Parser; +} + +export interface IterationInfo { + iteration: number; +} + +export interface ResultInfo { + result: string | string[]; + [k: string]: unknown; +} + +// TagRenderer -> TagAcessor -> TagProcessor +export interface TagRenderer { + makeAccessor: (t: TemplateInfo, i: IterationInfo) => TagAccessor; + finishIteration: (t: TemplateInfo, i: IterationInfo) => void; + finishRun: (t: TemplateInfo, x: ResultInfo) => void; +} + +const MAX_ITERATIONS = 50; + +const stringifyNodes = (nodes: ASTNode[]): string[] => { + const result: string[] = []; + + if (nodes.length === 0) { + return result; + } + + let currentString = ""; + + for (const node of nodes) { + const stringified = node.toString(); + + if (typeof stringified === "string") { + currentString += stringified; + } else { + result.push(currentString); + currentString = ""; + } + } + + result.push(currentString); + + return result; +}; + +export class Template { + readonly textFragments: string[]; + readonly parser: Parser; + + readonly nodes: ASTNode[]; + + protected constructor( + fragments: string[], + preparsed: ASTNode[] | null, + delimiters?: Delimiters, + ) { + this.textFragments = fragments; + this.parser = new Parser(delimiters); + this.nodes = preparsed ?? this.parser.parse(fragments); + } + + static make(text: string, delimiters?: Delimiters) { + return new Template([text], null, delimiters); + } + + static makeFromFragments(texts: string[], delimiters?: Delimiters) { + return new Template(texts, null, delimiters); + } + + render( + tagRenderer: TagRenderer, + callback: (t: string[]) => void = () => { + /* do nothing */ + }, + ): string[] { + const templateInfo: TemplateInfo = { + template: this, + parser: this.parser, + }; + + let nodes = this.nodes; + let ready = false; + + for (let i = 0; i < MAX_ITERATIONS && !ready; i++) { + const iterationInfo: IterationInfo = { + iteration: i, + }; + + const tagAccessor = tagRenderer.makeAccessor( + templateInfo, + iterationInfo, + ); + + nodes = nodes.flatMap((node: ASTNode, index: number) => + node.evaluate(this.parser, tagAccessor, [index]), + ); + ready = nodes.reduce(nodesAreReady, true); + + tagRenderer.finishIteration(templateInfo, iterationInfo); + } + + const result = stringifyNodes(nodes); + + callback(result); + tagRenderer.finishRun(templateInfo, { result: result }); + + return result; + } +} diff --git a/ts/src/template/types.ts b/ts/src/template/types.ts new file mode 100644 index 0000000..18ed9d4 --- /dev/null +++ b/ts/src/template/types.ts @@ -0,0 +1,40 @@ +import type { ASTNode, TagNode } from "./nodes"; +import type { Optic } from "./optics"; + +export type TagPath = number[]; + +export enum Status { + Ready, + NotReady, + ContinueTags, + // when ready == true; result needs parsing, iterate down the new structure + ContainsTags, + // when ready == false; result needs parsing, but continue in normal order +} + +export interface ProcessorOutput { + result: string | ASTNode[] | null; + status: Status; +} + +export interface DataOptions { + optics?: Optic[]; + blockOptics: Optic[]; + inlineOptics: Optic[]; + capture: boolean; + [k: string]: unknown; +} + +export interface RoundInfo { + path: TagPath; + depth: number; + ready: boolean; + isCapture: boolean; +} + +export type TagAccessor = (name: string) => TagProcessor; + +export interface TagProcessor { + execute: (data: TagNode, round: RoundInfo) => ProcessorOutput; + getOptions: () => DataOptions; +} diff --git a/ts/src/template/utils.ts b/ts/src/template/utils.ts new file mode 100644 index 0000000..a2d1c82 --- /dev/null +++ b/ts/src/template/utils.ts @@ -0,0 +1,18 @@ +export function* intersperse2d( + lists: T[][], + delim: T, +): Generator { + let first = true; + + for (const list of lists) { + if (first) { + first = false; + } else { + yield delim; + } + + for (const item of list) { + yield item; + } + } +} diff --git a/ts/src/types.ts b/ts/src/types.ts new file mode 100644 index 0000000..4dffb85 --- /dev/null +++ b/ts/src/types.ts @@ -0,0 +1,117 @@ +import type { FilterApi } from "./filterManager/filters"; +import type { ManagerInfo } from "./filterManager/index"; +import type { + Filter as FilterType, + WeakFilter as WeakFilterType, +} from "./filterManager/filters"; +import type { RegistrarApi } from "./filterManager/registrar"; +import type { DeferredEntry as DefEntry } from "./filterManager/deferred"; +export type { DeferredOptions } from "./filterManager/deferred"; + +import type { TagNode } from "./template/nodes"; +import type { TemplateInfo, IterationInfo, ResultInfo } from "./template"; +import type { RoundInfo, DataOptions } from "./template/types"; + +export type Internals

> = ManagerInfo< + TagNode, + TemplateInfo, + IterationInfo, + RoundInfo, + ResultInfo, + DataOptions, + P +> & + TemplateInfo & + IterationInfo & + RoundInfo; +export type DeferredInternals

> = ManagerInfo< + TagNode, + TemplateInfo, + IterationInfo, + RoundInfo, + ResultInfo, + DataOptions, + P +> & + TemplateInfo & + IterationInfo; +export type AftermathInternals

> = ManagerInfo< + TagNode, + TemplateInfo, + IterationInfo, + RoundInfo, + ResultInfo, + DataOptions, + P +> & + TemplateInfo; +export type DeferredEntry

> = DefEntry< + DeferredInternals

+>; +export type AftermathEntry

> = DefEntry< + AftermathInternals

+>; + +export type Registrar

> = RegistrarApi< + TagNode, + Internals

, + DataOptions +>; +export type Filters

> = FilterApi< + TagNode, + Internals

+>; +export type Filter

> = FilterType< + TagNode, + Internals

+>; +export type WeakFilter

> = WeakFilterType< + TagNode, + Internals

+>; +export type Recipe> = ( + options?: Record, +) => (filters: Registrar) => void; +export type Eval, U> = ( + t: TagNode, + i: Internals, +) => U; + +export type { + FilterResult, + WeakFilterResult, + FilterApi, +} from "./filterManager/filters"; +export type { DeferredApi, Deferred } from "./filterManager/deferred"; + +export type { DataOptions } from "./template/types"; +export type { TagNode } from "./template/nodes"; +export type { Optic, WeakSeparator, WeakCircumfix } from "./template/optics"; + +export type { Stylizer } from "./stylizer"; + +export type Triple = [T, T, T]; + +export interface WrapOptions { + wrapId: string; + getTagnames: (o: Record) => string[]; + setTagnames: ( + o: Record, + newNames: string[], + ) => Record; +} + +export type InactiveBehavior> = ( + contexter: WeakFilter, + ellipser: WeakFilter, +) => WeakFilter; + +export type InactiveAdapter> = ( + behavior: InactiveBehavior, +) => InactiveBehavior; + +export interface RecipeOptions { + [key: string]: any; +} + +export type Un = Record; diff --git a/ts/src/utils.ts b/ts/src/utils.ts new file mode 100644 index 0000000..c86e9a9 --- /dev/null +++ b/ts/src/utils.ts @@ -0,0 +1,49 @@ +export const id = (v: T): T => v; +export const id2 = (_v: U, w: T): T => w; +export const constant = (x: T) => (_y: U) => x; + +export const zeroWidthSpace = "​"; + +export const shuffle = (array: T[]): T[] => { + const result = array.slice(0); + let currentIndex = array.length; + + // While there remain elements to shuffle... + while (currentIndex !== 0) { + // Pick a remaining element... + const randomIndex = Math.floor(Math.random() * currentIndex); + currentIndex -= 1; + + // And swap it with the current element. + const temporaryValue: T = result[currentIndex]; + result[currentIndex] = result[randomIndex]; + result[randomIndex] = temporaryValue; + } + + return result; +}; + +export const sortWithIndices = (items: T[], indices: number[]): T[] => { + const result: T[] = []; + + for (const idx of indices) { + const maybeItem: T = items[idx]; + + if (maybeItem) { + result.push(maybeItem); + } + } + + if (indices.length < items.length) { + const remainingItemIndices: number[] = Array.from( + new Array(items.length - indices.length), + (_x, i) => i + indices.length, + ); + + for (const idx of remainingItemIndices) { + result.push(items[idx]); + } + } + + return result; +}; diff --git a/ts/src/wrappers/collection.ts b/ts/src/wrappers/collection.ts new file mode 100644 index 0000000..b174019 --- /dev/null +++ b/ts/src/wrappers/collection.ts @@ -0,0 +1,25 @@ +import type { Registrar, Recipe, WrapOptions } from "../types"; + +import { defaultTagnameGetter, defaultTagnameSetter } from "./wrappers"; + +type RecipePart> = [ + Recipe, + Partial, +]; + +export const collection = >( + recipeParts: RecipePart[], +): Recipe => (options?: Record) => ( + registrar: Registrar, +) => { + const theOptions = options ?? {}; + + for (const [recipe, wrapOptions = {}] of recipeParts) { + const { + setTagnames = defaultTagnameSetter, + getTagnames = defaultTagnameGetter, + } = wrapOptions; + + recipe(setTagnames(theOptions, getTagnames(theOptions)))(registrar); + } +}; diff --git a/ts/src/wrappers/index.ts b/ts/src/wrappers/index.ts new file mode 100644 index 0000000..08fdbbe --- /dev/null +++ b/ts/src/wrappers/index.ts @@ -0,0 +1,8 @@ +export { + wrap, + wrapWithDeferred as deferred, + wrapWithAftermath as aftermath, +} from "./wrappers"; +export { sum, sumFour } from "./sum"; +export { product } from "./product"; +export { collection } from "./collection"; diff --git a/ts/src/wrappers/product.ts b/ts/src/wrappers/product.ts new file mode 100644 index 0000000..76725be --- /dev/null +++ b/ts/src/wrappers/product.ts @@ -0,0 +1,55 @@ +import type { + TagNode, + RecipeOptions, + Registrar, + WeakFilter, + WeakFilterResult, + Internals, + Recipe, + WrapOptions, +} from "../types"; + +import { defaultTagnameGetter, defaultTagnameSetter } from "./wrappers"; + +export const product = >( + recipeFirst: Recipe, + recipeSecond: Recipe, + multiply: ( + fst: WeakFilterResult, + snd: WeakFilterResult, + ) => WeakFilter = () => () => ({ ready: true }), + { wrapId, setTagnames }: WrapOptions = { + wrapId: "product", + getTagnames: defaultTagnameGetter, + setTagnames: defaultTagnameSetter, + }, +): Recipe => ({ + tagname = "prod", + + optionsFirst = {}, + optionsSecond = {}, +}: { + tagname?: string; + + optionsFirst?: RecipeOptions; + optionsSecond?: RecipeOptions; +} = {}) => (registrar: Registrar) => { + const tagnameTrue = `${tagname}:${wrapId}:fst`; + const tagnameFalse = `${tagname}:${wrapId}:snd`; + + recipeFirst(setTagnames(optionsFirst, [tagnameTrue]))(registrar); + recipeSecond(setTagnames(optionsSecond, [tagnameFalse]))(registrar); + + const productFilter = (tag: TagNode, internals: Internals) => { + return multiply( + internals.filters.getOrDefault(tagnameTrue)(tag, internals), + internals.filters.getOrDefault(tagnameFalse)(tag, internals), + )(tag, internals); + }; + + registrar.register( + tagname, + productFilter, + registrar.getOptions(tagnameTrue /* have to be same for True/False */), + ); +}; diff --git a/ts/src/wrappers/sum.ts b/ts/src/wrappers/sum.ts new file mode 100644 index 0000000..7569656 --- /dev/null +++ b/ts/src/wrappers/sum.ts @@ -0,0 +1,112 @@ +import type { + TagNode, + Registrar, + Eval, + Internals, + Recipe, + WrapOptions, +} from "../types"; + +import { defaultTagnameGetter, defaultTagnameSetter } from "./wrappers"; + +export const sum = >( + recipeFalse: Recipe, + recipeTrue: Recipe, + predicate: Eval, + { wrapId, setTagnames }: WrapOptions = { + wrapId: "sum", + getTagnames: defaultTagnameGetter, + setTagnames: defaultTagnameSetter, + }, +): Recipe => ({ + tagname = "sum", + + optionsFalse = {}, + optionsTrue = {}, +}: { + tagname?: string; + + optionsTrue?: Record; + optionsFalse?: Record; +} = {}) => (registrar: Registrar) => { + const tagnameFalse = `${tagname}:${wrapId}:false`; + const tagnameTrue = `${tagname}:${wrapId}:true`; + + recipeFalse(setTagnames(optionsFalse, [tagnameFalse]))(registrar); + recipeTrue(setTagnames(optionsTrue, [tagnameTrue]))(registrar); + + const sumFilter = (tag: TagNode, internals: Internals) => { + return predicate(tag, internals) + ? internals.filters.getOrDefault(tagnameTrue)(tag, internals) + : internals.filters.getOrDefault(tagnameFalse)(tag, internals); + }; + + registrar.register( + tagname, + sumFilter, + registrar.getOptions(tagnameTrue /* have to be same for True/False */), + ); +}; + +export const sumFour = >( + recipeZero: Recipe, + recipeOne: Recipe, + recipeTwo: Recipe, + recipeThree: Recipe, + predicateOne: Eval, + predicateTwo: Eval, + { wrapId, setTagnames }: WrapOptions = { + wrapId: "sumFour", + getTagnames: defaultTagnameGetter, + setTagnames: defaultTagnameSetter, + }, +): Recipe => ({ + tagname = "sum", + + optionsZero = {}, + optionsOne = {}, + optionsTwo = {}, + optionsThree = {}, +}: { + tagname?: string; + + optionsThree?: Record; + optionsTwo?: Record; + optionsOne?: Record; + optionsZero?: Record; + + setTagname?: (options: Record, newName: string) => void; +} = {}) => (registrar: Registrar) => { + const tagnameZero = `${tagname}:${wrapId}:zero`; + const tagnameTwo = `${tagname}:${wrapId}:two`; + + sum( + recipeZero, + recipeOne, + predicateOne, + )({ + tagname: tagnameZero, + + optionsFalse: setTagnames(optionsZero, [tagnameZero]), + optionsTrue: setTagnames(optionsOne, [tagnameZero]), + })(registrar); + + sum( + recipeTwo, + recipeThree, + predicateOne, + )({ + tagname: tagnameTwo, + + optionsFalse: setTagnames(optionsTwo, [tagnameTwo]), + optionsTrue: setTagnames(optionsThree, [tagnameTwo]), + })(registrar); + + const sumFourFilter = (tag: TagNode, internals: Internals) => { + return predicateTwo(tag, internals) + ? internals.filters.getOrDefault(tagnameTwo)(tag, internals) + : internals.filters.getOrDefault(tagnameZero)(tag, internals); + }; + + registrar.register(tagname, sumFourFilter); +}; diff --git a/ts/src/wrappers/wrappers.ts b/ts/src/wrappers/wrappers.ts new file mode 100644 index 0000000..c1cc1f0 --- /dev/null +++ b/ts/src/wrappers/wrappers.ts @@ -0,0 +1,99 @@ +import type { + TagNode, + RecipeOptions, + Registrar, + Internals, + Deferred, + Recipe, + WrapOptions, + DeferredApi, + DeferredOptions, + Un, +} from "../types"; + +interface WithInternalKeyword { + keyInternal: string; +} + +export const defaultTagnameGetter = (o: RecipeOptions): string[] => + Object.prototype.hasOwnProperty.call(o, "tagname") ? [o["tagname"]] : []; + +export const defaultTagnameSetter = ( + o: RecipeOptions, + newNames: string[], +): RecipeOptions => ({ ...o, tagname: newNames[0] }); + +const defaultWrapId = "wrapped"; + +export const wrap = ( + wrapped: ( + tag: TagNode & WithInternalKeyword, + internals: Internals, + ) => void, +) => ( + mainRecipe: Recipe, + { wrapId, getTagnames, setTagnames }: WrapOptions = { + wrapId: defaultWrapId, + getTagnames: defaultTagnameGetter, + setTagnames: defaultTagnameSetter, + }, +): Recipe => (options: RecipeOptions = {}) => ( + registrar: Registrar, +): void => { + const tagnames = getTagnames(options); + + const makeInternalKeyword = (keyword: string) => + `${keyword}:${wrapId}:internal`; + + const keywordMap = new Map(); + const alteredTagnames = tagnames.map((keyword) => { + const internalKeyword = makeInternalKeyword(keyword); + keywordMap.set(keyword, internalKeyword); + return internalKeyword; + }); + + mainRecipe(setTagnames(options, alteredTagnames))(registrar); + + const wrapFilter = (tag: TagNode, inter: Internals) => { + const internalKeyword = keywordMap.get(tag.key); + + wrapped( + Object.assign({}, tag, { keyInternal: internalKeyword }), + inter, + ); + + return inter.filters.getOrDefault(internalKeyword)(tag, inter); + }; + + for (const keyword of tagnames) { + registrar.register( + keyword, + wrapFilter, + registrar.getOptions(makeInternalKeyword(keyword)), + ); + } +}; + +const wrapWithDeferredTemplate = ( + getDeferredApi: (i: Internals) => DeferredApi, +) => ( + mainRecipe: Recipe, + action: Deferred, + options: Partial = {}, + wrapOptions: WrapOptions = { + wrapId: defaultWrapId, + getTagnames: defaultTagnameGetter, + setTagnames: defaultTagnameSetter, + }, +): Recipe => { + return wrap((t, internals: Internals) => { + getDeferredApi(internals).registerIfNotExists(t.keyInternal, action, options); + })(mainRecipe, wrapOptions); +}; + +export const wrapWithDeferred = wrapWithDeferredTemplate( + (internals) => internals.deferred, +); +export const wrapWithAftermath = wrapWithDeferredTemplate( + (internals) => internals.aftermath, +); diff --git a/ts/style/README.adoc b/ts/style/README.adoc new file mode 100644 index 0000000..ae081e3 --- /dev/null +++ b/ts/style/README.adoc @@ -0,0 +1,28 @@ += Closet CSS classees + +.General +---- +.is-front +.is-back +.is-active +.is-inactive +---- + +.Clozes +---- +.closet-cloze +.closet-cloze__answer +.closet-cloze__hint +.closet-cloze__ellipsis +---- + +.Multiple Choice +---- +.closet-multiple-choice +.closet-multiple-choice__item +.closet-multiple-choice__separator +.closet-multiple-choice__option +.closet-multiple-choice__correct +.closet-mulitple-choice__wrong +.closet-mulitple-choice__ellipsis +---- diff --git a/ts/style/_cloze.scss b/ts/style/_cloze.scss new file mode 100644 index 0000000..e16bdd1 --- /dev/null +++ b/ts/style/_cloze.scss @@ -0,0 +1,26 @@ +@use "utils"; + +@mixin cloze { + $denim-blue: #1560bd; + + .closet-cloze { + @include utils.wrap("[", "]"); + + &.is-active { + color: $denim-blue; + } + + &.is-back, + &.is-inactive { + @include utils.wrap-reset; + } + } + + .closet-cloze__hint:empty { + @include utils.placeholder("..."); + } + + .closet-cloze__ellipsis { + @include utils.placeholder("..."); + } +} diff --git a/ts/style/_multiple-choice.scss b/ts/style/_multiple-choice.scss new file mode 100644 index 0000000..47dba8a --- /dev/null +++ b/ts/style/_multiple-choice.scss @@ -0,0 +1,31 @@ +@use "utils"; + +@mixin multiple-choice { + .closet-multiple-choice { + @include utils.wrap("( ", " )"); + + &.is-inactive { + @include utils.wrap-reset; + } + } + + .closet-multiple-choice__separator { + @include utils.placeholder(", "); + } + + .closet-multiple-choice__option { + color: orange; + } + + .closet-multiple-choice__correct { + color: lime; + } + + .closet-multiple-choice__wrong { + color: red; + } + + .closet-multiple-choice__ellipsis { + @include utils.placeholder("..."); + } +} diff --git a/ts/style/_rect.scss b/ts/style/_rect.scss new file mode 100644 index 0000000..4f190d8 --- /dev/null +++ b/ts/style/_rect.scss @@ -0,0 +1,28 @@ +@use "utils"; + +@mixin rect { + .closet-rect__rect { + fill: moccasin; + stroke: olive; + + .is-active & { + fill: salmon; + stroke: yellow; + } + + .is-back.is-active & { + fill: transparent; + stroke: transparent; + } + } + + .closet-rect__ellipsis { + fill: transparent; + stroke: transparent; + } + + .closet-rect__label { + stroke: black; + stroke-width: 0.5; + } +} diff --git a/ts/style/_shuffle-question.scss b/ts/style/_shuffle-question.scss new file mode 100644 index 0000000..bcfef0a --- /dev/null +++ b/ts/style/_shuffle-question.scss @@ -0,0 +1,23 @@ +@use "utils"; + +@mixin shuffle-question($name) { + .closet-#{$name} { + @include utils.wrap("«", "»"); + + &.is-active .closet-#{$name}__item { + color: mediumpurple; + } + + &.is-inactive { + @include utils.wrap-reset; + } + } + + .closet-#{$name}__separator { + @include utils.placeholder(", "); + } + + .closet-#{$name}__ellipsis { + @include utils.placeholder("..."); + } +} diff --git a/ts/style/_shuffle.scss b/ts/style/_shuffle.scss new file mode 100644 index 0000000..dde01bc --- /dev/null +++ b/ts/style/_shuffle.scss @@ -0,0 +1,7 @@ +@use "utils"; + +@mixin shuffle { + .closet-shuffle__separator { + @include utils.placeholder(", "); + } +} diff --git a/ts/style/_utils.scss b/ts/style/_utils.scss new file mode 100644 index 0000000..aeb9bb0 --- /dev/null +++ b/ts/style/_utils.scss @@ -0,0 +1,25 @@ +@mixin wrap($before, $after) { + &::before { + content: $before; + } + + &::after { + content: $after; + } +} + +@mixin wrap-reset { + &::before { + content: normal; + } + + &::after { + content: normal; + } +} + +@mixin placeholder($s) { + &::before { + content: $s; + } +} diff --git a/ts/style/base.scss b/ts/style/base.scss new file mode 100644 index 0000000..9f36d49 --- /dev/null +++ b/ts/style/base.scss @@ -0,0 +1,32 @@ +@use "shuffle"; +@use "cloze"; +@use "multiple-choice"; +@use "shuffle-question"; +@use "rect"; + +.android .card:not(.mathjax-rendered) { + visibility: hidden; +} + +.closet-delay { + visibility: hidden; +} + +.closet-log { + color: black; + background-color: rgb(255, 147, 40); + + margin-top: 1rem; + padding: 1rem; + border-radius: 0.5rem; +} + +@include shuffle.shuffle; + +@include cloze.cloze; +@include multiple-choice.multiple-choice; +@include shuffle-question.shuffle-question("mingle"); +@include shuffle-question.shuffle-question("jumble"); +@include shuffle-question.shuffle-question("sort"); + +@include rect.rect; diff --git a/ts/style/editor.scss b/ts/style/editor.scss new file mode 100644 index 0000000..ee0aff9 --- /dev/null +++ b/ts/style/editor.scss @@ -0,0 +1,17 @@ +@use "rect"; + +img { + max-width: 100% !important; + max-height: var(--closet-max-height); +} + +.closet-select-mode { + /* slightly below top */ + vertical-align: 20%; + /* night-mode fix */ + background-color: var(--frame-bg); + border-color: var(--border); + color: var(--text-fg); +} + +@include rect.rect; diff --git a/ts/test/README.md b/ts/test/README.md new file mode 100644 index 0000000..70230dc --- /dev/null +++ b/ts/test/README.md @@ -0,0 +1,8 @@ +# Tests + +## Browser tests + +Only work if you turn off CORS for the file protocol +To do this in firefox, go to about:config, and set the following to false: + +- `security.fileuri.strict_origin_policy` diff --git a/ts/test/browser/.parentlock b/ts/test/browser/.parentlock new file mode 100644 index 0000000..e69de29 diff --git a/ts/test/browser/dist/README.md b/ts/test/browser/dist/README.md new file mode 100644 index 0000000..1233971 --- /dev/null +++ b/ts/test/browser/dist/README.md @@ -0,0 +1,3 @@ +# Test dist + +Output of build-test goes here diff --git a/ts/test/browser/index.html b/ts/test/browser/index.html new file mode 100644 index 0000000..e9122cd --- /dev/null +++ b/ts/test/browser/index.html @@ -0,0 +1,59 @@ + + + Mocha + + + + + + + + + You need to run `npm run serve-test` and open the served link! +

+ + + + + + + + + + + + +
+
+
+
+
+
get me
+
+
+
+ +
+
+
+
+
+
+
+
+
+
+
+ +
+
+ + diff --git a/ts/test/browser/specs/childnode.spec.js b/ts/test/browser/specs/childnode.spec.js new file mode 100644 index 0000000..0a9a526 --- /dev/null +++ b/ts/test/browser/specs/childnode.spec.js @@ -0,0 +1,203 @@ +import { ChildNodeSpan } from "../dist/src/browserUtils.js"; + +const assert = chai.assert; + +describe("ChildNodeSpan", () => { + before(() => { + const parent = document.querySelector("#childnodetest > .first"); + assert.strictEqual( + parent.childNodes.length, + 9, + "parent has wrong childNodes", + ); + }); + + it("defaults to the whole span", () => { + const parent = document.querySelector("#childnodetest > .first"); + const test = new ChildNodeSpan(parent); + + assert.strictEqual(test.from, 0, "from is not set correctly"); + assert.strictEqual(test.to, 8, "to is not set correctly"); + }); + + it("can be set via index arg", () => { + const parent = document.querySelector("#childnodetest > .first"); + const test = new ChildNodeSpan( + parent, + { + type: "index", + value: 0, + }, + { + type: "index", + value: 3, + }, + ); + + assert.strictEqual(test.from, 0, "from is not set correctly"); + assert.strictEqual(test.to, 3, "to is not set correctly"); + }); + + it("can be set via element arg", () => { + const parent = document.querySelector("#childnodetest > .first"); + const startElement = document.querySelector( + "#childnodetest > .first > #first-start-here", + ); + const endElement = document.querySelector( + "#childnodetest > .first > #first-end-here", + ); + + const test = new ChildNodeSpan( + parent, + { + type: "node", + value: startElement, + }, + { + type: "node", + value: endElement, + }, + ); + + assert.strictEqual(test.from, 1, "from is not set correctly"); + assert.strictEqual(test.to, 5, "to is not set correctly"); + }); + + it("can be set via predicate arg", () => { + const parent = document.querySelector("#childnodetest > .first"); + const test = new ChildNodeSpan( + parent, + { + type: "predicate", + value: (v) => v.id === "first-start-here", + }, + { + type: "predicate", + value: (v) => v.id === "first-end-here", + }, + ); + + assert.strictEqual(test.from, 1, "from is not set correctly"); + assert.strictEqual(test.to, 5, "to is not set correctly"); + }); + + it("respects exclusive argument", () => { + const parent = document.querySelector("#childnodetest > .first"); + const test = new ChildNodeSpan( + parent, + { + type: "predicate", + value: (v) => v.id === "first-start-here", + exclusive: true, + }, + { + type: "predicate", + value: (v) => v.id === "first-end-here", + exclusive: true, + }, + ); + + assert.strictEqual(test.from, 2, "from is not set correctly"); + assert.strictEqual(test.to, 4, "to is not set correctly"); + }); + + it("exclusive can make span length to 1", () => { + const parent = document.querySelector("#childnodetest > .first"); + const test = new ChildNodeSpan( + parent, + { + type: "predicate", + value: (v) => v.id === "first-start-here", + exclusive: true, + }, + { + type: "predicate", + value: (v) => v.id === "first-very-close", + exclusive: true, + }, + ); + + assert.strictEqual(test.from, 2, "from is not set correctly"); + assert.strictEqual(test.to, 2, "to is not set correctly"); + }); + + it("exclusive breaks if just one apart", () => { + const parent = document.querySelector("#childnodetest > .first"); + const test = new ChildNodeSpan( + parent, + { + type: "predicate", + value: (v) => v.id === "first-start-here", + exclusive: true, + }, + { + type: "predicate", + value: (v) => + v.previousSibling && + v.previousSibling.id === "first-start-here", + exclusive: true, + }, + ); + + assert.isFalse(test.valid, "should be invalid"); + }); + + it("respects startAtIndex with index argument", () => { + const parent = document.querySelector("#childnodetest > .first"); + const test = new ChildNodeSpan( + parent, + { + type: "index", + value: 0, + startAtIndex: 0, + }, + { + type: "index", + value: 0, + startAtIndex: 3, + }, + ); + + assert.strictEqual(test.from, 0, "from is not set correctly"); + assert.strictEqual(test.to, 3, "to is not set correctly"); + }); + + it("respects startAtIndex with predicate argument", () => { + const parent = document.querySelector("#childnodetest > .first"); + const test = new ChildNodeSpan( + parent, + { + type: "predicate", + value: (v) => v.className === "foo", + startAtIndex: 2, + }, + { + type: "predicate", + value: (v) => v.className === "foo", + startAtIndex: 4, + }, + ); + + assert.strictEqual(test.from, 3, "from is not set correctly"); + assert.strictEqual(test.to, 7, "to is not set correctly"); + }); + + it("respects automatic startAtIndex at to argument", () => { + const parent = document.querySelector("#childnodetest > .first"); + const test = new ChildNodeSpan( + parent, + { + type: "predicate", + value: (v) => v.className === "foo", + startAtIndex: 2, + }, + { + type: "predicate", + value: (v) => v.className === "foo", + }, + ); + + assert.strictEqual(test.from, 3, "from is not set correctly"); + assert.strictEqual(test.to, 3, "to is not set correctly"); + }); +}); diff --git a/ts/test/browser/specs/intersplice.spec.js b/ts/test/browser/specs/intersplice.spec.js new file mode 100644 index 0000000..4136d8f --- /dev/null +++ b/ts/test/browser/specs/intersplice.spec.js @@ -0,0 +1,35 @@ +import { interspliceChildNodes } from "../dist/src/browserUtils.js"; + +const assert = chai.assert; + +describe("interspliceChildNodes", () => { + before(() => { + const parent = document.querySelector("#intersplicetest"); + assert.strictEqual( + parent.childNodes.length, + 15, + "parent has wrong childNodes", + ); + }); + + it("finds individual nodes", () => { + const parent = document.querySelector("#intersplicetest"); + const test = interspliceChildNodes(parent, { + type: "predicate", + value: (v) => v.className === "foo", + }); + + assert.lengthOf(test, 3, "should have 3 child nodes"); + }); + + it("finds subspan from nodes", () => { + const parent = document.querySelector("#intersplicetest"); + const test = interspliceChildNodes(parent, { + type: "predicate", + value: (v) => + v.className === "bar" || v.nodeType === Node.TEXT_NODE, + }); + + assert.lengthOf(test, 3, "should have 3 child nodes"); + }); +}); diff --git a/ts/test/src/template/tagSelector.spec.ts b/ts/test/src/template/tagSelector.spec.ts new file mode 100644 index 0000000..285a978 --- /dev/null +++ b/ts/test/src/template/tagSelector.spec.ts @@ -0,0 +1,35 @@ +import { TagSelector } from "../../../src/template/tagSelector"; + +describe("parseTagSelector", () => { + describe("catch-all patterns", () => { + test("* matches everything", () => { + const pred = TagSelector.make("*"); + + expect(pred.check("c", 1, 5)).toBeTruthy(); + expect(pred.check("hello", null, 0)).toBeTruthy(); + }); + + test("** matches everything", () => { + const pred = TagSelector.make("**"); + + expect(pred.check("c", 1, 5)).toBeTruthy(); + expect(pred.check("hello", null, 0)).toBeTruthy(); + }); + + test("**:* matches everything", () => { + const pred = TagSelector.make("**:*"); + + expect(pred.check("c", 1, 5)).toBeTruthy(); + expect(pred.check("hello", null, 0)).toBeTruthy(); + }); + + test("*{} matches any text, but no nums", () => { + const pred = TagSelector.make("*{}"); + + expect(pred.check("c", null, 5)).toBeTruthy(); + expect(pred.check("hello", null, 0)).toBeTruthy(); + expect(pred.check("cr", 1, 5)).toBeFalsy(); + expect(pred.check("hella", 2, 0)).toBeFalsy(); + }); + }); +}); diff --git a/ts/tsconfig.json b/ts/tsconfig.json new file mode 100644 index 0000000..d328209 --- /dev/null +++ b/ts/tsconfig.json @@ -0,0 +1,18 @@ +{ + "extends": "@tsconfig/svelte/tsconfig.json", + "include": ["src/**/*"], + "compilerOptions": { + "target": "es6", + "strict": true, + /* Svelte template variables, e.g. on:event or @const, cannot be typed yet */ + "noImplicitAny": false, + "isolatedModules": true, + "esModuleInterop": true, + "skipLibCheck": true, + "typeRoots": ["node_modules/@types", "../anki/ts/typings"], + "paths": { + /* This alias should only be used for typing needs */ + "@anki/*": ["../anki/ts/*"] + } + } +} diff --git a/ts/yarn.lock b/ts/yarn.lock new file mode 100644 index 0000000..2b02ee7 --- /dev/null +++ b/ts/yarn.lock @@ -0,0 +1,2526 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@aashutoshrathi/word-wrap@^1.2.3": + version "1.2.6" + resolved "https://registry.yarnpkg.com/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz#bd9154aec9983f77b3a034ecaa015c2e4201f6cf" + integrity sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA== + +"@ampproject/remapping@^2.2.1": + version "2.2.1" + resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.2.1.tgz#99e8e11851128b8702cd57c33684f1d0f260b630" + integrity sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg== + dependencies: + "@jridgewell/gen-mapping" "^0.3.0" + "@jridgewell/trace-mapping" "^0.3.9" + +"@esbuild/android-arm64@0.19.3": + version "0.19.3" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.19.3.tgz#91a3b1b4a68c01ffdd5d8ffffb0a83178a366ae0" + integrity sha512-w+Akc0vv5leog550kjJV9Ru+MXMR2VuMrui3C61mnysim0gkFCPOUTAfzTP0qX+HpN9Syu3YA3p1hf3EPqObRw== + +"@esbuild/android-arm@0.19.3": + version "0.19.3" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.19.3.tgz#08bd09f2ebc312422f4e94ae954821f9cf37b39e" + integrity sha512-Lemgw4io4VZl9GHJmjiBGzQ7ONXRfRPHcUEerndjwiSkbxzrpq0Uggku5MxxrXdwJ+pTj1qyw4jwTu7hkPsgIA== + +"@esbuild/android-x64@0.19.3": + version "0.19.3" + resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.19.3.tgz#b1dffec99ed5505fc57561e8758b449dba4924fe" + integrity sha512-FKQJKkK5MXcBHoNZMDNUAg1+WcZlV/cuXrWCoGF/TvdRiYS4znA0m5Il5idUwfxrE20bG/vU1Cr5e1AD6IEIjQ== + +"@esbuild/darwin-arm64@0.19.3": + version "0.19.3" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.19.3.tgz#2e0db5ad26313c7f420f2cd76d9d263fc49cb549" + integrity sha512-kw7e3FXU+VsJSSSl2nMKvACYlwtvZB8RUIeVShIEY6PVnuZ3c9+L9lWB2nWeeKWNNYDdtL19foCQ0ZyUL7nqGw== + +"@esbuild/darwin-x64@0.19.3": + version "0.19.3" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.19.3.tgz#ebe99f35049180023bb37999bddbe306b076a484" + integrity sha512-tPfZiwF9rO0jW6Jh9ipi58N5ZLoSjdxXeSrAYypy4psA2Yl1dAMhM71KxVfmjZhJmxRjSnb29YlRXXhh3GqzYw== + +"@esbuild/freebsd-arm64@0.19.3": + version "0.19.3" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.3.tgz#cf8b58ba5173440ea6124a3d0278bfe4ce181c20" + integrity sha512-ERDyjOgYeKe0Vrlr1iLrqTByB026YLPzTytDTz1DRCYM+JI92Dw2dbpRHYmdqn6VBnQ9Bor6J8ZlNwdZdxjlSg== + +"@esbuild/freebsd-x64@0.19.3": + version "0.19.3" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.19.3.tgz#3f283099810ef1b8468cd1a9400c042e3f12e2a7" + integrity sha512-nXesBZ2Ad1qL+Rm3crN7NmEVJ5uvfLFPLJev3x1j3feCQXfAhoYrojC681RhpdOph8NsvKBBwpYZHR7W0ifTTA== + +"@esbuild/linux-arm64@0.19.3": + version "0.19.3" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.19.3.tgz#a8b3aa69653ac504a51aa73739fb06de3a04d1ff" + integrity sha512-qXvYKmXj8GcJgWq3aGvxL/JG1ZM3UR272SdPU4QSTzD0eymrM7leiZH77pvY3UetCy0k1xuXZ+VPvoJNdtrsWQ== + +"@esbuild/linux-arm@0.19.3": + version "0.19.3" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.19.3.tgz#ff6a2f68d4fc3ab46f614bca667a1a81ed6eea26" + integrity sha512-zr48Cg/8zkzZCzDHNxXO/89bf9e+r4HtzNUPoz4GmgAkF1gFAFmfgOdCbR8zMbzFDGb1FqBBhdXUpcTQRYS1cQ== + +"@esbuild/linux-ia32@0.19.3": + version "0.19.3" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.19.3.tgz#5813baf70e406304e8931b200e39d0293b488073" + integrity sha512-7XlCKCA0nWcbvYpusARWkFjRQNWNGlt45S+Q18UeS///K6Aw8bB2FKYe9mhVWy/XLShvCweOLZPrnMswIaDXQA== + +"@esbuild/linux-loong64@0.19.3": + version "0.19.3" + resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.19.3.tgz#21110f29b5e31dc865c7253fde8a2003f7e8b6fd" + integrity sha512-qGTgjweER5xqweiWtUIDl9OKz338EQqCwbS9c2Bh5jgEH19xQ1yhgGPNesugmDFq+UUSDtWgZ264st26b3de8A== + +"@esbuild/linux-mips64el@0.19.3": + version "0.19.3" + resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.19.3.tgz#4530fc416651eadeb1acc27003c00eac769eb8fd" + integrity sha512-gy1bFskwEyxVMFRNYSvBauDIWNggD6pyxUksc0MV9UOBD138dKTzr8XnM2R4mBsHwVzeuIH8X5JhmNs2Pzrx+A== + +"@esbuild/linux-ppc64@0.19.3": + version "0.19.3" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.19.3.tgz#facf910b0d397e391b37b01a1b4f6e363b04e56b" + integrity sha512-UrYLFu62x1MmmIe85rpR3qou92wB9lEXluwMB/STDzPF9k8mi/9UvNsG07Tt9AqwPQXluMQ6bZbTzYt01+Ue5g== + +"@esbuild/linux-riscv64@0.19.3": + version "0.19.3" + resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.19.3.tgz#4a67abe97a495430d5867340982f5424a64f2aac" + integrity sha512-9E73TfyMCbE+1AwFOg3glnzZ5fBAFK4aawssvuMgCRqCYzE0ylVxxzjEfut8xjmKkR320BEoMui4o/t9KA96gA== + +"@esbuild/linux-s390x@0.19.3": + version "0.19.3" + resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.19.3.tgz#c5fb47474b9f816d81876c119dbccadf671cc5f6" + integrity sha512-LlmsbuBdm1/D66TJ3HW6URY8wO6IlYHf+ChOUz8SUAjVTuaisfuwCOAgcxo3Zsu3BZGxmI7yt//yGOxV+lHcEA== + +"@esbuild/linux-x64@0.19.3": + version "0.19.3" + resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.19.3.tgz#f22d659969ab78dc422f1df8d9a79bc1e7b12ee3" + integrity sha512-ogV0+GwEmvwg/8ZbsyfkYGaLACBQWDvO0Kkh8LKBGKj9Ru8VM39zssrnu9Sxn1wbapA2qNS6BiLdwJZGouyCwQ== + +"@esbuild/netbsd-x64@0.19.3": + version "0.19.3" + resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.19.3.tgz#e9b046934996991f46b8c1cadac815aa45f84fd4" + integrity sha512-o1jLNe4uzQv2DKXMlmEzf66Wd8MoIhLNO2nlQBHLtWyh2MitDG7sMpfCO3NTcoTMuqHjfufgUQDFRI5C+xsXQw== + +"@esbuild/openbsd-x64@0.19.3": + version "0.19.3" + resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.19.3.tgz#b287ef4841fc1067bbbd9a60549e8f9cf1b7ee3a" + integrity sha512-AZJCnr5CZgZOdhouLcfRdnk9Zv6HbaBxjcyhq0StNcvAdVZJSKIdOiPB9az2zc06ywl0ePYJz60CjdKsQacp5Q== + +"@esbuild/sunos-x64@0.19.3": + version "0.19.3" + resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.19.3.tgz#b2b8ba7d27907c7245f6e57dc62f3b88693f84b0" + integrity sha512-Acsujgeqg9InR4glTRvLKGZ+1HMtDm94ehTIHKhJjFpgVzZG9/pIcWW/HA/DoMfEyXmANLDuDZ2sNrWcjq1lxw== + +"@esbuild/win32-arm64@0.19.3": + version "0.19.3" + resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.19.3.tgz#1974c8c180c9add4962235662c569fcc4c8f43dd" + integrity sha512-FSrAfjVVy7TifFgYgliiJOyYynhQmqgPj15pzLyJk8BUsnlWNwP/IAy6GAiB1LqtoivowRgidZsfpoYLZH586A== + +"@esbuild/win32-ia32@0.19.3": + version "0.19.3" + resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.19.3.tgz#b02cc2dd8b6aed042069680f01f45fdfd3de5bc4" + integrity sha512-xTScXYi12xLOWZ/sc5RBmMN99BcXp/eEf7scUC0oeiRoiT5Vvo9AycuqCp+xdpDyAU+LkrCqEpUS9fCSZF8J3Q== + +"@esbuild/win32-x64@0.19.3": + version "0.19.3" + resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.19.3.tgz#e5036be529f757e58d9a7771f2f1b14782986a74" + integrity sha512-FbUN+0ZRXsypPyWE2IwIkVjDkDnJoMJARWOcFZn4KPPli+QnKqF0z1anvfaYe3ev5HFCpRDLLBDHyOALLppWHw== + +"@eslint-community/eslint-utils@^4.2.0", "@eslint-community/eslint-utils@^4.4.0": + version "4.4.0" + resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz#a23514e8fb9af1269d5f7788aa556798d61c6b59" + integrity sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA== + dependencies: + eslint-visitor-keys "^3.3.0" + +"@eslint-community/regexpp@^4.5.1", "@eslint-community/regexpp@^4.6.1": + version "4.8.1" + resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.8.1.tgz#8c4bb756cc2aa7eaf13cfa5e69c83afb3260c20c" + integrity sha512-PWiOzLIUAjN/w5K17PoF4n6sKBw0gqLHPhywmYHP4t1VFQQVYeb1yWsJwnMVEMl3tUHME7X/SJPZLmtG7XBDxQ== + +"@eslint/eslintrc@^2.1.2": + version "2.1.2" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-2.1.2.tgz#c6936b4b328c64496692f76944e755738be62396" + integrity sha512-+wvgpDsrB1YqAMdEUCcnTlpfVBH7Vqn6A/NT3D8WVXFIaKMlErPIZT3oCIAVCOtarRpMtelZLqJeU3t7WY6X6g== + dependencies: + ajv "^6.12.4" + debug "^4.3.2" + espree "^9.6.0" + globals "^13.19.0" + ignore "^5.2.0" + import-fresh "^3.2.1" + js-yaml "^4.1.0" + minimatch "^3.1.2" + strip-json-comments "^3.1.1" + +"@eslint/js@8.49.0": + version "8.49.0" + resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.49.0.tgz#86f79756004a97fa4df866835093f1df3d03c333" + integrity sha512-1S8uAY/MTJqVx0SC4epBq+N2yhuwtNwLbJYNZyhL2pO1ZVKn5HFXav5T41Ryzy9K9V7ZId2JB2oy/W4aCd9/2w== + +"@humanwhocodes/config-array@^0.11.11": + version "0.11.11" + resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.11.tgz#88a04c570dbbc7dd943e4712429c3df09bc32844" + integrity sha512-N2brEuAadi0CcdeMXUkhbZB84eskAc8MEX1By6qEchoVywSgXPIjou4rYsl0V3Hj0ZnuGycGCjdNgockbzeWNA== + dependencies: + "@humanwhocodes/object-schema" "^1.2.1" + debug "^4.1.1" + minimatch "^3.0.5" + +"@humanwhocodes/module-importer@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz#af5b2691a22b44be847b0ca81641c5fb6ad0172c" + integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA== + +"@humanwhocodes/object-schema@^1.2.1": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz#b520529ec21d8e5945a1851dfd1c32e94e39ff45" + integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA== + +"@jridgewell/gen-mapping@^0.3.0": + version "0.3.3" + resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz#7e02e6eb5df901aaedb08514203b096614024098" + integrity sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ== + dependencies: + "@jridgewell/set-array" "^1.0.1" + "@jridgewell/sourcemap-codec" "^1.4.10" + "@jridgewell/trace-mapping" "^0.3.9" + +"@jridgewell/gen-mapping@^0.3.5": + version "0.3.8" + resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz#4f0e06362e01362f823d348f1872b08f666d8142" + integrity sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA== + dependencies: + "@jridgewell/set-array" "^1.2.1" + "@jridgewell/sourcemap-codec" "^1.4.10" + "@jridgewell/trace-mapping" "^0.3.24" + +"@jridgewell/resolve-uri@^3.1.0": + version "3.1.1" + resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz#c08679063f279615a3326583ba3a90d1d82cc721" + integrity sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA== + +"@jridgewell/set-array@^1.0.1": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72" + integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw== + +"@jridgewell/set-array@^1.2.1": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.2.1.tgz#558fb6472ed16a4c850b889530e6b36438c49280" + integrity sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A== + +"@jridgewell/source-map@^0.3.3": + version "0.3.6" + resolved "https://registry.yarnpkg.com/@jridgewell/source-map/-/source-map-0.3.6.tgz#9d71ca886e32502eb9362c9a74a46787c36df81a" + integrity sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ== + dependencies: + "@jridgewell/gen-mapping" "^0.3.5" + "@jridgewell/trace-mapping" "^0.3.25" + +"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.13", "@jridgewell/sourcemap-codec@^1.4.14", "@jridgewell/sourcemap-codec@^1.4.15": + version "1.4.15" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32" + integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg== + +"@jridgewell/trace-mapping@^0.3.17", "@jridgewell/trace-mapping@^0.3.18", "@jridgewell/trace-mapping@^0.3.19", "@jridgewell/trace-mapping@^0.3.9": + version "0.3.19" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.19.tgz#f8a3249862f91be48d3127c3cfe992f79b4b8811" + integrity sha512-kf37QtfW+Hwx/buWGMPcR60iF9ziHa6r/CZJIHbmcm4+0qrXiVdxegAH0F6yddEVQ7zdkjcGCgCzUu+BcbhQxw== + dependencies: + "@jridgewell/resolve-uri" "^3.1.0" + "@jridgewell/sourcemap-codec" "^1.4.14" + +"@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25": + version "0.3.25" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz#15f190e98895f3fc23276ee14bc76b675c2e50f0" + integrity sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ== + dependencies: + "@jridgewell/resolve-uri" "^3.1.0" + "@jridgewell/sourcemap-codec" "^1.4.14" + +"@mdn/browser-compat-data@^5.2.34", "@mdn/browser-compat-data@^5.3.13": + version "5.3.16" + resolved "https://registry.yarnpkg.com/@mdn/browser-compat-data/-/browser-compat-data-5.3.16.tgz#c3b6585c256461fe5e2eac85182b11b36ea2678b" + integrity sha512-b0kKg2weqKDLI+Ai5+tocgUEIidccdSfzUndbS2YnwIp5aVvd3M0D+DCcbrsSOSgMyrV9QKMqogtqMIjKwvDxw== + +"@nodelib/fs.scandir@2.1.5": + version "2.1.5" + resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" + integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g== + dependencies: + "@nodelib/fs.stat" "2.0.5" + run-parallel "^1.1.9" + +"@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2": + version "2.0.5" + resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b" + integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== + +"@nodelib/fs.walk@^1.2.3", "@nodelib/fs.walk@^1.2.8": + version "1.2.8" + resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a" + integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== + dependencies: + "@nodelib/fs.scandir" "2.1.5" + fastq "^1.6.0" + +"@parcel/watcher-android-arm64@2.5.1": + version "2.5.1" + resolved "https://registry.yarnpkg.com/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.1.tgz#507f836d7e2042f798c7d07ad19c3546f9848ac1" + integrity sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA== + +"@parcel/watcher-darwin-arm64@2.5.1": + version "2.5.1" + resolved "https://registry.yarnpkg.com/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.1.tgz#3d26dce38de6590ef79c47ec2c55793c06ad4f67" + integrity sha512-eAzPv5osDmZyBhou8PoF4i6RQXAfeKL9tjb3QzYuccXFMQU0ruIc/POh30ePnaOyD1UXdlKguHBmsTs53tVoPw== + +"@parcel/watcher-darwin-x64@2.5.1": + version "2.5.1" + resolved "https://registry.yarnpkg.com/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.1.tgz#99f3af3869069ccf774e4ddfccf7e64fd2311ef8" + integrity sha512-1ZXDthrnNmwv10A0/3AJNZ9JGlzrF82i3gNQcWOzd7nJ8aj+ILyW1MTxVk35Db0u91oD5Nlk9MBiujMlwmeXZg== + +"@parcel/watcher-freebsd-x64@2.5.1": + version "2.5.1" + resolved "https://registry.yarnpkg.com/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.1.tgz#14d6857741a9f51dfe51d5b08b7c8afdbc73ad9b" + integrity sha512-SI4eljM7Flp9yPuKi8W0ird8TI/JK6CSxju3NojVI6BjHsTyK7zxA9urjVjEKJ5MBYC+bLmMcbAWlZ+rFkLpJQ== + +"@parcel/watcher-linux-arm-glibc@2.5.1": + version "2.5.1" + resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.1.tgz#43c3246d6892381db473bb4f663229ad20b609a1" + integrity sha512-RCdZlEyTs8geyBkkcnPWvtXLY44BCeZKmGYRtSgtwwnHR4dxfHRG3gR99XdMEdQ7KeiDdasJwwvNSF5jKtDwdA== + +"@parcel/watcher-linux-arm-musl@2.5.1": + version "2.5.1" + resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.1.tgz#663750f7090bb6278d2210de643eb8a3f780d08e" + integrity sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q== + +"@parcel/watcher-linux-arm64-glibc@2.5.1": + version "2.5.1" + resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.1.tgz#ba60e1f56977f7e47cd7e31ad65d15fdcbd07e30" + integrity sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w== + +"@parcel/watcher-linux-arm64-musl@2.5.1": + version "2.5.1" + resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.1.tgz#f7fbcdff2f04c526f96eac01f97419a6a99855d2" + integrity sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg== + +"@parcel/watcher-linux-x64-glibc@2.5.1": + version "2.5.1" + resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.1.tgz#4d2ea0f633eb1917d83d483392ce6181b6a92e4e" + integrity sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A== + +"@parcel/watcher-linux-x64-musl@2.5.1": + version "2.5.1" + resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.1.tgz#277b346b05db54f55657301dd77bdf99d63606ee" + integrity sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg== + +"@parcel/watcher-win32-arm64@2.5.1": + version "2.5.1" + resolved "https://registry.yarnpkg.com/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.1.tgz#7e9e02a26784d47503de1d10e8eab6cceb524243" + integrity sha512-RFzklRvmc3PkjKjry3hLF9wD7ppR4AKcWNzH7kXR7GUe0Igb3Nz8fyPwtZCSquGrhU5HhUNDr/mKBqj7tqA2Vw== + +"@parcel/watcher-win32-ia32@2.5.1": + version "2.5.1" + resolved "https://registry.yarnpkg.com/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.1.tgz#2d0f94fa59a873cdc584bf7f6b1dc628ddf976e6" + integrity sha512-c2KkcVN+NJmuA7CGlaGD1qJh1cLfDnQsHjE89E60vUEMlqduHGCdCLJCID5geFVM0dOtA3ZiIO8BoEQmzQVfpQ== + +"@parcel/watcher-win32-x64@2.5.1": + version "2.5.1" + resolved "https://registry.yarnpkg.com/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.1.tgz#ae52693259664ba6f2228fa61d7ee44b64ea0947" + integrity sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA== + +"@parcel/watcher@^2.4.1": + version "2.5.1" + resolved "https://registry.yarnpkg.com/@parcel/watcher/-/watcher-2.5.1.tgz#342507a9cfaaf172479a882309def1e991fb1200" + integrity sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg== + dependencies: + detect-libc "^1.0.3" + is-glob "^4.0.3" + micromatch "^4.0.5" + node-addon-api "^7.0.0" + optionalDependencies: + "@parcel/watcher-android-arm64" "2.5.1" + "@parcel/watcher-darwin-arm64" "2.5.1" + "@parcel/watcher-darwin-x64" "2.5.1" + "@parcel/watcher-freebsd-x64" "2.5.1" + "@parcel/watcher-linux-arm-glibc" "2.5.1" + "@parcel/watcher-linux-arm-musl" "2.5.1" + "@parcel/watcher-linux-arm64-glibc" "2.5.1" + "@parcel/watcher-linux-arm64-musl" "2.5.1" + "@parcel/watcher-linux-x64-glibc" "2.5.1" + "@parcel/watcher-linux-x64-musl" "2.5.1" + "@parcel/watcher-win32-arm64" "2.5.1" + "@parcel/watcher-win32-ia32" "2.5.1" + "@parcel/watcher-win32-x64" "2.5.1" + +"@tsconfig/svelte@^5.0.2": + version "5.0.2" + resolved "https://registry.yarnpkg.com/@tsconfig/svelte/-/svelte-5.0.2.tgz#c9ed3575c5445afb14965bb76e8446fbf7e4a0e6" + integrity sha512-BRbo1fOtyVbhfLyuCWw6wAWp+U8UQle+ZXu84MYYWzYSEB28dyfnRBIE99eoG+qdAC0po6L2ScIEivcT07UaMA== + +"@types/estree@*", "@types/estree@^1.0.0", "@types/estree@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.1.tgz#aa22750962f3bf0e79d753d3cc067f010c95f194" + integrity sha512-LG4opVs2ANWZ1TJoKc937iMmNstM/d0ae1vNbnBvBhqCSezgVUOzcLCqbI5elV8Vy6WKwKjaqR+zO9VKirBBCA== + +"@types/jest@^27.0.1": + version "27.5.2" + resolved "https://registry.yarnpkg.com/@types/jest/-/jest-27.5.2.tgz#ec49d29d926500ffb9fd22b84262e862049c026c" + integrity sha512-mpT8LJJ4CMeeahobofYWIjFo0xonRS/HfxnVEPMPFSQdGUt1uHCnoPT7Zhb+sjDU2wz0oKV0OLUR0WzrHNgfeA== + dependencies: + jest-matcher-utils "^27.0.0" + pretty-format "^27.0.0" + +"@types/json-schema@^7.0.12": + version "7.0.13" + resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.13.tgz#02c24f4363176d2d18fc8b70b9f3c54aba178a85" + integrity sha512-RbSSoHliUbnXj3ny0CNFOoxrIDV6SUGyStHsvDqosw6CkdPV8TtWGlfecuK4ToyMEAql6pzNxgCFKanovUzlgQ== + +"@types/mocha@^9.0.0": + version "9.1.1" + resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-9.1.1.tgz#e7c4f1001eefa4b8afbd1eee27a237fee3bf29c4" + integrity sha512-Z61JK7DKDtdKTWwLeElSEBcWGRLY8g95ic5FoQqI9CMx0ns/Ghep3B4DfcEimiKMvtamNVULVNKEsiwV3aQmXw== + +"@types/pug@^2.0.6": + version "2.0.6" + resolved "https://registry.yarnpkg.com/@types/pug/-/pug-2.0.6.tgz#f830323c88172e66826d0bde413498b61054b5a6" + integrity sha512-SnHmG9wN1UVmagJOnyo/qkk0Z7gejYxOYYmaAwr5u2yFYfsupN3sg10kyzN8Hep/2zbHxCnsumxOoRIRMBwKCg== + +"@types/semver@^7.5.0": + version "7.5.2" + resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.5.2.tgz#31f6eec1ed7ec23f4f05608d3a2d381df041f564" + integrity sha512-7aqorHYgdNO4DM36stTiGO3DvKoex9TQRwsJU6vMaFGyqpBA1MNZkz+PG3gaNUPpTAOYhT1WR7M1JyA3fbS9Cw== + +"@typescript-eslint/eslint-plugin@^6.7.2": + version "6.7.2" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.7.2.tgz#f18cc75c9cceac8080a9dc2e7d166008c5207b9f" + integrity sha512-ooaHxlmSgZTM6CHYAFRlifqh1OAr3PAQEwi7lhYhaegbnXrnh7CDcHmc3+ihhbQC7H0i4JF0psI5ehzkF6Yl6Q== + dependencies: + "@eslint-community/regexpp" "^4.5.1" + "@typescript-eslint/scope-manager" "6.7.2" + "@typescript-eslint/type-utils" "6.7.2" + "@typescript-eslint/utils" "6.7.2" + "@typescript-eslint/visitor-keys" "6.7.2" + debug "^4.3.4" + graphemer "^1.4.0" + ignore "^5.2.4" + natural-compare "^1.4.0" + semver "^7.5.4" + ts-api-utils "^1.0.1" + +"@typescript-eslint/parser@^6.7.2": + version "6.7.2" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-6.7.2.tgz#e0ae93771441b9518e67d0660c79e3a105497af4" + integrity sha512-KA3E4ox0ws+SPyxQf9iSI25R6b4Ne78ORhNHeVKrPQnoYsb9UhieoiRoJgrzgEeKGOXhcY1i8YtOeCHHTDa6Fw== + dependencies: + "@typescript-eslint/scope-manager" "6.7.2" + "@typescript-eslint/types" "6.7.2" + "@typescript-eslint/typescript-estree" "6.7.2" + "@typescript-eslint/visitor-keys" "6.7.2" + debug "^4.3.4" + +"@typescript-eslint/scope-manager@6.7.2": + version "6.7.2" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-6.7.2.tgz#cf59a2095d2f894770c94be489648ad1c78dc689" + integrity sha512-bgi6plgyZjEqapr7u2mhxGR6E8WCzKNUFWNh6fkpVe9+yzRZeYtDTbsIBzKbcxI+r1qVWt6VIoMSNZ4r2A+6Yw== + dependencies: + "@typescript-eslint/types" "6.7.2" + "@typescript-eslint/visitor-keys" "6.7.2" + +"@typescript-eslint/type-utils@6.7.2": + version "6.7.2" + resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-6.7.2.tgz#ed921c9db87d72fa2939fee242d700561454f367" + integrity sha512-36F4fOYIROYRl0qj95dYKx6kybddLtsbmPIYNK0OBeXv2j9L5nZ17j9jmfy+bIDHKQgn2EZX+cofsqi8NPATBQ== + dependencies: + "@typescript-eslint/typescript-estree" "6.7.2" + "@typescript-eslint/utils" "6.7.2" + debug "^4.3.4" + ts-api-utils "^1.0.1" + +"@typescript-eslint/types@6.7.2": + version "6.7.2" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-6.7.2.tgz#75a615a6dbeca09cafd102fe7f465da1d8a3c066" + integrity sha512-flJYwMYgnUNDAN9/GAI3l8+wTmvTYdv64fcH8aoJK76Y+1FCZ08RtI5zDerM/FYT5DMkAc+19E4aLmd5KqdFyg== + +"@typescript-eslint/typescript-estree@6.7.2": + version "6.7.2" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-6.7.2.tgz#ce5883c23b581a5caf878af641e49dd0349238c7" + integrity sha512-kiJKVMLkoSciGyFU0TOY0fRxnp9qq1AzVOHNeN1+B9erKFCJ4Z8WdjAkKQPP+b1pWStGFqezMLltxO+308dJTQ== + dependencies: + "@typescript-eslint/types" "6.7.2" + "@typescript-eslint/visitor-keys" "6.7.2" + debug "^4.3.4" + globby "^11.1.0" + is-glob "^4.0.3" + semver "^7.5.4" + ts-api-utils "^1.0.1" + +"@typescript-eslint/utils@6.7.2": + version "6.7.2" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-6.7.2.tgz#b9ef0da6f04932167a9222cb4ac59cb187165ebf" + integrity sha512-ZCcBJug/TS6fXRTsoTkgnsvyWSiXwMNiPzBUani7hDidBdj1779qwM1FIAmpH4lvlOZNF3EScsxxuGifjpLSWQ== + dependencies: + "@eslint-community/eslint-utils" "^4.4.0" + "@types/json-schema" "^7.0.12" + "@types/semver" "^7.5.0" + "@typescript-eslint/scope-manager" "6.7.2" + "@typescript-eslint/types" "6.7.2" + "@typescript-eslint/typescript-estree" "6.7.2" + semver "^7.5.4" + +"@typescript-eslint/visitor-keys@6.7.2": + version "6.7.2" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-6.7.2.tgz#4cb2bd786f1f459731b0ad1584c9f73e1c7a4d5c" + integrity sha512-uVw9VIMFBUTz8rIeaUT3fFe8xIUx8r4ywAdlQv1ifH+6acn/XF8Y6rwJ7XNmkNMDrTW+7+vxFFPIF40nJCVsMQ== + dependencies: + "@typescript-eslint/types" "6.7.2" + eslint-visitor-keys "^3.4.1" + +acorn-jsx@^5.3.2: + version "5.3.2" + resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" + integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== + +acorn@^8.10.0, acorn@^8.9.0: + version "8.10.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.10.0.tgz#8be5b3907a67221a81ab23c7889c4c5526b62ec5" + integrity sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw== + +acorn@^8.8.2: + version "8.14.1" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.14.1.tgz#721d5dc10f7d5b5609a891773d47731796935dfb" + integrity sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg== + +ajv@^6.12.4: + version "6.12.6" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" + integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== + dependencies: + fast-deep-equal "^3.1.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.2" + +ansi-regex@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" + integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== + +ansi-styles@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" + integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== + dependencies: + color-convert "^1.9.0" + +ansi-styles@^4.1.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" + integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== + dependencies: + color-convert "^2.0.1" + +ansi-styles@^5.0.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-5.2.0.tgz#07449690ad45777d1924ac2abb2fc8895dba836b" + integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA== + +anymatch@~3.1.2: + version "3.1.3" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e" + integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw== + dependencies: + normalize-path "^3.0.0" + picomatch "^2.0.4" + +argparse@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" + integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== + +aria-query@^5.3.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-5.3.0.tgz#650c569e41ad90b51b3d7df5e5eed1c7549c103e" + integrity sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A== + dependencies: + dequal "^2.0.3" + +array-buffer-byte-length@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz#fabe8bc193fea865f317fe7807085ee0dee5aead" + integrity sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A== + dependencies: + call-bind "^1.0.2" + is-array-buffer "^3.0.1" + +array-union@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" + integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== + +arraybuffer.prototype.slice@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.2.tgz#98bd561953e3e74bb34938e77647179dfe6e9f12" + integrity sha512-yMBKppFur/fbHu9/6USUe03bZ4knMYiwFBcyiaXB8Go0qNehwX6inYPzK9U0NeQvGxKthcmHcaR8P5MStSRBAw== + dependencies: + array-buffer-byte-length "^1.0.0" + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + get-intrinsic "^1.2.1" + is-array-buffer "^3.0.2" + is-shared-array-buffer "^1.0.2" + +ast-metadata-inferer@^0.8.0: + version "0.8.0" + resolved "https://registry.yarnpkg.com/ast-metadata-inferer/-/ast-metadata-inferer-0.8.0.tgz#0f94c3425e310d8da45823ab2161142e3f134343" + integrity sha512-jOMKcHht9LxYIEQu+RVd22vtgrPaVCtDRQ/16IGmurdzxvYbDd5ynxjnyrzLnieG96eTcAyaoj/wN/4/1FyyeA== + dependencies: + "@mdn/browser-compat-data" "^5.2.34" + +available-typed-arrays@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz#92f95616501069d07d10edb2fc37d3e1c65123b7" + integrity sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw== + +axobject-query@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/axobject-query/-/axobject-query-3.2.1.tgz#39c378a6e3b06ca679f29138151e45b2b32da62a" + integrity sha512-jsyHu61e6N4Vbz/v18DHwWYKK0bSWLqn47eeDSKPB7m8tqMHF9YJ+mhIk2lVteyZrY8tnSj/jHOv4YiTCuCJgg== + dependencies: + dequal "^2.0.3" + +balanced-match@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" + integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== + +binary-extensions@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" + integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== + +brace-expansion@^1.1.7: + version "1.1.11" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +braces@^3.0.2, braces@~3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" + integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== + dependencies: + fill-range "^7.0.1" + +braces@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789" + integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA== + dependencies: + fill-range "^7.1.1" + +browserslist@^4.21.10: + version "4.21.10" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.10.tgz#dbbac576628c13d3b2231332cb2ec5a46e015bb0" + integrity sha512-bipEBdZfVH5/pwrvqc+Ub0kUPVfGUhlKxbvfD+z1BDnPEO/X98ruXGA1WP5ASpAFKan7Qr6j736IacbZQuAlKQ== + dependencies: + caniuse-lite "^1.0.30001517" + electron-to-chromium "^1.4.477" + node-releases "^2.0.13" + update-browserslist-db "^1.0.11" + +buffer-crc32@^0.2.5: + version "0.2.13" + resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242" + integrity sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ== + +buffer-from@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" + integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== + +call-bind@^1.0.0, call-bind@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c" + integrity sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA== + dependencies: + function-bind "^1.1.1" + get-intrinsic "^1.0.2" + +callsites@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" + integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== + +caniuse-lite@^1.0.30001517, caniuse-lite@^1.0.30001524: + version "1.0.30001535" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001535.tgz#908a5b7ef11172f51f0b88f3d850aef1c6a3cf7b" + integrity sha512-48jLyUkiWFfhm/afF7cQPqPjaUmSraEhK4j+FCTJpgnGGEZHqyLe3hmWH7lIooZdSzXL0ReMvHz0vKDoTBsrwg== + +chalk@^2.4.1: + version "2.4.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" + integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== + dependencies: + ansi-styles "^3.2.1" + escape-string-regexp "^1.0.5" + supports-color "^5.3.0" + +chalk@^4.0.0: + version "4.1.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" + integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + +"chokidar@>=3.0.0 <4.0.0", chokidar@^3.4.1: + version "3.5.3" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd" + integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw== + dependencies: + anymatch "~3.1.2" + braces "~3.0.2" + glob-parent "~5.1.2" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.6.0" + optionalDependencies: + fsevents "~2.3.2" + +chokidar@^4.0.0: + version "4.0.3" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-4.0.3.tgz#7be37a4c03c9aee1ecfe862a4a23b2c70c205d30" + integrity sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA== + dependencies: + readdirp "^4.0.1" + +code-red@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/code-red/-/code-red-1.0.4.tgz#59ba5c9d1d320a4ef795bc10a28bd42bfebe3e35" + integrity sha512-7qJWqItLA8/VPVlKJlFXU+NBlo/qyfs39aJcuMT/2ere32ZqvF5OSxgdM5xOfJJ7O429gg2HM47y8v9P+9wrNw== + dependencies: + "@jridgewell/sourcemap-codec" "^1.4.15" + "@types/estree" "^1.0.1" + acorn "^8.10.0" + estree-walker "^3.0.3" + periscopic "^3.1.0" + +color-convert@^1.9.0: + version "1.9.3" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" + integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== + dependencies: + color-name "1.1.3" + +color-convert@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== + dependencies: + color-name "~1.1.4" + +color-name@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" + integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw== + +color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + +commander@^2.19.0, commander@^2.20.0: + version "2.20.3" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" + integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== + +cross-env@^7.0.3: + version "7.0.3" + resolved "https://registry.yarnpkg.com/cross-env/-/cross-env-7.0.3.tgz#865264b29677dc015ba8418918965dd232fc54cf" + integrity sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw== + dependencies: + cross-spawn "^7.0.1" + +cross-spawn@^6.0.5: + version "6.0.5" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" + integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ== + dependencies: + nice-try "^1.0.4" + path-key "^2.0.1" + semver "^5.5.0" + shebang-command "^1.2.0" + which "^1.2.9" + +cross-spawn@^7.0.1, cross-spawn@^7.0.2: + version "7.0.3" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" + integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== + dependencies: + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^2.0.1" + +css-tree@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-2.3.1.tgz#10264ce1e5442e8572fc82fbe490644ff54b5c20" + integrity sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw== + dependencies: + mdn-data "2.0.30" + source-map-js "^1.0.1" + +debug@^4.1.1, debug@^4.3.2, debug@^4.3.4: + version "4.3.4" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" + integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== + dependencies: + ms "2.1.2" + +deep-is@^0.1.3: + version "0.1.4" + resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" + integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== + +define-data-property@^1.0.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/define-data-property/-/define-data-property-1.1.0.tgz#0db13540704e1d8d479a0656cf781267531b9451" + integrity sha512-UzGwzcjyv3OtAvolTj1GoyNYzfFR+iqbGjcnBEENZVCpM4/Ng1yhGNvS3lR/xDS74Tb2wGG9WzNSNIOS9UVb2g== + dependencies: + get-intrinsic "^1.2.1" + gopd "^1.0.1" + has-property-descriptors "^1.0.0" + +define-properties@^1.1.3, define-properties@^1.1.4, define-properties@^1.2.0: + version "1.2.1" + resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.2.1.tgz#10781cc616eb951a80a034bafcaa7377f6af2b6c" + integrity sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg== + dependencies: + define-data-property "^1.0.1" + has-property-descriptors "^1.0.0" + object-keys "^1.1.1" + +dequal@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/dequal/-/dequal-2.0.3.tgz#2644214f1997d39ed0ee0ece72335490a7ac67be" + integrity sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA== + +detect-indent@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-6.1.0.tgz#592485ebbbf6b3b1ab2be175c8393d04ca0d57e6" + integrity sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA== + +detect-libc@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" + integrity sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg== + +diff-sequences@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-27.5.1.tgz#eaecc0d327fd68c8d9672a1e64ab8dccb2ef5327" + integrity sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ== + +dir-glob@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f" + integrity sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA== + dependencies: + path-type "^4.0.0" + +discontinuous-range@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/discontinuous-range/-/discontinuous-range-1.0.0.tgz#e38331f0844bba49b9a9cb71c771585aab1bc65a" + integrity sha512-c68LpLbO+7kP/b1Hr1qs8/BJ09F5khZGTxqxZuhzxpmwJKOgRFHJWIb9/KmqnqHhLdO55aOxFH/EGBvUQbL/RQ== + +doctrine@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961" + integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w== + dependencies: + esutils "^2.0.2" + +electron-to-chromium@^1.4.477: + version "1.4.523" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.523.tgz#f82f99243c827df05c26776d49712cb284972df6" + integrity sha512-9AreocSUWnzNtvLcbpng6N+GkXnCcBR80IQkxRC9Dfdyg4gaWNUPBujAHUpKkiUkoSoR9UlhA4zD/IgBklmhzg== + +error-ex@^1.3.1: + version "1.3.2" + resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" + integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== + dependencies: + is-arrayish "^0.2.1" + +es-abstract@^1.22.1: + version "1.22.2" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.22.2.tgz#90f7282d91d0ad577f505e423e52d4c1d93c1b8a" + integrity sha512-YoxfFcDmhjOgWPWsV13+2RNjq1F6UQnfs+8TftwNqtzlmFzEXvlUwdrNrYeaizfjQzRMxkZ6ElWMOJIFKdVqwA== + dependencies: + array-buffer-byte-length "^1.0.0" + arraybuffer.prototype.slice "^1.0.2" + available-typed-arrays "^1.0.5" + call-bind "^1.0.2" + es-set-tostringtag "^2.0.1" + es-to-primitive "^1.2.1" + function.prototype.name "^1.1.6" + get-intrinsic "^1.2.1" + get-symbol-description "^1.0.0" + globalthis "^1.0.3" + gopd "^1.0.1" + has "^1.0.3" + has-property-descriptors "^1.0.0" + has-proto "^1.0.1" + has-symbols "^1.0.3" + internal-slot "^1.0.5" + is-array-buffer "^3.0.2" + is-callable "^1.2.7" + is-negative-zero "^2.0.2" + is-regex "^1.1.4" + is-shared-array-buffer "^1.0.2" + is-string "^1.0.7" + is-typed-array "^1.1.12" + is-weakref "^1.0.2" + object-inspect "^1.12.3" + object-keys "^1.1.1" + object.assign "^4.1.4" + regexp.prototype.flags "^1.5.1" + safe-array-concat "^1.0.1" + safe-regex-test "^1.0.0" + string.prototype.trim "^1.2.8" + string.prototype.trimend "^1.0.7" + string.prototype.trimstart "^1.0.7" + typed-array-buffer "^1.0.0" + typed-array-byte-length "^1.0.0" + typed-array-byte-offset "^1.0.0" + typed-array-length "^1.0.4" + unbox-primitive "^1.0.2" + which-typed-array "^1.1.11" + +es-set-tostringtag@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz#338d502f6f674301d710b80c8592de8a15f09cd8" + integrity sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg== + dependencies: + get-intrinsic "^1.1.3" + has "^1.0.3" + has-tostringtag "^1.0.0" + +es-to-primitive@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a" + integrity sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA== + dependencies: + is-callable "^1.1.4" + is-date-object "^1.0.1" + is-symbol "^1.0.2" + +es6-promise@^3.1.2: + version "3.3.1" + resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-3.3.1.tgz#a08cdde84ccdbf34d027a1451bc91d4bcd28a613" + integrity sha512-SOp9Phqvqn7jtEUxPWdWfWoLmyt2VaJ6MpvP9Comy1MceMXqE6bxvaTu4iaxpYYPzhny28Lc+M87/c2cPK6lDg== + +esbuild-sass-plugin@^2.15.0: + version "2.15.0" + resolved "https://registry.yarnpkg.com/esbuild-sass-plugin/-/esbuild-sass-plugin-2.15.0.tgz#268a31617a79ee92ae30db4b91ce3603bfa687ce" + integrity sha512-T0GCHVfeuGBBgY5k19RbExd7vVuC3lzrK8IZbXOqZftw6N9lTBnZuqKhnhdAJBcu6wek7K/fXJ2zzY6KrcNtAg== + dependencies: + resolve "^1.22.2" + sass "^1.65.1" + +esbuild-svelte@^0.8.0: + version "0.8.0" + resolved "https://registry.yarnpkg.com/esbuild-svelte/-/esbuild-svelte-0.8.0.tgz#a64d6e7f3df0a111fca939f8fd2917cceea7aa91" + integrity sha512-uKcPf1kl2UGMjrfHChv4dLxGAvCNhf9s72mHo19ZhKP+LrVOuQkOM/g8GE7MiGpoqjpk8UHqL08uLRbSKXhmhw== + dependencies: + "@jridgewell/trace-mapping" "^0.3.19" + +esbuild@^0.19.3: + version "0.19.3" + resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.19.3.tgz#d9268cd23358eef9d76146f184e0c55ff8da7bb6" + integrity sha512-UlJ1qUUA2jL2nNib1JTSkifQTcYTroFqRjwCFW4QYEKEsixXD5Tik9xML7zh2gTxkYTBKGHNH9y7txMwVyPbjw== + optionalDependencies: + "@esbuild/android-arm" "0.19.3" + "@esbuild/android-arm64" "0.19.3" + "@esbuild/android-x64" "0.19.3" + "@esbuild/darwin-arm64" "0.19.3" + "@esbuild/darwin-x64" "0.19.3" + "@esbuild/freebsd-arm64" "0.19.3" + "@esbuild/freebsd-x64" "0.19.3" + "@esbuild/linux-arm" "0.19.3" + "@esbuild/linux-arm64" "0.19.3" + "@esbuild/linux-ia32" "0.19.3" + "@esbuild/linux-loong64" "0.19.3" + "@esbuild/linux-mips64el" "0.19.3" + "@esbuild/linux-ppc64" "0.19.3" + "@esbuild/linux-riscv64" "0.19.3" + "@esbuild/linux-s390x" "0.19.3" + "@esbuild/linux-x64" "0.19.3" + "@esbuild/netbsd-x64" "0.19.3" + "@esbuild/openbsd-x64" "0.19.3" + "@esbuild/sunos-x64" "0.19.3" + "@esbuild/win32-arm64" "0.19.3" + "@esbuild/win32-ia32" "0.19.3" + "@esbuild/win32-x64" "0.19.3" + +escalade@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" + integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== + +escape-string-regexp@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" + integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg== + +escape-string-regexp@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" + integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== + +eslint-plugin-compat@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-compat/-/eslint-plugin-compat-4.2.0.tgz#eeaf80daa1afe495c88a47e9281295acae45c0aa" + integrity sha512-RDKSYD0maWy5r7zb5cWQS+uSPc26mgOzdORJ8hxILmWM7S/Ncwky7BcAtXVY5iRbKjBdHsWU8Yg7hfoZjtkv7w== + dependencies: + "@mdn/browser-compat-data" "^5.3.13" + ast-metadata-inferer "^0.8.0" + browserslist "^4.21.10" + caniuse-lite "^1.0.30001524" + find-up "^5.0.0" + lodash.memoize "^4.1.2" + semver "^7.5.4" + +eslint-scope@^7.2.2: + version "7.2.2" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.2.2.tgz#deb4f92563390f32006894af62a22dba1c46423f" + integrity sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg== + dependencies: + esrecurse "^4.3.0" + estraverse "^5.2.0" + +eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.1, eslint-visitor-keys@^3.4.3: + version "3.4.3" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz#0cd72fe8550e3c2eae156a96a4dddcd1c8ac5800" + integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag== + +eslint@^8.49.0: + version "8.49.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.49.0.tgz#09d80a89bdb4edee2efcf6964623af1054bf6d42" + integrity sha512-jw03ENfm6VJI0jA9U+8H5zfl5b+FvuU3YYvZRdZHOlU2ggJkxrlkJH4HcDrZpj6YwD8kuYqvQM8LyesoazrSOQ== + dependencies: + "@eslint-community/eslint-utils" "^4.2.0" + "@eslint-community/regexpp" "^4.6.1" + "@eslint/eslintrc" "^2.1.2" + "@eslint/js" "8.49.0" + "@humanwhocodes/config-array" "^0.11.11" + "@humanwhocodes/module-importer" "^1.0.1" + "@nodelib/fs.walk" "^1.2.8" + ajv "^6.12.4" + chalk "^4.0.0" + cross-spawn "^7.0.2" + debug "^4.3.2" + doctrine "^3.0.0" + escape-string-regexp "^4.0.0" + eslint-scope "^7.2.2" + eslint-visitor-keys "^3.4.3" + espree "^9.6.1" + esquery "^1.4.2" + esutils "^2.0.2" + fast-deep-equal "^3.1.3" + file-entry-cache "^6.0.1" + find-up "^5.0.0" + glob-parent "^6.0.2" + globals "^13.19.0" + graphemer "^1.4.0" + ignore "^5.2.0" + imurmurhash "^0.1.4" + is-glob "^4.0.0" + is-path-inside "^3.0.3" + js-yaml "^4.1.0" + json-stable-stringify-without-jsonify "^1.0.1" + levn "^0.4.1" + lodash.merge "^4.6.2" + minimatch "^3.1.2" + natural-compare "^1.4.0" + optionator "^0.9.3" + strip-ansi "^6.0.1" + text-table "^0.2.0" + +espree@^9.6.0, espree@^9.6.1: + version "9.6.1" + resolved "https://registry.yarnpkg.com/espree/-/espree-9.6.1.tgz#a2a17b8e434690a5432f2f8018ce71d331a48c6f" + integrity sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ== + dependencies: + acorn "^8.9.0" + acorn-jsx "^5.3.2" + eslint-visitor-keys "^3.4.1" + +esquery@^1.4.2: + version "1.5.0" + resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.5.0.tgz#6ce17738de8577694edd7361c57182ac8cb0db0b" + integrity sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg== + dependencies: + estraverse "^5.1.0" + +esrecurse@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" + integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== + dependencies: + estraverse "^5.2.0" + +estraverse@^5.1.0, estraverse@^5.2.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" + integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== + +estree-walker@^3.0.0, estree-walker@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-3.0.3.tgz#67c3e549ec402a487b4fc193d1953a524752340d" + integrity sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g== + dependencies: + "@types/estree" "^1.0.0" + +esutils@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" + integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== + +fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" + integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== + +fast-glob@^3.2.7, fast-glob@^3.2.9: + version "3.3.1" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.1.tgz#784b4e897340f3dbbef17413b3f11acf03c874c4" + integrity sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg== + dependencies: + "@nodelib/fs.stat" "^2.0.2" + "@nodelib/fs.walk" "^1.2.3" + glob-parent "^5.1.2" + merge2 "^1.3.0" + micromatch "^4.0.4" + +fast-json-stable-stringify@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" + integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== + +fast-levenshtein@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" + integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== + +fastq@^1.6.0: + version "1.15.0" + resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.15.0.tgz#d04d07c6a2a68fe4599fea8d2e103a937fae6b3a" + integrity sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw== + dependencies: + reusify "^1.0.4" + +file-entry-cache@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027" + integrity sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg== + dependencies: + flat-cache "^3.0.4" + +fill-range@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" + integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== + dependencies: + to-regex-range "^5.0.1" + +fill-range@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292" + integrity sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg== + dependencies: + to-regex-range "^5.0.1" + +find-up@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" + integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== + dependencies: + locate-path "^6.0.0" + path-exists "^4.0.0" + +flat-cache@^3.0.4: + version "3.1.0" + resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.1.0.tgz#0e54ab4a1a60fe87e2946b6b00657f1c99e1af3f" + integrity sha512-OHx4Qwrrt0E4jEIcI5/Xb+f+QmJYNj2rrK8wiIdQOIrB9WrrJL8cjZvXdXuBTkkEwEqLycb5BeZDV1o2i9bTew== + dependencies: + flatted "^3.2.7" + keyv "^4.5.3" + rimraf "^3.0.2" + +flatted@^3.2.7: + version "3.2.9" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.9.tgz#7eb4c67ca1ba34232ca9d2d93e9886e611ad7daf" + integrity sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ== + +for-each@^0.3.3: + version "0.3.3" + resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.3.tgz#69b447e88a0a5d32c3e7084f3f1710034b21376e" + integrity sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw== + dependencies: + is-callable "^1.1.3" + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== + +fsevents@~2.3.2: + version "2.3.3" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" + integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== + +function-bind@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" + integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== + +function.prototype.name@^1.1.6: + version "1.1.6" + resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.6.tgz#cdf315b7d90ee77a4c6ee216c3c3362da07533fd" + integrity sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + functions-have-names "^1.2.3" + +functions-have-names@^1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834" + integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ== + +get-intrinsic@^1.0.2, get-intrinsic@^1.1.1, get-intrinsic@^1.1.3, get-intrinsic@^1.2.0, get-intrinsic@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.1.tgz#d295644fed4505fc9cde952c37ee12b477a83d82" + integrity sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw== + dependencies: + function-bind "^1.1.1" + has "^1.0.3" + has-proto "^1.0.1" + has-symbols "^1.0.3" + +get-symbol-description@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/get-symbol-description/-/get-symbol-description-1.0.0.tgz#7fdb81c900101fbd564dd5f1a30af5aadc1e58d6" + integrity sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.1.1" + +glob-parent@^5.1.2, glob-parent@~5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" + integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== + dependencies: + is-glob "^4.0.1" + +glob-parent@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-6.0.2.tgz#6d237d99083950c79290f24c7642a3de9a28f9e3" + integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A== + dependencies: + is-glob "^4.0.3" + +glob@^7.1.3: + version "7.2.3" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" + integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.1.1" + once "^1.3.0" + path-is-absolute "^1.0.0" + +globals@^13.19.0: + version "13.21.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-13.21.0.tgz#163aae12f34ef502f5153cfbdd3600f36c63c571" + integrity sha512-ybyme3s4yy/t/3s35bewwXKOf7cvzfreG2lH0lZl0JB7I4GxRP2ghxOK/Nb9EkRXdbBXZLfq/p/0W2JUONB/Gg== + dependencies: + type-fest "^0.20.2" + +globalthis@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/globalthis/-/globalthis-1.0.3.tgz#5852882a52b80dc301b0660273e1ed082f0b6ccf" + integrity sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA== + dependencies: + define-properties "^1.1.3" + +globby@^11.1.0: + version "11.1.0" + resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b" + integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g== + dependencies: + array-union "^2.1.0" + dir-glob "^3.0.1" + fast-glob "^3.2.9" + ignore "^5.2.0" + merge2 "^1.4.1" + slash "^3.0.0" + +gopd@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.0.1.tgz#29ff76de69dac7489b7c0918a5788e56477c332c" + integrity sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA== + dependencies: + get-intrinsic "^1.1.3" + +graceful-fs@^4.1.2, graceful-fs@^4.1.3: + version "4.2.11" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" + integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== + +graphemer@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/graphemer/-/graphemer-1.4.0.tgz#fb2f1d55e0e3a1849aeffc90c4fa0dd53a0e66c6" + integrity sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag== + +has-bigints@^1.0.1, has-bigints@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.2.tgz#0871bd3e3d51626f6ca0966668ba35d5602d6eaa" + integrity sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ== + +has-flag@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" + integrity sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw== + +has-flag@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" + integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== + +has-property-descriptors@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz#610708600606d36961ed04c196193b6a607fa861" + integrity sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ== + dependencies: + get-intrinsic "^1.1.1" + +has-proto@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/has-proto/-/has-proto-1.0.1.tgz#1885c1305538958aff469fef37937c22795408e0" + integrity sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg== + +has-symbols@^1.0.2, has-symbols@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" + integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== + +has-tostringtag@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.0.tgz#7e133818a7d394734f941e73c3d3f9291e658b25" + integrity sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ== + dependencies: + has-symbols "^1.0.2" + +has@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" + integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== + dependencies: + function-bind "^1.1.1" + +hosted-git-info@^2.1.4: + version "2.8.9" + resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9" + integrity sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw== + +ignore@^5.2.0, ignore@^5.2.4: + version "5.2.4" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.4.tgz#a291c0c6178ff1b960befe47fcdec301674a6324" + integrity sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ== + +immutable@^4.0.0: + version "4.3.4" + resolved "https://registry.yarnpkg.com/immutable/-/immutable-4.3.4.tgz#2e07b33837b4bb7662f288c244d1ced1ef65a78f" + integrity sha512-fsXeu4J4i6WNWSikpI88v/PcVflZz+6kMhUfIwc5SY+poQRPnaf5V7qds6SUyUN3cVxEzuCab7QIoLOQ+DQ1wA== + +immutable@^5.0.2: + version "5.1.1" + resolved "https://registry.yarnpkg.com/immutable/-/immutable-5.1.1.tgz#d4cb552686f34b076b3dcf23c4384c04424d8354" + integrity sha512-3jatXi9ObIsPGr3N5hGw/vWWcTkq6hUYhpQz4k0wLC+owqWi/LiugIw9x0EdNZ2yGedKN/HzePiBvaJRXa0Ujg== + +import-fresh@^3.2.1: + version "3.3.0" + resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" + integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== + dependencies: + parent-module "^1.0.0" + resolve-from "^4.0.0" + +imurmurhash@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" + integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA== + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA== + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +internal-slot@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.5.tgz#f2a2ee21f668f8627a4667f309dc0f4fb6674986" + integrity sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ== + dependencies: + get-intrinsic "^1.2.0" + has "^1.0.3" + side-channel "^1.0.4" + +is-array-buffer@^3.0.1, is-array-buffer@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/is-array-buffer/-/is-array-buffer-3.0.2.tgz#f2653ced8412081638ecb0ebbd0c41c6e0aecbbe" + integrity sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.2.0" + is-typed-array "^1.1.10" + +is-arrayish@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" + integrity sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg== + +is-bigint@^1.0.1: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.4.tgz#08147a1875bc2b32005d41ccd8291dffc6691df3" + integrity sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg== + dependencies: + has-bigints "^1.0.1" + +is-binary-path@~2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" + integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== + dependencies: + binary-extensions "^2.0.0" + +is-boolean-object@^1.1.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.1.2.tgz#5c6dc200246dd9321ae4b885a114bb1f75f63719" + integrity sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA== + dependencies: + call-bind "^1.0.2" + has-tostringtag "^1.0.0" + +is-callable@^1.1.3, is-callable@^1.1.4, is-callable@^1.2.7: + version "1.2.7" + resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.7.tgz#3bc2a85ea742d9e36205dcacdd72ca1fdc51b055" + integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA== + +is-core-module@^2.13.0: + version "2.13.0" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.13.0.tgz#bb52aa6e2cbd49a30c2ba68c42bf3435ba6072db" + integrity sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ== + dependencies: + has "^1.0.3" + +is-date-object@^1.0.1: + version "1.0.5" + resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.5.tgz#0841d5536e724c25597bf6ea62e1bd38298df31f" + integrity sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ== + dependencies: + has-tostringtag "^1.0.0" + +is-extglob@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== + +is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1: + version "4.0.3" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" + integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== + dependencies: + is-extglob "^2.1.1" + +is-negative-zero@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.2.tgz#7bf6f03a28003b8b3965de3ac26f664d765f3150" + integrity sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA== + +is-number-object@^1.0.4: + version "1.0.7" + resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.7.tgz#59d50ada4c45251784e9904f5246c742f07a42fc" + integrity sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ== + dependencies: + has-tostringtag "^1.0.0" + +is-number@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" + integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== + +is-path-inside@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283" + integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== + +is-reference@^3.0.0, is-reference@^3.0.1: + version "3.0.2" + resolved "https://registry.yarnpkg.com/is-reference/-/is-reference-3.0.2.tgz#154747a01f45cd962404ee89d43837af2cba247c" + integrity sha512-v3rht/LgVcsdZa3O2Nqs+NMowLOxeOm7Ay9+/ARQ2F+qEoANRcqrjAZKGN0v8ymUetZGgkp26LTnGT7H0Qo9Pg== + dependencies: + "@types/estree" "*" + +is-regex@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.4.tgz#eef5663cd59fa4c0ae339505323df6854bb15958" + integrity sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg== + dependencies: + call-bind "^1.0.2" + has-tostringtag "^1.0.0" + +is-shared-array-buffer@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz#8f259c573b60b6a32d4058a1a07430c0a7344c79" + integrity sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA== + dependencies: + call-bind "^1.0.2" + +is-string@^1.0.5, is-string@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.7.tgz#0dd12bf2006f255bb58f695110eff7491eebc0fd" + integrity sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg== + dependencies: + has-tostringtag "^1.0.0" + +is-symbol@^1.0.2, is-symbol@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.4.tgz#a6dac93b635b063ca6872236de88910a57af139c" + integrity sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg== + dependencies: + has-symbols "^1.0.2" + +is-typed-array@^1.1.10, is-typed-array@^1.1.12, is-typed-array@^1.1.9: + version "1.1.12" + resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.12.tgz#d0bab5686ef4a76f7a73097b95470ab199c57d4a" + integrity sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg== + dependencies: + which-typed-array "^1.1.11" + +is-weakref@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-weakref/-/is-weakref-1.0.2.tgz#9529f383a9338205e89765e0392efc2f100f06f2" + integrity sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ== + dependencies: + call-bind "^1.0.2" + +isarray@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.5.tgz#8af1e4c1221244cc62459faf38940d4e644a5723" + integrity sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw== + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== + +jest-diff@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-27.5.1.tgz#a07f5011ac9e6643cf8a95a462b7b1ecf6680def" + integrity sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw== + dependencies: + chalk "^4.0.0" + diff-sequences "^27.5.1" + jest-get-type "^27.5.1" + pretty-format "^27.5.1" + +jest-get-type@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-27.5.1.tgz#3cd613c507b0f7ace013df407a1c1cd578bcb4f1" + integrity sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw== + +jest-matcher-utils@^27.0.0: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-27.5.1.tgz#9c0cdbda8245bc22d2331729d1091308b40cf8ab" + integrity sha512-z2uTx/T6LBaCoNWNFWwChLBKYxTMcGBRjAt+2SbP929/Fflb9aa5LGma654Rz8z9HLxsrUaYzxE9T/EFIL/PAw== + dependencies: + chalk "^4.0.0" + jest-diff "^27.5.1" + jest-get-type "^27.5.1" + pretty-format "^27.5.1" + +js-yaml@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" + integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== + dependencies: + argparse "^2.0.1" + +json-buffer@3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13" + integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ== + +json-parse-better-errors@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9" + integrity sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw== + +json-schema-traverse@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" + integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== + +json-stable-stringify-without-jsonify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" + integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw== + +keyv@^4.5.3: + version "4.5.3" + resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.3.tgz#00873d2b046df737963157bd04f294ca818c9c25" + integrity sha512-QCiSav9WaX1PgETJ+SpNnx2PRRapJ/oRSXM4VO5OGYGSjrxbKPVFVhB3l2OCbLCk329N8qyAtsJjSjvVBWzEug== + dependencies: + json-buffer "3.0.1" + +levn@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade" + integrity sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ== + dependencies: + prelude-ls "^1.2.1" + type-check "~0.4.0" + +load-json-file@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-4.0.0.tgz#2f5f45ab91e33216234fd53adab668eb4ec0993b" + integrity sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw== + dependencies: + graceful-fs "^4.1.2" + parse-json "^4.0.0" + pify "^3.0.0" + strip-bom "^3.0.0" + +locate-character@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/locate-character/-/locate-character-3.0.0.tgz#0305c5b8744f61028ef5d01f444009e00779f974" + integrity sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA== + +locate-path@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" + integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== + dependencies: + p-locate "^5.0.0" + +lodash.memoize@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" + integrity sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag== + +lodash.merge@^4.6.2: + version "4.6.2" + resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" + integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== + +lru-cache@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" + integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== + dependencies: + yallist "^4.0.0" + +magic-string@^0.27.0: + version "0.27.0" + resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.27.0.tgz#e4a3413b4bab6d98d2becffd48b4a257effdbbf3" + integrity sha512-8UnnX2PeRAPZuN12svgR9j7M1uWMovg/CEnIwIG0LFkXSJJe4PdfUGiTGl8V9bsBHFUtfVINcSyYxd7q+kx9fA== + dependencies: + "@jridgewell/sourcemap-codec" "^1.4.13" + +magic-string@^0.30.0: + version "0.30.3" + resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.30.3.tgz#403755dfd9d6b398dfa40635d52e96c5ac095b85" + integrity sha512-B7xGbll2fG/VjP+SWg4sX3JynwIU0mjoTc6MPpKNuIvftk6u6vqhDnk1R80b8C2GBR6ywqy+1DcKBrevBg+bmw== + dependencies: + "@jridgewell/sourcemap-codec" "^1.4.15" + +mdn-data@2.0.30: + version "2.0.30" + resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.30.tgz#ce4df6f80af6cfbe218ecd5c552ba13c4dfa08cc" + integrity sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA== + +memorystream@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/memorystream/-/memorystream-0.3.1.tgz#86d7090b30ce455d63fbae12dda51a47ddcaf9b2" + integrity sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw== + +merge2@^1.3.0, merge2@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" + integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== + +micromatch@^4.0.4: + version "4.0.5" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6" + integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA== + dependencies: + braces "^3.0.2" + picomatch "^2.3.1" + +micromatch@^4.0.5: + version "4.0.8" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.8.tgz#d66fa18f3a47076789320b9b1af32bd86d9fa202" + integrity sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA== + dependencies: + braces "^3.0.3" + picomatch "^2.3.1" + +min-indent@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/min-indent/-/min-indent-1.0.1.tgz#a63f681673b30571fbe8bc25686ae746eefa9869" + integrity sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg== + +minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" + integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== + dependencies: + brace-expansion "^1.1.7" + +minimist@^1.2.0, minimist@^1.2.6: + version "1.2.8" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" + integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== + +mkdirp@^0.5.1: + version "0.5.6" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.6.tgz#7def03d2432dcae4ba1d611445c48396062255f6" + integrity sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw== + dependencies: + minimist "^1.2.6" + +moo@^0.5.0, moo@^0.5.1: + version "0.5.2" + resolved "https://registry.yarnpkg.com/moo/-/moo-0.5.2.tgz#f9fe82473bc7c184b0d32e2215d3f6e67278733c" + integrity sha512-iSAJLHYKnX41mKcJKjqvnAN9sf0LMDTXDEvFv+ffuRR9a1MIuXLjMNL6EsnDHSkKLTWNqQQ5uo61P4EbU4NU+Q== + +mri@^1.1.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/mri/-/mri-1.2.0.tgz#6721480fec2a11a4889861115a48b6cbe7cc8f0b" + integrity sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA== + +ms@2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" + integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== + +natural-compare@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" + integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== + +nearley@^2.19.6: + version "2.20.1" + resolved "https://registry.yarnpkg.com/nearley/-/nearley-2.20.1.tgz#246cd33eff0d012faf197ff6774d7ac78acdd474" + integrity sha512-+Mc8UaAebFzgV+KpI5n7DasuuQCHA89dmwm7JXw3TV43ukfNQ9DnBH3Mdb2g/I4Fdxc26pwimBWvjIw0UAILSQ== + dependencies: + commander "^2.19.0" + moo "^0.5.0" + railroad-diagrams "^1.0.0" + randexp "0.4.6" + +nice-try@^1.0.4: + version "1.0.5" + resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" + integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== + +node-addon-api@^7.0.0: + version "7.1.1" + resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-7.1.1.tgz#1aba6693b0f255258a049d621329329322aad558" + integrity sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ== + +node-releases@^2.0.13: + version "2.0.13" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.13.tgz#d5ed1627c23e3461e819b02e57b75e4899b1c81d" + integrity sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ== + +normalize-package-data@^2.3.2: + version "2.5.0" + resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" + integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA== + dependencies: + hosted-git-info "^2.1.4" + resolve "^1.10.0" + semver "2 || 3 || 4 || 5" + validate-npm-package-license "^3.0.1" + +normalize-path@^3.0.0, normalize-path@~3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" + integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== + +npm-run-all@^4.1.5: + version "4.1.5" + resolved "https://registry.yarnpkg.com/npm-run-all/-/npm-run-all-4.1.5.tgz#04476202a15ee0e2e214080861bff12a51d98fba" + integrity sha512-Oo82gJDAVcaMdi3nuoKFavkIHBRVqQ1qvMb+9LHk/cF4P6B2m8aP04hGf7oL6wZ9BuGwX1onlLhpuoofSyoQDQ== + dependencies: + ansi-styles "^3.2.1" + chalk "^2.4.1" + cross-spawn "^6.0.5" + memorystream "^0.3.1" + minimatch "^3.0.4" + pidtree "^0.3.0" + read-pkg "^3.0.0" + shell-quote "^1.6.1" + string.prototype.padend "^3.0.0" + +object-inspect@^1.12.3, object-inspect@^1.9.0: + version "1.12.3" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.3.tgz#ba62dffd67ee256c8c086dfae69e016cd1f198b9" + integrity sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g== + +object-keys@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" + integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== + +object.assign@^4.1.4: + version "4.1.4" + resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.4.tgz#9673c7c7c351ab8c4d0b516f4343ebf4dfb7799f" + integrity sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + has-symbols "^1.0.3" + object-keys "^1.1.1" + +once@^1.3.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== + dependencies: + wrappy "1" + +optionator@^0.9.3: + version "0.9.3" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.3.tgz#007397d44ed1872fdc6ed31360190f81814e2c64" + integrity sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg== + dependencies: + "@aashutoshrathi/word-wrap" "^1.2.3" + deep-is "^0.1.3" + fast-levenshtein "^2.0.6" + levn "^0.4.1" + prelude-ls "^1.2.1" + type-check "^0.4.0" + +p-limit@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" + integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== + dependencies: + yocto-queue "^0.1.0" + +p-locate@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" + integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== + dependencies: + p-limit "^3.0.2" + +parent-module@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" + integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== + dependencies: + callsites "^3.0.0" + +parse-json@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-4.0.0.tgz#be35f5425be1f7f6c747184f98a788cb99477ee0" + integrity sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw== + dependencies: + error-ex "^1.3.1" + json-parse-better-errors "^1.0.1" + +path-exists@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" + integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== + +path-is-absolute@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== + +path-key@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" + integrity sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw== + +path-key@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" + integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== + +path-parse@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" + integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== + +path-type@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-3.0.0.tgz#cef31dc8e0a1a3bb0d105c0cd97cf3bf47f4e36f" + integrity sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg== + dependencies: + pify "^3.0.0" + +path-type@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" + integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== + +periscopic@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/periscopic/-/periscopic-3.1.0.tgz#7e9037bf51c5855bd33b48928828db4afa79d97a" + integrity sha512-vKiQ8RRtkl9P+r/+oefh25C3fhybptkHKCZSPlcXiJux2tJF55GnEj3BVn4A5gKfq9NWWXXrxkHBwVPUfH0opw== + dependencies: + "@types/estree" "^1.0.0" + estree-walker "^3.0.0" + is-reference "^3.0.0" + +picocolors@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" + integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== + +picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" + integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== + +pidtree@^0.3.0: + version "0.3.1" + resolved "https://registry.yarnpkg.com/pidtree/-/pidtree-0.3.1.tgz#ef09ac2cc0533df1f3250ccf2c4d366b0d12114a" + integrity sha512-qQbW94hLHEqCg7nhby4yRC7G2+jYHY4Rguc2bjw7Uug4GIJuu1tvf2uHaZv5Q8zdt+WKJ6qK1FOI6amaWUo5FA== + +pify@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176" + integrity sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg== + +prelude-ls@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" + integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== + +prettier-plugin-svelte@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/prettier-plugin-svelte/-/prettier-plugin-svelte-3.0.3.tgz#a823295167f27dc71a4462ee6cb3da9f3f5ca2ea" + integrity sha512-dLhieh4obJEK1hnZ6koxF+tMUrZbV5YGvRpf2+OADyanjya5j0z1Llo8iGwiHmFWZVG/hLEw/AJD5chXd9r3XA== + +prettier@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.0.3.tgz#432a51f7ba422d1469096c0fdc28e235db8f9643" + integrity sha512-L/4pUDMxcNa8R/EthV08Zt42WBO4h1rarVtK0K+QJG0X187OLo7l699jWw0GKuwzkPQ//jMFA/8Xm6Fh3J/DAg== + +pretty-format@^27.0.0, pretty-format@^27.5.1: + version "27.5.1" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-27.5.1.tgz#2181879fdea51a7a5851fb39d920faa63f01d88e" + integrity sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ== + dependencies: + ansi-regex "^5.0.1" + ansi-styles "^5.0.0" + react-is "^17.0.1" + +punycode@^2.1.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.0.tgz#f67fa67c94da8f4d0cfff981aee4118064199b8f" + integrity sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA== + +queue-microtask@^1.2.2: + version "1.2.3" + resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" + integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== + +railroad-diagrams@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/railroad-diagrams/-/railroad-diagrams-1.0.0.tgz#eb7e6267548ddedfb899c1b90e57374559cddb7e" + integrity sha512-cz93DjNeLY0idrCNOH6PviZGRN9GJhsdm9hpn1YCS879fj4W+x5IFJhhkRZcwVgMmFF7R82UA/7Oh+R8lLZg6A== + +randexp@0.4.6: + version "0.4.6" + resolved "https://registry.yarnpkg.com/randexp/-/randexp-0.4.6.tgz#e986ad5e5e31dae13ddd6f7b3019aa7c87f60ca3" + integrity sha512-80WNmd9DA0tmZrw9qQa62GPPWfuXJknrmVmLcxvq4uZBdYqb1wYoKTmnlGUchvVWe0XiLupYkBoXVOxz3C8DYQ== + dependencies: + discontinuous-range "1.0.0" + ret "~0.1.10" + +react-is@^17.0.1: + version "17.0.2" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0" + integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== + +read-pkg@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-3.0.0.tgz#9cbc686978fee65d16c00e2b19c237fcf6e38389" + integrity sha512-BLq/cCO9two+lBgiTYNqD6GdtK8s4NpaWrl6/rCO9w0TUS8oJl7cmToOZfRYllKTISY6nt1U7jQ53brmKqY6BA== + dependencies: + load-json-file "^4.0.0" + normalize-package-data "^2.3.2" + path-type "^3.0.0" + +readdirp@^4.0.1: + version "4.1.2" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-4.1.2.tgz#eb85801435fbf2a7ee58f19e0921b068fc69948d" + integrity sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg== + +readdirp@~3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" + integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA== + dependencies: + picomatch "^2.2.1" + +regexp.prototype.flags@^1.5.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.5.1.tgz#90ce989138db209f81492edd734183ce99f9677e" + integrity sha512-sy6TXMN+hnP/wMy+ISxg3krXx7BAtWVO4UouuCN/ziM9UEne0euamVNafDfvC83bRNr95y0V5iijeDQFUNpvrg== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + set-function-name "^2.0.0" + +resolve-from@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" + integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== + +resolve@^1.10.0, resolve@^1.22.2: + version "1.22.6" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.6.tgz#dd209739eca3aef739c626fea1b4f3c506195362" + integrity sha512-njhxM7mV12JfufShqGy3Rz8j11RPdLy4xi15UurGJeoHLfJpVXKdh3ueuOqbYUcDZnffr6X739JBo5LzyahEsw== + dependencies: + is-core-module "^2.13.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + +ret@~0.1.10: + version "0.1.15" + resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" + integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg== + +reusify@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" + integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== + +rimraf@^2.5.2: + version "2.7.1" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" + integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== + dependencies: + glob "^7.1.3" + +rimraf@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" + integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== + dependencies: + glob "^7.1.3" + +run-parallel@^1.1.9: + version "1.2.0" + resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" + integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA== + dependencies: + queue-microtask "^1.2.2" + +sade@^1.7.4: + version "1.8.1" + resolved "https://registry.yarnpkg.com/sade/-/sade-1.8.1.tgz#0a78e81d658d394887be57d2a409bf703a3b2701" + integrity sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A== + dependencies: + mri "^1.1.0" + +safe-array-concat@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/safe-array-concat/-/safe-array-concat-1.0.1.tgz#91686a63ce3adbea14d61b14c99572a8ff84754c" + integrity sha512-6XbUAseYE2KtOuGueyeobCySj9L4+66Tn6KQMOPQJrAJEowYKW/YR/MGJZl7FdydUdaFu4LYyDZjxf4/Nmo23Q== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.2.1" + has-symbols "^1.0.3" + isarray "^2.0.5" + +safe-regex-test@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/safe-regex-test/-/safe-regex-test-1.0.0.tgz#793b874d524eb3640d1873aad03596db2d4f2295" + integrity sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.1.3" + is-regex "^1.1.4" + +sander@^0.5.0: + version "0.5.1" + resolved "https://registry.yarnpkg.com/sander/-/sander-0.5.1.tgz#741e245e231f07cafb6fdf0f133adfa216a502ad" + integrity sha512-3lVqBir7WuKDHGrKRDn/1Ye3kwpXaDOMsiRP1wd6wpZW56gJhsbp5RqQpA6JG/P+pkXizygnr1dKR8vzWaVsfA== + dependencies: + es6-promise "^3.1.2" + graceful-fs "^4.1.3" + mkdirp "^0.5.1" + rimraf "^2.5.2" + +sass@^1.65.1: + version "1.67.0" + resolved "https://registry.yarnpkg.com/sass/-/sass-1.67.0.tgz#fed84d74b9cd708db603b1380d6dc1f71bb24f6f" + integrity sha512-SVrO9ZeX/QQyEGtuZYCVxoeAL5vGlYjJ9p4i4HFuekWl8y/LtJ7tJc10Z+ck1c8xOuoBm2MYzcLfTAffD0pl/A== + dependencies: + chokidar ">=3.0.0 <4.0.0" + immutable "^4.0.0" + source-map-js ">=0.6.2 <2.0.0" + +sass@^1.67.0: + version "1.87.0" + resolved "https://registry.yarnpkg.com/sass/-/sass-1.87.0.tgz#8cceb36fa63fb48a8d5d7f2f4c13b49c524b723e" + integrity sha512-d0NoFH4v6SjEK7BoX810Jsrhj7IQSYHAHLi/iSpgqKc7LaIDshFRlSg5LOymf9FqQhxEHs2W5ZQXlvy0KD45Uw== + dependencies: + chokidar "^4.0.0" + immutable "^5.0.2" + source-map-js ">=0.6.2 <2.0.0" + optionalDependencies: + "@parcel/watcher" "^2.4.1" + +"semver@2 || 3 || 4 || 5", semver@^5.5.0: + version "5.7.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" + integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== + +semver@^7.5.4: + version "7.5.4" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e" + integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA== + dependencies: + lru-cache "^6.0.0" + +set-function-name@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/set-function-name/-/set-function-name-2.0.1.tgz#12ce38b7954310b9f61faa12701620a0c882793a" + integrity sha512-tMNCiqYVkXIZgc2Hnoy2IvC/f8ezc5koaRFkCjrpWzGpCd3qbZXPzVy9MAZzK1ch/X0jvSkojys3oqJN0qCmdA== + dependencies: + define-data-property "^1.0.1" + functions-have-names "^1.2.3" + has-property-descriptors "^1.0.0" + +shebang-command@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" + integrity sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg== + dependencies: + shebang-regex "^1.0.0" + +shebang-command@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" + integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== + dependencies: + shebang-regex "^3.0.0" + +shebang-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" + integrity sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ== + +shebang-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" + integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== + +shell-quote@^1.6.1: + version "1.8.1" + resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.8.1.tgz#6dbf4db75515ad5bac63b4f1894c3a154c766680" + integrity sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA== + +side-channel@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf" + integrity sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw== + dependencies: + call-bind "^1.0.0" + get-intrinsic "^1.0.2" + object-inspect "^1.9.0" + +slash@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" + integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== + +sorcery@^0.11.0: + version "0.11.0" + resolved "https://registry.yarnpkg.com/sorcery/-/sorcery-0.11.0.tgz#310c80ee993433854bb55bb9aa4003acd147fca8" + integrity sha512-J69LQ22xrQB1cIFJhPfgtLuI6BpWRiWu1Y3vSsIwK/eAScqJxd/+CJlUuHQRdX2C9NGFamq+KqNywGgaThwfHw== + dependencies: + "@jridgewell/sourcemap-codec" "^1.4.14" + buffer-crc32 "^0.2.5" + minimist "^1.2.0" + sander "^0.5.0" + +"source-map-js@>=0.6.2 <2.0.0", source-map-js@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c" + integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw== + +source-map-support@~0.5.20: + version "0.5.21" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" + integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== + dependencies: + buffer-from "^1.0.0" + source-map "^0.6.0" + +source-map@^0.6.0: + version "0.6.1" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== + +spdx-correct@^3.0.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.2.0.tgz#4f5ab0668f0059e34f9c00dce331784a12de4e9c" + integrity sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA== + dependencies: + spdx-expression-parse "^3.0.0" + spdx-license-ids "^3.0.0" + +spdx-exceptions@^2.1.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz#3f28ce1a77a00372683eade4a433183527a2163d" + integrity sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A== + +spdx-expression-parse@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz#cf70f50482eefdc98e3ce0a6833e4a53ceeba679" + integrity sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q== + dependencies: + spdx-exceptions "^2.1.0" + spdx-license-ids "^3.0.0" + +spdx-license-ids@^3.0.0: + version "3.0.14" + resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.14.tgz#d16b09577b7de461c8c3e69734c4b9b467ac791f" + integrity sha512-U0eS5wcpu/O2/QZk6PcAMOA8H3ZuvRe4mFHA3Q+LNl1SRDmfQ+mD3RoD6tItqnvqubJ32m/zV2Z/ikSmxccD1Q== + +string.prototype.padend@^3.0.0: + version "3.1.5" + resolved "https://registry.yarnpkg.com/string.prototype.padend/-/string.prototype.padend-3.1.5.tgz#311ef3a4e3c557dd999cdf88fbdde223f2ac0f95" + integrity sha512-DOB27b/2UTTD+4myKUFh+/fXWcu/UDyASIXfg+7VzoCNNGOfWvoyU/x5pvVHr++ztyt/oSYI1BcWBBG/hmlNjA== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + +string.prototype.trim@^1.2.8: + version "1.2.8" + resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.2.8.tgz#f9ac6f8af4bd55ddfa8895e6aea92a96395393bd" + integrity sha512-lfjY4HcixfQXOfaqCvcBuOIapyaroTXhbkfJN3gcB1OtyupngWK4sEET9Knd0cXd28kTUqu/kHoV4HKSJdnjiQ== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + +string.prototype.trimend@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.7.tgz#1bb3afc5008661d73e2dc015cd4853732d6c471e" + integrity sha512-Ni79DqeB72ZFq1uH/L6zJ+DKZTkOtPIHovb3YZHQViE+HDouuU4mBrLOLDn5Dde3RF8qw5qVETEjhu9locMLvA== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + +string.prototype.trimstart@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.7.tgz#d4cdb44b83a4737ffbac2d406e405d43d0184298" + integrity sha512-NGhtDFu3jCEm7B4Fy0DpLewdJQOZcQ0rGbwQ/+stjnrp2i+rlKeCvos9hOIeCmqwratM47OBxY7uFZzjxHXmrg== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + +strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + +strip-bom@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" + integrity sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA== + +strip-indent@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-3.0.0.tgz#c32e1cee940b6b3432c771bc2c54bcce73cd3001" + integrity sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ== + dependencies: + min-indent "^1.0.0" + +strip-json-comments@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" + integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== + +supports-color@^5.3.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" + integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== + dependencies: + has-flag "^3.0.0" + +supports-color@^7.1.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" + integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== + dependencies: + has-flag "^4.0.0" + +supports-preserve-symlinks-flag@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" + integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== + +svelte-check@^3.5.1: + version "3.5.1" + resolved "https://registry.yarnpkg.com/svelte-check/-/svelte-check-3.5.1.tgz#88265b41623b9374ff35b69802497287073d693d" + integrity sha512-+Zb4iHxAhdUtcUg/WJPRjlS1RJalIsWAe9Mz6G1zyznSs7dDkT7VUBdXc3q7Iwg49O/VrZgyJRvOJkjuBfKjFA== + dependencies: + "@jridgewell/trace-mapping" "^0.3.17" + chokidar "^3.4.1" + fast-glob "^3.2.7" + import-fresh "^3.2.1" + picocolors "^1.0.0" + sade "^1.7.4" + svelte-preprocess "^5.0.4" + typescript "^5.0.3" + +svelte-preprocess-esbuild@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/svelte-preprocess-esbuild/-/svelte-preprocess-esbuild-3.0.1.tgz#1f31c7a46f56d4c60dde3ec5fe72f2ddf40ad700" + integrity sha512-OV4P/onki7hfuzIpYOd92TT0JSm4y67PvkAAM7RigLd869VEvNh2f6J+ewrEqAgTnZzVQcOWzuaU4hPISNPFpA== + +svelte-preprocess@^5.0.4: + version "5.0.4" + resolved "https://registry.yarnpkg.com/svelte-preprocess/-/svelte-preprocess-5.0.4.tgz#2123898e079a074f7f4ef1799e10e037f5bcc55b" + integrity sha512-ABia2QegosxOGsVlsSBJvoWeXy1wUKSfF7SWJdTjLAbx/Y3SrVevvvbFNQqrSJw89+lNSsM58SipmZJ5SRi5iw== + dependencies: + "@types/pug" "^2.0.6" + detect-indent "^6.1.0" + magic-string "^0.27.0" + sorcery "^0.11.0" + strip-indent "^3.0.0" + +svelte@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/svelte/-/svelte-4.2.0.tgz#0e4304c15524450b22fba02516eb72efbd8847b6" + integrity sha512-kVsdPjDbLrv74SmLSUzAsBGquMs4MPgWGkGLpH+PjOYnFOziAvENVzgJmyOCV2gntxE32aNm8/sqNKD6LbIpeQ== + dependencies: + "@ampproject/remapping" "^2.2.1" + "@jridgewell/sourcemap-codec" "^1.4.15" + "@jridgewell/trace-mapping" "^0.3.18" + acorn "^8.9.0" + aria-query "^5.3.0" + axobject-query "^3.2.1" + code-red "^1.0.3" + css-tree "^2.3.1" + estree-walker "^3.0.3" + is-reference "^3.0.1" + locate-character "^3.0.0" + magic-string "^0.30.0" + periscopic "^3.1.0" + +terser@^5.3.2: + version "5.39.0" + resolved "https://registry.yarnpkg.com/terser/-/terser-5.39.0.tgz#0e82033ed57b3ddf1f96708d123cca717d86ca3a" + integrity sha512-LBAhFyLho16harJoWMg/nZsQYgTrg5jXOn2nCYjRUcZZEdE3qa2zb8QEDRUGVZBW4rlazf2fxkg8tztybTaqWw== + dependencies: + "@jridgewell/source-map" "^0.3.3" + acorn "^8.8.2" + commander "^2.20.0" + source-map-support "~0.5.20" + +text-table@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" + integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw== + +to-regex-range@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" + integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== + dependencies: + is-number "^7.0.0" + +ts-api-utils@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-1.0.3.tgz#f12c1c781d04427313dbac808f453f050e54a331" + integrity sha512-wNMeqtMz5NtwpT/UZGY5alT+VoKdSsOOP/kqHFcUW1P/VRhH2wJ48+DN2WwUliNbQ976ETwDL0Ifd2VVvgonvg== + +type-check@^0.4.0, type-check@~0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" + integrity sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew== + dependencies: + prelude-ls "^1.2.1" + +type-fest@^0.20.2: + version "0.20.2" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4" + integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ== + +typed-array-buffer@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/typed-array-buffer/-/typed-array-buffer-1.0.0.tgz#18de3e7ed7974b0a729d3feecb94338d1472cd60" + integrity sha512-Y8KTSIglk9OZEr8zywiIHG/kmQ7KWyjseXs1CbSo8vC42w7hg2HgYTxSWwP0+is7bWDc1H+Fo026CpHFwm8tkw== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.2.1" + is-typed-array "^1.1.10" + +typed-array-byte-length@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/typed-array-byte-length/-/typed-array-byte-length-1.0.0.tgz#d787a24a995711611fb2b87a4052799517b230d0" + integrity sha512-Or/+kvLxNpeQ9DtSydonMxCx+9ZXOswtwJn17SNLvhptaXYDJvkFFP5zbfU/uLmvnBJlI4yrnXRxpdWH/M5tNA== + dependencies: + call-bind "^1.0.2" + for-each "^0.3.3" + has-proto "^1.0.1" + is-typed-array "^1.1.10" + +typed-array-byte-offset@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/typed-array-byte-offset/-/typed-array-byte-offset-1.0.0.tgz#cbbe89b51fdef9cd6aaf07ad4707340abbc4ea0b" + integrity sha512-RD97prjEt9EL8YgAgpOkf3O4IF9lhJFr9g0htQkm0rchFp/Vx7LW5Q8fSXXub7BXAODyUQohRMyOc3faCPd0hg== + dependencies: + available-typed-arrays "^1.0.5" + call-bind "^1.0.2" + for-each "^0.3.3" + has-proto "^1.0.1" + is-typed-array "^1.1.10" + +typed-array-length@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/typed-array-length/-/typed-array-length-1.0.4.tgz#89d83785e5c4098bec72e08b319651f0eac9c1bb" + integrity sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng== + dependencies: + call-bind "^1.0.2" + for-each "^0.3.3" + is-typed-array "^1.1.9" + +typescript@^5.0.3, typescript@^5.2.2: + version "5.2.2" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.2.2.tgz#5ebb5e5a5b75f085f22bc3f8460fba308310fa78" + integrity sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w== + +unbox-primitive@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.2.tgz#29032021057d5e6cdbd08c5129c226dff8ed6f9e" + integrity sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw== + dependencies: + call-bind "^1.0.2" + has-bigints "^1.0.2" + has-symbols "^1.0.3" + which-boxed-primitive "^1.0.2" + +update-browserslist-db@^1.0.11: + version "1.0.11" + resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz#9a2a641ad2907ae7b3616506f4b977851db5b940" + integrity sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA== + dependencies: + escalade "^3.1.1" + picocolors "^1.0.0" + +uri-js@^4.2.2: + version "4.4.1" + resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" + integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== + dependencies: + punycode "^2.1.0" + +validate-npm-package-license@^3.0.1: + version "3.0.4" + resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a" + integrity sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew== + dependencies: + spdx-correct "^3.0.0" + spdx-expression-parse "^3.0.0" + +which-boxed-primitive@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6" + integrity sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg== + dependencies: + is-bigint "^1.0.1" + is-boolean-object "^1.1.0" + is-number-object "^1.0.4" + is-string "^1.0.5" + is-symbol "^1.0.3" + +which-typed-array@^1.1.11: + version "1.1.11" + resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.11.tgz#99d691f23c72aab6768680805a271b69761ed61a" + integrity sha512-qe9UWWpkeG5yzZ0tNYxDmd7vo58HDBc39mZ0xWWpolAGADdFOzkfamWLDxkOWcvHQKVmdTyQdLD4NOfjLWTKew== + dependencies: + available-typed-arrays "^1.0.5" + call-bind "^1.0.2" + for-each "^0.3.3" + gopd "^1.0.1" + has-tostringtag "^1.0.0" + +which@^1.2.9: + version "1.3.1" + resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" + integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== + dependencies: + isexe "^2.0.0" + +which@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" + integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== + dependencies: + isexe "^2.0.0" + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== + +yallist@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" + integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== + +yocto-queue@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" + integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== From 24b81bdb17909e64822e198ee903e74a94fc8412 Mon Sep 17 00:00:00 2001 From: Abdo Date: Sat, 10 May 2025 13:13:32 +0300 Subject: [PATCH 2/7] Fix tests --- src/anking_notetypes/editor.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/anking_notetypes/editor.py b/src/anking_notetypes/editor.py index 244a2dc..af1169c 100644 --- a/src/anking_notetypes/editor.py +++ b/src/anking_notetypes/editor.py @@ -20,8 +20,6 @@ from .notetype_setting_definitions import is_io_note_type -addon_package = mw.addonManager.addonFromModule(__name__) -mw.addonManager.setWebExports(__name__, r"(web|resources)/.*(css|js)") occlude_shortcut = "Ctrl+O" occlusion_behavior = "autopaste" @@ -140,6 +138,7 @@ def include_closet_code(webcontent, context) -> None: if not isinstance(context, Editor): return + addon_package = mw.addonManager.addonFromModule(__name__) webcontent.css.append(f"/_addons/{addon_package}/web/editor.css") webcontent.js.append(f"/_addons/{addon_package}/web/editor.js") @@ -239,6 +238,7 @@ def toggle_occlusion_mode(editor): editor.web.eval("EditorCloset.setInactive()") return + addon_package = mw.addonManager.addonFromModule(__name__) js_path = json.dumps(f"_addons/{addon_package}/resources/{anking_io_js_filename()}") max_code_fields = get_max_code_field(editor) @@ -316,3 +316,4 @@ def init_webview(): editor_will_load_note.append(clear_occlusion_mode) editor_did_load_note.append(maybe_refocus) editor_will_munge_html.append(remove_occlusion_code) + mw.addonManager.setWebExports(__name__, r"(web|resources)/.*(css|js)") From ab8b2cfb390a0c21af42ce790082d846731efd5f Mon Sep 17 00:00:00 2001 From: Abdo Date: Sat, 10 May 2025 13:24:49 +0300 Subject: [PATCH 3/7] Fix newline in version --- ts/build.mjs | 2 +- ts/scripts/compile-style.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ts/build.mjs b/ts/build.mjs index df7264e..10a9f6a 100644 --- a/ts/build.mjs +++ b/ts/build.mjs @@ -3,7 +3,7 @@ import * as esbuild from "esbuild"; import fs from "fs"; function getVersion() { - return fs.readFileSync(".version", { encoding: "utf8", flag: "r" }); + return fs.readFileSync(".version", { encoding: "utf8", flag: "r" }).trim(); } const production = env.NODE_ENV === "production"; diff --git a/ts/scripts/compile-style.js b/ts/scripts/compile-style.js index 6cf1df7..28aa022 100644 --- a/ts/scripts/compile-style.js +++ b/ts/scripts/compile-style.js @@ -5,7 +5,7 @@ const sass = require("sass"); const __basedir = `${__dirname}/..`; const getVersion = () => { - return fs.readFileSync(`${__basedir}/.version`, { encoding: 'utf8', flag: 'r' }); + return fs.readFileSync(`${__basedir}/.version`, { encoding: 'utf8', flag: 'r' }).trim(); } const renderFile = (input, output) => { sass.render( From 94df3508a4bb7e28c376bdee4bf6fcdcc3a98d8b Mon Sep 17 00:00:00 2001 From: Abdo Date: Sat, 10 May 2025 14:58:02 +0300 Subject: [PATCH 4/7] Make compatible with Closet --- src/anking_notetypes/editor.py | 41 +-- .../IO-one by one/Back Template.html | 16 +- .../IO-one by one/IO-one by one.json | 3 +- .../note_types/IO-one by one/Styling.css | 8 +- .../Physeo-IO one by one/Back Template.html | 16 +- .../Physeo-IO one by one.json | 3 +- .../Physeo-IO one by one/Styling.css | 8 +- .../resources/__ankingio-0.6.2.css | 90 +++---- .../resources/__ankingio-0.6.2.js | 48 ++-- src/anking_notetypes/web/editor.css | 14 +- src/anking_notetypes/web/editor.js | 235 +++++++++--------- ts/src/browser/occlusionEditor.ts | 6 +- ts/src/browser/rect.ts | 8 +- ts/src/browser/svgClasses.ts | 6 +- ts/src/browser/utils.ts | 8 +- ts/src/filterManager.ts | 16 +- ts/src/flashcard/cloze.ts | 10 +- ts/src/flashcard/multipleChoice.ts | 14 +- ts/src/flashcard/shuffleQuestion.ts | 10 +- ts/src/recipes/shuffling.ts | 6 +- ts/src/template/anki/delay.ts | 6 +- ts/src/template/anki/initialize.ts | 6 +- ts/src/template/anki/persistence.ts | 2 +- ts/src/template/anki/utils.ts | 2 +- ts/src/template/browser/index.ts | 6 +- ts/style/README.adoc | 22 +- ts/style/_cloze.scss | 6 +- ts/style/_multiple-choice.scss | 12 +- ts/style/_rect.scss | 6 +- ts/style/_shuffle-question.scss | 8 +- ts/style/_shuffle.scss | 2 +- ts/style/base.scss | 4 +- ts/style/editor.scss | 4 +- 33 files changed, 324 insertions(+), 328 deletions(-) diff --git a/src/anking_notetypes/editor.py b/src/anking_notetypes/editor.py index af1169c..bba5034 100644 --- a/src/anking_notetypes/editor.py +++ b/src/anking_notetypes/editor.py @@ -94,7 +94,7 @@ def make_insertion_js(field_index: int, text: str) -> str: cmd = ( f"pycmd(`key:{field_index}:${{getNoteId()}}:{escaped}`); " - f"EditorCloset.setFieldHTML({field_index}, `{escaped}`); " + f"EditorIO.setFieldHTML({field_index}, `{escaped}`); " ) return cmd @@ -106,7 +106,7 @@ def insert_into_zero_indexed(editor, text: str) -> None: if not match or int(match[0]) != 0: continue - editor.web.eval(f"EditorCloset.insertIntoZeroIndexed(`{text}`, {index}); ") + editor.web.eval(f"EditorIO.insertIntoZeroIndexed(`{text}`, {index}); ") break @@ -157,13 +157,13 @@ def add_occlusion_messages( if isinstance(context, Editor): editor: Editor = context global old_occlusion_indices, occlusion_editor_active # pylint: disable=global-statement - if message.startswith("oldOcclusions"): + if message.startswith("ankingOldOcclusions"): _, _src, index_text = message.split(":", 2) old_occlusion_indices = process_occlusion_index_text(index_text) return (True, None) - elif message.startswith("newOcclusions"): + elif message.startswith("ankingNewOcclusions"): _, _src, index_text = message.split(":", 2) indices = process_occlusion_index_text(index_text) @@ -172,7 +172,7 @@ def add_occlusion_messages( return (True, could_fill) - elif message.startswith("occlusionText"): + elif message.startswith("ankingOcclusionText"): text = message.split(":", 1)[1] if occlusion_behavior == "autopaste": @@ -182,19 +182,19 @@ def add_occlusion_messages( return (True, None) - elif message == "occlusionEditorActive": + elif message == "ankingOcclusionEditorActive": occlusion_editor_active = True return (True, None) - elif message == "occlusionEditorInactive": + elif message == "ankingOcclusionEditorInactive": occlusion_editor_active = False return (True, None) - elif message.startswith("closetRefocus"): + elif message.startswith("ankingClosetRefocus"): refocus(editor) return (True, None) - elif message.startswith("closetMultipleImages"): + elif message.startswith("ankingClosetMultipleImages"): showInfo("Cannot start occlusion editor if field contains multiple images.") return (True, None) @@ -235,7 +235,7 @@ def toggle_occlusion_mode(editor): if not is_io_note_type(model["name"]): showInfo("Please choose an AnKing image occlusion note type") - editor.web.eval("EditorCloset.setInactive()") + editor.web.eval("EditorIO.setInactive()") return addon_package = mw.addonManager.addonFromModule(__name__) @@ -245,7 +245,7 @@ def toggle_occlusion_mode(editor): if not mw.focusWidget() is editor: refocus(editor) - editor.web.eval(f"EditorCloset.toggleOcclusionMode({js_path}, {max_code_fields})") + editor.web.eval(f"EditorIO.toggleOcclusionMode({js_path}, {max_code_fields})") def add_occlusion_button(buttons, editor): @@ -256,13 +256,15 @@ def add_occlusion_button(buttons, editor): occlusion_button = editor._addButton( # pylint: disable=protected-access str(icon_path.absolute()), - "occlude", + "anking_occlude", f"Put all fields into occlusion mode ({shortcut_as_text})", - id="closetOcclude", + id="ankingOcclude", disables=False, ) - editor._links["occlude"] = toggle_occlusion_mode # pylint: disable=protected-access + editor._links[ # pylint: disable=protected-access + "anking_occlude" + ] = toggle_occlusion_mode buttons.insert(-1, occlusion_button) @@ -271,7 +273,8 @@ def wrap_as_option(name: str, code: str, tooltip: str, shortcut_text: str) -> st def add_buttons(buttons, editor): - editor.occlusion_editor_active = False + global occlusion_editor_active # pylint: disable=global-statement + occlusion_editor_active = False add_occlusion_button(buttons, editor) @@ -282,7 +285,7 @@ def add_occlusion_shortcut(cuts, editor): occlusion_container_pattern = re.compile( # remove trailing
to prevent accumulation - r'
.*?().*?
(
)*' + r'
.*?().*?
(
)*' ) @@ -296,16 +299,16 @@ def remove_occlusion_code(txt: str, _editor) -> str: def clear_occlusion_mode(js, _note, _editor): - return f"EditorCloset.clearOcclusionMode().then(() => {{ {js} }}); " + return f"EditorIO.clearOcclusionMode().then(() => {{ {js} }}); " def refocus(editor): editor.web.setFocus() - editor.web.eval("EditorCloset.refocus(); ") + editor.web.eval("EditorIO.refocus(); ") def maybe_refocus(editor): - editor.web.eval("EditorCloset.maybeRefocus(); ") + editor.web.eval("EditorIO.maybeRefocus(); ") def init_webview(): diff --git a/src/anking_notetypes/note_types/IO-one by one/Back Template.html b/src/anking_notetypes/note_types/IO-one by one/Back Template.html index d983d08..432ce32 100644 --- a/src/anking_notetypes/note_types/IO-one by one/Back Template.html +++ b/src/anking_notetypes/note_types/IO-one by one/Back Template.html @@ -319,11 +319,11 @@ toggleAllButton.title = `Shortcut: ${window.toggleAllShortcut}`; revealNextButton.title = `Shortcut: ${window.revealNextShortcut}`; - let rect = document.querySelector(".closet-rect.is-active") + let rect = document.querySelector(".anking-rect.is-active") if (rect) { activate(rect) observer.disconnect() - globalThis.AnkingIORects = document.querySelectorAll(".closet-rect.is-active") + globalThis.AnkingIORects = document.querySelectorAll(".anking-rect.is-active") for (let rect of globalThis.AnkingIORects) { rect.addEventListener("click", reveal) } @@ -366,7 +366,7 @@ window.toggleNext = function() { - let active = document.querySelector(".closet-rect.is-highlighted") + let active = document.querySelector(".anking-rect.is-highlighted") if (active) incrementalReveal.call(active) } @@ -379,7 +379,7 @@ for (let rect of globalThis.AnkingIORects) { hide.call(rect) } - let newActiveRect = document.querySelector(".closet-rect.is-active") + let newActiveRect = document.querySelector(".anking-rect.is-active") activate(newActiveRect) } else { for (let rect of globalThis.AnkingIORects) { @@ -421,7 +421,7 @@ setupIncrementalIO() - -