diff --git a/tests/fuzz/00_hello_world/_assets/image-placeholder.jpg b/tests/fuzz/00_hello_world/_assets/image-placeholder.jpg new file mode 100644 index 0000000..97c24fe Binary files /dev/null and b/tests/fuzz/00_hello_world/_assets/image-placeholder.jpg differ diff --git a/tests/fuzz/00_hello_world/file_to_mutate.html b/tests/fuzz/00_hello_world/file_to_mutate.html new file mode 100644 index 0000000..e7c0872 --- /dev/null +++ b/tests/fuzz/00_hello_world/file_to_mutate.html @@ -0,0 +1,145 @@ + + + + + + + + EXAMPLE - PDF + + + +
+

Nothing except the [html2pdf] element is printed. +
Only what is visible on the sheets is printed: +

+
+ + +
+

EXAMPLE

+

This image is in /_assets:

+

+ copy a stable link +

+

Some test.

+
+ + +
+ + + + + + +
+ + + diff --git a/tests/fuzz/00_hello_world/test_case.py b/tests/fuzz/00_hello_world/test_case.py new file mode 100644 index 0000000..d79a10d --- /dev/null +++ b/tests/fuzz/00_hello_world/test_case.py @@ -0,0 +1,21 @@ +import os + +from html2pdf4doc.main_fuzzer import fuzz_test +from tests.fuzz.conftest import create_build_folder, FuzzConfig, create_failed_mutants_folder + +PATH_TO_THIS_FOLDER = os.path.dirname(__file__) + +def test(fuzz_config: FuzzConfig): + build_folder = create_build_folder(PATH_TO_THIS_FOLDER) + + fuzz_test( + path_to_input_file=os.path.join( + build_folder, + "file_to_mutate.html" + ), + path_to_root=build_folder, + path_to_failed_mutants_dir=create_failed_mutants_folder(PATH_TO_THIS_FOLDER), + total_mutations=fuzz_config.total_mutations, + strict_mode=True, + strict_mode_2=False, + ) diff --git a/tests/fuzz/20_L1_Open_Requirements_Tool_202511/assets/autogen.css b/tests/fuzz/20_L1_Open_Requirements_Tool_202511/assets/autogen.css new file mode 100644 index 0000000..3224e74 --- /dev/null +++ b/tests/fuzz/20_L1_Open_Requirements_Tool_202511/assets/autogen.css @@ -0,0 +1,257 @@ +/* A wrapper for styling the markup generated by MarkupRenderer. */ + +sdoc-autogen { + /* + TODO: 'display: contents;' + Turn this back on when the bug is fixed: + Selenium cannot detect text inside a container + with the 'display: contents;' directive + */ + /* display: contents; */ + + /* hyphens: auto; */ + + --autogen-v-rhythm: calc(var(--base-rhythm, 8px) * 2); +} + +sdoc-autogen a, +sdoc-autogen a:link, +sdoc-autogen a:visited { + text-decoration: underline; +} + +/* Table */ + +sdoc-autogen table { + border-collapse: collapse; + margin: var(--base-padding) 0; + font-size: 1rem; + + /*** add scroll for wide tables */ + /* issue#1370 https://css-tricks.com/preventing-a-grid-blowout/ */ + /* border: 1px solid #ccc; */ + border: none; + display: block; + overflow-x: auto; + white-space: nowrap; +} + +sdoc-autogen table caption { + font-weight: bold; + padding-bottom: 1rem; +} + +sdoc-autogen table th { + background-color: var(--color-bg-main); +} + +sdoc-autogen table th, +sdoc-autogen table td { + padding: var(--base-rhythm) calc(var(--base-rhythm) * 1.5); + vertical-align: top; + text-align: left; + border: 1px solid #ccc; + + /* issue#1370 https://css-tricks.com/preventing-a-grid-blowout/ */ + /*** add scroll for wide tables (unset) */ + white-space: initial; +} + +/* Typography */ + +sdoc-autogen p { + margin: var(--autogen-v-rhythm) 0; +} + +/* blockquote */ + +sdoc-autogen blockquote { + color: #666; + padding: .25em 0 0.25em 1rem; + border-left: 4px solid #ccc; + margin: var(--autogen-v-rhythm) 0; +} + +/* pre.code */ + +sdoc-autogen pre.code { + font-family: var(--code-font-family); + + font-size: var(--code-font-size); + line-height: 2; + margin: var(--code-font-size) 0; + padding: calc(var(--code-font-size)*1) + calc(var(--code-font-size)*1) + calc(var(--code-font-size)*1) + calc(var(--code-font-size)*2); + + overflow: auto; + background-color: var(--color-bg-main); + border: 1px solid var(--color-border); +} + +/* We have to override this for the print version because the printer + interprets scroll-bars differently in different environments, + which breaks HTML2PDF. + Also, we need to show the whole code in its entirety. + */ +[html2pdf] sdoc-autogen pre.code { + overflow: unset; + white-space: pre-wrap; + overflow-wrap: break-word; +} + +/* ``some text`` is generated into: by docutils. */ +sdoc-autogen tt.literal { + position: relative; + padding: 0 4px; + font-style: normal; + font-family: var(--code-font-family); + font-size: var(--code-font-size); + background-color: var(--color-bg-main); + border: 1px solid var(--color-border); + border-radius: 4px; + + overflow-wrap: break-word; + word-wrap: break-word; + -webkit-box-decoration-break: clone; + box-decoration-break: clone; +} + +/* ul */ + +sdoc-autogen ul, +sdoc-autogen ol { + padding-left: 1.6em; + margin: var(--autogen-v-rhythm) 0; +} + +/* list in table */ +sdoc-autogen td ul, +sdoc-autogen dt ol { + margin: 0; + padding-left: 1em; +} + +/* object */ + +sdoc-autogen img, +sdoc-autogen object { + max-width: 100%; + height: auto; + padding: 1em; + background: var(--color-bg-contrast); +} + +/* + ************************** + automatically added by RST + ************************** +*/ + +div.document { + /* alarm style for detecting unwrapped blocks */ + border: 1px dashed red; +} + +sdoc-autogen div.document { + display: contents; + border: none; +} + +/* block margins */ + +sdoc-autogen .document > *:first-child, /* RST */ +sdoc-autogen > *:first-child { + margin-top: 0 !important; +} + +sdoc-autogen .document > *:last-child, /* RST */ +sdoc-autogen > *:last-child { + margin-bottom: 0 !important; +} + +/* admonition by RST */ +/* "attention", "caution", "danger", "error", "hint", "important", "note", "tip", "warning" */ + +sdoc-autogen .admonition { + display: block; + overflow: hidden; + padding: 0 var(--autogen-v-rhythm); + border: var(--requirement-border-width, 1px) solid; + border-radius: var(--requirement-border-radius); + margin: var(--autogen-v-rhythm) 0; + color: var(--color-fg-main); +} + +sdoc-autogen .admonition .admonition-title { + margin: 0; + padding-top: calc(0.5 * var(--base-rhythm)); + padding-bottom: calc(0.5 * var(--base-rhythm)); + color: currentColor; + font-weight: 600; + position: relative; +} + +sdoc-autogen .admonition .admonition-title::after { + content: ''; + position: absolute; + top: 0; bottom: 0; + left: calc(-1 * var(--autogen-v-rhythm)); + right: calc(-1 * var(--autogen-v-rhythm)); + /* background:repeating-linear-gradient( + -45deg, + rgba(255, 255, 255, .25), + rgba(255, 255, 255, .25) 10px, + rgba(255, 255, 255, .0) 10px, + rgba(255, 255, 255, .0) 20px + ); */ + background-color: currentColor; + opacity: 0.1; +} + +sdoc-autogen .admonition > *:not(.admonition-title) { + color: var(--color-fg-main); +} + +sdoc-autogen .admonition.attention { + color: Crimson; +} + +sdoc-autogen .admonition.caution { + color: Crimson; +} + +sdoc-autogen .admonition.important { + color: OrangeRed; +} + +sdoc-autogen .admonition.danger { + color: red; +} + +sdoc-autogen .admonition.error { + color: Red; +} + +sdoc-autogen .admonition.warning { + color: DarkOrange; +} + +sdoc-autogen .admonition.warning .admonition-title::before { + /* content: '⚠️'; */ + margin-right: var(--base-rhythm); +} + +sdoc-autogen .admonition.note { + /* color: CornflowerBlue; */ + color: SteelBlue; +} + +sdoc-autogen .admonition.hint { + color: DarkSlateBlue; +} + +sdoc-autogen .admonition.tip { + color: MediumSlateBlue; +} diff --git a/tests/fuzz/20_L1_Open_Requirements_Tool_202511/assets/base.css b/tests/fuzz/20_L1_Open_Requirements_Tool_202511/assets/base.css new file mode 100644 index 0000000..6471e25 --- /dev/null +++ b/tests/fuzz/20_L1_Open_Requirements_Tool_202511/assets/base.css @@ -0,0 +1,154 @@ +/* @import url('https://fonts.googleapis.com/css2?family=Noto+Sans+Mono:wght@400;500;600;700&family=Noto+Sans:ital,wght@0,400;0,500;0,600;0,700;1,400;1,500;1,600;1,700&display=swap'); */ + +/* .test-content {} */ + +:root { + + --color-bg-main: #F2F5F9; + --color-fg-main: #444; + + --color-bg-contrast: #fff; + --color-fg-contrast: #000; + + --color-fg-accent: rgb(242, 100, 42); + --color-bg-accent: rgb(242 100 42 / 10%); + + --color-accent: #274466; + --color-highlight: rgb(255, 255, 200); + --color-highlight-secondary: rgb(230, 236, 242); + + --color-fg-secondary: rgba(0, 0, 0, .5); + --color-bg-secondary: rgba(0, 0, 0, .025); + + --color-fg-secondary-invert: rgb(255 255 255 / 50%); + --color-bg-secondary-invert: rgb(0 0 0 / 10%); + + --color-bg-ui: #282c42; + + --color-red: rgb(200, 0, 0); + --color-blue: rgb(50, 100, 200); + --color-green: rgb(0, 100, 100); + + --color-danger: var(--color-red); + --color-cancel: var(--color-fg-secondary); + --color-submit: var(--color-action); + + --color-link: var(--color-fg-secondary); + --color-action: var(--color-fg-accent); + --color-hover: var(--color-fg-contrast); + + --color-border: rgba(0,0,0,.1); + --color-placeholder: rgba(0,0,0,.25); + + --base-border: 1px solid var(--color-border); + --code-border-color: var(--color-border); + + --scrollbarBG: transparent; + --thumbBG: rgba(0,0,0,.05); + + --base-rhythm: 8px; + + --base-font-size: calc(var(--base-rhythm)*2); + --base-line-height: 1.6; + + --font-size: var(--base-font-size); + + --font-size-l: 1.25rem; + --font-size-sm: 0.8125rem; + --font-size-xsm: 0.75rem; + --font-size-xxsm: 11px; + --code-font-size: .85em; + + --base-font-family: 'Noto Sans', sans-serif; + --code-font-family: 'Noto Sans Mono', consolas, monaco, monospace; + /* + DEV NOTE: To use system fonts, + uncomment the following two variables, replacing the previous two: + */ + /* --base-font-family: ui-sans-serif, system-ui, -apple-system, "system-ui", "Segoe UI", "Helvetica Neue", Helvetica, Arial, sans-serif; */ + /* --code-font-family: monospace; */ + + --base-gap: calc(var(--base-rhythm)*6); + --tree-gap: calc(var(--base-rhythm)*4); + --base-padding: calc(var(--base-rhythm)*2); + + --base-elevation-0: 0 0 0 rgba(0,0,0,0); + --base-elevation-node: 0 0 16px rgba(0,0,0,.1); + --base-elevation-modal: 0 0 32px rgba(0,0,0,.32); + + --main-elevation-shadow: inset 8px 8px 16px rgba(0,0,0,.2); + --base-elevation: 0 0 16px rgba(0,0,0,.2); + + --traceability-arrow: 1.25rem; + + --card-width: 300px; +} + +body { + margin: 0; + padding: 0; + + font-family: var(--base-font-family); + font-size: var(--base-font-size); + line-height: var(--base-line-height); + color: var(--color-fg-main); + background-color: var(--color-bg-main); +} + +@font-face { + font-family: 'Noto Sans'; + font-style: normal; + font-weight: 100 900; + /* font-stretch: 100%; */ + font-display: swap; + src: url('./fonts/NotoSans-VariableFont_wdth,wght.ttf') format('truetype-variations'); +} + +@font-face { + font-family: 'Noto Sans'; + font-style: italic; + font-weight: 100 900; + /* font-stretch: 100%; */ + font-display: swap; + src: url('./fonts/NotoSans-Italic-VariableFont_wdth,wght.ttf') format('truetype-variations'); +} + +@font-face { + font-family: 'Noto Sans Mono'; + font-style: normal; + font-weight: 100 900; + /* font-stretch: 100%; */ + font-display: swap; + src: url('./fonts/NotoSansMono-VariableFont_wdth,wght.ttf') format('truetype-variations'); +} + +* { box-sizing: border-box; } + +sdoc-scope { + display: contents; +} + +/* scrollbar */ + +* { + scrollbar-color: var(--thumbBG) var(--scrollbarBG); +} +::-webkit-scrollbar:horizontal, +::-webkit-scrollbar:vertical, +::-webkit-scrollbar, +::-webkit-scrollbar-track:horizontal, +::-webkit-scrollbar-track:vertical, +::-webkit-scrollbar-track, +::-webkit-scrollbar-corner { + background-color: var(--scrollbarBG); +} +::-webkit-scrollbar-thumb:horizontal, +::-webkit-scrollbar-thumb:vertical, +::-webkit-scrollbar-thumb { + background-color: var(--thumbBG) + /* + background-color: var(--scrollbarBG); + border: 3px solid var(--thumbBG); + border-radius: 6px; + */ +} diff --git a/tests/fuzz/20_L1_Open_Requirements_Tool_202511/assets/collapsible_list.js b/tests/fuzz/20_L1_Open_Requirements_Tool_202511/assets/collapsible_list.js new file mode 100644 index 0000000..6ff3dab --- /dev/null +++ b/tests/fuzz/20_L1_Open_Requirements_Tool_202511/assets/collapsible_list.js @@ -0,0 +1,263 @@ +// To collapse and expand TOC branches + +const ROOT_SELECTOR = 'js-collapsible_list'; + +const BRANCH_SELECTOR = `collapsible_list__branch`; +const LIST_DEFAULT = 'open'; +const SYMBOL_MINUS = '-'; +const SYMBOL_PLUS = '+'; +const SYMBOL_SIZE = 16; + +const STYLE = ` +[data-${BRANCH_SELECTOR}] { + display: flex; + align-items: center; + justify-content: center; + -webkit-box-align: center; + -webkit-box-pack: center; + background-clip: padding-box; + cursor: pointer; + user-select: none; + transition: .3s; + width: ${SYMBOL_SIZE}px; + height: ${SYMBOL_SIZE}px; + font-size: ${SYMBOL_SIZE * 0.875}px; + font-weight: bold; + line-height: 0; + border-radius: 50%; + color: rgba(0,0,0,0.5); + box-shadow: rgb(0 0 0 / 10%) 0px 1px 2px 0px; + position: absolute; + top: ${SYMBOL_SIZE * 0.5}px; + left: -${SYMBOL_SIZE * 0.75}px; +} + +[data-${BRANCH_SELECTOR}]:hover { + border: 1px solid rgba(0, 0, 0, 0.15); + box-shadow: rgb(0 0 0 / 15%) 0px 2px 8px 0px; + color: rgba(0,0,0,1); +} + +[data-${BRANCH_SELECTOR}='closed']::before { + content: '${SYMBOL_PLUS}'; +} + +[data-${BRANCH_SELECTOR}='open']::before { + content: '${SYMBOL_MINUS}'; +} + +[data-${BRANCH_SELECTOR}='closed'] ~ ul { /* Subsequent-sibling */ + display: none; +} + +[data-${BRANCH_SELECTOR}='open'] ~ ul { /* Subsequent-sibling */ + display: unset; +} + +[${ROOT_SELECTOR}-bulk] { + display: flex; + gap: 8px; + z-index: 2; + position: fixed; + margin-left: ${SYMBOL_SIZE * 0.5}px; + pointer-events: none; + padding: 8px; + background: #F2F5F9; + box-shadow: #F2F5F9 0px 8px 8px 0px; +} + +[${ROOT_SELECTOR}-bulk] > div { + cursor: pointer; + user-select: none; + transition: .3s; + width: ${SYMBOL_SIZE}px; + height: ${SYMBOL_SIZE}px; + position: relative; + pointer-events: auto; +} + +[${ROOT_SELECTOR}-bulk] > div::before { + content: attr(data-action); + display: flex; + align-items: center; + justify-content: center; + -webkit-box-align: center; + -webkit-box-pack: center; + background-clip: padding-box; + width: ${SYMBOL_SIZE}px; + height: ${SYMBOL_SIZE}px; + font-size: ${SYMBOL_SIZE * 0.875}px; + font-weight: bold; + line-height: 0; + border-radius: 50%; + background: #F2F5F9; + box-shadow: rgb(0 0 0 / 10%) 0 0px 0px 1px, rgb(0 0 0 / 10%) 2px 1px 1px 0px; + position: absolute; + top: 0; + left: 0; + z-index: 2; +} + +[${ROOT_SELECTOR}-bulk] > div:hover::before { + background: #FFF; +} + +[${ROOT_SELECTOR}-bulk] > div::after { + content: ''; + width: ${SYMBOL_SIZE}px; + height: ${SYMBOL_SIZE}px; + border-radius: 50%; + position: absolute; + box-shadow: rgb(0 0 0 / 15%) 0px 0px 0px 1px; + top: -${SYMBOL_SIZE * 0.25}px; + left: ${SYMBOL_SIZE * 0.25}px; + z-index: 1; +} + +[${ROOT_SELECTOR}-has-bulk] { + margin-top: 32px; +} +`; + +function addStyleElement(target, styleTextContent, attr = 'style') { + const style = document.createElement('style'); + style.setAttribute(`${ROOT_SELECTOR}-${attr}`, ''); + style.textContent = styleTextContent; + target.before(style); +} + +function prepareList(target) { + const ulList = [...target.querySelectorAll('ul')]; + const ulHandlerList = ulList.map( + ul => { + const parent = ul.parentNode; + const ulHandler = document.createElement('div'); + + // parent.insertBefore(ulHandler, ul); + // * I need the button to come before the link as well + parent.prepend(ulHandler); + // Required: + parent.style = "position:relative"; + return ulHandler; + } + ) + return ulHandlerList; +} + +function addBulkHandler(target, handler) { + target.before(handler); + target.setAttribute(`${ROOT_SELECTOR}-has-bulk`, ''); +} + +function createBulkHandler(list) { + const bulk = document.createElement('div'); + bulk.setAttribute(`${ROOT_SELECTOR}-bulk`, ''); + + const bulkPlus = document.createElement('div'); + bulkPlus.dataset.action = SYMBOL_PLUS; + const bulkMinus = document.createElement('div'); + bulkMinus.dataset.action = SYMBOL_MINUS; + + // add event listeners + bulkPlus.addEventListener('click', () => { + bulkToggle(list, 'closed'); + }); + bulkMinus.addEventListener('click', () => { + bulkToggle(list, 'open'); + }); + + bulk.append(bulkPlus, bulkMinus); + return bulk; +} + +function processList(list) { + // This defines how a document is opened: + // with a collapsed or expanded TOC. + const storage = sessionStorage.getItem('collapsibleToc'); + + // If there is no information in the storage, we set the default list state. + const initState = storage || LIST_DEFAULT; + + list.forEach(item => { + item.dataset[BRANCH_SELECTOR] = initState; + + // add event listeners + item.addEventListener('click', () => { + toggle(item); + }); + item.addEventListener('dblclick', () => { + bulkToggle(list, item.dataset[BRANCH_SELECTOR]); + }); + }) +} + +function toggle(item) { + const oldState = item.dataset[BRANCH_SELECTOR]; + const nextState = (oldState === 'closed') ? 'open' : 'closed'; + item.dataset[BRANCH_SELECTOR] = nextState; +} + +function bulkToggle(list, oldState) { + const nextState = (oldState === 'closed') ? 'open' : 'closed'; + // Add last bulk to local storage: + sessionStorage.setItem('collapsibleToc', nextState); + // Update list: + list.forEach(el => el.dataset[BRANCH_SELECTOR] = nextState); +} + +// ****** + +function run() { + const toc = document.querySelector(`[${ROOT_SELECTOR}]`); + + // Processes the list and makes it collapse, if that makes sense + // (if the expanded list was long and would cause scrolling). + // Returns the processed list. + const branchList = prepareList(toc); + + // Do it if that makes sense (if there are branches + // in the list that could in principle be collapsible): + if (branchList.length > 0) { + processList(branchList); + addStyleElement(toc, STYLE); + + // Uncomment to add buttons for bulk operations: + // addBulkHandler(listElement, createBulkHandler(branchList)); + } +} + +function isCollapsibleList(node) { + return node.nodeType === 1 && node.hasAttribute(ROOT_SELECTOR) +} + +window.addEventListener("load",function(){ + + let mutatingFrame = document.querySelector('#frame-toc'); + if (!mutatingFrame) { + console.error("#frame-toc not found"); + return; + } + + new MutationObserver(function (mutationsList, observer) { + + for (let mutation of mutationsList) { + if (mutation.type === 'childList') { + let addedToc = Array.from(mutation.addedNodes).find(node => isCollapsibleList(node)); + if (addedToc) { + run() + } + } + } + + }).observe( + mutatingFrame, + { + childList: true, + // subtree: true + } + ); + + // * Call for the first time. + run() + +},false); diff --git a/tests/fuzz/20_L1_Open_Requirements_Tool_202511/assets/collapsible_toc.js b/tests/fuzz/20_L1_Open_Requirements_Tool_202511/assets/collapsible_toc.js new file mode 100644 index 0000000..a3c37e4 --- /dev/null +++ b/tests/fuzz/20_L1_Open_Requirements_Tool_202511/assets/collapsible_toc.js @@ -0,0 +1,200 @@ +// To collapse and expand TOC branches + +const ROOT_ID = 'toc'; +const SS_ITEM = 'collapsibleTOC'; // sessionStorageItem +const BRANCH_DATA_ATTR = `branch`; // li with a branch inside +const HANDLER_DATA_ATTR = `handler`; // handler +const NODE_ID_DATA_ATTR = `nodeid`; // from sdoc markup +const _TRUE = 'collapsed'; +const _FALSE = 'expanded'; + +const ROOT_SELECTOR = 'js-collapsible_list'; // is used in tests + +const SYMBOL_FALSE = '-'; +const SYMBOL_TRUE = '+'; +const SYMBOL_SIZE = 16; + +const STYLE = ` +[data-${HANDLER_DATA_ATTR}] { + display: flex; + align-items: center; + justify-content: center; + -webkit-box-align: center; + -webkit-box-pack: center; + background-clip: padding-box; + cursor: pointer; + user-select: none; + transition: .3s; + width: ${SYMBOL_SIZE}px; + height: ${SYMBOL_SIZE}px; + font-size: ${SYMBOL_SIZE * 0.875}px; + font-weight: bold; + line-height: 0; + border-radius: 50%; + color: rgba(0,0,0,0.5); + box-shadow: rgb(0 0 0 / 10%) 0px 1px 2px 0px; + position: absolute; + top: ${SYMBOL_SIZE * 0.5}px; + left: -${SYMBOL_SIZE * 0.75}px; +} + +[data-${HANDLER_DATA_ATTR}]:hover { + border: 1px solid rgba(0, 0, 0, 0.15); + box-shadow: rgb(0 0 0 / 15%) 0px 2px 8px 0px; + color: rgba(0,0,0,1); +} + +[data-${HANDLER_DATA_ATTR}='${_TRUE}']::before { + content: '${SYMBOL_TRUE}'; +} + +[data-${HANDLER_DATA_ATTR}='${_FALSE}']::before { + content: '${SYMBOL_FALSE}'; +} + +[data-${BRANCH_DATA_ATTR}='${_TRUE}'] > ul { + display: none; +} + +[data-${BRANCH_DATA_ATTR}='${_FALSE}'] > ul { + display: unset; +} + +`; + +function sessionStorageGet() { + const item = sessionStorage.getItem(SS_ITEM); + const res = item ? JSON.parse(item) : {}; + return res +} + +function sessionStorageSet(obj) { + const string = JSON.stringify(obj); + sessionStorage.setItem(SS_ITEM, string) +} + +function updateSessionStorage() { + const obj = {}; + document.querySelector(`[${ROOT_SELECTOR}]`) + .querySelectorAll(`[data-${BRANCH_DATA_ATTR}]`) + .forEach( + branch => obj[branch.dataset[NODE_ID_DATA_ATTR]] = branch.dataset[BRANCH_DATA_ATTR] + ); + sessionStorageSet(obj); +} + +function addStyleElement(target, styleTextContent, attr = 'style') { + const style = document.createElement('style'); + style.setAttribute(`collapsible-toc-${attr}`, ''); + style.textContent = styleTextContent; + target.before(style); +} + +function setBranchState(handler, state) { + handler.dataset[HANDLER_DATA_ATTR] = state; + handler.parentNode.dataset[BRANCH_DATA_ATTR] = state; +} + +function createHandler(state) { + const div = document.createElement('div'); + state && setBranchState(item, state); + return div +} + +function processToc(toc) { + const storage = sessionStorageGet(); + + const branchList = toc.querySelectorAll('ul'); + + // Do it if that makes sense (if there are branches + // in the list that could in principle be collapsible): + if (branchList.length > 0) { + addStyleElement(toc, STYLE); + + branchList.forEach( + ul => { + const handler = createHandler(); + + const parentNode = ul.parentNode; + const nodeID = parentNode.dataset[NODE_ID_DATA_ATTR]; + const currentState = storage[nodeID] || _FALSE; + + // parent.insertBefore(handler, ul); + // * I need the button to come before the link as well + parentNode.prepend(handler); + // Required: + parentNode.style = "position:relative"; + + // * run after the items have been added to the DOM + setBranchState(handler, currentState); + + // * add event listeners + handler.addEventListener('click', () => { + toggle(handler); + }); + handler.addEventListener('dblclick', () => { + bulkToggleChildBrunches(handler); + }); + } + ); + + updateSessionStorage(); + } +} + +function toggle(handler) { + const newState = (handler.dataset[HANDLER_DATA_ATTR] === _FALSE) ? _TRUE : _FALSE; + setBranchState(handler, newState); + updateSessionStorage(); +} + +function bulkToggleChildBrunches(handler) { + const newState = (handler.dataset[HANDLER_DATA_ATTR] === _FALSE) ? _TRUE : _FALSE; + const list = handler.parentNode.querySelectorAll(`[data-${HANDLER_DATA_ATTR}]`); + list.forEach(handler => { + setBranchState(handler, newState); + }); + updateSessionStorage(); +} + +// ****** + +function run() { + processToc(document.querySelector(`[${ROOT_SELECTOR}]`)); +} + +function isCollapsibleList(node) { + return node.nodeType === 1 && node.id === ROOT_ID +} + +window.addEventListener("load", function() { + + let mutatingFrame = document.querySelector('#frame-toc'); + if (!mutatingFrame) { + console.error("#frame-toc not found"); + return; + } + + new MutationObserver(function (mutationsList, observer) { + + for (let mutation of mutationsList) { + if (mutation.type === 'childList') { + let addedToc = Array.from(mutation.addedNodes).find(node => isCollapsibleList(node)); + if (addedToc) { + run() + } + } + } + + }).observe( + mutatingFrame, + { + childList: true, + // subtree: true + } + ); + + // * Call for the first time. + run() + +},false); diff --git a/tests/fuzz/20_L1_Open_Requirements_Tool_202511/assets/collapsible_tree.js b/tests/fuzz/20_L1_Open_Requirements_Tool_202511/assets/collapsible_tree.js new file mode 100644 index 0000000..3af9dde --- /dev/null +++ b/tests/fuzz/20_L1_Open_Requirements_Tool_202511/assets/collapsible_tree.js @@ -0,0 +1,32 @@ +const ROOT_SELECTOR = 'js-collapsible_tree'; + +window.addEventListener("DOMContentLoaded", function(){ + + const tree = document.querySelector(`[${ROOT_SELECTOR}]`); + if (!tree) { return } + + const summaries = tree.querySelectorAll('summary'); + + summaries.forEach(summary => { + summary.addEventListener('dblclick', () => { + const details = summary.parentElement; + if (details.nodeName !== 'DETAILS') { + console.warn('The DETAILS tag has an unexpected structure.'); + return + } + const open = details.hasAttribute("open"); + + const innerSummaries = details.querySelectorAll('summary'); + + innerSummaries.forEach(summary => { + const details = summary.parentElement; + if (open) { + details.removeAttribute("open") + } else ( + details.setAttribute("open", "") + ) + }) + }); + }) + +},false); diff --git a/tests/fuzz/20_L1_Open_Requirements_Tool_202511/assets/content.css b/tests/fuzz/20_L1_Open_Requirements_Tool_202511/assets/content.css new file mode 100644 index 0000000..596cf49 --- /dev/null +++ b/tests/fuzz/20_L1_Open_Requirements_Tool_202511/assets/content.css @@ -0,0 +1,203 @@ +/* .main */ + +.main { + position: relative; + overflow: auto; + scroll-behavior: smooth; + scrollbar-gutter: stable both-edges; + padding: var(--base-gap) /* == calc(var(--base-rhythm)*6) */ + calc(var(--base-rhythm)*6); /* compensate both-edges scrollbar-gutter */ + + height: 100%; + background-color: var(--color-bg-main); + + display: flex; + flex-direction: column; + justify-content: flex-start; + align-items: stretch; + gap: var(--base-rhythm); +} + +/* redefine main layout grid */ + +[data-viewtype="diff"] .main { + padding-bottom: 0; +} + +[data-viewtype="source-file"] .main { + padding: 0; + scrollbar-gutter: unset; +} + +.main_sticky_header { + position: sticky; + top: 0; + left: 0; + z-index: 11; + + display: flex; + flex-direction: column; + gap: var(--base-rhythm); + + /* margin-bottom: calc(var(--base-rhythm)*4); */ + background-color: var(--color-bg-main); + border: none; +} + +.main_sticky_header > * { + position: relative; +} + +.main_sticky_header::before { + content: ''; + position: absolute; + bottom: 0; + top: -60px; + left: calc(-1 * var(--base-gap)); + right: calc(-1 * var(--base-gap)); + background-color: var(--color-bg-main); + z-index: 0; +} + +/* .content */ + +.content { + width: 100%; + min-width: calc(var(--card-width) + calc(var(--base-padding)*4)); +} + +[data-viewtype="document"] .content { + display: block; + max-width: 900px; + margin-bottom: 300px; + margin-left: auto; + margin-right: auto; +} + +[data-viewtype="traceability"] .content { + display: grid; + place-items: stretch stretch; + grid-template-columns: minmax(min-content, max-content) + minmax(var(--card-width), calc(2*var(--card-width))) + minmax(min-content, max-content); + gap: var(--requirement-tree-margin) 0; + width: -moz-fit-content; + width: fit-content; + /* overflow: hidden; */ +} + +[data-viewtype="requirements-coverage"] .content, +[data-viewtype="deep_traceability"] .content { + display: grid; + place-items: stretch stretch; + grid-template-columns: minmax(min-content, max-content) + max-content + minmax(min-content, max-content); + gap: var(--requirement-tree-margin) 0; + width: -moz-fit-content; + width: fit-content; + + /* fon node-controls: */ + /* overflow: hidden; */ +} + +[data-viewtype="table"] .content { + background-color: var(--color-bg-contrast); + display: block; + /* aligns the width of the white box of the content and the table: */ + width: fit-content; +} + +[data-viewtype="table"] sdoc-node .free_text{ + max-width: 900px; +} + +[data-viewtype="search"] .content { + display: grid; + place-items: stretch stretch; + grid-template-columns: 1fr; + gap: var(--tree-gap) 0; + width: -moz-fit-content; + width: fit-content; +} + +/* TODO */ +/* used in TR, DTR, requirements_coverage: */ +.content_section { + display: contents; +} +/* TODO */ +.content_item { + position: relative; + display: flex; + flex-direction: column; + flex-wrap: nowrap; + align-content: stretch; + align-items: stretch; +} + +/* traceability */ + +.content_item[data-role="parents"] { + grid-column: 1 / 2; +} + +.content_item[data-role="current"] { + grid-column: 2 / 3; +} + +[data-viewtype="deep_traceability"] .content_item[data-role="current"] { + /* central column */ + width: var(--card-width); +} + +[data-viewtype="requirements-coverage"] .content_item[data-role="current"] { + /* central column */ + width: calc(var(--card-width)*0.75); +} + +.content_item[data-role="children"] { + grid-column: 3 / 4; +} + +[data-viewtype="deep_traceability"] .content_item[data-role="current"]::before, +[data-viewtype="traceability"] .content_item[data-role="current"]::before { + /* for vertical line in 'current' column */ + content: ''; + position: absolute; + top: 0; + bottom: calc(var(--requirement-tree-margin)*(-1)); + left: 50%; + border-left: 1px dotted #000; +} + +[data-viewtype="deep_traceability"] section:last-child .content_item[data-role="current"]::before, +[data-viewtype="traceability"] section:last-child .content_item[data-role="current"]::before { + /* the last section doesn't need a vertical connector under the middle node */ + content: none; +} + +/* placeholder */ + +sdoc-main-placeholder { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + color: var(--color-placeholder); + font-weight: 700; + font-family: var(--code-font-family); + width: 100%; + height: 100%; +} + +sdoc-main-legend { + display: block; + color: var(--color-placeholder); + font-weight: 700; + font-family: var(--code-font-family); + max-width: 1024px; + padding: var(--base-gap); + font-weight: 500; + margin-bottom: auto; /* To align the element at the top of the container that uses 'display:flex' */ +} diff --git a/tests/fuzz/20_L1_Open_Requirements_Tool_202511/assets/controllers/anchor_controller.js b/tests/fuzz/20_L1_Open_Requirements_Tool_202511/assets/controllers/anchor_controller.js new file mode 100644 index 0000000..58bf9af --- /dev/null +++ b/tests/fuzz/20_L1_Open_Requirements_Tool_202511/assets/controllers/anchor_controller.js @@ -0,0 +1,88 @@ +(() => { + + const ANCHOR_SELECTOR = 'sdoc-anchor[data-anchor]'; + const ANCHOR_BLOCK_SELECTOR = '.anchor_block'; + const ANCHOR_BUTTON_SELECTOR = '.anchor_button'; + const ANCHOR_BASE_ICON_SELECTOR = '.anchor_base_icon'; + const ANCHOR_CHECK_ICON_SELECTOR = '.anchor_check_icon'; + const ANCHOR_BACK_LINKS_SELECTOR = '.anchor_back_links'; + const ANCHOR_BACK_LINKS_NUMBER_SELECTOR = '.anchor_back_links_number'; + + class AnchorController extends Stimulus.Controller { + initialize() { + // this.element == sdoc-node + // Processing node anchors and inline anchors in the text: + const anchors = [...this.element.querySelectorAll(ANCHOR_SELECTOR)]; + anchors.forEach(anchor => { + + // Note: template already applies conditions whether an anchorBlock is rendered. + // JS assumes that if anchorBlock exists, it is valid to process. + const anchorBlock = anchor.querySelector(ANCHOR_BLOCK_SELECTOR); + + if (anchorBlock) { + const anchorText = anchor.dataset.anchor; + const anchorButton = anchor.querySelector(ANCHOR_BUTTON_SELECTOR); + const anchorIcon = anchor.querySelector(ANCHOR_BASE_ICON_SELECTOR); + const checkIcon = anchor.querySelector(ANCHOR_CHECK_ICON_SELECTOR); + + anchorButton.addEventListener("click", function (event) { + event.preventDefault(); + updateClipboard(anchorText, confirmMessage(anchorButton, anchorIcon, checkIcon)) + }); + } + }) + } + } + + Stimulus.application.register("anchor_controller", AnchorController); + + function updateClipboard(newClip, callback) { + navigator.clipboard.writeText(newClip).then(() => { + /* clipboard successfully set */ + () => callback(); + console.info('clipboard successfully set: ', newClip); + }, () => { + /* clipboard write failed */ + console.warn('clipboard write failed'); + }); + } + + function confirmMessage(anchorButton, anchorIcon, checkIcon) { + const element = document.createElement('div'); + element.style.position = 'absolute'; + element.style.zIndex = 10; + element.style.width = '100%'; + element.style.height = '100%'; + element.style.paddingLeft = '32px'; + element.style.left = 0; + element.style.background = 'black'; + element.style.color = 'white'; + element.style.fontWeight = 'bold'; + element.style.display = 'flex'; + element.style.alignItems = 'center'; + element.style.justifyContent = 'flex-start'; + // element.innerHTML = '✔️ copied!'; + + // initial opacity + let op = 1; + element.style.opacity = op; + anchorButton.style.opacity = 1; + + anchorIcon.style.display = 'none'; + checkIcon.style.display = 'inline'; + const fadeTimer = setInterval(() => { + if (op <= 0.1) { + clearInterval(fadeTimer); + element.remove(); + anchorIcon.style.display = 'inline'; + checkIcon.style.display = 'none'; + anchorButton.style.opacity = ''; + } + element.style.opacity = op; + op -= op * 0.1; + }, 30); + + // return element; + anchorButton.append(element); + } +})(); diff --git a/tests/fuzz/20_L1_Open_Requirements_Tool_202511/assets/controllers/autocompletable_field_controller.js b/tests/fuzz/20_L1_Open_Requirements_Tool_202511/assets/controllers/autocompletable_field_controller.js new file mode 100644 index 0000000..7439708 --- /dev/null +++ b/tests/fuzz/20_L1_Open_Requirements_Tool_202511/assets/controllers/autocompletable_field_controller.js @@ -0,0 +1,372 @@ +// MIT License +// +// based on https://github.com/afcapel/stimulus-autocomplete/ +// Copyright (c) 2021 Alberto Fernández-Capel + +(() => { + + const optionSelector = "[role='option']:not([aria-disabled])" + const activeSelector = "[aria-selected='true']" + + class AutoCompletable extends Stimulus.Controller { + static targets = ["name"] + static classes = ["selected"] + static values = { + ready: Boolean, + url: String, + minLength: Number, + delay: { type: Number, default: 10 }, + queryParam: { type: String, default: "q" }, + multipleChoice: Boolean, + } + static uniqOptionId = 0 + + connect() { + // this.element is the DOM element to which the controller is connected to. + const autocompletable = this.element + this.autocompletable = autocompletable + this.hidden = autocompletable.nextElementSibling + this.results = this.hidden.nextElementSibling + this.abortController = null; + + this.close() + + if (!autocompletable.hasAttribute("autocompletable")) autocompletable.setAttribute("autocompletable", "off") + autocompletable.setAttribute("spellcheck", "false") + + this.mouseDown = false + + this.onInputChange = debounce(this.onInputChange, this.delayValue) + + this.autocompletable.addEventListener("input", this.onInputChange) + + autocompletable.addEventListener("keydown", (event) => { + const handler = this[`on${event.key}Keydown`] + if (handler) handler(event) + }); + + autocompletable.addEventListener("blur", (event) => { + if (this.mouseDown) return + this.close() + }); + + autocompletable.addEventListener("click", (event) => { + /* Toggle between showing / hiding results. */ + if (this.resultsShown) { + this.hideAndRemoveOptions(); + } else { + /* If minLengthValue is 0, we want to get all possible options (i.e. for SingleChoice). + Otherwise, we want narrow-down-as-you-type behavior, and filter on remainig options. + */ + const query = this.minLengthValue == 0 ? "" : this.autocompletable.innerText.trim(); + this.fetchResults(query); + } + }); + + this.results.addEventListener("mousedown", this.onResultsMouseDown) + this.results.addEventListener("click", this.onResultsClick) + + if (autocompletable.hasAttribute("autofocus")) { + autocompletable.focus() + } + + this.readyValue = true + } + + disconnect() { + this.autocompletable.removeEventListener("keydown", this.onKeydown) + this.autocompletable.removeEventListener("blur", this.onInputBlur) + this.autocompletable.removeEventListener("input", this.onInputChange) + + this.results.removeEventListener("mousedown", this.onResultsMouseDown) + this.results.removeEventListener("click", this.onResultsClick) + } + + sibling(next) { + const options = this.options + const selected = this.selectedOption + const index = options.indexOf(selected) + const sibling = next ? options[index + 1] : options[index - 1] + const def = next ? options[0] : options[options.length - 1] + return sibling || def + } + + select(target) { + const previouslySelected = this.selectedOption + if (previouslySelected) { + previouslySelected.removeAttribute("aria-selected") + previouslySelected.classList.remove(...this.selectedClassesOrDefault) + } + + target.setAttribute("aria-selected", "true") + target.classList.add(...this.selectedClassesOrDefault) + this.autocompletable.setAttribute("aria-activedescendant", target.id) + target.scrollIntoView({ behavior: "auto", block: "nearest" }) + } + + selectText(text) { + const normalizedText = text.trim().toLowerCase(); + const match = this.options.find(option => { + const label = option.getAttribute("data-autocompletable-label") || option.textContent; + return label.trim().toLowerCase() === normalizedText; + }); + + if (match) { + this.select(match); + } + } + + onEscapeKeydown = (event) => { + if (!this.resultsShown) return + + this.hideAndRemoveOptions() + event.stopPropagation() + event.preventDefault() + } + + onArrowDownKeydown = (event) => { + if (!this.resultsShown) return + + const item = this.sibling(true) + if (item) this.select(item) + event.preventDefault() + } + + onArrowUpKeydown = (event) => { + if (!this.resultsShown) return + + const item = this.sibling(false) + if (item) this.select(item) + event.preventDefault() + } + + onTabKeydown = (event) => { + if (!this.resultsShown) return + + /* Either use the selected options, or else select the first result. */ + const selected = this.selectedOption || this.sibling(true) + this.commit(selected) + event.preventDefault(); + } + + onEnterKeydown = (event) => { + const selected = this.selectedOption + if (selected && this.resultsShown) { + this.commit(selected) + } + /* single line, dont allow enter */ + event.preventDefault() + } + + commit(selected) { + if (selected.getAttribute("aria-disabled") === "true") return + + if (selected instanceof HTMLAnchorElement) { + selected.click() + this.close() + return + } + + const textValue = selected.getAttribute("data-autocompletable-label") || selected.textContent.trim() + let suggestion = selected.getAttribute("data-autocompletable-value") || textValue + + if (this.multipleChoiceValue) { + // Get the current text content + const text = this.autocompletable.innerText || ""; + const parts = text.split(","); + + // Replace the last incomplete token with the suggestion. + parts[parts.length - 1] = " " + suggestion; + suggestion = parts.map(p => p.trim()).join(", ") + } + + this.autocompletable.innerText = suggestion + this.hidden.value = suggestion + + // Move the cursor to the end of the input. + this.autocompletable.focus(); + const range = document.createRange(); + range.selectNodeContents(this.autocompletable); + range.collapse(false); + const sel = window.getSelection(); + sel.removeAllRanges(); + sel.addRange(range); + + this.hidden.dispatchEvent(new Event("input")) + this.hidden.dispatchEvent(new Event("change")) + + this.autocompletable.focus() + this.hideAndRemoveOptions() + + this.element.dispatchEvent( + new CustomEvent("autocompletable.change", { + bubbles: true, + detail: { value: suggestion, textValue: textValue, selected: selected } + }) + ) + } + + clear() { + this.autocompletable.innerText = "" + this.hidden.value = "" + } + + onResultsClick = (event) => { + if (!(event.target instanceof Element)) return + const selected = event.target.closest(optionSelector) + if (selected) this.commit(selected) + } + + onResultsMouseDown = () => { + this.mouseDown = true + this.results.addEventListener("mouseup", () => { + this.mouseDown = false + }, { once: true }) + } + + onInputChange = () => { + const query = this.autocompletable.innerText.trim() + if (query && query.length >= this.minLengthValue) { + this.fetchResults(query) + } else { + this.hideAndRemoveOptions() + } + + const text = filterSingleLine(this.autocompletable.innerText) + this.hidden.value = text + } + + identifyOptions() { + const prefix = this.results.id || "autocompletable" + const optionsWithoutId = this.results.querySelectorAll(`${optionSelector}:not([id])`) + optionsWithoutId.forEach(el => el.id = `${prefix}-option-${this.constructor.uniqOptionId++}`) + } + + hideAndRemoveOptions() { + this.close() + this.results.innerHTML = null + } + + fetchResults = async (query) => { + if (!this.hasUrlValue) return + + /* Abort the previous request as we are about to send a new one. */ + if (this.abortController) { + this.abortController.abort(); + } + this.abortController = new AbortController(); + const signal = this.abortController.signal; + + const url = this.buildURL(query) + try { + this.element.dispatchEvent(new CustomEvent("loadstart")) + const html = await this.doFetch(url, signal) + this.replaceResults(html) + /* Check if an entry matches the current text and select it. */ + this.selectText(this.autocompletable.innerText.trim()); + this.element.dispatchEvent(new CustomEvent("load")) + this.element.dispatchEvent(new CustomEvent("loadend")) + } catch (error) { + if (error.name === 'AbortError') { + return; + } + this.element.dispatchEvent(new CustomEvent("error")) + this.element.dispatchEvent(new CustomEvent("loadend")) + throw error + } + } + + buildURL(query) { + const url = new URL(this.urlValue, window.location.href) + const params = new URLSearchParams(url.search.slice(1)) + params.append(this.queryParamValue, query) + url.search = params.toString() + + return url.toString() + } + + doFetch = async (url, signal) => { + const response = await fetch(url, {signal}) + + if (!response.ok) { + throw new Error(`Server responded with status ${response.status}`) + } + + const html = await response.text() + return html + } + + replaceResults(html) { + this.results.innerHTML = html + this.identifyOptions() + if (!!this.options) { + this.open() + } else { + this.close() + } + } + + open() { + if (this.resultsShown) return + + this.resultsShown = true + this.element.setAttribute("aria-expanded", "true") + this.element.dispatchEvent( + new CustomEvent("toggle", { + detail: { action: "open", autocompletable: this.autocompletable, results: this.results } + }) + ) + } + + close() { + if (!this.resultsShown) return + + this.resultsShown = false + this.autocompletable.removeAttribute("aria-activedescendant") + this.element.setAttribute("aria-expanded", "false") + this.element.dispatchEvent( + new CustomEvent("toggle", { + detail: { action: "close", autocompletable: this.autocompletable, results: this.results } + }) + ) + } + + get resultsShown() { + return !this.results.hidden + } + + set resultsShown(value) { + this.results.hidden = !value + } + + get options() { + return Array.from(this.results.querySelectorAll(optionSelector)) + } + + get selectedOption() { + return this.results.querySelector(activeSelector) + } + + get selectedClassesOrDefault() { + return this.hasSelectedClass ? this.selectedClasses : ["autocomplete-active"] + } + + } + + Stimulus.application.register("autocompletable", AutoCompletable); + + function filterSingleLine(text) { + return text.replace(/\s/g, ' ').replace(/\s\s+/g, ' ') + } + + const debounce = (fn, delay = 10) => { + let timeoutId = null + + return (...args) => { + clearTimeout(timeoutId) + timeoutId = setTimeout(fn, delay) + } + } + +})(); + diff --git a/tests/fuzz/20_L1_Open_Requirements_Tool_202511/assets/controllers/copy_stable_link_button_controller.js b/tests/fuzz/20_L1_Open_Requirements_Tool_202511/assets/controllers/copy_stable_link_button_controller.js new file mode 100644 index 0000000..6307b5c --- /dev/null +++ b/tests/fuzz/20_L1_Open_Requirements_Tool_202511/assets/controllers/copy_stable_link_button_controller.js @@ -0,0 +1,82 @@ +(() => { + + class CopyStableLinkController extends Stimulus.Controller { + connect() { + const button = this.element; + + button.addEventListener("click", (event) => { + event.preventDefault(); + + const link = button.dataset.path; + + const copyIcon = this.element.querySelector(".copy_to_clipboard-copy_icon"); + const doneIcon = this.element.querySelector(".copy_to_clipboard-done_icon"); + + // Resolve any relative URLs with respect to current URL. + const resolved = ( + this._isAbsoluteURL(link) + ? link + : new URL(link, window.location.href).href + ); + + // Expand folder to index if we run from the local file system. + const expanded = ( + (window.location.protocol === 'file:') + ? resolved.replace(/#/, 'index.html?a=') + : resolved.replace(/#/, '?a=') + ); + + this._updateClipboard(expanded, this._confirmCopy(button, copyIcon, doneIcon)); + }); + } + + _isAbsoluteURL(url) { + try { + new URL(url); // throws if it's relative + return true; + } catch { + return false; + } + } + + _updateClipboard(newClip, callback) { + navigator.clipboard.writeText(newClip).then(() => { + /* clipboard successfully set */ + () => callback(); + console.info('Clipboard successfully set: ', newClip); + }, () => { + /* clipboard write failed */ + console.warn('Clipboard write failed'); + }); + } + + _confirmCopy(button, copyIcon, doneIcon) { + // initial opacity + let op = 1; + + // make button visible + button.style.opacity = 1; + + // make DONE icon visible (instead of default COPY) + copyIcon.style.display = 'none'; + doneIcon.style.display = 'contents'; + + const fadeTimer = setInterval(() => { + if (op <= 0.1) { + clearInterval(fadeTimer); + + // make button invisible + button.style.opacity = ''; + + // make COPY icon visible back + copyIcon.style.display = 'contents'; + doneIcon.style.display = 'none'; + } + op -= op * 0.1; + }, 30); + } + } + + Stimulus.application.register("copy_stable_link_button", CopyStableLinkController); + +})(); diff --git a/tests/fuzz/20_L1_Open_Requirements_Tool_202511/assets/controllers/copy_to_clipboard_controller.js b/tests/fuzz/20_L1_Open_Requirements_Tool_202511/assets/controllers/copy_to_clipboard_controller.js new file mode 100644 index 0000000..e7e6976 --- /dev/null +++ b/tests/fuzz/20_L1_Open_Requirements_Tool_202511/assets/controllers/copy_to_clipboard_controller.js @@ -0,0 +1,70 @@ +(() => { + + class CopyToClipboard extends Stimulus.Controller { + connect() { + const button = this.element.querySelector(".copy_to_clipboard-button"); + + // Add event listener + button.addEventListener("click", (event) => { + event.preventDefault(); + + const clip = this.element.querySelector("sdoc-field-content").innerText.trim(); + const copyIcon = this.element.querySelector(".copy_to_clipboard-copy_icon"); + const doneIcon = this.element.querySelector(".copy_to_clipboard-done_icon"); + const cover = this.element.querySelector(".copy_to_clipboard-cover"); + _updateClipboard( + clip, + _confirm(button, copyIcon, doneIcon, cover) + ) + }); + } + } + + Stimulus.application.register("copy_to_clipboard", CopyToClipboard); + + function _updateClipboard(newClip, callback) { + navigator.clipboard.writeText(newClip).then(() => { + /* clipboard successfully set */ + () => callback(); + console.info('clipboard successfully set: ', newClip); + }, () => { + /* clipboard write failed */ + console.warn('clipboard write failed'); + }); + } + + function _confirm(button, copyIcon, doneIcon, cover) { + // initial opacity + let op = 1; + + // make button visible + button.style.opacity = 1; + + // initial cover + cover.style.background = `rgba(242, 100, 42, ${op})`; + + // make DONE icon visible (instead of default COPY) + copyIcon.style.display = 'none'; + doneIcon.style.display = 'contents'; + + const fadeTimer = setInterval(() => { + // update cover + cover.style.background = `rgba(242, 100, 42, ${op})`; + if (op <= 0.1) { + clearInterval(fadeTimer); + + // make button invisible + button.style.opacity = ''; + + // reset cover + cover.style.background = `rgba(242, 100, 42, 0)`; + + // make COPY icon visible back + copyIcon.style.display = 'contents'; + doneIcon.style.display = 'none'; + } + op -= op * 0.1; + }, 30); + } + +})(); diff --git a/tests/fuzz/20_L1_Open_Requirements_Tool_202511/assets/controllers/deletable_field_controller.js b/tests/fuzz/20_L1_Open_Requirements_Tool_202511/assets/controllers/deletable_field_controller.js new file mode 100644 index 0000000..db60193 --- /dev/null +++ b/tests/fuzz/20_L1_Open_Requirements_Tool_202511/assets/controllers/deletable_field_controller.js @@ -0,0 +1,19 @@ +(() => { + + class DeletableField extends Stimulus.Controller { + connect() { + // this.element is the DOM element to which the controller is connected to. + const thisElement = this.element; + const commentLinks = thisElement.querySelectorAll("[data-js-delete-field-action]"); + commentLinks.forEach(link => { + link.addEventListener("click", function (event) { + event.preventDefault(); + thisElement.remove(); + }); + }); + } + } + + Stimulus.application.register("deletable_field", DeletableField); + +})(); diff --git a/tests/fuzz/20_L1_Open_Requirements_Tool_202511/assets/controllers/draggable_list_controller.js b/tests/fuzz/20_L1_Open_Requirements_Tool_202511/assets/controllers/draggable_list_controller.js new file mode 100644 index 0000000..b73331b --- /dev/null +++ b/tests/fuzz/20_L1_Open_Requirements_Tool_202511/assets/controllers/draggable_list_controller.js @@ -0,0 +1,334 @@ +(() => { + + const DL_SELECTOR = 'js-draggable_list'; + const DL_ITEM_SELECTOR = 'li'; + const DL_ZONE_CHILD = 32; + + const CSS = ` +${DL_ITEM_SELECTOR}[draggable="true"] { +} +${DL_ITEM_SELECTOR}[draggable="true"]:hover { + cursor: -webkit-grab; + cursor: -moz-grab; + cursor: -o-grab; + cursor: -ms-grab; + cursor: grab; +} +${DL_ITEM_SELECTOR}[draggable="true"]:active { + cursor: -webkit-grabbing; + cursor: -moz-grabbing; + cursor: -o-grabbing; + cursor: -ms-grabbing; + cursor: grabbing; +} +.dropIndicator { + display: block; + position: absolute; + left: 0; right: 0; + z-index: 2; +} +.dropIndicator::before { + position: absolute; + content: ''; + top: -2px; + left: 0; right: 0; + height: 4px; + background: rgb(242,100,42); + opacity: 0.3; + pointer-events: none; +} +.dropIndicator::after { + position: absolute; + content: ''; + top: -2px; + height: 4px; + width: 100%; + background: rgb(242,100,42); + pointer-events: none; +} +.dragIndicator { + position: absolute; + top: 0; right: 0; + transition: 0.3s; +} +${DL_ITEM_SELECTOR}[draggable="true"] .dragIndicator::before { + position: absolute; + width: 8px; + height: 24px; + top: 0; left: 100%; + content: "⋮⋮"; +} +[data-last_moved="true"] { + position: relative; +} +[data-last_moved="true"]::after { + content: ''; + position: absolute; + top: 0; left: 0; right: 0; bottom: 0; + pointer-events: none; + animation-name: last_moved; + animation-duration: 1s; +} +@keyframes last_moved { + from { + background: rgba(255,255,255,0.5); + } + to { + background: rgba(255,255,255,0); + } +} +[data-dragging="true"] { + background: rgba(255,255,255,0.5) !important; +} + +`; + + const dragState = { + item: null, + reference: null, + option: null, + } + + function resetDragState() { + dragState.item = null; + dragState.reference = null; + dragState.option = null; + } + + function setDragItem(item) { + dragState.item = item; + } + function setDropReference(reference) { + dragState.reference = reference; + } + function setDropOption(option) { + dragState.option = option; + } + + const dropIndicator = createDropIndicator(); + const dragIndicator = createDragIndicator(); + + class DraggableList extends Stimulus.Controller { + static targets = ["name"]; + + initialize() { + // this.element.style.paddingRight = '10px'; + + const last_moved_node_id = this.element.dataset.last_moved_node_id; + + addStyle(this.element, CSS, 'style'); + + // Add event listeners. + const draggableList = [...this.element.querySelectorAll(DL_ITEM_SELECTOR)]; + draggableList.forEach((item) => { + item.addEventListener('dragstart', dragStart); + item.addEventListener('dragend', dragEnd); + item.addEventListener('dragover', dragOver); + item.addEventListener('dragenter', dragEnter); + item.addEventListener('dragleave', dragLeave); + item.addEventListener('drop', dragDrop); + + item.addEventListener("mouseover", mouseOver); + item.addEventListener("mouseleave", mouseLeave); + + last_moved_node_id + && (item.dataset.nodeid === last_moved_node_id) + && (item.dataset.last_moved = 'true'); + + item.setAttribute('draggable', true); + }); + + // Prevent drag for links inside the list. + [...this.element.querySelectorAll('a')].forEach((item) => { + item.setAttribute('draggable', false); + }) + } + } + + Stimulus.application.register("draggable_list", DraggableList); + + function addStyle(target, css, attr = 'style') { + const style = document.createElement('style'); + style.setAttribute(`${DL_SELECTOR}-${attr}`, ''); + style.textContent = css; + target.parentNode.insertBefore(style, target); + } + + function createDropIndicator() { + const dropIndicator = document.createElement('div'); + dropIndicator.className = 'dropIndicator'; + return dropIndicator + } + + function createDragIndicator() { + const dragIndicator = document.createElement('div'); + dragIndicator.className = 'dragIndicator'; + return dragIndicator + } + + function dragStart(event) { + event.stopImmediatePropagation(); + event.dataTransfer.dropEffect = "move"; + event.dataTransfer.effectAllowed = 'move'; + + setDragItem(this); + + setTimeout(() => { + this.dataset.dragging = true; + }, 0); + } + + function dragEnd(event) { + updateDropIndication(); + resetDragState(); + this.dataset.dragging = false; + } + + function updateDropIndication(target, option) { + setDropOption(option); + switch (option) { + case 'child': + target.append(dropIndicator); + dropIndicator.style.paddingLeft = `${DL_ZONE_CHILD}px`; + dropIndicator.style.top = ''; + dropIndicator.style.bottom = 0; + break; + case 'after': + target.append(dropIndicator); + dropIndicator.style.paddingLeft = ''; + dropIndicator.style.top = ''; + dropIndicator.style.bottom = 0; + break; + case 'before': + target.append(dropIndicator); + dropIndicator.style.paddingLeft = ''; + dropIndicator.style.top = 0; + dropIndicator.style.bottom = ''; + break; + default: + // dropIndicator is declared in the global scope, + // so this case works if there are no parameters: + dropIndicator.remove(); + } + } + + function isDropMatter() { + return + } + + function dragOver(event) { + event.preventDefault(); + event.stopImmediatePropagation(); + event.dataTransfer.dropEffect = "move"; + + setDropReference(this); + + // https://developer.mozilla.org/en-US/docs/Web/API/Node/contains + // A node is contained inside itself, + // so (dragState.item !== dragState.reference) + // is the same as (dragState.item.contains(dragState.reference)). + + if (dragState.item.contains(dragState.reference)) { + // We do not process the case when the item does not actually displace. + updateDropIndication(); + } else { + + const bounding = this.getBoundingClientRect(); + + // We do not process the case when the item + // does not actually displace. + + if (event.clientY < bounding.top + (bounding.bottom - bounding.top) * 0.5) { + // * BEFORE + // The mouse is in the top half of the Reference. + + if (dragState.item.nextElementSibling === dragState.reference) { + // We do not process the case when the item + // does not actually displace. + updateDropIndication(); + } else { + updateDropIndication(this, 'before'); + } + + } else { + // * AFTER + // The mouse is in the bottom half of the Reference. + // The dragged element can become a child or just the next one. + + if (event.clientX > bounding.left + DL_ZONE_CHILD) { + // * AFTER / child + // The mouse is shifted to the right of the boundary, + // which indicates the intention to insert the item as a child. + + updateDropIndication(this, 'child'); + + } else { + // * AFTER / just the next item + // The mouse is to the left of the DL_ZONE_CHILD boundary, which points + // to the intention to insert just the next one in the list. + + if (dragState.item.previousElementSibling === dragState.reference) { + // We do not process the case when the item + // does not actually displace. + updateDropIndication(); + } else { + updateDropIndication(this, 'after'); + } + } + } + } + } + + function dragEnter() { } + function dragLeave() { } + + function dragDrop(event) { + event.preventDefault(); + event.stopImmediatePropagation(); + dragState.option && fetchDroppedItemData(dragState.item, dragState.reference, dragState.option); + } + + function mouseOver(event) { + event.preventDefault(); + event.stopImmediatePropagation(); + if (event.target === this) { + event.target.append(dragIndicator); + } + } + function mouseLeave(event) { + event.preventDefault(); + event.stopImmediatePropagation(); + if (event.target === this) { + dragIndicator.remove(); + } + } + + function fetchDroppedItemData(dragItem, dropReference, whereto) { + console.assert(['before', 'after', 'child'].includes(whereto), 'whereto is', whereto) + if (dragItem !== dropReference) { + // Build formData object. + let formData = new FormData(); + formData.append('moved_node_mid', dragItem.dataset.nodeid); + formData.append('target_mid', dropReference.dataset.nodeid); + formData.append('whereto', whereto); + + fetch("/actions/document/move_node", + { + body: formData, + method: "post", + headers: { + "Accept": "text/vnd.turbo-stream.html", + }, + }).then(r => r.text()) + .then(html => Turbo.renderStreamMessage(html)); + } + } + + function moveNodeBefore(dragItem, dropReference) { + // The real insertion of the moved node is done by the server, + // so this functionality is not implemented. + //// dropReference.parentNode.insertBefore(dragItem, dropReference); + //// dragItem.dataset.last_moved = 'true'; + } + +})(); diff --git a/tests/fuzz/20_L1_Open_Requirements_Tool_202511/assets/controllers/dropdown_menu_controller.js b/tests/fuzz/20_L1_Open_Requirements_Tool_202511/assets/controllers/dropdown_menu_controller.js new file mode 100644 index 0000000..56398a0 --- /dev/null +++ b/tests/fuzz/20_L1_Open_Requirements_Tool_202511/assets/controllers/dropdown_menu_controller.js @@ -0,0 +1,46 @@ +(() => { + + const HANDLER_SELECTOR = '[js-dropdown-menu-handler]'; + const LIST_SELECTOR = '[js-dropdown-menu-list]'; + + class DropdownMenu extends Stimulus.Controller { + static targets = ["name"]; + + connect() { + this.registerListEvents(); + } + + registerListEvents(params) { + const toggle = this.element.querySelector(HANDLER_SELECTOR); + const content = this.element.querySelector(LIST_SELECTOR); + + const show = () => { + toggle.setAttribute('aria-expanded', true); + content.setAttribute('aria-hidden', false); + } + + const hide = () => { + toggle.setAttribute('aria-expanded', false); + content.setAttribute('aria-hidden', true); + } + + toggle.addEventListener('click', event => { + event.stopPropagation(); + JSON.parse(toggle.getAttribute('aria-expanded')) ? hide() : show(); + }) + + const buttonList = [...content.querySelectorAll('a')]; + buttonList.forEach(button => { + button.addEventListener('click', event => hide()); + }) + + const handleClosure = event => !content.contains(event.target) && hide(); + + window.addEventListener('click', handleClosure); + window.addEventListener('focusin', handleClosure); + } + } + + Stimulus.application.register("dropdown_menu", DropdownMenu); + +})(); diff --git a/tests/fuzz/20_L1_Open_Requirements_Tool_202511/assets/controllers/editable_field_controller.js b/tests/fuzz/20_L1_Open_Requirements_Tool_202511/assets/controllers/editable_field_controller.js new file mode 100644 index 0000000..46bd281 --- /dev/null +++ b/tests/fuzz/20_L1_Open_Requirements_Tool_202511/assets/controllers/editable_field_controller.js @@ -0,0 +1,52 @@ +(() => { + + class EditableField extends Stimulus.Controller { + static targets = ["name"]; + + connect() { + // this.element is the DOM element to which the controller is connected to. + const editable = this.element; + + const isSingle = (editable.dataset.editable == 'single') ? true : false; + const hidden = editable.nextElementSibling; + + editable.addEventListener('paste', (event) => { + event.preventDefault(); + + const clipboardText = (event.clipboardData || window.clipboardData).getData('text'); + const text = isSingle ? filterSingleLine(clipboardText) : clipboardText; + + const selection = window.getSelection(); + + if (selection.rangeCount) { + selection.deleteFromDocument(); + selection.getRangeAt(0).insertNode(document.createTextNode(text)); + } + + hidden.value = editable.innerText; + }); + + editable.addEventListener('input', (event) => { + const editedText = editable.innerText; + const text = isSingle ? filterSingleLine(editedText) : editedText; + + hidden.value = text; + }); + + if (isSingle) { + editable.addEventListener('keydown', (event) => { + if (event.key === 'Enter') { + event.preventDefault(); + } + }); + } + } + } + + Stimulus.application.register("editablefield", EditableField); + + function filterSingleLine(text) { + return text.replace(/\s/g, ' ').replace(/\s\s+/g, ' ') + } + +})(); diff --git a/tests/fuzz/20_L1_Open_Requirements_Tool_202511/assets/controllers/modal_controller.js b/tests/fuzz/20_L1_Open_Requirements_Tool_202511/assets/controllers/modal_controller.js new file mode 100644 index 0000000..a33ca93 --- /dev/null +++ b/tests/fuzz/20_L1_Open_Requirements_Tool_202511/assets/controllers/modal_controller.js @@ -0,0 +1,47 @@ +(() => { + + class ModalController extends Stimulus.Controller { + initialize() { + // this.element is the DOM element to which the controller is connected to. + const thisElement = this.element; + + // Cancel button selector: [stimulus-modal-cancel-button] + const cancelButton = thisElement.querySelector('[stimulus-modal-cancel-button]'); + + // Clicking on the backdrop in this implementation + // does not close the modal window: + // const backdrop = thisElement.querySelector('sdoc-backdrop'); + + // Removing a modal window code added using Turbo: + const removeModal = () => { + thisElement.remove() + } + + // Removing the Escape listener: + const removeEscapeListener = () => { + document.removeEventListener("keydown", listenEscape); + } + + // Listening to Escape: + const listenEscape = (event) => { + if (event.key === 'Escape') { + removeModal(); + removeEscapeListener(); + } + }; + + // Add Listeners: + + document.addEventListener("keydown", listenEscape); + + cancelButton.addEventListener("click", function (event) { + event.preventDefault(); + removeModal(); + }); + + } + } + + Stimulus.application.register("modal_controller", ModalController); + +})(); diff --git a/tests/fuzz/20_L1_Open_Requirements_Tool_202511/assets/controllers/scroll_into_view_controller.js b/tests/fuzz/20_L1_Open_Requirements_Tool_202511/assets/controllers/scroll_into_view_controller.js new file mode 100644 index 0000000..1617734 --- /dev/null +++ b/tests/fuzz/20_L1_Open_Requirements_Tool_202511/assets/controllers/scroll_into_view_controller.js @@ -0,0 +1,11 @@ +(() => { + + class ScrollIntoView extends Stimulus.Controller { + connect() { + this.element.scrollIntoView() + } + } + + Stimulus.application.register("scroll_into_view", ScrollIntoView); + +})(); diff --git a/tests/fuzz/20_L1_Open_Requirements_Tool_202511/assets/controllers/tabs_controller.js b/tests/fuzz/20_L1_Open_Requirements_Tool_202511/assets/controllers/tabs_controller.js new file mode 100644 index 0000000..d5ead3f --- /dev/null +++ b/tests/fuzz/20_L1_Open_Requirements_Tool_202511/assets/controllers/tabs_controller.js @@ -0,0 +1,71 @@ +(() => { + + class Tabs extends Stimulus.Controller { + initialize() { + + // ** Tab buttons like this: + // + // Id1 + // Id2 + // + const sdocTabs = document.createElement('sdoc-tabs'); + const sdocTabContent = [...this.element.querySelectorAll('sdoc-tab-content')]; + const tabsData = sdocTabContent.reduce( + (accumulator, currentSdocTabContentElement, currentIndex) => { + // get data from the contents of the tab element in the DOM: + const key = currentSdocTabContentElement.id; + const state = currentSdocTabContentElement.hasAttribute('active'); + + const errors = [...currentSdocTabContentElement.querySelectorAll('sdoc-form-error')]; + + // create and prepare tab element: + const sdocTab = document.createElement('sdoc-tab'); + sdocTab.style.order = currentIndex; + sdocTab.innerHTML = key; + sdocTab.addEventListener("mouseup", () => { + this._activateTab(tabsData.tabs, key) + }); + sdocTabs.append(sdocTab); + sdocTab.setAttribute('data-testid', `form-tab-${key}`); + state && sdocTab.setAttribute('active', ''); + + if (errors.length) { + sdocTab.setAttribute('data-errors', errors.length); + accumulator.errors.push(key); + } + + // update accumulator (form state): + accumulator.tabs[key] = {}; + accumulator.tabs[key].element = currentSdocTabContentElement; + accumulator.tabs[key].order = currentIndex; + accumulator.tabs[key].state = state; + accumulator.tabs[key].handler = sdocTab; + accumulator.tabs[key].errors = errors.length; + + return accumulator; + }, { + tabs: {}, + errors: [] + }); + + // put tab handlers to the DOM + sdocTabContent.length && this.element.prepend(sdocTabs); + + // activate first tab with errors + tabsData.errors.length && this._activateTab(tabsData.tabs, tabsData.errors[0]); + } + + _activateTab(tabs, tabName) { + for (const { element, handler } of Object.values(tabs)) { + element.removeAttribute('active'); + handler.removeAttribute('active'); + } + // activate the tab: + tabs[tabName].element.setAttribute('active', ''); + tabs[tabName].handler.setAttribute('active', ''); + } + } + + Stimulus.application.register("tabs", Tabs); + +})(); diff --git a/tests/fuzz/20_L1_Open_Requirements_Tool_202511/assets/diff.css b/tests/fuzz/20_L1_Open_Requirements_Tool_202511/assets/diff.css new file mode 100644 index 0000000..35ab06f --- /dev/null +++ b/tests/fuzz/20_L1_Open_Requirements_Tool_202511/assets/diff.css @@ -0,0 +1,531 @@ +/* diff */ +:root { + --pre-stripe: 20px; + --pre-stripe-color: rgba(0,0,0,0.02); + --pre-block-bg-color: rgba(0,0,0,0.01); + + --diff-block-color-left: rgba(255, 55, 55, .05); + --diff-word-color-left: rgba(255, 55, 55, .2); + --diff-icon-color-left: rgba(255, 55, 55, .75); + --diff-document-color-left: rgba(255, 55, 55, 1); + + --diff-block-color-right: rgba(20, 120, 20, .05); + --diff-word-color-right: rgba(20, 120, 20, .2); + --diff-icon-color-right: rgba(20, 120, 20, .75); + --diff-document-color-right: rgba(20, 120, 20, 1); +} + +/* + *** diff *** + */ + +.diff { + scroll-behavior: smooth; + background-color: var(--color-bg-main); + + display: grid; + place-items: stretch stretch; + place-content: stretch stretch; + grid-template-columns: minmax(0, 1fr) /* issue#1370 https://css-tricks.com/preventing-a-grid-blowout/ */ + minmax(0, 1fr); + grid-template-rows: minmax(0, max-content) + minmax(0, 1fr); + + gap: var(--base-rhythm); + + min-height: 0; /* to prevent from overflowing the parent flex container */ +} + +/* controls */ + +.diff_controls { + display: flex; + justify-content: flex-end; + gap: calc(var(--base-rhythm)/2); +} + +#diff_left_open { + color: var(--diff-document-color-left); +} + +#diff_right_open { + color: var(--diff-document-color-right); +} + +#diff_left_close, +#diff_right_close { + color: var(--color-link); +} + +#diff_left_open:hover, +#diff_right_open:hover { + color: var(--color-hover); +} + +/* columns */ +.diff_column { + overflow: auto; + overflow-y: scroll; + + overflow-wrap: break-word; + + border-radius: 4px; + border: 1px solid var(--color-border); + + position: relative; /* for position:sticky */ +} + +.diff_column[left] { + direction:rtl; +} + +.diff_column[right] { + direction:ltr; +} + +.diff_inner { + direction: initial; + padding-bottom: 100%; /* to balance the scrolling freely */ +} + +.diff_content { + display: flex; + flex-direction: column; + gap: var(--base-rhythm); + padding: var(--base-rhythm); +} + +/* details with summary */ + +.diff details { + width: 100%; +} + +.diff summary { + list-style: none; + display: flex; + gap: var(--base-rhythm); + cursor: pointer; + color: var(--color-link); +} + +.diff details[modified] > summary { + color: var(--color-fg-accent); +} + +.diff details[modified="left"] > summary { + color: var(--diff-document-color-left); +} + +.diff details[modified="right"] > summary { + color: var(--diff-document-color-right); +} + +.diff summary:hover, +.diff details[modified] > summary:hover { + color: var(--color-hover); +} + +.diff summary::-webkit-details-marker { + display: none; +} + +.diff details > summary::before { + content:"+"; +} + +.diff details[open] > summary::before { + content:"–"; +} + +/* document / details */ + +.diff_document { + background-color: var(--color-bg-contrast); + border: 1px solid transparent; + border-radius: 4px; + padding: 0 var(--base-rhythm); +} + +.diff_document[modified] { + border-color: var(--color-fg-accent); +} + +.diff_document[modified="left"] { + border-color: var(--diff-document-color-left, rgba(255, 55, 55, 1)); +} + +.diff_document[modified="right"] { + border-color: var(--diff-document-color-right, rgba(20, 120, 20, 1)); +} + +.diff_document > summary { + line-height: 1.2; + padding: var(--base-rhythm) 0; + font-size: var(--font-size-sm); + font-weight: 700; + justify-content: space-between; /* '+' to right */ +} + +.diff_document[open] > summary { + /* + When the details are closed up, the summary sometimes covers the bottom border. + That's why we only add the background for cases where the card is open + and sticking makes sense. + */ + background: var(--color-bg-contrast); + position: sticky; + top: 0; +} + +.diff_document > summary .document_title { + flex-grow: 1; +} + +.diff_document[open] > summary { + border-bottom: 1px dotted; +} + +.diff_document > summary::before, +.diff_document[open] > summary::before { + content: none !important; +} + +.diff_document > summary::after { + content:"+"; +} + +.diff_document[open] > summary::after { + content:"–"; +} + +/* folder */ + +.diff_folder { + display: flex; + align-items: center; + justify-content: flex-start; + column-gap: calc(var(--base-rhythm)/2); + font-size: var(--font-size-sm); + min-width: 0; + line-height: 1.5; + padding-top: var(--base-rhythm); +} + +/* node content */ + +.diff_node { + margin: var(--base-rhythm) 0; +} + +.diff_node_fields { + display: flex; + flex-direction: column; + margin: var(--base-rhythm) 0; + row-gap: calc(var(--base-rhythm) / 2); +} + +.diff_node > .diff_node_fields { + padding-left: calc(var(--base-rhythm)*2); +} + +.diff_node_field { + display: flex; + flex-wrap: wrap; + align-items: baseline; + column-gap: calc(var(--base-rhythm) / 2); +} + +.diff_node_field > .badge { + line-height: var(--pre-stripe); +} + +/* + *** changelog *** + */ + +.changelog { + display: flex; + flex-direction: column; +} + +.changelog .sdoc-table_key_value { + width: auto; +} + +.changelog .diff_column { + margin-right: -16px; + border-radius: 0; + border: none; + border-right: 1px solid var(--color-border); +} + +.changelog_summary { + padding: calc(2 * var(--base-rhythm)); +} + +.changelog_content { + display: flex; + flex-direction: column; + gap: var(--base-rhythm); + margin-bottom: calc(4 * var(--base-rhythm)); +} + +.changelog_changes { + display: flex; + flex-direction: column; + gap: var(--base-rhythm); +} + +.changelog_change { + background-color: var(--color-bg-contrast); + border: 1px solid var(--color-border); + border-radius: 4px; + display: flex; + flex-direction: row; + flex-wrap: wrap; +} + +.changelog_change_meta { + background-color: var(--color-bg-main); + width: 100%; + font-size: var(--font-size-xsm); + font-weight: 700; + display: flex; + gap: var(--base-rhythm); + padding: var(--base-rhythm); +} + +.changelog_change_type { + font-size: var(--font-size-xsm); +} + +.changelog_change_type.removed { + color: red; +} + +.changelog_change_type.modified { + color: orange; +} + +.changelog_change_type.added { + color: green; +} + +.changelog_change_node { + width: 100%; + padding: var(--base-rhythm); +} + +@media (min-width: 768px) { + .changelog_change_node { + /* width: calc(50% - var(--base-rhythm)); */ + width: 50%; + } +} + +.changelog_node_null { + display: flex; + justify-content: center; + align-items: center; + color: var(--color-placeholder); + + height: 100%; + min-height: 32px; + background:repeating-linear-gradient( + -45deg, + rgba(234, 234, 234, 0.1), + rgba(234, 234, 234, .1) 10px, + rgba(234, 234, 234, .15) 10px, + rgba(234, 234, 234, .15) 20px + ); + +} + +/* + *** MISC *** + */ + +/* badge */ + +.badge { + white-space: nowrap; +} + +.badge::before { + content: attr(text); + padding: 0 calc(var(--base-rhythm)/2); + border: 1px solid; + border-radius: calc(var(--base-rhythm)/2); + font-size: var(--font-size-xxsm); + font-weight: 600; + text-transform: uppercase; +} + +/* in summary: */ +[modified="left"] > summary .badge::before, +/* in field: */ +[modified="left"] > .badge::before { + color: white; + background-color: var(--diff-icon-color-left); + border-color: var(--diff-icon-color-left); +} + +/* in summary: */ +[modified="right"] > summary .badge::before, +/* in field: */ +[modified="right"] > .badge::before { + color: white; + background-color: var(--diff-icon-color-right); + border-color: var(--diff-icon-color-right); +} + +/* pre */ + +.sdoc_pre_content { + flex-grow: 1; + white-space: pre-wrap; + overflow-x: clip; + font-family: monospace; + font-size: 0.85em; + line-height: var(--pre-stripe); + background-image: repeating-linear-gradient( + to bottom, + var(--pre-stripe-color), + var(--pre-stripe-color) var(--pre-stripe), + transparent var(--pre-stripe), + transparent calc(var(--pre-stripe)*2) + ); + background-color: var(--pre-block-bg-color); +} + +[multiline] .sdoc_pre_content { + flex-basis: 100%; +} + +[modified="left"] > .sdoc_pre_content { + background-color: var(--diff-block-color-left); +} +.lambda_red {background-color: var(--diff-word-color-left);} + +[modified="right"] > .sdoc_pre_content { + background-color: var(--diff-block-color-right); +} +.lambda_green {background-color: var(--diff-word-color-right);} + +/* preloaded */ +/* + The element has been pre-loaded + and a smooth appearance effect is added to it. + The element will retain the computed values set by the last keyframe. + */ +.preloaded { + position: relative; + animation: fadeInPreloaded 1s ease-in-out; + animation-fill-mode: forwards; +} + +@keyframes fadeInPreloaded { + from { + opacity: 0; + } + to { + opacity: 1; + } +} + +/* skeleton */ + +.skeleton_spinner_container { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + min-height: 333px; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + grid-column: 1/3; + grid-row: 2/3; + z-index: 11; +} + +@keyframes change-color { + from {background-color: rgba(255,255,255,0.8);} + to {background-color: rgba(255,255,255,0.3);} +} + +@keyframes pulse { + 0%, 100% { + opacity: .5; + } + 50% { + opacity: 1; + } +} + +.skeleton_spinner { + position: relative; + text-indent: -9999em; + animation: mulShdSpin 1.3s infinite linear; + transform: translateZ(0); + /* size */ + font-size: 8px; + margin: 36px; + /* dot */ + color: var(--color-fg-accent); + width: 1em; + height: 1em; + border-radius: 50%; +} + +@keyframes mulShdSpin { + 0%, + 100% { + box-shadow: 0 -3em 0 0.2em, + 2em -2em 0 0em, 3em 0 0 -1em, + 2em 2em 0 -1em, 0 3em 0 -1em, + -2em 2em 0 -1em, -3em 0 0 -1em, + -2em -2em 0 0; + } + 12.5% { + box-shadow: 0 -3em 0 0, 2em -2em 0 0.2em, + 3em 0 0 0, 2em 2em 0 -1em, 0 3em 0 -1em, + -2em 2em 0 -1em, -3em 0 0 -1em, + -2em -2em 0 -1em; + } + 25% { + box-shadow: 0 -3em 0 -0.5em, + 2em -2em 0 0, 3em 0 0 0.2em, + 2em 2em 0 0, 0 3em 0 -1em, + -2em 2em 0 -1em, -3em 0 0 -1em, + -2em -2em 0 -1em; + } + 37.5% { + box-shadow: 0 -3em 0 -1em, 2em -2em 0 -1em, + 3em 0em 0 0, 2em 2em 0 0.2em, 0 3em 0 0em, + -2em 2em 0 -1em, -3em 0em 0 -1em, -2em -2em 0 -1em; + } + 50% { + box-shadow: 0 -3em 0 -1em, 2em -2em 0 -1em, + 3em 0 0 -1em, 2em 2em 0 0em, 0 3em 0 0.2em, + -2em 2em 0 0, -3em 0em 0 -1em, -2em -2em 0 -1em; + } + 62.5% { + box-shadow: 0 -3em 0 -1em, 2em -2em 0 -1em, + 3em 0 0 -1em, 2em 2em 0 -1em, 0 3em 0 0, + -2em 2em 0 0.2em, -3em 0 0 0, -2em -2em 0 -1em; + } + 75% { + box-shadow: 0em -3em 0 -1em, 2em -2em 0 -1em, + 3em 0em 0 -1em, 2em 2em 0 -1em, 0 3em 0 -1em, + -2em 2em 0 0, -3em 0em 0 0.2em, -2em -2em 0 0; + } + 87.5% { + box-shadow: 0em -3em 0 0, 2em -2em 0 -1em, + 3em 0 0 -1em, 2em 2em 0 -1em, 0 3em 0 -1em, + -2em 2em 0 0, -3em 0em 0 0, -2em -2em 0 0.2em; + } +} diff --git a/tests/fuzz/20_L1_Open_Requirements_Tool_202511/assets/diff.js b/tests/fuzz/20_L1_Open_Requirements_Tool_202511/assets/diff.js new file mode 100644 index 0000000..3d77bbc --- /dev/null +++ b/tests/fuzz/20_L1_Open_Requirements_Tool_202511/assets/diff.js @@ -0,0 +1,153 @@ +window.addEventListener("load",function(){ + + const mutatingFrame = document.querySelector('#diff_result'); + + var observer = new MutationObserver(function (mutationsList, observer) { + + const diffTabRoot = mutatingFrame.querySelector('.diff'); + if (diffTabRoot) { + // triggers on the diff tab + prepareBulkButtons(diffTabRoot); + syncDiff(diffTabRoot); + } + + // Disable the observer after the first trigger + // because the functions modify the elements inside the #diff_result. + observer.disconnect(); + }); + + observer.observe( + mutatingFrame, + { + childList: true, + subtree: true + } + ); + +},false); + +function prepareBulkButtons(root) { + + const leftColumn = root.querySelector('.diff_column[left]'); + const rightColumn = root.querySelector('.diff_column[right]'); + + const leftOpenBtn = root.querySelector('#diff_left_open'); + const leftCloseBtn = root.querySelector('#diff_left_close'); + const rightOpenBtn = root.querySelector('#diff_right_open'); + const rightCloseBtn = root.querySelector('#diff_right_close'); + + const detailsLeftAll = [...leftColumn.querySelectorAll('details')]; + const detailsRightAll = [...rightColumn.querySelectorAll('details')]; + const detailsLeft = [...leftColumn.querySelectorAll('details[modified]')]; + const detailsRight = [...rightColumn.querySelectorAll('details[modified]')]; + + leftOpenBtn.addEventListener("click", (event) => { + event.preventDefault(); + _openAll(detailsLeft); + }); + leftCloseBtn.addEventListener("click", (event) => { + event.preventDefault(); + _closeAll(detailsLeftAll); + leftColumn.scrollTo(0, 0); + }); + rightOpenBtn.addEventListener("click", (event) => { + event.preventDefault(); + _openAll(detailsRight); + }); + rightCloseBtn.addEventListener("click", (event) => { + event.preventDefault(); + _closeAll(detailsRightAll); + rightColumn.scrollTo(0, 0); + }); +} + +function syncDiff(root) { + + const sync = [...root.querySelectorAll('button[uid]')] + .reduce((acc, curr) => { + const uid = curr.getAttribute('uid'); + const side = curr.getAttribute('side'); + acc[uid] = { + [side]: curr, + ...acc[uid] + } + return acc + },{}); + + Object.entries(sync).forEach(([uid, obj]) => { + if (!obj.left) { + obj.right.remove() + return + }; + + if (!obj.right) { + obj.left.remove() + return + }; + + obj.left.addEventListener("click", (event) => { + event.preventDefault(); + + // _openAll(detailsLeftAll); + _openAncestors(obj.left, root); + _scrollTo(obj.left); + + setTimeout(() => { + // _openAll(detailsRightAll); + _openAncestors(obj.right, root); + _scrollTo(obj.right); + }, 900); + }); + + obj.right.addEventListener("click", (event) => { + event.preventDefault(); + + // _openAll(detailsRightAll); + _openAncestors(obj.right, root); + _scrollTo(obj.right); + + setTimeout(() => { + // _openAll(detailsLeftAll); + _openAncestors(obj.left, root); + _scrollTo(obj.left); + }, 900); + }); + }) +}; + +function _openAncestors(element, root) { + + let currentElement = element; + + while (currentElement && currentElement !== root) { + currentElement = currentElement.parentElement; + + if (currentElement && currentElement.tagName === 'DETAILS') { + currentElement.setAttribute('open', ''); + } + } +}; + +function _scrollTo(target) { + // We insert a special element into the button ('button[uid]') + // that compensates by 40 pixels + // for the height of the sticky header + // at the top of the scroll container: + // *** + // + + const a = target.querySelector('a-a') || target; + a.scrollIntoView({ behavior: "smooth" }); +}; + +function _closeAll(details) { + details.forEach(detail => { + detail.removeAttribute("open") + }); +}; + +function _openAll(details) { + details.forEach(detail => { + detail.setAttribute("open", "") + }); +}; diff --git a/tests/fuzz/20_L1_Open_Requirements_Tool_202511/assets/element.css b/tests/fuzz/20_L1_Open_Requirements_Tool_202511/assets/element.css new file mode 100644 index 0000000..fc5c6af --- /dev/null +++ b/tests/fuzz/20_L1_Open_Requirements_Tool_202511/assets/element.css @@ -0,0 +1,1822 @@ + + +[data-viewtype="source-file"] .requirement__link:hover { + text-decoration: none; +} + +svg { + /* Disable shrinking of icons in flex strings */ + flex-shrink: 0; +} + +/* typography */ + +h1 { font-size: 2em; } +h2 { font-size: 1.6em; } +h3 { font-size: 1.4em; } +h4 { font-size: 1.2em; } +h5 { font-size: 1em; } +h6 { font-size: 1em; } + +h1:first-child, +h2:first-child, +h3:first-child, +h4:first-child, +h5:first-child, +h6:first-child { + margin-top: 0; +} + +/* a */ + +a { + /* Disabled link styles */ + text-decoration: none; + color: gray; + transition: color .2s ease; +} + +a:link, +a:visited, +a[href] { /* or a[href] */ + /* Enabled link styles */ + text-decoration: none; + color: var(--color-link); +} + +a:hover { + /* color: blueviolet; */ + color: var(--color-hover); +} + +a[aria-disabled="true"] { + /* color: currentColor; */ + cursor: not-allowed; + opacity: 0.5; + text-decoration: none; + pointer-events: none; +} + +/* misc */ + +code { + position: relative; + padding: 0 4px; + font-style: normal; + font-family: var(--code-font-family); + font-size: var(--code-font-size); + background-color: var(--color-bg-main); + border: 1px solid var(--color-border); + border-radius: 4px; + + overflow-wrap: break-word; + word-wrap: break-word; + -webkit-box-decoration-break: clone; + box-decoration-break: clone; +} + +/* action_button */ + +.actions_group { + display: flex; + /* header context: */ + column-gap: calc(var(--base-rhythm)/2); + margin-left: auto; +} + +.action_button, +a.action_button, +a.action_button:link, +a.action_button:visited { + font-size:var(--font-size-xsm); + font-weight: 600; + text-align: left; + text-decoration: none; + white-space: nowrap; + position: relative; + display: inline-flex; + + align-items: center; + justify-content: center; + -webkit-box-align: center; + -webkit-box-pack: center; + border-radius: 6px; + border: 1px solid transparent; + backface-visibility: hidden; + user-select: none; + cursor: pointer; + appearance: none; + + /* 1.5 column-gap is compensated by SVG negative margin */ + column-gap: calc(var(--base-rhythm)*1.5); + padding-left: calc(var(--base-rhythm)*1.5); + padding-right: calc(var(--base-rhythm)*1.5); + min-height: calc(var(--base-rhythm)*4); + max-width: 100%; + + /* box-shadow: rgb(0 0 0 / 10%) 0px 1px 2px 0px; */ + color: var(--color-action); + background-color: rgb(255, 255, 255); + border-color: rgba(0, 0, 0, 0.05); + background-clip: padding-box; + + transition: 0.2s; +} + +.action_button:hover, +a.action_button:hover { + box-shadow: rgb(0 0 0 / 10%) 0px 2px var(--base-rhythm) 0px; + color: var(--color-hover); + z-index: 6; +} + +.action_button:disabled, +.action_button:disabled:hover, +.action_button[aria-disabled="true"], +.action_button[aria-disabled="true"]:hover { + color: var(--color-action); + opacity: .4; + cursor: default; +} + +.action_button svg { + /* + action_button 1.5 column-gap + is compensated by SVG negative margin + */ + margin-left: calc(var(--base-rhythm)*(-.5)); + margin-right: calc(var(--base-rhythm)*(-.5)); +} + +[data-action-type="submit"], +.action_button[type=submit] { + color: var(--color-submit); +} + +[data-action-type="action"], +[data-action-type="confirm_delete"] { + color: var(--color-action) !important; + border-color: var(--color-action) !important; +} + +[data-action-type="delete"], +[data-action-type="confirm_delete"] { + color: var(--color-danger) !important; + border-color: var(--color-danger) !important; +} + +[data-action-type="confirm_delete"]:hover { + color: var(--color-bg-contrast) !important; + background: var(--color-danger) !important; +} + +[data-action-type="cancel"], +.action_button[href*="cancel"] { + color: var(--color-cancel) !important; +} + +.action_icon, +a.action_icon, +a.action_icon:link, +a.action_icon:visited { + position: relative; + display: inline-flex; + align-items: center; + justify-content: center; + -webkit-box-align: center; + -webkit-box-pack: center; + backface-visibility: hidden; + user-select: none; + cursor: pointer; + appearance: none; + border: none; + border-color: transparent; + background-color: rgba(0,0,0,0); + background: rgba(0,0,0,0); + box-shadow: none; + background-clip: padding-box; + + color: var(--color-action); + transition: 0.2s; + padding: calc(var(--base-rhythm)*.5); + z-index: 2; +} + +.action_icon.secondary, +a.action_icon.secondary, +a.action_icon.secondary:link, +a.action_icon.secondary:visited { + color: var(--color-link); +} + +.action_icon.secondary:hover, +a.action_icon.secondary:hover, +.action_icon:hover, +a.action_icon:hover { + color: var(--color-hover); + z-index: 6; +} + +.action_button.compact { + column-gap: 0; + padding-left: calc(var(--base-rhythm)* 0.75); + padding-right: calc(var(--base-rhythm)* 0.75); + min-height: calc(var(--base-rhythm)*3); +} + +.action_button.compact .action_button_compact__arising { + overflow: hidden; + + max-width: 0; + margin-left: 0; + + transition-property: max-width, margin; + transition-duration: .5s; + transition-timing-function: ease-in-out; + transition-delay: 1s; +} + +.action_button.compact:hover > .action_button_compact__arising { + max-width: 200px; + margin-left: var(--base-rhythm); + + transition-property: max-width, margin; + transition-duration: .5s; + transition-timing-function: ease-in-out; + transition-delay: 0; +} + +/* TODO */ +sdoc-node-controls .action_button { + /* depends on sdoc-node-controls: */ + /* + width: calc(var(--base-rhythm)*4); + height: calc(var(--base-rhythm)*4); + */ + padding: 0 !important; + width: unset; + height: unset; + min-height: calc(var(--base-rhythm)*3) !important; + max-width: 100%; + max-height: 100%; + aspect-ratio: 1; + border-radius: 20% !important; + + vertical-align: top; +} + +/* field_action */ +/* TODO: optimize code with .action_icon & .action_button */ + +.field_action { /* button */ + font-size:var(--font-size-xsm); + font-weight: 600; + text-align: left; + text-decoration: none; + white-space: nowrap; + + position: static; + display: inline-flex; + align-items: center; + justify-content: center; + -webkit-box-align: center; + -webkit-box-pack: center; + backface-visibility: hidden; + user-select: none; + cursor: pointer; + appearance: none; + border: none; + border-color: transparent; + background-color: rgba(0,0,0,0); + background: rgba(0,0,0,0); + box-shadow: none; + background-clip: padding-box; + + color: var(--color-action); + transition: 0.2s; + padding: calc(var(--base-rhythm)*.5); + z-index: 2; +} + +.field_action:hover, +a.field_action:hover { + color: var(--color-hover); + z-index: 6; +} + +.field_action::before { + content: ''; + position: absolute; + /* The element is expected to be placed in a context + that defines the top and bottom boundaries. + + The width of the element must be enough + to cover the entire available context, + and will be cut off by the parent element via overflow. + */ + top: calc(var(--base-rhythm)*(-3)); /* -2 */ + bottom: calc(var(--base-rhythm)*(-1)); /* -2 */ + left: -100vw; + right: -100vw; + z-index: 0; + pointer-events: none; + /* Determines the shade of the color through the opacity of the element. + The color is passed from the parent. + */ + transition: background-color 0.3s; + background-color: transparent; + opacity: .1; /* Defines the color shade. */ +} + +.field_action:hover::before { + background: currentcolor; +} + +/* *** + USED BY copy_to_clipboard controller + */ + +sdoc-field { + display: flex; + /* it is assumed that it will automatically stretch, as a block element, + to the entire field space, and the elements in the service area will be + positioned relative to it: + */ + position: relative; + flex-grow: 1; + width: 100%; /* holds within the width of the parent */ +} + +sdoc-field-content { + flex-grow: 1; + width: 100%; /* holds within the width of the parent */ +} + +.copy_to_clipboard-cover { + position: absolute; + z-index: 98; + top: 0; + bottom: 0; + left: 0; + right: 0; + pointer-events: none; + + border-top-right-radius: 6px; /* TODO 4 + in button*/ + background-color: rgba(242, 100, 42, 0); + transition: .5s ease-out; +} + +.copy_to_clipboard-cover:hover { + background-color: rgba(242, 100, 42, 0.05); + transition: .5s ease-out; +} + +/* Fix: Prevent .copy_to_clipboard-button from receiving hover when covered by dropdown menu */ +sdoc-field > sdoc-field-service .copy_to_clipboard-button { + visibility: hidden; +} + +sdoc-field:hover > sdoc-field-service .copy_to_clipboard-button { + visibility: visible; +} + +/* button overrides */ +.copy_to_clipboard-button.action_button { + height: calc(var(--base-rhythm)*3); + width: calc(var(--base-rhythm)*3); + min-height: unset; + padding: var(--base-rhythm); + position: absolute; + right: 0; + pointer-events: auto; + + max-height: 100%; /* for meta row: to keep the copy button from expanding out of the content line */ +} + +/* button behavior */ +sdoc-field .copy_to_clipboard-button { + transition: .5s ease-out; + opacity: 0; +} + +sdoc-field:hover .copy_to_clipboard-button { + opacity: 1; +} + +/* *** + USED BY copy_stable_link controller + inside sdoc-node + */ + +.copy_stable_link-button { + display: inline-flex; + align-items: center; + justify-content: center; + padding: var(--base-rhythm); + + position: absolute; + left: 0; + top: 0; + + cursor: pointer; + transition: .5s ease-out; + opacity: 0; +} + +.copy_stable_link-button:hover { + color: var(--color-action); +} + +sdoc-node:hover .copy_stable_link-button { + opacity: 1; +} + +/* ANCHOR */ + +sdoc-anchor { + /* for Fixed Headers + Section Anchors */ + /* calc(var(--base-gap) + var(--base-padding)); */ + scroll-snap-margin-top: var(--base-gap); + scroll-margin-top: var(--base-gap); +} + +sdoc-node > sdoc-anchor { + scroll-snap-margin-top: unset; /** top: 0; */ + scroll-margin-top: unset; /** top: 0; */ + + display: block; + position: absolute; + top: 0; + bottom: 0; + z-index: 0; +} + +sdoc-anchor.anchor_in_rst { + top:unset; + /* + For anchors generated within the text, + we shift them to the left, beyond the node + (by the sdoc-node padding-inline). + */ + transform: translateX(calc(-4 * var(--base-rhythm))); +} + +sdoc-anchor:target + sdoc-node-content sdoc-node-title, +sdoc-anchor:target + sdoc-section sdoc-section-title, +sdoc-anchor:target + .requirement__title { + background-color: var(--color-highlight); +} + +sdoc-anchor .anchor_block { + display: none; +} + +/* + This styles should only be triggered if + export/html/_static/controllers/anchor_controller.js + processes the parent sdoc-node. +*/ +sdoc-node[data-controller="anchor_controller"] sdoc-anchor { + position: absolute; + z-index: 2; + left: 0; + transition: opacity 0.5s; + opacity: 0; +} + +sdoc-node[data-controller="anchor_controller"]:hover sdoc-anchor { + opacity: 1; +} + +sdoc-node[data-controller="anchor_controller"] sdoc-anchor.anchor_has_back_links { + opacity: 1; +} + +sdoc-node[data-controller="anchor_controller"] sdoc-anchor:hover { + z-index: 10; +} + +sdoc-node[data-controller="anchor_controller"] sdoc-anchor .anchor_block { + display: block; + position: absolute; + top: 0; + left: calc(-4 * var(--base-rhythm) - 3px); + + border-radius: 6px; + transition: opacity 0.2s; + opacity: .6; +} + +sdoc-node[data-controller="anchor_controller"] sdoc-anchor:hover .anchor_block { + background-color: rgb(255, 255, 255); + border-color: rgba(0, 0, 0, 0.05); + box-shadow: rgb(0 0 0 / 10%) 0px 2px var(--base-rhythm) 0px; + opacity: 1; +} + +sdoc-node[data-controller="anchor_controller"] sdoc-anchor .anchor_button { + cursor: pointer; + font-size:var(--font-size-sm); + font-weight: 600; + text-align: left; + white-space: nowrap; + position: relative; + display: inline-flex; + align-items: center; + justify-content: flex-start; + + border-radius: 6px; + border: 1px solid transparent; + background-clip: padding-box; + + /* 1.5 column-gap is compensated by SVG negative margin */ + column-gap: calc(var(--base-rhythm)*1.5); + padding-left: calc(var(--base-rhythm)*1.5); + padding-right: calc(var(--base-rhythm)*1.5); + min-height: calc(var(--base-rhythm)*4); + min-width: calc(var(--base-rhythm)*4); + + /* box-shadow: rgb(0 0 0 / 10%) 0px 1px 2px 0px; */ + /* color: var(--color-action); */ + + overflow: hidden; + + transition: opacity 0.2s; + opacity: .6; +} + +sdoc-node[data-controller="anchor_controller"] sdoc-anchor .anchor_button:hover { + color: var(--color-hover); + z-index: 6; + + opacity: 1; +} + +sdoc-node[data-controller="anchor_controller"] sdoc-anchor .anchor_button svg { + color: var(--color-fg-secondary); + margin-left: calc(var(--base-rhythm)*(-.5)); + margin-right: calc(var(--base-rhythm)*(-.5)); +} + +sdoc-node[data-controller="anchor_controller"] sdoc-anchor.anchor_has_back_links .anchor_button svg { + color: black; + opacity: 1; +} + +sdoc-node[data-controller="anchor_controller"] sdoc-anchor .anchor_button:hover svg { + color: var(--color-action) !important; +} + +sdoc-node[data-controller="anchor_controller"] sdoc-anchor .anchor_button_text { + font-size: var(--font-size-sm); + font-weight: bolder; + + display: none; +} + +sdoc-node[data-controller="anchor_controller"] sdoc-anchor:hover .anchor_button_text { + display: block; +} + +sdoc-node[data-controller="anchor_controller"] sdoc-anchor .anchor_back_links { + min-width: 300px; + padding-left: calc(var(--base-rhythm)*4); + padding-right: var(--base-rhythm); + padding-top: var(--base-rhythm); + padding-bottom: calc(var(--base-rhythm)*1); + + display: none; +} + +sdoc-node[data-controller="anchor_controller"] sdoc-anchor:hover .anchor_back_links { + display: block; +} + +sdoc-node[data-controller="anchor_controller"] sdoc-anchor .anchor_back_links a { + display: list-item; +} +sdoc-node[data-controller="anchor_controller"] sdoc-anchor .anchor_back_links a::marker { + content: '⇠ '; + width: calc(var(--base-rhythm)*4); +} + +sdoc-node[data-controller="anchor_controller"] sdoc-anchor .anchor_back_links_number { + color: var(--color-fg-accent); + position: absolute; + width: calc(var(--base-rhythm)*4); + text-align: center; + top: calc(var(--base-rhythm)*3); + left: 0; + font-weight: bold; +} + +sdoc-node[data-controller="anchor_controller"] sdoc-anchor:hover .anchor_back_links_number { + top: calc(var(--base-rhythm)*5); +} + +/* nav */ + +.nav { + list-style: none; + padding: 0; + margin: 0; + + display: flex; + flex-flow: column nowrap; + background-color: var(--color-bg-ui); + width: var(--base-gap); + height: 100%; +} + +.nav_list { + list-style: none; + padding: 0; + margin: 0; +} + +.nav_list li { + margin-bottom: var(--base-rhythm); +} + +.nav_list__link { + display: flex; + column-gap: var(--base-rhythm); +} + +/* nav_button */ + +.nav_button { + display: flex; + justify-content: center; + align-items: center; + cursor: pointer; + + text-decoration: none; + + height: var(--base-gap); + width: var(--base-gap); + transition: 0.3s; + + color: var(--color-fg-secondary); +} + +.nav_button:hover { + color: var(--color-fg-contrast); +} + +.nav .nav_button { + color: var(--color-fg-secondary-invert); + background-color: transparent; + border-left: 2px solid transparent; +} + +.nav .nav_button:hover { + color: var(--color-bg-contrast); + border-color: var(--color-bg-contrast); +} + +/* should be like .nav .nav_button:hover */ +[data-viewtype="document-tree"] [data-link="index"], +[data-viewtype="search"] [data-link="search"], +[data-viewtype="diff"] [data-link="diff"], +[data-viewtype="requirements-coverage"] [data-link="requirements_coverage"], +[data-viewtype="traceability-matrix"] [data-link="traceability-matrix"], +[data-viewtype="coverage-tree"] [data-link="source_coverage"] { + color: var(--color-bg-contrast); + border-color: var(--color-bg-contrast); + cursor: default; +} + +/* sdoc tabs (page nav) */ + +.sdoc-tabs { + border-bottom: 1px solid var(--color-border); +} + +.sdoc-tab-list { + display: flex; + overflow: auto; + margin-bottom: -1px; +} + +.sdoc-tab { + padding: var(--base-rhythm) calc(2 * var(--base-rhythm)); + font-size: var(--font-size-xsm); + font-weight: 600; + line-height: 1; + color: var(--color-link); + text-decoration: none; + background-color: transparent; + border-width: 1px 1px 0px; + border-top-style: solid; + border-right-style: solid; + border-left-style: solid; + border-top-color: transparent; + border-right-color: transparent; + border-left-color: transparent; + border-image: initial; + border-bottom-style: initial; + border-bottom-color: initial; + + color: var(--color-fg-contrast); +} + +.sdoc-tab:hover { + color: var(--color-hover); +} + +.sdoc-tab[active] { + color: var(--color-fg-contrast); + border-color: var(--color-border); + border-top-right-radius: 4px; + border-top-left-radius: 4px; + background-color: var(--color-bg-main); +} + +/* tags */ + +.tags { + padding-bottom: var(--base-gap); +} + +.tag { + display: inline-flex; + align-items: center; + background-color: rgba(0,0,0,0.1); + border: 2px solid rgba(0,0,0,0.05); + font-size: 12px; + line-height: 1; + vertical-align: middle; + margin: 2px 0px; + padding-left: 8px; + border-radius: 2em; +} + +.tag_badge { + display: inline-flex; + justify-content: center; + align-items: center; + font-size: var(--font-size-xxsm); + background-color: rgba(255, 255, 255, 0.75); + border: 4px solid transparent; + min-width: 18px; + height: 18px; + border-radius: 2em; + margin-left: 4px; +} + +/* coverage */ + +.value-bar { + display: flex; + height: 1rem; + align-items: center; + gap: 4px; +} + +.value-bar_bar { + width: 3rem; + border-radius: .5rem; + + /* box-shadow: inset 0px 2px 2px 0px #ccc; */ + border: 1px solid rgba(0,0,0,0.1); + position: relative; + overflow: hidden; + + height: 0.75rem; + + background-color: rgba(255, 255, 255, 0.2); +} + +.value-bar_bar[data-value] { + border: 1px solid rgb(80, 240, 40); + background-color: rgba(255, 255, 255, 1); +} + +.value-bar_filler { + position: absolute; + top: 0; + bottom: 0; + left: 0; + background: rgb(80, 240, 40); +} + +.value-bar_text { + width: 2.5rem; + text-align: right; + font-size: 0.75rem; + line-height: 1; + font-weight: 600; +} + +ol.arabic { + list-style: decimal; +} + +ol.loweralpha { + list-style: lower-alpha; +} + +ol.upperalpha { + list-style: upper-alpha; +} + +ol.lowerroman { + list-style: lower-roman; +} + +ol.upperroman { + list-style: upper-roman; +} + +/* svg icon */ + +svg.svg_icon polyline, +svg.svg_icon line, +svg.svg_icon circle, +svg.svg_icon rect, +svg.svg_icon path { + fill: none; + fill-rule: evenodd; + stroke-width: 1.5; + /* stroke: #000; */ + stroke: currentColor; + stroke-linecap: round; + stroke-linejoin: round; + /* transition: 0.2s; */ +} + +svg.svg_icon { + width: calc(var(--base-rhythm)*2); + height: calc(var(--base-rhythm)*2); + flex: 0 0 auto; + background: transparent; + /* color: rgb(246, 153, 13); */ +} + +.svg_icon_hover_visible { + display: none; +} +.anchor_button:hover .svg_icon_hover_visible, +svg:hover .svg_icon_hover_visible { + display: inline; +} +.svg_icon_not_hover_visible { + display: inline; +} +.anchor_button:hover .svg_icon_not_hover_visible, +svg:hover .svg_icon_not_hover_visible { + display: none; +} + +/* .header */ + +.header { + height: calc(var(--base-rhythm)*6); + display: flex; + align-items: center; + justify-content: flex-start; + column-gap: calc(var(--base-rhythm)/2); + padding-left: calc(var(--base-rhythm)*2); + padding-right: var(--base-rhythm); + border-bottom: var(--base-border); + min-width: 0; +} + +.header__document_title { + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + font-size: var(--font-size-sm); + font-weight: 700; + + flex-shrink: 0.1; +} + +.header__project_name { + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + font-size: var(--font-size-sm); + font-weight: 400; + min-width: calc(var(--base-rhythm)*2.5); + + flex-shrink: 100; +} + +/* .footer */ + +.footer { + display: flex; + align-items: center; + justify-content: flex-end; + height: 100%; + padding: var(--base-rhythm) calc(var(--base-rhythm)*2); + column-gap: calc(var(--base-rhythm)/2); + background-color: var(--color-bg-ui); + color: var(--color-fg-secondary-invert); + font-size: .85rem; +} + +a.strictdoc__link { + color: var(--color-fg-accent); + fill: var(--color-fg-accent); +} + +a.strictdoc__version { + color: var(--color-fg-secondary-invert); + fill: var(--color-fg-secondary-invert); + display: flex; + column-gap: calc(var(--base-rhythm)/2); + align-items: center; +} + +a.strictdoc__link:hover, +a.strictdoc__version:hover { + color: var(--color-bg-main); + fill: var(--color-bg-main); +} + +/* --md-source-version-icon: url(data:image/svg+xml;charset=utf-8,); */ + + +/* pagetype */ + +.pagetype { + font-size: var(--font-size-xxsm); + font-weight: 700; + text-transform: uppercase; + color: var(--color-fg-secondary); + display: flex; + align-items: center; + justify-content: center; + line-height: 1; +} + +/* viewtype */ + +.viewtype { + position: relative; + font-size: var(--font-size-xsm); +} + +.viewtype__handler { + font-weight: 500; + color: var(--color-fg-accent); + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + column-gap: calc(var(--base-rhythm)/2); + user-select: none; +} + +.viewtype__menu { + position: absolute; + z-index: 99; + left: calc(var(--base-rhythm)*(-2)); + top: calc(var(--base-rhythm)*3); + margin: 0; + list-style: none; + padding: var(--base-rhythm); + background: var(--color-bg-contrast); + box-shadow: 0 2px 8px rgb(0 0 0 / 20%); + border-radius: var(--base-rhythm); + font-weight: 500; + display: flex; + flex-direction: column; + row-gap: calc(var(--base-rhythm)/2); +} + +.viewtype__menu[aria-hidden="true"] { + display: none; +} + +.viewtype__menu[aria-hidden="false"] { + display: flex; +} + +.viewtype__menu_header { + padding: var(--base-rhythm); + text-transform: uppercase; + color: var(--color-fg-secondary); + font-weight: 700; + font-size: var(--font-size-xxsm); +} + +.viewtype__menu_item { + padding: 0; + margin: 0; + list-style: none; + white-space: nowrap; + display: block; + padding: var(--base-rhythm); + border-radius: calc(var(--base-rhythm)/2); +} + +.viewtype__menu_item, +.viewtype__menu_item:link, +.viewtype__menu_item:visited { + text-decoration: none; + color: var(--color-fg-main); +} + +.viewtype__menu_item:hover { + background-color: var(--color-bg-secondary); + color: var(--color-fg-accent); +} + +[data-viewtype="document"] [data-viewtype_link="document"], +[data-viewtype="table"] [data-viewtype_link="table"], +[data-viewtype="traceability"] [data-viewtype_link="traceability"], +[data-viewtype="deep_traceability"] [data-viewtype_link="deep_traceability"], +[data-viewtype="standalone_document"] [data-viewtype_link="standalone_document"], +[data-viewtype="html2pdf"] [data-viewtype_link="html2pdf"], +.viewtype__menu_item.active { + background-color: var(--color-bg-accent); + color: var(--color-fg-accent); + cursor: default; +} + +/* sdoc-menu */ + +sdoc-menu menu { + position: absolute; + z-index: 99; + right: 100%; + top: 0; + margin: 0; + list-style: none; + padding: var(--base-rhythm); + background: var(--color-bg-contrast); + box-shadow: 0 2px 8px rgb(0 0 0 / 20%); + border-radius: var(--base-rhythm); + font-weight: 500; + + display: flex; + flex-direction: column; + row-gap: calc(var(--base-rhythm)/2); + +} + +sdoc-menu.add_node menu { + display: grid; + grid-template-columns: minmax(min-content, max-content) + minmax(min-content, max-content) + minmax(min-content, max-content) + minmax(min-content, max-content); +} + +sdoc-menu menu[aria-hidden="true"] { + display: none; +} + +sdoc-menu menu[aria-hidden="false"] { + display: flex; +} +sdoc-menu.add_node menu[aria-hidden="false"] { + display: grid; +} + +sdoc-menu header:first-child { + /* "Add node" */ + grid-column: 1 / -1; + border-bottom: var(--base-border); +} + +sdoc-menu menu a { + padding: 0; + margin: 0; + list-style: none; + white-space: nowrap; + display: flex; /* affects the 'add node' buttons */ + padding: var(--base-rhythm); + border-radius: calc(var(--base-rhythm)/2); + font-size: var(--font-size-xsm); +} + +sdoc-menu menu a, +sdoc-menu menu a:link, +sdoc-menu menu a:visited { + text-decoration: none; + color: var(--color-fg-main); +} + +sdoc-menu menu a:hover { + background-color: var(--color-bg-secondary); + color: var(--color-fg-accent); +} + +sdoc-menu menu header { + padding: var(--base-rhythm); + text-transform: uppercase; + color: var(--color-fg-secondary); + font-weight: 700; + font-size: var(--font-size-xxsm); +} + +/* plus to close */ +sdoc-menu-handler[aria-expanded="true"] svg { + transform: rotate(45deg); + color: black; +} + +/* affects other menus: */ +[aria-expanded="true"] svg { + color: black; +} + +/* tree */ + +.tree { + display: flex; + flex-direction: column; + justify-content: flex-start; + align-items: flex-start; + gap: var(--base-rhythm); + margin-top: calc(var(--base-rhythm) * 2); + margin-bottom: calc(var(--base-rhythm) * 8); + padding-left: calc(var(--base-rhythm) * 2); + padding-right: calc(var(--base-rhythm) * 2); +} + +.tree_fragments { /* ul */ + margin-top: 0; + margin-bottom: 0; + padding-left: calc(var(--base-rhythm) * 2); + padding-right: 0; + list-style: none; + width: 100%; +} + +.tree_fragments li { + list-style: none; + width: 100%; +} + +.tree_fragments, +.tree_fragments li { + display: flex; + flex-direction: column; + justify-content: flex-start; + align-items: flex-start; + gap: var(--base-rhythm); +} + +.tree_folder { + display: flex; + align-items: flex-start; + justify-content: flex-start; + column-gap: var(--base-rhythm); + min-width: 0; + line-height: 1.5; + width: 99%; + padding-top: 8px; + margin-top: 8px; + opacity: 0.6; + position: relative; + border-top: var(--base-border); +} + +.tree_folder:first-child { + border-top: none; +} + +.tree_folder_path { + font-size: var(--font-size-xsm); + font-weight: 400; +} + +a.tree_item, +.tree_item { + display: flex; + align-items: flex-start; + justify-content: flex-start; + column-gap: var(--base-rhythm); + font-size: var(--font-size-sm); + min-width: 0; + line-height: 1.5; + width: 99%; /* To calculate cite overflow */ +} + +.tree_item[active] { + color: var(--color-fg-contrast); +} + +.document_title { + font-size: var(--font-size-sm); + font-weight: 700; + width: 99%; /* To calculate cite overflow */ +} + +.document_title[data-file_name]::after { + content: attr(data-file_name); + display: block; + text-overflow: ellipsis; + overflow: hidden; + width: 99%; /* To calculate cite overflow */ + font-weight:400; + opacity:0.6; +} + +/** TOC **/ + +.toc, +.toc ul, +.toc li { + list-style: none; + padding: 0; + margin: 0; +} + +.toc { + margin-top: calc(var(--base-rhythm) * 2); + padding-left: calc(var(--base-rhythm) * 2); + font-size: var(--font-size-sm); + line-height: 1.2; + padding-bottom: var(--base-gap); +} + +.toc li { + margin: 0; + padding-left: var(--base-rhythm); + position: relative; +} + +.toc a { + display: block; + position: relative; + padding-top: var(--base-rhythm); + padding-bottom: var(--base-rhythm); + padding-right: var(--base-rhythm); +} + +.toc a, +.toc a:link, +.toc a:visited { + text-decoration: none; + color: var(--color-fg-secondary); +} +.toc a:hover { + color: var(--color-fg-contrast); +} + +.toc a:focus, +.toc a:active { + background: transparent; +} + +.toc-title-no-link { + color: var(--color-fg-secondary); + opacity: .6; + display: block; + padding-top: var(--base-rhythm); + padding-bottom: var(--base-rhythm); + padding-right: var(--base-rhythm); +} + +/* TOC targeted */ +.toc a[targeted]::before { + position: absolute; + z-index: -1; + content: ''; + top: 0; + bottom: 0; + right: 0; + left: -200px; /* parent has overflow */ + background-color: var(--color-highlight); +} + +/* TOC intersected */ +.toc a::after { + position: absolute; + z-index: -1; + content: ''; + top: 0; + right: 0; + bottom: 0; + width: 8px; + background-color: var(--color-bg-main); +} + +.toc a[intersected]::after { + position: absolute; + z-index: -1; + content: ''; + top: 0; + right: 0; + bottom: 0; + width: 8px; + background-color: var(--color-highlight); +} + +/* TOC parented */ +/* behavior depends on collapsible_list.js */ +.toc [data-collapsible_list__branch="closed"] ~ a[parented]::after { + background-color: var(--color-highlight); +} + +/* pdf-toc */ + +.pdf-toc { + display: table; + width: 100%; +} +.pdf-toc-row { + display: table-row; +} +.pdf-toc-cell { + display: table-cell; + padding-top: var(--base-rhythm); + padding-right: var(--base-rhythm); + font-size: 1em; + line-height: 1.5; +} +.pdf-toc-cell[dotted] { + position: relative; + width: 100%; /* to get max */ +} +.pdf-toc-cell[dotted] > * { + background-color: white; + display: inline; + padding-right: 8px; + position: relative; +} +.pdf-toc-cell[dotted]::before { + content: ''; + position: absolute; + border-bottom: dotted 2px rgba(0,0,0,.4); + bottom: .4em; + left: .8em; + right: .8em; +} +.pdf-toc-cell:last-child { /* page number */ + vertical-align: bottom; + padding-right: 0; + text-align: right; +} + +/* table_key_value component */ + +.sdoc-table_key_value { + display: grid; + grid-template-columns: minmax(min-content, max-content) minmax(min-content, 1fr); + place-items: stretch stretch; + place-content: stretch stretch; + position: relative; + border: var(--base-border); + border-radius: calc(var(--base-rhythm)*0.5); + + margin: var(--base-padding) 0; + padding: calc(var(--base-rhythm)*0.5); + gap: 2px; + + width: fit-content; + max-width: 100%; + /* overflow-x: auto; */ +} + +.sdoc-table_key_value-section, +.sdoc-table_key_value-key, +.sdoc-table_key_value-value { + padding: var(--base-rhythm) calc(var(--base-rhythm)*2); + background-color: var(--color-bg-secondary); +} + +.sdoc-table_key_value-key { + grid-column: 1 / 2; + color: var(--color-fg-main); + + display: flex; + flex-direction: row; + justify-content: flex-start; + align-items: flex-start; + + position: relative; + transition: 0.3s all; +} + +a.sdoc-table_key_value-key { + background-color: rgba(255,255,255,0.5); + color: var(--color-fg-contrast); + padding-right: calc(var(--base-rhythm)*5); + align-items: center; +} + +a.sdoc-table_key_value-key:hover { + background-color: rgba(255,255,255,1); + color: var(--color-fg-accent); +} + +a.sdoc-table_key_value-key::after { + content: "▸"; + position: absolute; + right: calc(var(--base-rhythm)*2); + color: var(--color-fg-accent); + font-size: var(--font-size-sm); +} + +.sdoc-table_key_value-value { + grid-column: 2 / 3; +} + +.sdoc-table_key_value-section { + grid-column: 1 / -1; + + padding: calc(var(--base-rhythm)*2); + padding-top: calc(var(--base-rhythm)*4); + padding-bottom: calc(var(--base-rhythm)*1); + font-family: var(--code-font-family); + + font-size: 0.85em; + font-weight: 500; + text-transform: uppercase; + color: var(--color-fg-secondary); + background-color: var(--color-bg-main); +} + +/* badge */ + +.badge { + white-space: nowrap; +} + +.badge::before { + content: attr(text); + padding: 0 calc(var(--base-rhythm)/2); + border: 1px solid; + border-radius: calc(var(--base-rhythm)/2); + font-size: var(--font-size-xxsm); + font-weight: 600; + text-transform: uppercase; +} + +.error { + color: var(--color-danger); +} + +/* tabs */ + +sdoc-tab-content { + display: none; +} +sdoc-tab-content[active] { + display: contents; +} + +sdoc-tabs { + grid-column: 1 / -1; + + /* compensate top padding for sdoc-form-grid: */ + /* margin-top: calc(var(--base-rhythm)*(-4)); */ + padding: calc(var(--base-rhythm)*(1)) calc(var(--base-rhythm)*(2)) 0; + + background-color: var(--color-bg-main); + border-radius: 4px; + + display: flex; +} + +sdoc-tab { + font-size: var(--font-size-xsm); + font-weight: 600; + text-align: left; + text-decoration: none; + white-space: nowrap; + position: relative; + display: inline-flex; + + align-items: center; + justify-content: center; + -webkit-box-align: center; + -webkit-box-pack: center; + + border: 1px solid transparent; + border-bottom: none; + border-radius: 4px 4px 0 0; + + user-select: none; + cursor: pointer; + appearance: none; + + /* 1.5 column-gap is compensated by SVG negative margin */ + /* column-gap: calc(var(--base-rhythm)*1.5); */ + + min-height: calc(var(--base-rhythm)*4); + padding-left: calc(var(--base-rhythm)*1.5); + padding-right: calc(var(--base-rhythm)*1.5); + + color: rgba(0, 0, 0, 0.5); + background-color: rgba(255, 255, 255, 0); + background-clip: padding-box; + + transition: 0.2s; +} + +sdoc-tab:hover { + background-color: rgba(255, 255, 255, 0.5); +} + +sdoc-tab[active] { + color: var(--color-hover); + background-color: rgba(255, 255, 255, 1); + cursor: default; +} + +sdoc-tab[data-errors] { + color: var(--color-danger); + border-color: var(--color-danger); +} + +sdoc-tab[data-errors]::after { + content: attr(data-errors); + display: flex; + justify-content: center; + align-items: center; + background-color: var(--color-danger); + color: var(--color-bg-contrast); + font-size: var(--font-size-xsm); + height: calc(var(--font-size-xsm)*1.75); + width: calc(var(--font-size-xsm)*1.75); + aspect-ratio: 1; + border-radius: 50%; + + /* parent column-gap == 0 */ + position: relative; + right: calc(var(--base-rhythm)*(-1)); +} + +sdoc-tabs.in_aside_panel { + background-color: var(--color-highlight-secondary); + border-bottom: 4px solid var(--color-bg-main); +} + +sdoc-tabs.in_aside_panel sdoc-tab:hover { + background-color: rgba(255, 255, 255, 0.25); +} + +sdoc-tabs.in_aside_panel sdoc-tab[active] { + background-color: var(--color-bg-main); +} + +/* document_issues */ + +.document_issues-banner { + display: flex; + flex-direction: column; + position: relative; + margin-bottom: var(--base-gap); + font-size: var(--font-size-sm); +} + +.document_issues-toggler { + text-align: right; +} + +.document_issues-banner_details { + display: block; + overflow: hidden; + color: var(--color-danger); + border: 1px solid; + border-radius: var(--requirement-border-radius); +} + +.document_issues-banner_summary { + font-weight: 600; + position: relative; + color: var(--color-danger); + padding-inline: calc(2 * var(--base-rhythm)); + padding-block: var(--base-rhythm); + cursor: pointer; + user-select: none; + display: flex; +} + +.document_issues-banner_summary::before { + content: '▸'; + margin-right: var(--base-rhythm); +} + +[open] > .document_issues-banner_summary::before { + content: '▾'; +} + +.document_issues-banner_summary::after { + content: ''; + position: absolute; + inset: 0; + background-color: currentColor; + opacity: 0.1; +} + +.document_issues-banner_content { + position: relative; + color: var(--color-fg-contrast); + padding-inline: calc(2 * var(--base-rhythm)); + padding-block: calc(1 * var(--base-rhythm)); +} + +.document_issues-banner ul { + margin: 0; + padding-inline-start: 20px; +} + +.field_issue { + grid-column: 1 / -1; + position: relative; + color: var(--color-danger); + padding: var(--base-rhythm); + font-size: var(--font-size-sm); + line-height: 1.5; +} + +.field_issue-ribbon { + position: relative; + padding: calc(.5 * var(--base-rhythm)) var(--base-rhythm); + border-radius: calc(.5 * var(--base-rhythm)); + border: 2px solid; +} + +.field_issue-ribbon::before { + position: absolute; + content: ''; + bottom: 100%; + left: var(--base-rhythm); + width: 0; + height: 0; + border-left: 6px solid transparent; + border-right: 6px solid transparent; + border-bottom: 6px solid currentColor; +} + +.field_issue-ribbon::after { + content: ''; + position: absolute; + inset: 0; +} + +/* + ** SEARCH bar and result area + */ + +.search-box { + /* DEV: to push to the right in the header flex box */ + margin-left: auto; +} + +.actions_group + .search-box { + /* DEV: if on the server to the right of the action buttons */ + margin-left: unset; +} + +.search-input { + z-index: 101; + position: relative; + min-height: calc(var(--base-rhythm) * 6); /* = .header height */ + display: flex; + align-items: center; + justify-content: flex-end; +} + +.search-input input { + width: 145px; + transition: all .3s ease-out; +} + +.search-box[active] .search-input { + width: 100%; + border-bottom: var(--base-border); + padding-inline: var(--base-rhythm); +} + +.search-box[active] .search-input input { + width: 100%; +} + +.search-box[active] { + z-index: 100; + position: fixed; + width: 50%; + min-width: 300px; + right: 0; + top: 0; + bottom: 0; + background-color: white; + box-shadow: var(--base-elevation); + + display: flex; + flex-direction: column; + min-height: 0; +} + +.search-results { + display: none; +} + +.search-box[active] .search-results { + display: flex; + flex-direction: column; + flex: 1 1 auto; + min-height: 0; +} + +.search-results-navigation { + display: flex; + align-items: center; + justify-content: space-between; + flex: 0 0 auto; + + padding-inline: var(--base-rhythm); + padding-block: calc(var(--base-rhythm) / 2); + border-bottom: var(--base-border); +} + +.search-results-count { + font-size: var(--font-size-sm); + color: var(--color-fg-secondary); +} + +.search-results-count b { + color: var(--color-action); +} + +.search-results-navigation-buttons { + display: flex; + gap: 2px; +} + +.search-results-navigation-button { + display: inline-flex; + align-items: center; + justify-content: center; + + block-size: 1rem; + font-size: 1rem; + line-height: 0; + font-family: monospace; + + aspect-ratio: 1 / 1; + + border: 1px solid; + border-radius: 4px; + background-color: white; + cursor: pointer; +} + +.search-results-navigation-button:hover { + color: var(--color-action); +} + +.search-results-navigation-button#previous, +.search-results-navigation-button#next { + aspect-ratio: 1.75 / 1; +} + +.search-results-navigation-button#start::after { + content: '«'; +} +.search-results-navigation-button#previous::after { + content: '‹'; +} +.search-results-navigation-button#next::after { + content: '›'; +} +.search-results-navigation-button#end::after { + content: '»'; +} + +.search-results-navigation-button[disabled] { + color: #ccc; + cursor: default; +} + +.search-suggestions { + flex: 1 1 auto; + min-height: 0; + overflow: auto; +} + +.static_search-result-node { + padding: calc(var(--base-rhythm)*2); + border-bottom: var(--base-border); +} + +.static_search-result-node-field { + margin-bottom: var(--base-rhythm); +} + +.static_search-result-node-field-key { + font-size: var(--font-size-sm); + font-family: var(--code-font-family); + font-weight: 500; + color: var(--requirement-label-color); +} + +.static_search-result-node-link, +.static_search-result-node-link a { + color: var(--color-action); +} + +.static_search-result-node-link a:hover { + color: var(--color-hover); +} diff --git a/tests/fuzz/20_L1_Open_Requirements_Tool_202511/assets/favicon.ico b/tests/fuzz/20_L1_Open_Requirements_Tool_202511/assets/favicon.ico new file mode 100644 index 0000000..a1cd1eb Binary files /dev/null and b/tests/fuzz/20_L1_Open_Requirements_Tool_202511/assets/favicon.ico differ diff --git a/tests/fuzz/20_L1_Open_Requirements_Tool_202511/assets/fonts/NotoSans-Italic-VariableFont_wdth,wght.ttf b/tests/fuzz/20_L1_Open_Requirements_Tool_202511/assets/fonts/NotoSans-Italic-VariableFont_wdth,wght.ttf new file mode 100644 index 0000000..4e962ee Binary files /dev/null and b/tests/fuzz/20_L1_Open_Requirements_Tool_202511/assets/fonts/NotoSans-Italic-VariableFont_wdth,wght.ttf differ diff --git a/tests/fuzz/20_L1_Open_Requirements_Tool_202511/assets/fonts/NotoSans-VariableFont_wdth,wght.ttf b/tests/fuzz/20_L1_Open_Requirements_Tool_202511/assets/fonts/NotoSans-VariableFont_wdth,wght.ttf new file mode 100644 index 0000000..f7d0d78 Binary files /dev/null and b/tests/fuzz/20_L1_Open_Requirements_Tool_202511/assets/fonts/NotoSans-VariableFont_wdth,wght.ttf differ diff --git a/tests/fuzz/20_L1_Open_Requirements_Tool_202511/assets/fonts/NotoSansMono-VariableFont_wdth,wght.ttf b/tests/fuzz/20_L1_Open_Requirements_Tool_202511/assets/fonts/NotoSansMono-VariableFont_wdth,wght.ttf new file mode 100644 index 0000000..122ef75 Binary files /dev/null and b/tests/fuzz/20_L1_Open_Requirements_Tool_202511/assets/fonts/NotoSansMono-VariableFont_wdth,wght.ttf differ diff --git a/tests/fuzz/20_L1_Open_Requirements_Tool_202511/assets/form.css b/tests/fuzz/20_L1_Open_Requirements_Tool_202511/assets/form.css new file mode 100644 index 0000000..be530b8 --- /dev/null +++ b/tests/fuzz/20_L1_Open_Requirements_Tool_202511/assets/form.css @@ -0,0 +1,595 @@ +/* form */ + +sdoc-backdrop { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + display: flex; + justify-content: center; + align-items: center; + background: rgba(0,0,0,.5); + z-index: 99999; +} + +sdoc-modal { + display: grid; + grid-template-columns: minmax(0, 1fr); /* issue#1370 https://css-tricks.com/preventing-a-grid-blowout/ */ + grid-template-rows: min-content minmax(0, 1fr) min-content; + grid-template-areas: + "modal-header" + "modal-content" + "modal-footer"; + place-items: stretch stretch; + place-content: stretch stretch; + + background-color: var(--color-bg-contrast); + border-radius: 8px; + + width: 600px; + max-width: 90vw; + min-width: 375px; + max-height: 90vh; + + box-shadow: var(--base-elevation-modal); + overflow: hidden; + overflow-y: auto; +} + +sdoc-modal[context="confirm"] { + width: 600px; +} + +sdoc-modal[context="form"] { + width: 90vw; + height: 90vh; + max-width: 600px; + max-height: 90vh; +} + +sdoc-modal-container { + padding: calc(var(--base-rhythm)*4); +} + +sdoc-modal-header { + grid-area: modal-header; + padding-top: calc(var(--base-rhythm)*2); + padding-bottom: calc(var(--base-rhythm)*2); + padding-left: calc(var(--base-rhythm)*4); + padding-right: calc(var(--base-rhythm)*4); + font-size: 1.25rem; + font-weight: 600; +} + +.sdoc-modal-header-back-button { + position: relative; + left: calc(var(--base-rhythm) * (-1)); + display: inline-flex; + padding-right: var(--base-rhythm); +} + +.sdoc-modal-header-back-button:hover { + color: var(--color-action); +} + +sdoc-modal-content { + grid-area: modal-content; + border-top: var(--base-border); + border-bottom: var(--base-border); + /* padding-top: calc(var(--base-rhythm)*2); */ + /* padding-bottom: calc(var(--base-rhythm)*2); */ + overflow: auto; + scroll-behavior: smooth; +} + +sdoc-modal-message { + display: block; + padding-left: calc(var(--base-rhythm)*4); + padding-right: calc(var(--base-rhythm)*4); + margin-top: calc(var(--base-rhythm)*2); + margin-bottom: calc(var(--base-rhythm)*2); +} + +sdoc-modal-footer { + grid-area: modal-footer; + display: flex; + justify-content: flex-end; /* buttons to right */ + padding-top: calc(var(--base-rhythm)*2); + padding-bottom: calc(var(--base-rhythm)*2); + padding-left: calc(var(--base-rhythm)*4); + padding-right: calc(var(--base-rhythm)*3); /* buttons to right */ + column-gap: var(--base-rhythm); +} + +/* sdoc-form */ + +sdoc-form { + display: block; + position: relative; + background-color: var(--color-bg-contrast); + border-radius: 4px; + z-index: 11; + border: 1px solid var(--color-fg-accent); +} + +sdoc-form-header { + display: block; + padding-top: 0; + padding-left: calc(var(--base-rhythm)*4); + padding-right: calc(var(--base-rhythm)*4); + font-size: 1.25rem; + font-weight: 600; +} + +sdoc-form-descr { + display: block; + padding-top: 0; + padding-bottom: 0; + padding-left: calc(var(--base-rhythm)*4); + padding-right: calc(var(--base-rhythm)*4); + margin-top: calc(var(--base-rhythm)*2); + margin-bottom: calc(var(--base-rhythm)*2); +} + +sdoc-form-footer { + display: flex; + justify-content: flex-start; /* buttons to left */ + padding-top: 0; + padding-bottom: 0; + padding-left: calc(var(--base-rhythm)*3); /* buttons to left */ + padding-right: calc(var(--base-rhythm)*4); + margin-top: calc(var(--base-rhythm)*2); + margin-bottom: calc(var(--base-rhythm)*2); + column-gap: var(--base-rhythm); +} + +sdoc-form-field { + /* + Flex can not be used, it is necessary exactly "display: block" because of + the effect on the display of the contenteditable element contained inside. + Extra spaces are formed there and it breaks the Selenium end2end tests. + */ + display: block; + position: relative; + padding-top: 0; + padding-bottom: 0; + padding-left: calc(var(--base-rhythm)*4); + padding-right: calc(var(--base-rhythm)*4); + margin-top: calc(var(--base-rhythm)*4); + margin-bottom: calc(var(--base-rhythm)*4); + min-width: 200px; +} + +sdoc-form-row-main { + display: flex; + flex-direction: column; + position: relative; +} + +sdoc-form-row-aside { + display: flex; + flex-wrap: nowrap; + align-items: flex-start; + justify-content: flex-end; + position: relative; +} + +sdoc-form-row-aside [data-action-type="delete"]:hover, +sdoc-form-row-aside [data-action-type="delete"] { + color: var(--color-danger); +} + +sdoc-form-row-aside [data-action-type="action"]:hover, +sdoc-form-row-aside [data-action-type="action"] { + color: var(--color-action); +} + +sdoc-form-row-aside [data-action-type="move_up"]:hover, +sdoc-form-row-aside [data-action-type="move_up"] { + color: var(--color-green); +} + +sdoc-form-row-aside [data-action-type="move_down"]:hover, +sdoc-form-row-aside [data-action-type="move_down"] { + color: var(--color-blue); +} + +sdoc-form-row [data-action-type="add_field"]:only-child { + /* + It is expected to be a button (.action_button) + to add fields like "comment" or "link" + to the requirement edit form. + */ + justify-content: flex-start; + /* color: rgba(0,0,0,0.5) !important; */ + border: none; + top: -40%; +} + +/* + fields grid: + inside sdoc-form-grid +*/ + +sdoc-form-grid { + display: grid; + place-items: stretch stretch; + place-content: stretch stretch; + grid-template-columns: minmax(0, min-content) + minmax(0, 1fr) /* issue#1370 https://css-tricks.com/preventing-a-grid-blowout/ */ + minmax(0, min-content); + gap: + calc(var(--base-rhythm)*4) + var(--base-rhythm); + + padding: + calc(var(--base-rhythm)*4) + calc(var(--base-rhythm)*3); /* (4-1) related to column gap */ + + overflow-x: clip; /* Prevents overflow issues affecting: + - field_action::before (highlight visibility on delete) and + - autocompletable (dropdown visibility) */ + + /* content container has no padding-block */ + margin-top: calc(var(--base-rhythm)*2); + margin-bottom: calc(var(--base-rhythm)*2); +} + +sdoc-form-grid + sdoc-form-field { + padding: 0; + margin: 0; + } + +sdoc-form-grid + sdoc-form-row { + display: contents; + } + +sdoc-form-grid + sdoc-form-row-aside:first-of-type { + grid-column: 1 / 2; /* left */ + } + +sdoc-form-grid + sdoc-form-row-main { + grid-column: 2 / 3; /* center */ + } + +sdoc-form-grid + sdoc-form-row-aside:last-of-type { + grid-column: 3 / 4; /* right */ + } + +/* sdoc-form-field-group */ + +sdoc-form-field-group { + /* display: grid; + place-items: stretch stretch; + place-content: stretch stretch; */ + + display: flex; + flex-direction: row; + flex-wrap: wrap; + + padding: var(--base-rhythm); + border: var(--base-border); + border-radius: var(--base-rhythm); + + gap: calc(var(--base-rhythm)*3); /* label (2) + 1 */ + padding-top: calc(var(--base-rhythm)*3); /* related to column gap */ + padding-bottom: calc(var(--base-rhythm)*1); + + position: relative; +} + +sdoc-form-field-group::before { + /* TODO move to other label */ + content: attr(data-field-label); + position: absolute; + font-family: var(--code-font-family); + font-size: var(--font-size-xsm); + font-weight: 500; + color: var(--color-placeholder); + text-transform: uppercase; + top: calc(var(--base-rhythm)*(-1.25)); + right: var(--base-rhythm); + padding: 0 var(--base-rhythm); + background: var(--color-bg-contrast); + transition: color .2s ease; +} + +sdoc-form-field-group:focus-within::before { + color: var(--color-fg-accent); +} + +/** + * sdoc-contenteditable + */ + +sdoc-contenteditable, +sdoc-autocompletable { + display: block; + position: relative; +} + +sdoc-contenteditable[contenteditable="false"], +sdoc-autocompletable[contenteditable="false"] { + color: var(--color-fg-secondary); +} + +.autocomplete-items { + position: absolute; + border: 1px solid var(--color-action); + background-color: var(--color-bg-contrast); + top: 100%; + left: 0; + right: 0; + width: 33vw; + max-height: 60vh; + overflow-y: auto; + z-index: 999; +} + +.autocomplete-active { + /*when navigating through the items using the arrow keys:*/ + background-color: var(--color-highlight) !important; +} + +.autocompletable-result-item { + position:relative; + display:block; + padding:.25rem 0.25rem; + margin-bottom:-1px; + border:1px solid rgba(0,0,0,.125) +} + +.monospace { + font-family: var(--code-font-family); +} + +.form__requirement_title { + font-size: 1.125em; + font-weight: 700; +} + +/* field name // before */ + +/* todo: Join sdoc-contenteditable with sdoc-form-field */ + +sdoc-form-field label, +sdoc-contenteditable::before, +sdoc-autocompletable::before { + /* for contenteditable */ + content: attr(data-field-label); + /* for all */ + position: absolute; + font-family: var(--code-font-family); + font-size: var(--font-size-xsm); + font-weight: 500; + color: var(--color-placeholder); + text-transform: uppercase; + top: calc(var(--base-rhythm)*(-2.25)); + transition: color .2s ease; + + white-space: nowrap; +} + +sdoc-form-field:focus-within label, +sdoc-contenteditable:focus::before, +sdoc-autocompletable:focus::before { + color: var(--color-fg-accent); +} + +/* sdoc-form-field select */ +sdoc-form-field select { + /* A reset of styles, including removing the default dropdown arrow */ + /* appearance: none; */ + background-color: transparent; + /* border: none; */ + border: 1px solid var(--color-placeholder); + border-radius: 6px; + + padding: 0 1em 0 .5em; + margin: 0; + width: 100%; + font-family: inherit; + font-size: inherit; + cursor: inherit; + line-height: inherit; + /* Stack above custom arrow */ + z-index: 1; + /* Remove focus outline, will add on alternate element */ + outline: none; +} + +sdoc-form-field select:focus { + border: 1px solid var(--color-fg-accent); +} + +/* Remove dropdown arrow in IE10 & IE11 + @link https://www.filamentgroup.com/lab/select-css.html +*/ +sdoc-form-field select::-ms-expand { + display: none; +} + +/* placeholder // after */ + +[placeholder]:empty::after { + content: attr(placeholder); + pointer-events: none; + color: var(--color-placeholder); + /* For Firefox: */ + display: block; +} + +[placeholder]:empty:focus::after { + color: rgba(242, 100, 42,.2); +} + +sdoc-contenteditable[data-field-suffix]:not(:empty)::after, +sdoc-autocompletable[data-field-suffix]:not(:empty)::after { + content: attr(data-field-suffix); + color: var(--color-fg-accent); + margin-left: 4px; +} + +/* data-field-suffix */ + +[contenteditable=true] { + white-space: pre-wrap; + word-break: break-word; + overflow-wrap: anywhere; + outline: none; + /* Without this hack ( display: inline-block ) + Chrome generates duplicated new lines when Enter pressed. + Add style "display:inline-block;" to contenteditable, + it will not generate div, p, span automatic in chrome */ + /* https://stackoverflow.com/a/24689390/598057 */ + /* Also, the parent element should not be allowed to have a flex display. + In order to isolate the 'contenteditable, + the structure "sdoc-form-row-main > sdoc-form-field > contenteditable" + is implemented. + */ + display: inline-block; + /* This is to prevent the field from shifting when the second line appears while typing: */ + vertical-align: top; + width: 100%; +} + +[contenteditable=true][data-field-type="multiline"] { + font-family: var(--code-font-family); +} + + + +form[data-controller~="scroll_into_view"] { + /* fix on 1px border and smth unruly */ + scroll-snap-margin-top: calc(var(--base-padding) + 1px); + scroll-margin-top: calc(var(--base-padding) + 1px); +} + +/* input */ + +sdoc-form input[type="text"] { + padding: var(--base-rhythm); + font-size: var(--font-size); + border: 1px solid var(--color-border); + border-radius: 3px; + outline: transparent; + width: 100%; + transition: border-color calc(var(--transition, 0.2) * 1s) ease; +} + +sdoc-form input[type="text"]:focus { + border-color: var(--color-action); + color: var(--color-action); +} + +/* diff */ + +sdoc-form[diff] { + grid-column: 1 / -1; + + margin: 0; + background-color: transparent; + border: none; +} + +sdoc-form[diff] form { + display: flex; + gap: var(--base-rhythm); + position: relative; + padding: var(--base-rhythm); + background-color: var(--color-bg-contrast); + border: 1px solid var(--color-border); + border-radius: 4px; +} + +/* search */ + +sdoc-form[search] { + display: block; + background-color: transparent; + border: none; + border-radius: 0; +} + +sdoc-form[search] form { + display: flex; + gap: var(--base-rhythm); + position: relative; + padding: var(--base-rhythm); + background-color: var(--color-bg-contrast); + border: 1px solid var(--color-border); + border-radius: 4px; +} + +sdoc-form[search][success] { + border-bottom: 1px solid var(--color-border); +} + +sdoc-form-error { + display: block; + color: var(--color-danger); + font-size: 12px; + /* + set 'order' to be displayed after any other items, + in the context of using flex: + */ + order: 11; + /* + FOR: + components/grammar_form_element/index.jinja + "Relations_Row" + id="document__editable_grammar_relations" + */ + grid-column: 2 / -1; +} + +sdoc-form-error + sdoc-form-error { + margin-top: var(--base-rhythm); +} + +sdoc-form-error + sdoc-form-field-group { + border-color: var(--color-danger); +} + + +sdoc-form-field-group[errors]::before, /* Grammar -> label for group of fields */ +sdoc-contenteditable[errors]::before, /* inside contenteditable errors block does not affected */ +sdoc-autocompletable[errors]::before, +sdoc-form-error + sdoc-form-field > label, /* Grammar -> relation field ; File filed */ +sdoc-form-error + sdoc-form-field > sdoc-contenteditable::before, +sdoc-form-error + sdoc-form-field > sdoc-autocompletable::before { + color: var(--color-danger); +} + +.sdoc-form-error, +.sdoc-form-success, +.sdoc-form-reset { + display: block; + padding: var(--base-rhythm) calc(var(--base-rhythm)*2); + position: relative; +} + +.sdoc-form-error { + color: var(--color-danger); +} + +.sdoc-form-success { + color: var(--color-action); +} + +.sdoc-form-reset { + position: absolute; + right: 0; + bottom: 0; +} + +.sdoc-form-error, +.sdoc-form-success { + padding-right: 120px; /* == sdoc-form-reset width */ +} diff --git a/tests/fuzz/20_L1_Open_Requirements_Tool_202511/assets/html2pdf4doc.css b/tests/fuzz/20_L1_Open_Requirements_Tool_202511/assets/html2pdf4doc.css new file mode 100644 index 0000000..f7ead23 --- /dev/null +++ b/tests/fuzz/20_L1_Open_Requirements_Tool_202511/assets/html2pdf4doc.css @@ -0,0 +1,4 @@ +html2pdf-print-forced-page-break + sdoc-node-content[node-view="narrative"], +html2pdf-page + sdoc-node-content[node-view="narrative"] { + border-top: none; +} diff --git a/tests/fuzz/20_L1_Open_Requirements_Tool_202511/assets/layout.css b/tests/fuzz/20_L1_Open_Requirements_Tool_202511/assets/layout.css new file mode 100644 index 0000000..8ecd6df --- /dev/null +++ b/tests/fuzz/20_L1_Open_Requirements_Tool_202511/assets/layout.css @@ -0,0 +1,116 @@ +.layout { + height: 100vh; + width: 100%; + display: grid; + + grid-template-columns: + fit-content(var(--base-gap)) + fit-content(20%) + fit-content(20%) + minmax( 0, 1fr ) /* issue#1370 https://css-tricks.com/preventing-a-grid-blowout/ */ + fit-content(20%) + auto; + grid-template-rows: + var(--base-gap) + minmax( 0, 1fr ) /* https://github.com/w3c/csswg-drafts/issues/1777 */ + calc(var(--base-rhythm)*4); + grid-template-areas: + "nav header header header header aside" + "nav tree bar_left main bar_right aside" + "nav footer footer footer footer aside"; + + place-items: stretch stretch; + place-content: stretch stretch; + + overflow: hidden; /* Prevents scrolling before children's styles are triggered */ +} + +.layout_nav { + grid-area: nav; + position: relative; + z-index: 10; +} + +.layout_tree { + grid-area: tree; + min-width: 0; +} + +.layout_toc, +.layout_toc[data-position='left'] { + grid-area: bar_left; +} +.layout_toc[data-position='right'] { + grid-area: bar_right; +} + +.layout_footer { + grid-area: footer; +} + +.layout_header { + grid-area: header; + min-width: 0; +} + +.layout_aside { + grid-area: aside; + min-width: 0; +} + +.layout_main { + grid-area: main; + min-width: 0; +} + +/* */ + +.section-number { + margin-right: .5rem; + font-size: 0.85em; + font-weight: bold; +} + +/* messages */ + +.mars { + position: fixed; + z-index: 1111; + top: 0; + bottom: 0; + left: 0; + right: 0; + pointer-events: none; + + display: flex; + justify-content: center; + /* align-items: flex-end */ + align-items: center; + + padding: calc(var(--base-rhythm)*10); +} + +/* */ + +sdoc-toast { + pointer-events: initial; + text-align: center; + border-radius: var(--base-rhythm); + box-shadow: 0 8px 32px rgba(0,0,0,.3); + background: var(--color-bg-ui); + color: var(--color-fg-secondary-invert); +} + +sdoc-toast:not(:empty) { + padding: .75rem 2rem; +} + +sdoc-toast a, +sdoc-toast a:link, +sdoc-toast a:visited { + color: var(--color-bg-contrast); +} + +sdoc-toast a:hover { + color: var(--color-fg-accent); +} diff --git a/tests/fuzz/20_L1_Open_Requirements_Tool_202511/assets/node.css b/tests/fuzz/20_L1_Open_Requirements_Tool_202511/assets/node.css new file mode 100644 index 0000000..cf45ed3 --- /dev/null +++ b/tests/fuzz/20_L1_Open_Requirements_Tool_202511/assets/node.css @@ -0,0 +1,246 @@ +turbo-frame { + display: contents; +} + +sdoc-node { + display: block; + position: relative; +} + +/* sdoc-node[show-node-type-name] sdoc-node-content { + margin-top: calc(2 * var(--base-rhythm)); +} */ + +sdoc-node[show-node-type-name]::before { + content: attr(show-node-type-name); + position: absolute; + top: calc(1 * var(--base-rhythm)); + right: calc(4 * var(--base-rhythm)); + + font-size: 10px; + font-family: var(--code-font-family); + font-weight: 600; + line-height: 1; + text-transform: uppercase; + + padding-top: calc(0.25 * var(--base-rhythm)); + padding-bottom: calc(0.5 * var(--base-rhythm)); + padding-left: calc(0.75 * var(--base-rhythm)); + padding-right: calc(0.75 * var(--base-rhythm)); + border-radius: 3px; + border: var(--requirement-border-width, 1px) solid var(--requirement-border-color, #bfbfbf); + + color: var(--requirement-label-color); + background-color: var(--color-bg-contrast); +} + +/* sdoc-node */ + +sdoc-node { + background-color: var(--color-bg-contrast); + border-radius: 3px; + + /* padding: calc(3 * var(--base-rhythm)) calc(4 * var(--base-rhythm)); */ + padding-inline: calc(4 * var(--base-rhythm)); + padding-block: calc(3 * var(--base-rhythm)); +} + +sdoc-node[node-role="root"] { + padding-top: calc(3 * var(--base-rhythm)); +} + +sdoc-node[node-style="readonly"][node-role="requirement"] { + background: none; + background-color: transparent; + outline: none; + padding-left: 0; + padding-right: 0; + padding-bottom: 0; + margin: 0; +} + +sdoc-node[show-node-type-name][node-style="readonly"]::before { + right: 0; +} + +sdoc-node[show-node-type-name][node-view="plain"]::before { + content: none; +} + +/* editable_node */ + +sdoc-node[node-style="card"][node-role="requirement"], +[data-editable_node="on"] { + box-shadow: var(--base-elevation-0); + transition: box-shadow .5s; +} + +sdoc-node[node-style="card"][node-role="requirement"]:hover, +[data-editable_node="on"]:hover { + box-shadow: var(--base-elevation-node); + z-index: 10; +} + +/* sdoc-node[node-style="card"] */ + +sdoc-node[node-style="card"] { + background-color: var(--color-bg-contrast); + border-radius: 3px; + padding: 0; +} + +sdoc-node[node-style="card"][node-role="text"], +sdoc-node[node-style="card"][node-role="section"] { + background-color: var(--color-bg-contrast); + padding: calc(var(--base-rhythm)) calc(var(--base-rhythm)*2); +} + +[data-role='current'] sdoc-node[node-style="card"] { + background-color: var(--color-bg-contrast); +} + +[data-role='current'] [node-role="requirement"] { + +} + +[data-role='parents'] sdoc-node[node-style="card"], +[data-role='children'] sdoc-node[node-style="card"] { + background-color: var(--color-bg-secondary); + width: var(--card-width); +} + +sdoc-node[node-style="card"].highlighted { + background-color: var(--color-highlight); +} + +[data-viewtype="traceability"] sdoc-node[node-style="card"] + sdoc-node[node-style="card"] { + margin-top: var(--base-padding); +} + +[data-viewtype="deep_traceability"] sdoc-node[node-style="card"] { + /* width: var(--card-width); */ + /* flex-grow: 1; */ +} + +[data-viewtype="requirements-coverage"] sdoc-node[node-style="card"] { + width: calc(var(--card-width)*0.75); + /* width: auto; */ + font-size: .85em; + line-height: 1.4; +} + +/* nouid */ + +sdoc-node.nouid { + /* background-color: rgb(240, 220, 220); */ +} + +.nouid sdoc-node-title, +.nouid .requirement__title { + color: #502222; +} + +/* sdoc-node-controls */ + +sdoc-node-controls[data-direction~="column"], +sdoc-node-controls { + position: absolute; + + display: flex; + justify-content: flex-start; + + transition: .5s ease-out; + opacity: 0; + + /* HACK: [sdoc-node outline hack] */ + left: calc(100% + 1px); + top: -2px; + + /* default: column to right */ + bottom: 0; + right: unset; + width: calc(var(--base-rhythm)*4); /* determines the size of the buttons */ + height: unset; + + justify-content: flex-start; + flex-direction: column; +} + +sdoc-node-controls[data-direction~="column"] { + flex-direction: column; + justify-content: flex-start; + + left: 100%; + top: 0; + bottom: 0; + right: unset; + width: calc(var(--base-rhythm)*4); /* determines the size of the buttons */ + height: unset; +} + +sdoc-node-controls[data-direction~="row"] { + flex-direction: row; + justify-content: flex-end; + + top: calc(100% - 4px); + left: 0; + right: 0; + bottom: unset; + height: calc(var(--base-rhythm)*4); /* determines the size of the buttons */ + width: unset; +} + +sdoc-main-placeholder + sdoc-node-controls { + opacity: 1; +} + +sdoc-node-controls:hover, +sdoc-node:hover sdoc-node-controls { + opacity: 1; +} + +sdoc-node:hover sdoc-node-controls:hover { + opacity: 1; +} + +sdoc-menu { + display: flex; + align-items: stretch; + /* default: */ + flex-direction: column; +} + +sdoc-menu-handler { + display: flex; + align-items: stretch; + /* default: */ + flex-direction: column; +} + +sdoc-menu, /* default: */ +sdoc-menu-handler, /* default: */ +sdoc-node-controls[data-direction~="column"] sdoc-menu, +sdoc-node-controls[data-direction~="column"] sdoc-menu-handler { + flex-direction: column; +} + +sdoc-node-controls[data-direction~="row"] sdoc-menu, +sdoc-node-controls[data-direction~="row"] sdoc-menu-handler { + flex-direction: row; +} + +/* math equation numbering support */ +div.math { + position: relative; + text-align: center; + padding-right: 3em; /* reserve space for eqno */ + overflow: hidden; /* prevents scrollbars */ +} + +.math .eqno { + position: absolute; + right: 0; + top: 0; + font-size: 90%; + white-space: nowrap; +} \ No newline at end of file diff --git a/tests/fuzz/20_L1_Open_Requirements_Tool_202511/assets/node_content.css b/tests/fuzz/20_L1_Open_Requirements_Tool_202511/assets/node_content.css new file mode 100644 index 0000000..f1c4845 --- /dev/null +++ b/tests/fuzz/20_L1_Open_Requirements_Tool_202511/assets/node_content.css @@ -0,0 +1,495 @@ +:root { + --requirement-container-limit: 500px; + --requirement-border-color: rgb(230, 230, 230); /* = var(--color-border) on white without opacity */ + --requirement-border-width: 1px; + --requirement-border-radius: 4px; + --requirement-inner-borders-width: 1px; + --requirement-label-color: var(--color-fg-secondary, #808080); + --requirement-bg-dark-color: var(--color-bg-main, #F2F5F9); + --requirement-bg-light-color: var(--color-bg-contrast, #FFFFFF); +} + +/* sdoc-node-content */ + +sdoc-node-content { + display: grid; + grid-template-columns: minmax(0, 1fr); /* issue#1370 https://css-tricks.com/preventing-a-grid-blowout/ */ + place-items: stretch stretch; + place-content: stretch stretch; + gap: var(--requirement-inner-borders-width); + + position: relative; + background-color: var(--color-bg-contrast); /* affects the color of the space between cells */ + border: + var(--requirement-border-width, 1px) + solid + var(--requirement-border-color); + + border-radius: var(--requirement-border-radius); + + + min-width: 300px; + + /* and use overflow-wrap: break-word; in the field */ + max-width: 100%; + + /* + DEV: to show anchors (in the context) to the left of the node, + need to remove this: + overflow-x: auto; + */ +} + +/* Workaround for html2pdf4doc print bug: + (https://github.com/strictdoc-project/strictdoc/issues/2450#issuecomment-3299134769) + Disable background for elements that trigger header/footer overlap. + This hides the background of problematic split (not sliced) sections + to prevent them from covering header/footer areas during printing. + Real fix will be applied in html2pdf4doc in a future release. */ +@media print { + sdoc-node-content { + background: none; + background-color: transparent; + } +} + +sdoc-node-title { + display: block; + + font-size: 1.125em; + font-weight: 700; +} + +sdoc-node-title:last-child { + margin-bottom: 0; +} + +sdoc-node-title * { + /* affect H inside title, if applicable */ + margin: 0; +} + +[node-style="card"] sdoc-node-title { + font-size: 1em; +} + +/* synonym to [node-view="inline"] */ +sdoc-node-content[node-view="simple"] sdoc-node-title { + padding-left: calc(var(--base-rhythm)*2); + padding-right: calc(var(--base-rhythm)*2); + padding-top: calc(var(--base-rhythm)*1); + padding-bottom: calc(var(--base-rhythm)*1); + + word-break: break-word; + overflow-wrap: break-word; +} + +/* sdoc-node-content[node-view="simple"] sdoc-node-title, */ +sdoc-node-content[node-view="table"] sdoc-node-title, +sdoc-node-content[node-view="zebra"] sdoc-node-title { + padding-left: calc(var(--base-rhythm)*2); + padding-right: calc(var(--base-rhythm)*2); + padding-top: calc(var(--base-rhythm)*1); + padding-bottom: calc(var(--base-rhythm)*1); + + border-top-right-radius: var(--requirement-border-radius); + border-top-left-radius: var(--requirement-border-radius); + + background-color: var(--requirement-bg-dark-color); + /* border-bottom: */ + outline: 1px solid var(--requirement-border-color); + + word-break: break-word; + overflow-wrap: break-word; +} + +sdoc-node-uid { + display: block; + padding-left: calc(var(--base-rhythm)*2); + padding-right: calc(var(--base-rhythm)*2); + padding-top: calc(var(--base-rhythm)*1); + padding-bottom: calc(var(--base-rhythm)*1); + font-size: var(--font-size-sm); + font-family: var(--code-font-family); + font-weight: 700; + + text-transform: uppercase; + color: var(--requirement-label-color); + + word-break: break-word; + overflow-wrap: break-word; +} + +sdoc-node-field-label { + display: flex; + align-items:flex-start; + + padding-left: calc(var(--base-rhythm)*2); + padding-right: calc(var(--base-rhythm)*2); + padding-top: calc(var(--base-rhythm)*1.75); + padding-bottom: calc(var(--base-rhythm)*1); + + font-size: var(--font-size-sm); + font-family: var(--code-font-family); + font-weight: 500; + line-height: 1; + color: var(--requirement-label-color); + + /* @mettta and @stanislaw are commenting this out because REQUIREMENT's field names + were split apart, even though there was enough screen width + word-break: break-word; + */ + overflow-wrap: break-word; +} + +sdoc-node-field { + display: block; + position: relative; + + padding-left: calc(var(--base-rhythm)*2); + padding-right: calc(var(--base-rhythm)*2); + padding-top: calc(var(--base-rhythm)*1); + padding-bottom: calc(var(--base-rhythm)*1); + + word-break: break-word; + overflow-wrap: break-word; +} + +[data-viewtype="html2pdf"] sdoc-node-field-label { + word-break: normal; +} + +/* for relations in requirement */ +sdoc-node-field > ol:first-child, +sdoc-node-field > ul:first-child { + margin-top: 0; +} +sdoc-node-field > ol:last-child, +sdoc-node-field > ul:last-child { + margin-bottom: 0; +} + +/* node-view="plain" */ + +sdoc-node-content[node-view="plain"] { + border: 0; + display: flex; + flex-direction: column; + gap: var(--base-padding); +} + +sdoc-node-content[node-view="plain"] sdoc-node-field-label { + display: none; +} + +sdoc-node-content[node-view="plain"] sdoc-node-field { + padding: 0; +} + +/* node-view="table" */ + +sdoc-node-content[node-view="table"] { + /* grid: */ + grid-template-columns: minmax(80px, min-content) minmax(0, 1fr); /* issue#1370 https://css-tricks.com/preventing-a-grid-blowout/ */ + /* border: */ + background-color: var(--requirement-border-color); +} + +sdoc-node-content[node-view="table"] + sdoc-node-title { + grid-column: 1 / 3; + } + +sdoc-node-content[node-view="table"] + sdoc-node-field-label { + grid-column: 1 / 2; + background-color: var(--requirement-bg-dark-color); + } + +sdoc-node-content[node-view="table"] + sdoc-node-field { + grid-column: 2 / 3; + background-color: var(--requirement-bg-light-color); + } + +sdoc-node:not([node-style="card"]) { + /* Making the node a container for the requirement: */ + container: node / inline-size; + /* + HACK: [sdoc-node outline hack] + Buggy behavior for @container CSS feature: + after window resize, the 1px vertically space + appears randomly between nodes. + */ + outline: 1px solid #fff; + margin: 1px 0; +} + +/* calc(var(--card-width) + calc(var(--base-padding)*4)) */ +/* 300 + 16*4 = 364 */ +@container node (width < 400px) { + sdoc-node-content[node-view="table"] { + /* removes columns: */ + grid-template-columns: minmax(0, 1fr); /* issue#1370 https://css-tricks.com/preventing-a-grid-blowout/ */ + /* removes inner border: */ + background-color: var(--requirement-bg-light-color); + } + + sdoc-node-content[node-view="table"] + sdoc-node-title, + sdoc-node-content[node-view="table"] + sdoc-node-field-label, + sdoc-node-content[node-view="table"] + sdoc-node-field { + /* removes columns: */ + grid-column: unset; + } +} + +@supports not (container-type: inline-size) { + /* TODO test 888px */ + @media (max-width: 888px) { + /* Do the same as if there was a container support */ + + sdoc-node-content[node-view="table"] { + /* removes columns: */ + grid-template-columns: minmax(0, 1fr); /* issue#1370 https://css-tricks.com/preventing-a-grid-blowout/ */ + /* removes inner border: */ + background-color: var(--requirement-bg-light-color); + } + + sdoc-node-content[node-view="table"] + sdoc-node-title, + sdoc-node-content[node-view="table"] + sdoc-node-field-label, + sdoc-node-content[node-view="table"] + sdoc-node-field { + /* removes columns: */ + grid-column: unset; + } + } +} + +/* node-view="zebra" */ + +sdoc-node-content[node-view="zebra"] { + grid-template-columns: minmax(0, 1fr); /* issue#1370 https://css-tricks.com/preventing-a-grid-blowout/ */ +} + +sdoc-node-content[node-view="zebra"] + sdoc-node-field-label { + background-color: var(--requirement-bg-dark-color); + } + +sdoc-node-content[node-view="zebra"] + sdoc-node-field { + background-color: var(--requirement-bg-light-color); + } + +/* node-view="simple" */ +/* synonym to "inline" */ + +sdoc-node-content[node-view="simple"] { + grid-template-columns: minmax(0, 1fr); /* issue#1370 https://css-tricks.com/preventing-a-grid-blowout/ */ +} + +sdoc-node-content[node-view="simple"] +sdoc-node-field-label { + background-color: #fff; + padding-bottom: 0; +} + +/* node-view="narrative" */ + +sdoc-node-content[node-view="narrative"] { + display: flex; + flex-direction: column; + padding-top: var(--base-rhythm); + border-radius: 0; + border-left: none; + border-right: none; + border-bottom: none; +} + +sdoc-node-content[node-view="narrative"] sdoc-node-title { + border: none; + outline: none; + background: none; + background-color: transparent; + padding: 0; + margin-bottom: var(--base-padding); + grid-column: 1 / -1; +} + +sdoc-node-content[node-view="narrative"] sdoc-node-field { + padding: 0; +} + +sdoc-node-content[node-view="narrative"] sdoc-node-field-label { + opacity: 0.8; + font-size: var(--font-size-sm); + line-height: calc(var(--font-size-sm) * 1.5); + padding: 0; +} + +sdoc-node-content[node-view="narrative"] .node_fields_group-primary { + display: flex; + flex-direction: column; + padding-top: var(--base-padding); +} + +sdoc-node-content[node-view="narrative"] .node_fields_group-primary sdoc-node-field { + margin-bottom: var(--base-padding); + color: var(--color-fg-contrast); +} + +sdoc-node-content[node-view="narrative"] .node_fields_group-primary sdoc-node-field:last-child { + margin-bottom: 0; +} + +sdoc-node-content[node-view="narrative"] .node_fields_group-secondary { + display: grid; + grid-template-columns: max-content 1fr; + gap: calc(.5 * var(--base-rhythm)); +} + +sdoc-node-content[node-view="narrative"] .node_fields_group-secondary sdoc-node-field { + font-size: var(--font-size-sm); + line-height: calc(var(--font-size-sm) * 1.5); + opacity: 0.6; +} + +sdoc-node-content[node-view="narrative"] .requirement__parent-uid, +sdoc-node-content[node-view="narrative"] .requirement__child-uid { + /* + affects UID in links; + make them lighter: + */ + font-weight: normal; +} + +/* section */ + +sdoc-section, +sdoc-section-title { + display: block; + margin: 0; +} + +sdoc-section-title { + font-weight: 700; +} + +sdoc-section-title { + margin-bottom: var(--base-padding); +} + +sdoc-section-title:last-child { + margin-bottom: 0; +} + +sdoc-section-title * { + margin: 0; + + /* This rule, when the element starts the page when printed, + creates illegal margins, + which breaks the rhythm of HTML2PDF4DOC + and generates blank pages: + */ + /* display: inline; */ +} + +/* TEXT node */ + +sdoc-text, +sdoc-section-text { + display: block; + margin: 0; +} + +[node-style="card"] sdoc-text { + padding: var(--base-padding); +} + +sdoc-section-text { + margin-top: var(--base-padding); +} + +sdoc-section-text:first-child { + margin-top: 0; +} + +/* meta */ + +sdoc-meta { + display: grid; + grid-template-columns: minmax(min-content, max-content) minmax(min-content, 1fr); + place-items: stretch stretch; + place-content: stretch stretch; + position: relative; + border: + var(--requirement-border-width, 1px) + solid + var(--requirement-border-color); + border-radius: var(--requirement-border-radius); + + font-size: var(--font-size-sm); + line-height: 24px; /* to keep the copy button from expanding out of the content line */ + /* margin: var(--base-padding) 0; */ + margin: 0; + padding: calc(var(--base-rhythm)*0.5); + row-gap: 2px; + + width: fit-content; + max-width: 100%; + overflow-x: auto; +} + +sdoc-meta-section, +sdoc-meta-label, +sdoc-meta-field { + display: flex; + align-items: flex-start; + padding: calc(var(--base-rhythm)*0.25) var(--base-rhythm); + background-color: var(--color-bg-contrast); +} + +sdoc-meta-label { + grid-column: 1 / 2; + font-family: var(--code-font-family); + font-weight: 700; + text-transform: uppercase; + color: var(--requirement-label-color); + background-color: var(--color-bg-secondary); +} + +sdoc-meta-field { + grid-column: 2 / 3; +} + +sdoc-meta-section { + grid-column: 1 / -1; +} + +/* PDF */ +/* There is no sdoc-node wrapper, so such neighbours and nesting is possible. */ +sdoc-text + sdoc-text, +sdoc-text + sdoc-section, +sdoc-text + sdoc-section-title, +sdoc-section + sdoc-text, +sdoc-section + sdoc-section, +sdoc-section + sdoc-section sdoc-section-title { + margin-top: calc(var(--base-rhythm)*4); +} + +/* requirement type tag */ + +.requirement__type-tag { + /* font-size: var(--font-size-sm); + font-family: var(--code-font-family); + font-weight: 500; + line-height: 1; + text-transform: uppercase; */ + color: var(--requirement-label-color); + white-space: nowrap; +} diff --git a/tests/fuzz/20_L1_Open_Requirements_Tool_202511/assets/pan_with_space.js b/tests/fuzz/20_L1_Open_Requirements_Tool_202511/assets/pan_with_space.js new file mode 100644 index 0000000..2a04d2d --- /dev/null +++ b/tests/fuzz/20_L1_Open_Requirements_Tool_202511/assets/pan_with_space.js @@ -0,0 +1,151 @@ +// This code in this file is a simplified version of the StackOverflow answer +// taken from: https://stackoverflow.com/a/33948409/598057. + +const MOUSEMOVE_SPEED_FACTOR = 1; +const KEYDOWN_SPEED_FACTOR = 20; +const PWS_SELECTOR = "[js-pan_with_space]"; + +function getPanElement() { + const element = document.querySelector(PWS_SELECTOR); + return element; +} + +window.addEventListener('load', function () { + var state = { + spacePressed: false, + isDown: false, + startX: 0, + startY: 0 + } + + const element = getPanElement(); + if (element) { + console.assert(!!element, "Expected a valid element."); + + document.addEventListener("keydown", function (e) { + if (e.key === ' ' || e.key === 'Spacebar') { + // ' ' is standard, 'Spacebar' was used by IE9 and Firefox < 37 + + e.preventDefault(); + e.stopPropagation(); + state.spacePressed = true; + element.style.cursor = 'move'; + element.style.scrollBehavior = 'auto'; + return; + } + + var moveFactor = KEYDOWN_SPEED_FACTOR; + if (e.altKey) { + moveFactor = KEYDOWN_SPEED_FACTOR * 10; + } + + if (e.key === 'ArrowDown') { + e.preventDefault(); + e.stopPropagation(); + element.scrollTop = element.scrollTop + moveFactor; + } + else if (e.key === 'ArrowUp') { + e.preventDefault(); + e.stopPropagation(); + element.scrollTop = element.scrollTop - moveFactor; + } + else if (e.key === 'ArrowLeft') { + e.preventDefault(); + e.stopPropagation(); + element.scrollLeft = element.scrollLeft - moveFactor; + } + else if (e.key === 'ArrowRight') { + e.preventDefault(); + e.stopPropagation(); + element.scrollLeft = element.scrollLeft + moveFactor; + } + }) + + document.addEventListener("keyup", function (e) { + if (e.key === ' ' || e.key === 'Spacebar') { + // ' ' is standard, 'Spacebar' was used by IE9 and Firefox < 37 + e.preventDefault(); + e.stopPropagation(); + state.spacePressed = false; + element.style.cursor = 'default'; + element.style.scrollBehavior = ''; + } + }); + + element.addEventListener("mousedown", function (e) { + if (!state.spacePressed) { + return; + } + + // Tell the browser we're handling this event. + e.preventDefault(); + e.stopPropagation(); + + // Calc the starting mouse X,Y for the drag. + state.startX = parseInt(e.clientX); + state.startY = parseInt(e.clientY); + + var mouseX = parseInt(e.clientX); + var mouseY = parseInt(e.clientY); + + state.isDown = true; + }); + + element.addEventListener("mouseup", function (e) { + if (!state.spacePressed) { + return; + } + + e.preventDefault(); + e.stopPropagation(); + + state.isDown = false; + }); + + element.addEventListener("mousemove", function (e) { + if (!state.isDown) { + return; + } + + if (!state.spacePressed) { + return; + } + + e.preventDefault(); + e.stopPropagation(); + + var mouseX = parseInt(e.clientX); + var mouseY = parseInt(e.clientY); + + var dx = mouseX - state.startX; + var dy = mouseY - state.startY; + + state.startX = mouseX; + state.startY = mouseY; + + var speedupFactor = MOUSEMOVE_SPEED_FACTOR; + element.scrollTo( + element.scrollLeft - dx * speedupFactor, element.scrollTop - dy * speedupFactor + ); + }); + + element.addEventListener("mouseleave", function (e) { + // Tell the browser we're handling this event. + e.preventDefault(); + e.stopPropagation(); + + state.isDown = false; + }); + } +}); + +// When a window is loaded, scroll to the central column of the DTR tree. +// The central column is the one where the current document's requirements are +// listed top-to-bottom. +document.addEventListener("DOMContentLoaded", function(event) { + const element = getPanElement(); + if (element) { + const firstNode = element.querySelector('.content_item[data-role="current"]'); + firstNode && firstNode.scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'center' }); + } +}); diff --git a/tests/fuzz/20_L1_Open_Requirements_Tool_202511/assets/project_map.js b/tests/fuzz/20_L1_Open_Requirements_Tool_202511/assets/project_map.js new file mode 100644 index 0000000..b6f8946 --- /dev/null +++ b/tests/fuzz/20_L1_Open_Requirements_Tool_202511/assets/project_map.js @@ -0,0 +1,1851 @@ +// map of the project for the stable_uri forwarder +const projectMap = { + "strictdoc/docs/strictdoc_01_user_guide.html": [ + + + {"MID":"c2d4542d5f1741c88dfcb4f68ad7dcbd","UID":"SDOC_UG","_LINK":"SDOC_UG" }, + + {"MID":"75a595ff028741d0bf9067bba8fb787a","_LINK":"1-Introduction" }, + + {"MID":"27023bf63c414fd998437961c77a0e2e","_LINK":"27023bf63c414fd998437961c77a0e2e" }, + + {"MID":"bcd0cf7c13cf4cf2ab176455aedc0c90","UID":"SDOC_UG_CONTACT","_LINK":"SDOC_UG_CONTACT" }, + + {"MID":"3ae6491057674be1abf057b7e0d9bf21","_LINK":"3ae6491057674be1abf057b7e0d9bf21" }, + + {"MID":"8e4ede8cdc70402b91a59026d31a7020","_LINK":"1.1.1-StrictDoc-office-hours" }, + + {"MID":"c46def916b6f4df692c95f47414feebb","_LINK":"c46def916b6f4df692c95f47414feebb" }, + + {"MID":"e0a00ca3085f444187050ce9576aa31b","_LINK":"2-Examples" }, + + {"MID":"2122186075e8455181178b01331101fa","UID":"SDOC_UG_HELLO_WORLD","_LINK":"SDOC_UG_HELLO_WORLD" }, + + {"MID":"ffbdb80fb21d48adbaeffb782d7994a2","_LINK":"ffbdb80fb21d48adbaeffb782d7994a2" }, + + {"MID":"83369fc77d584407b530b179a449479b","_LINK":"2.2-StrictDoc-Examples-repository" }, + + {"MID":"d6bde8e4055b42138d216b48692c8f26","_LINK":"d6bde8e4055b42138d216b48692c8f26" }, + + {"MID":"0a736f05ab154b2599988b63201d4f10","_LINK":"2.3-StrictDoc-Templates-repository" }, + + {"MID":"47c2088447d7452ba28962ee220ba63a","_LINK":"47c2088447d7452ba28962ee220ba63a" }, + + {"MID":"2b2e39c48cc740e5b8452d76881f667d","_LINK":"2.4-Other-examples" }, + + {"MID":"2eaa8bfe5a354fcbb704ea7fd94f3a0a","_LINK":"2eaa8bfe5a354fcbb704ea7fd94f3a0a" }, + + {"MID":"821b818006a14ed7aeb025d9ac8fe8b6","UID":"SDOC_UG_GETTING_STARTED","_LINK":"SDOC_UG_GETTING_STARTED" }, + + {"MID":"4e9f094a7b2949edb23c817c5e2276e4","_LINK":"3.1-Requirements" }, + + {"MID":"e7e4c2982d8e49deaa9b7413a09a4efa","_LINK":"e7e4c2982d8e49deaa9b7413a09a4efa" }, + + {"MID":"914da2bf10364a6fbd9fc7f48b96c6e5","_LINK":"3.2-Installing-StrictDoc-as-a-Pip-package-recommended-way" }, + + {"MID":"872a60c673854cfd909787f2379d88c7","_LINK":"872a60c673854cfd909787f2379d88c7" }, + + {"MID":"b3e2c50e2d7e46acbce7ea0c646776ca","_LINK":"3.3-Installing-nightly-StrictDoc-as-a-Pip-package" }, + + {"MID":"867f22a2767a434586d1ee4f7ee161fb","_LINK":"867f22a2767a434586d1ee4f7ee161fb" }, + + {"MID":"9e95ac9422f44aae92c0615176153815","_LINK":"3.4-Installing-StrictDoc-into-a-Docker-container" }, + + {"MID":"32e04944c2a14ead93248ff1ea37a68d","_LINK":"32e04944c2a14ead93248ff1ea37a68d" }, + + {"MID":"e441594df15841d38f717194b722f3de","_LINK":"3.5-Installing-StrictDoc-as-a-Snap-package-not-maintained" }, + + {"MID":"2adfe1bc6be542c3885ca4ea80b8d6e1","_LINK":"2adfe1bc6be542c3885ca4ea80b8d6e1" }, + + {"MID":"4af1193170a7416d81a799344dfc8ed6","_LINK":"3.6-Installing-StrictDoc-with-Nix" }, + + {"MID":"2045b5e37fc34a83b7457c43d816bf2c","_LINK":"2045b5e37fc34a83b7457c43d816bf2c" }, + + {"MID":"6cc72bf1a2a94999954e148a6bef374c","_LINK":"4-Running-StrictDoc" }, + + {"MID":"1685e4d63d9746b784b4a55f17b0de30","UID":"SECTION-UG-Static-HTML-export","_LINK":"SECTION-UG-Static-HTML-export" }, + + {"MID":"c092847abfa04295b63f943c951f24c8","_LINK":"c092847abfa04295b63f943c951f24c8" }, + + {"MID":"1a059d3ae7da45f8b53f3d74f3580980","UID":"SECTION-UG-Web-server","_LINK":"SECTION-UG-Web-server" }, + + {"MID":"1fc9f6f4f84d47439bf6aa3404227ca4","_LINK":"1fc9f6f4f84d47439bf6aa3404227ca4" }, + + {"MID":"9f7fcbdd8e564af8986349deb09745a5","_LINK":"4.3-Security-considerations" }, + + {"MID":"4fa89608f70e4a84a0e4b2930da28bbe","_LINK":"4fa89608f70e4a84a0e4b2930da28bbe" }, + + {"MID":"6d489d82b2e24bfeb2919be24262468f","UID":"SDOC_UG_IDE_SUPPORT","_LINK":"SDOC_UG_IDE_SUPPORT" }, + + {"MID":"aad57726976f4882982458de90d168c7","_LINK":"aad57726976f4882982458de90d168c7" }, + + {"MID":"24fb3436fa8c4b36929183eaac269376","UID":"SECTION-UG-SDoc-syntax","_LINK":"SECTION-UG-SDoc-syntax" }, + + {"MID":"556a4f61a4884f3088a743a0717f397f","_LINK":"556a4f61a4884f3088a743a0717f397f" }, + + {"MID":"43c9624d3c97433a97f8e8f39e2dd784","_LINK":"6.1-Document-structure" }, + + {"MID":"5419c0c9bfcc4922ac5aa79ddb333188","_LINK":"5419c0c9bfcc4922ac5aa79ddb333188" }, + + {"MID":"b4ceeb12bdf1448cb97b7974d4980189","UID":"SECTION-UG-DOCUMENT-ELEMENTS","_LINK":"SECTION-UG-DOCUMENT-ELEMENTS" }, + + {"MID":"db088d5977934056a851a24988e9266c","UID":"SECTION-UG-Document","_LINK":"SECTION-UG-Document" }, + + {"MID":"4b5c6f6fd70e44be8e5be10a38427e77","_LINK":"4b5c6f6fd70e44be8e5be10a38427e77" }, + + {"MID":"c75dc4ddd0c349e489817076877394a7","UID":"DOCUMENT_FIELD_OPTIONS","_LINK":"DOCUMENT_FIELD_OPTIONS" }, + + {"MID":"45f3cb9a3da54852a83670af72a4d21b","_LINK":"45f3cb9a3da54852a83670af72a4d21b" }, + + {"MID":"0f566e0d07bf4194adabc4fba2fdf919","_LINK":"6.2.1.1.1-ENABLE_MID" }, + + {"MID":"196b9e9d857548bebecc698903a593de","_LINK":"196b9e9d857548bebecc698903a593de" }, + + {"MID":"8cef59608d3942c88dc554f5531e2d8a","_LINK":"6.2.1.1.2-MARKUP" }, + + {"MID":"19f65eabcd1c4f28a0091133979b5b6e","_LINK":"19f65eabcd1c4f28a0091133979b5b6e" }, + + {"MID":"48c614864015426b8cb98d423201ad43","_LINK":"6.2.1.1.3-AUTO_LEVELS" }, + + {"MID":"915b35a9193348108e60968f9b60c1d0","_LINK":"915b35a9193348108e60968f9b60c1d0" }, + + {"MID":"8cde71c0f4d448c2a8939f9a81af7ac7","UID":"SECTION-UG-VIEW_STYLE","_LINK":"SECTION-UG-VIEW_STYLE" }, + + {"MID":"538a721c42824ad6b94a2f54780887ea","_LINK":"538a721c42824ad6b94a2f54780887ea" }, + + {"MID":"bdf01de0fc414b70a6a07118396b23b0","_LINK":"6.2.1.1.5-NODE_IN_TOC" }, + + {"MID":"e9e7522f3a094f5ea809f7bae93384d7","_LINK":"e9e7522f3a094f5ea809f7bae93384d7" }, + + {"MID":"726b6a1a2f854e82a7670cb8480b887e","_LINK":"6.2.1.2-Additional-Metadata" }, + + {"MID":"f86d45b7ad1a48ec919b43a695ca9126","_LINK":"f86d45b7ad1a48ec919b43a695ca9126" }, + + {"MID":"93085eee6a214a0a85aa94472f0a0a6c","UID":"SECTION-UG-Leaf-nodes","_LINK":"SECTION-UG-Leaf-nodes" }, + + {"MID":"23cbc84e92294aa0aeb0bb279d914480","_LINK":"6.2.2.1-TEXT" }, + + {"MID":"4474709783b74b1ca31fa754c712344e","_LINK":"4474709783b74b1ca31fa754c712344e" }, + + {"MID":"6426b56116ba428fbba395c79dc2eacc","_LINK":"6.2.2.2-REQUIREMENT" }, + + {"MID":"f221cd0c72f846b385ec2f0a9ddd15d5","_LINK":"f221cd0c72f846b385ec2f0a9ddd15d5" }, + + {"MID":"a1ad4861e5bc442baf38383b29c901a1","UID":"UG_COMPOSITE_NODE","_LINK":"UG_COMPOSITE_NODE" }, + + {"MID":"ce69fe89236a4bcfa104a640def4b64a","_LINK":"ce69fe89236a4bcfa104a640def4b64a" }, + + {"MID":"c8c15acf52a24c99922bdae6711f5120","UID":"ELEMENT_SECTION","_LINK":"ELEMENT_SECTION" }, + + {"MID":"14e5fea31b1c41bf8c743f18e51053e0","_LINK":"14e5fea31b1c41bf8c743f18e51053e0" }, + + {"MID":"9a571c9363ee4dc4a3cdc56abd2c57a7","_LINK":"6.2.3.1.1-Nesting-sections" }, + + {"MID":"fa700a9b975f493ba7ec2fd0a7b44bbe","_LINK":"fa700a9b975f493ba7ec2fd0a7b44bbe" }, + + {"MID":"0f52cfb294af480e806f0434d0a07757","_LINK":"6.2.3.1.2-Section-grammar-definition" }, + + {"MID":"7f2967ea1a41426ba15c0bdde2664920","_LINK":"7f2967ea1a41426ba15c0bdde2664920" }, + + {"MID":"324938c23b864864b4e8ff6a67b589a5","_LINK":"6.2.4-Reserved-fields" }, + + {"MID":"4d69fa95bd974d859973dbb34679a7c4","_LINK":"4d69fa95bd974d859973dbb34679a7c4" }, + + {"MID":"79b128720cd74a548d291e223b61c4bc","UID":"SECTION-UG-Machine-identifiers-MID","_LINK":"SECTION-UG-Machine-identifiers-MID" }, + + {"MID":"7fe2832d60014259a8d67a09f1db102a","_LINK":"7fe2832d60014259a8d67a09f1db102a" }, + + {"MID":"4afebe421fe341b8b609b1c24f47b708","_LINK":"6.2.4.1.1-Unique-vs-machine-identifiers-MID-vs-UID" }, + + {"MID":"a5a3d62d90f646aa8fac43795aa8fcbb","_LINK":"a5a3d62d90f646aa8fac43795aa8fcbb" }, + + {"MID":"6a7c6a10b0fc4db7a5b32abedc3bb31c","_LINK":"6.2.4.2-UID" }, + + {"MID":"2c3b050a2cd0444fb8948bb6adcb7085","_LINK":"2c3b050a2cd0444fb8948bb6adcb7085" }, + + {"MID":"f01eced244e842e28c0729d8458ac935","UID":"UG_NODE_WITHOUT_A_LEVEL","_LINK":"UG_NODE_WITHOUT_A_LEVEL" }, + + {"MID":"094425a33e8941d9a0016b4ed714c664","_LINK":"094425a33e8941d9a0016b4ed714c664" }, + + {"MID":"05c39a0bc4d9482ca4199c84b1981948","_LINK":"6.2.4.4-STATUS" }, + + {"MID":"448aa949724e4361a5b2f317c40c7492","_LINK":"6.2.4.5-TAGS" }, + + {"MID":"d7c2d72f849b413cb312d44813d25d5a","_LINK":"d7c2d72f849b413cb312d44813d25d5a" }, + + {"MID":"62480ac9dbdf4e4fb5c533abefa19997","_LINK":"6.2.4.6-TITLE" }, + + {"MID":"f5f9008f417e40aa8acd9da18ccd89d1","_LINK":"f5f9008f417e40aa8acd9da18ccd89d1" }, + + {"MID":"fb3b2d0bbf4c43abaa09b29b106346d4","_LINK":"6.2.4.7-STATEMENT" }, + + {"MID":"cea069906c2e4329bea0d97744984ba8","_LINK":"cea069906c2e4329bea0d97744984ba8" }, + + {"MID":"bf35d37584b34aabb5bf3b2e144beecb","_LINK":"6.2.4.8-RATIONALE" }, + + {"MID":"7e63bdbce418401da8eb5aa607fd8fc2","_LINK":"7e63bdbce418401da8eb5aa607fd8fc2" }, + + {"MID":"165b33aa61474a1db0ca0bd1f1279ba4","_LINK":"6.2.4.9-COMMENT" }, + + {"MID":"3928acde165b4505943154199ecd739a","_LINK":"3928acde165b4505943154199ecd739a" }, + + {"MID":"f10fd08172054423be7c00fcd16e61e9","UID":"SDOC_UG_REQUIREMENT_RELATIONS","_LINK":"SDOC_UG_REQUIREMENT_RELATIONS" }, + + {"MID":"c0c9c15d57e742ed80f12b8895f01de0","_LINK":"c0c9c15d57e742ed80f12b8895f01de0" }, + + {"MID":"c357b57be45a4871a6dfb877483de702","_LINK":"6.2.4.10.1-Relation-roles" }, + + {"MID":"a54f41576b2d40df92041862bef13600","_LINK":"a54f41576b2d40df92041862bef13600" }, + + {"MID":"57df0956302f4c728f6b35606320b660","UID":"UG_COMPOSABLE_DOCUMENTS","_LINK":"UG_COMPOSABLE_DOCUMENTS" }, + + {"MID":"6ed881a7cb8949cab3d1a1836846b716","_LINK":"6ed881a7cb8949cab3d1a1836846b716" }, + + {"MID":"952dd638d61c45ab950f271d53e56b2f","_LINK":"952dd638d61c45ab950f271d53e56b2f" }, + + {"MID":"0d29071c7f924ecdb11fc32da4ecc5e3","UID":"SECTION-UG-DOCUMENT-GRAMMAR","_LINK":"SECTION-UG-DOCUMENT-GRAMMAR" }, + + {"MID":"4c2244d6affb4653844f730e1de88266","_LINK":"4c2244d6affb4653844f730e1de88266" }, + + {"MID":"269600f411374fce975c3d7110ae74b6","_LINK":"6.3.1-Supported-field-types" }, + + {"MID":"a437c871dc3d46dba80460d5c8c22cf6","_LINK":"a437c871dc3d46dba80460d5c8c22cf6" }, + + {"MID":"3b9af7a4f6444664aa9e76343b477236","_LINK":"6.3.2-Grammar-element-properties" }, + + {"MID":"e1e5bda619f549188d8d7ae158aeaa44","_LINK":"6.3.2.1-IS_COMPOSITE" }, + + {"MID":"5396d4571b4c4204b093a44980a489ed","_LINK":"5396d4571b4c4204b093a44980a489ed" }, + + {"MID":"f2c8342dd6f5432c95ef6f80cf5e87ba","_LINK":"6.3.2.2-PREFIX" }, + + {"MID":"19d4dcbf0cd24f84996bcddb778e348c","_LINK":"19d4dcbf0cd24f84996bcddb778e348c" }, + + {"MID":"61df1b83c27e43909ad5640cf0d78962","UID":"SECTION-UG-VIEW_STYLE-2","_LINK":"SECTION-UG-VIEW_STYLE-2" }, + + {"MID":"4678706b4c864f459d1b478cd703fcc1","_LINK":"4678706b4c864f459d1b478cd703fcc1" }, + + {"MID":"04828fd96e2e42f3810af276d06859c1","UID":"SDOC_UG_GRAMMAR_RELATIONS","_LINK":"SDOC_UG_GRAMMAR_RELATIONS" }, + + {"MID":"012b0350547a48fc9ab3fbd6d9b5d502","_LINK":"012b0350547a48fc9ab3fbd6d9b5d502" }, + + {"MID":"16ce47f32874421986a5a843c5aeb2bf","UID":"SECTION-UG-Relation-roles","_LINK":"SECTION-UG-Relation-roles" }, + + {"MID":"01b8aa59963b42518d98c4e6b5e3259b","_LINK":"01b8aa59963b42518d98c4e6b5e3259b" }, + + {"MID":"ed4e4d30b3eb46ca976295aa27ede9e3","UID":"SDOC_UG_GRAMMAR_RELATIONS_PARENT_VS_CHILD","_LINK":"SDOC_UG_GRAMMAR_RELATIONS_PARENT_VS_CHILD" }, + + {"MID":"b119e614781044eab641e20eaec872e5","_LINK":"b119e614781044eab641e20eaec872e5" }, + + {"MID":"cd7d9c9b0efe443895d1b6eb44a3799b","_LINK":"6.3.4-IMPORT_FROM_FILE-Importing-grammar-from-file" }, + + {"MID":"df106206da0e446e87021fbf711fdb65","_LINK":"df106206da0e446e87021fbf711fdb65" }, + + {"MID":"d4292ad751ca407b8147e7204a934c4e","UID":"SDOC_UG_LINKS_AND_ANCHORS","_LINK":"SDOC_UG_LINKS_AND_ANCHORS" }, + + {"MID":"6ce1010a003146ec9e3e8d77ee89a082","_LINK":"6ce1010a003146ec9e3e8d77ee89a082" }, + + {"MID":"ac7ccc30e5bf489cb4929302c8ec21cf","_LINK":"6.4.1-Links" }, + + {"MID":"8b07c82f462348a58c3ab81be4db753d","_LINK":"8b07c82f462348a58c3ab81be4db753d" }, + + {"MID":"a1d9bb6b7df84b5eb6bf355542a5288a","_LINK":"6.4.2-Anchors" }, + + {"MID":"294d6a0e96424dc8a93d6502c43e8501","_LINK":"294d6a0e96424dc8a93d6502c43e8501" }, + + {"MID":"26b105a45eda470497148a7903ca731b","_LINK":"6.4.2.1-Anchor-example" }, + + {"MID":"ba71ef8f24b342178b28e87b7609b8d7","_LINK":"ba71ef8f24b342178b28e87b7609b8d7" }, + + {"MID":"ec9dcd4ad28a4852bc47723e667006df","_LINK":"6.5-Syntax-rules" }, + + {"MID":"926894ac8efc4e99943741691b3d4efe","UID":"SECTION-UG-Strict-rule-1","_LINK":"SECTION-UG-Strict-rule-1" }, + + {"MID":"2ebd1f2d3c8746a383d71232756c3bd3","_LINK":"2ebd1f2d3c8746a383d71232756c3bd3" }, + + {"MID":"1e465597f02842de92852be3a57f84cb","UID":"SECTION-UG-Strict-rule-2","_LINK":"SECTION-UG-Strict-rule-2" }, + + {"MID":"686eb01cf7db44c4bd1edd72ccb02ed0","_LINK":"686eb01cf7db44c4bd1edd72ccb02ed0" }, + + {"MID":"c741dd299b044fc9b72510256d48589e","UID":"SECTION-UG-Strict-rule-3","_LINK":"SECTION-UG-Strict-rule-3" }, + + {"MID":"c01ab5711463497498c91a968179d63c","_LINK":"c01ab5711463497498c91a968179d63c" }, + + {"MID":"31eb939aa5684f74b86be93ea7f14f7b","_LINK":"7-Markup" }, + + {"MID":"a3d675f38a1341548722239e994e55e1","_LINK":"a3d675f38a1341548722239e994e55e1" }, + + {"MID":"64d48546374e4bfd881733b0174fbebe","_LINK":"7.1-Images" }, + + {"MID":"e87ffdf6195c481cb39bc3d1ec29eca6","_LINK":"e87ffdf6195c481cb39bc3d1ec29eca6" }, + + {"MID":"83066d011cef4e8694aac5fbe90a357e","_LINK":"7.2-Mathjax-support" }, + + {"MID":"abdf406fe10847929dd7cb12a030456f","_LINK":"abdf406fe10847929dd7cb12a030456f" }, + + {"MID":"3e761d05262f44d29f48476c89ce3ca2","_LINK":"8-Export-formats" }, + + {"MID":"110260c57dbb47da87662686bc73d6e7","_LINK":"8.1-HTML-documentation-tree-by-StrictDoc" }, + + {"MID":"a6bfffddf89045a1a7ecf55c9681fe14","_LINK":"a6bfffddf89045a1a7ecf55c9681fe14" }, + + {"MID":"dcc6a2bc11424cf2b114b25a7f65530d","UID":"SECTION-UG-Inbound-Links","_LINK":"SECTION-UG-Inbound-Links" }, + + {"MID":"742831d32ca54791b53c1dbbbd070b55","_LINK":"742831d32ca54791b53c1dbbbd070b55" }, + + {"MID":"1b41e7c036cf4e3282fa3d4e687bf95d","_LINK":"8.1.2-Standalone-HTML-pages" }, + + {"MID":"5674bfd50f31420baf56625d79d1ebff","_LINK":"5674bfd50f31420baf56625d79d1ebff" }, + + {"MID":"f6091bab49714c1fa72c2fbca12c970b","UID":"SECTION-UG-HTML-export-via-Sphinx","_LINK":"SECTION-UG-HTML-export-via-Sphinx" }, + + {"MID":"bf8160f43703418db306785c48463123","_LINK":"bf8160f43703418db306785c48463123" }, + + {"MID":"c95a2b4c4fbd40fcbd92fe7f24c2cd91","UID":"SECTION-UG-PDF-export-via-Sphinx-LaTeX","_LINK":"SECTION-UG-PDF-export-via-Sphinx-LaTeX" }, + + {"MID":"58595abe6ff148afbb45e2b6adc25110","_LINK":"58595abe6ff148afbb45e2b6adc25110" }, + + {"MID":"e33a21323e2b48d692ed85cc56fd06e6","_LINK":"8.4-JSON" }, + + {"MID":"4d956edcd00846a98f59778dd7de9257","_LINK":"4d956edcd00846a98f59778dd7de9257" }, + + {"MID":"40050f96b9a64f57bbb0ad01f4d37264","_LINK":"9-Manage-project-tree" }, + + {"MID":"7f4b56d2a1104f539489bf7ddcdb1469","UID":"SECTION-UG-Automatic-assignment-of-requirements-UID","_LINK":"SECTION-UG-Automatic-assignment-of-requirements-UID" }, + + {"MID":"ed367ed0bc284d4d9318bf6c844c3e3e","_LINK":"ed367ed0bc284d4d9318bf6c844c3e3e" }, + + {"MID":"fa1cc31842144440a8ca310c7c0dedb2","UID":"SECTION-TRACEABILITY-REQS-TO-SOURCE-CODE","_LINK":"SECTION-TRACEABILITY-REQS-TO-SOURCE-CODE" }, + + {"MID":"4398b17f61ad4dd290e72d7888597f48","_LINK":"4398b17f61ad4dd290e72d7888597f48" }, + + {"MID":"845ff817ad9d483796b9dc6016bfc3c7","UID":"SECTION-UG-Language-aware-parsing-of-source-code","_LINK":"SECTION-UG-Language-aware-parsing-of-source-code" }, + + {"MID":"07bfe045a4934dd29d97fdb8ac567b7c","_LINK":"07bfe045a4934dd29d97fdb8ac567b7c" }, + + {"MID":"2cde4b7a4d42450c84c93989aeb4d53d","_LINK":"10.2-Linking-source-code-to-requirements" }, + + {"MID":"c787c538cc0a422fb2898b525ec1006d","_LINK":"c787c538cc0a422fb2898b525ec1006d" }, + + {"MID":"a72c5eceff5c446c968c0e23bda3d933","_LINK":"10.3-Linking-requirements-to-source-code" }, + + {"MID":"1c06fb7b90114633801d177459f482b5","_LINK":"1c06fb7b90114633801d177459f482b5" }, + + {"MID":"38599400293a4b31a7cb6cb748f3dfe5","UID":"SECTION-UG-File-relations-roles","_LINK":"SECTION-UG-File-relations-roles" }, + + {"MID":"4f477cb2e0ca42aaac814042f795b982","_LINK":"4f477cb2e0ca42aaac814042f795b982" }, + + {"MID":"c9da7df725564208971e7ee058ad8d73","UID":"SECTION-UG-Parsing-SDoc-source-nodes","_LINK":"SECTION-UG-Parsing-SDoc-source-nodes" }, + + {"MID":"72fe8c3f618042578850cfdf01868b6c","_LINK":"72fe8c3f618042578850cfdf01868b6c" }, + + {"MID":"31411ccbe80144868da8961ea2dd38bc","UID":"SECTION-UG-ReqIF-support","_LINK":"SECTION-UG-ReqIF-support" }, + + {"MID":"b510d9337e604bfda366f2ec605f4c88","_LINK":"b510d9337e604bfda366f2ec605f4c88" }, + + {"MID":"81f3e7ff2e584f789e0076d0ab560d45","_LINK":"11.1-Import-flow-ReqIF-SDoc" }, + + {"MID":"079ed6d7ec3f4040b59efce7ccd37267","_LINK":"079ed6d7ec3f4040b59efce7ccd37267" }, + + {"MID":"7d1678af20a84f5d8263061f36332773","_LINK":"11.2-Export-flow-SDoc-ReqIF" }, + + {"MID":"94641cac6b9748bfa2de8648ff197584","_LINK":"94641cac6b9748bfa2de8648ff197584" }, + + {"MID":"c87ecf181d564e44ad1981558bf2cab0","UID":"SECTION-UG-ReqIF-options","_LINK":"SECTION-UG-ReqIF-options" }, + + {"MID":"57dd1fb10ba64e2f8e0b75baf772b634","_LINK":"57dd1fb10ba64e2f8e0b75baf772b634" }, + + {"MID":"95643ee2edb84ad8b86f9cf2bb74aaae","UID":"SECTION-REQIF-DETAILS","_LINK":"SECTION-REQIF-DETAILS" }, + + {"MID":"edd5c3bf902e48deaee3248d12087a75","_LINK":"edd5c3bf902e48deaee3248d12087a75" }, + + {"MID":"5ad8e72bdcfb4ac1b942468af9180657","_LINK":"12-Excel-support" }, + + {"MID":"65c1657a8fb04179960fca4ece265b18","_LINK":"65c1657a8fb04179960fca4ece265b18" }, + + {"MID":"4a34ea5da82b4e469cf5a87178f69536","_LINK":"12.1-Import-flow-Excel-XLS-XLSX-SDoc" }, + + {"MID":"b01355b5a6d54409a0c7a684376a5278","_LINK":"b01355b5a6d54409a0c7a684376a5278" }, + + {"MID":"1cbdef9ee67b4392a3bdb64276e90e36","_LINK":"12.2-Export-flow-SDoc-Excel-XLSX" }, + + {"MID":"14161a9fa81d427f8a5a21367359339e","_LINK":"14161a9fa81d427f8a5a21367359339e" }, + + {"MID":"17a8adb92c8d4e7992e869355e73912d","_LINK":"13-Options" }, + + {"MID":"05f82bdf26f141738d2f511133633a2d","UID":"SDOC_UG_OPTIONS_PROJECT_LEVEL","_LINK":"SDOC_UG_OPTIONS_PROJECT_LEVEL" }, + + {"MID":"6449098df878484fbe8aed8a8b0e9a97","_LINK":"6449098df878484fbe8aed8a8b0e9a97" }, + + {"MID":"3671119d6a304d0b80c63f7e31faee88","_LINK":"13.1.1-Project-title" }, + + {"MID":"12501275599f4d7a954892e1a56778b9","_LINK":"12501275599f4d7a954892e1a56778b9" }, + + {"MID":"22741629bf054d6784faf541393e90d0","_LINK":"13.1.2-Path-to-assets" }, + + {"MID":"7d1102cda98c4ea18c6e646b73511097","_LINK":"7d1102cda98c4ea18c6e646b73511097" }, + + {"MID":"9fbabb0a6d0e46648f58ba7245776bd9","UID":"SECTION-UG-Path-to-cache-dir","_LINK":"SECTION-UG-Path-to-cache-dir" }, + + {"MID":"e94e9762cc0e4e1eafe57d0d9e03eb5b","_LINK":"e94e9762cc0e4e1eafe57d0d9e03eb5b" }, + + {"MID":"2c54380e174e42b6ba8eff74173ceb0f","_LINK":"13.1.4-Path-to-source-root" }, + + {"MID":"be42ecf5c8a84fc3bca0b73ea50647de","_LINK":"be42ecf5c8a84fc3bca0b73ea50647de" }, + + {"MID":"499fa0144cbd48c687ba0d5f9f7cb846","_LINK":"13.1.5-Path-to-custom-HTML2PDF-template" }, + + {"MID":"225dc4d22c084a67ae049fca1098bc1a","_LINK":"225dc4d22c084a67ae049fca1098bc1a" }, + + {"MID":"593a2abbdaa5470988103c70e87c4cfc","_LINK":"13.1.6-Include-exclude-document-paths" }, + + {"MID":"27e5981c451d4ad4938ab622e3cf0be2","_LINK":"27e5981c451d4ad4938ab622e3cf0be2" }, + + {"MID":"7284798d60a340c2a6369c651d1ee6be","_LINK":"13.1.7-Include-exclude-source-files-paths" }, + + {"MID":"3908f98fab5c47668ba752d0e93033ab","_LINK":"3908f98fab5c47668ba752d0e93033ab" }, + + {"MID":"b355fc6730d14eb1892ac6a25e47bf59","UID":"SDOC_UG_CONFIG_FEATURES","_LINK":"SDOC_UG_CONFIG_FEATURES" }, + + {"MID":"f43d8170c44b4d70ada2816fe71caf07","_LINK":"f43d8170c44b4d70ada2816fe71caf07" }, + + {"MID":"39f7752d17c848e4ae9acd53eaf8048b","_LINK":"13.1.8.1-Enable-all-features" }, + + {"MID":"d33880e1740d4d0892852cc8c425f66f","_LINK":"d33880e1740d4d0892852cc8c425f66f" }, + + {"MID":"35173b0d735a4d4f8c06256d1d4e1f89","_LINK":"13.1.8.2-Disable-all-features" }, + + {"MID":"9cf2ac683b7a4e939286146ef00631c1","_LINK":"9cf2ac683b7a4e939286146ef00631c1" }, + + {"MID":"fc0da01a4a114ddd888a90fd6a29e456","_LINK":"13.1.9-Server-configuration" }, + + {"MID":"c520cefdfe3e47ccb1a0ada95ee5c4f0","UID":"SECTION-UG-Host-and-port","_LINK":"SECTION-UG-Host-and-port" }, + + {"MID":"9943131c688c400b99e2db9ec4854480","_LINK":"9943131c688c400b99e2db9ec4854480" }, + + {"MID":"cc720b525adf46849f6117c18c97905e","_LINK":"13.2-Command-line-interface-options" }, + + {"MID":"d69f1fe42c754a34a5f360f47d4b4a5b","_LINK":"13.2.1-Project-title" }, + + {"MID":"81d8e1beb5904cb78dfde2712ff3f311","_LINK":"81d8e1beb5904cb78dfde2712ff3f311" }, + + {"MID":"6ea7b264c142461398e9c8711cfec8d3","_LINK":"13.2.2-Parallelization" }, + + {"MID":"f8ff07833ff942dfabdad2412350cd0c","_LINK":"f8ff07833ff942dfabdad2412350cd0c" }, + + {"MID":"8030a2ceb5014b9881529ef39d3c1fb7","_LINK":"14-Python-API" }, + + {"MID":"4504bffd6ced4aa78848ee9e03fdd4b7","_LINK":"4504bffd6ced4aa78848ee9e03fdd4b7" }, + + {"MID":"cb5115ac9a6d47a198ceac81c9d49b60","UID":"UG_PORTABILITY_CONSIDERATIONS","_LINK":"UG_PORTABILITY_CONSIDERATIONS" }, + + {"MID":"e15260727be84ce394386a4f95c627ae","_LINK":"e15260727be84ce394386a4f95c627ae" }, + + {"MID":"d49d64a25f654d0c9b39023e87e5fa19","_LINK":"16-Interoperability-with-other-tools" }, + + {"MID":"dc83f93c820944b6b04c0923a7ad7e69","UID":"SECTION-UG-Doxygen","_LINK":"SECTION-UG-Doxygen" }, + + {"MID":"a8664547ac7e41399d7d8d263869abc6","_LINK":"a8664547ac7e41399d7d8d263869abc6" }, + + {"MID":"1296f265cdd948d8a5c02fbfc1cc4065","_LINK":"16.2-Valispace" }, + + {"MID":"5fe39dedfa4640d2bd7127ff23b0e3de","_LINK":"5fe39dedfa4640d2bd7127ff23b0e3de" }, + + {"MID":"e3596b4fb0804ac28a8bb3d06bd81953","UID":"SDOC_UG_EXPERIMENTAL_FEATURES","_LINK":"SDOC_UG_EXPERIMENTAL_FEATURES" }, + + {"MID":"5b400736aec54789b2ed3ebcffcf961e","_LINK":"5b400736aec54789b2ed3ebcffcf961e" }, + + {"MID":"72fdb23051184714ab32e22859504a77","UID":"SECTION-UG-Search-and-filtering","_LINK":"SECTION-UG-Search-and-filtering" }, + + {"MID":"de584af947a445e5bcc782bab6a93baf","_LINK":"de584af947a445e5bcc782bab6a93baf" }, + + {"MID":"f2d766a310ef41959b0c6d581d042420","_LINK":"17.1.1-Query-engine" }, + + {"MID":"29ffc3e91d8045589b0837f647286128","_LINK":"29ffc3e91d8045589b0837f647286128" }, + + {"MID":"2da071ad45054d168a5ae2982266fb77","_LINK":"17.1.2-Filtering-content" }, + + {"MID":"d52d86750ad54ce1b3263ebb092451c0","_LINK":"d52d86750ad54ce1b3263ebb092451c0" }, + + {"MID":"241afbf6b7af40189eec8054d0d0201c","UID":"SECTION-UG-Project-statistics-screen","_LINK":"SECTION-UG-Project-statistics-screen" }, + + {"MID":"2fefc893de83432fa852886cc3a05fea","_LINK":"2fefc893de83432fa852886cc3a05fea" }, + + {"MID":"aa84a8c59a904c9dbb1052b1bff169bf","_LINK":"17.2.1-User-provided-custom-statistics-generator-experimental" }, + + {"MID":"7cf15de69efc46a5abe6b6fc786e4b71","_LINK":"7cf15de69efc46a5abe6b6fc786e4b71" }, + + {"MID":"aa7ebc36cd2947ac9aa293d6c76d8848","UID":"SECTION-UG-Diff-changelog-screen","_LINK":"SECTION-UG-Diff-changelog-screen" }, + + {"MID":"131a7392a33f4225a62539dd173a920b","_LINK":"131a7392a33f4225a62539dd173a920b" }, + + {"MID":"0a4573d025f14e61b84aad14349d42f4","UID":"SECTION-UG-HTML2PDF-document-generator","_LINK":"SECTION-UG-HTML2PDF-document-generator" }, + + {"MID":"6e610b9a381f4b5d9d26004dd286e001","_LINK":"6e610b9a381f4b5d9d26004dd286e001" }, + + {"MID":"61b3fa2fc31140ae95ae1a651cb18355","_LINK":"17.5-Mermaid-diagramming-and-charting-tool" }, + + {"MID":"6a4e4467a5c0451ea8291fd94309d525","_LINK":"6a4e4467a5c0451ea8291fd94309d525" }, + + {"MID":"74291dc6cfae47d2bae33066024f3807","_LINK":"17.6-Test-report-integration" }, + + {"MID":"07e78a4847554fbd87c44d7b11133f68","_LINK":"07e78a4847554fbd87c44d7b11133f68" }, + + {"MID":"e9c94abacb12425980aec8ed7ee752cb","_LINK":"17.6.1-JUnit-XML" }, + + {"MID":"eba63ebd45624139a8773cf42fe7d670","_LINK":"eba63ebd45624139a8773cf42fe7d670" }, + + {"MID":"06d9dbbeca204f5e8d0042e142b615bd","_LINK":"17.6.2-LLVM-Integrated-Tester-JUnit-XML-specifics" }, + + {"MID":"fab24f53f02f46e7a4cb49e333dfdc54","_LINK":"fab24f53f02f46e7a4cb49e333dfdc54" }, + + {"MID":"c7f5451905bf4fdb9a3a7dc97bb0e853","_LINK":"17.6.3-Robot-Framework-XML" }, + + {"MID":"6c3d98ca65cc40d585953237693ff2c6","_LINK":"6c3d98ca65cc40d585953237693ff2c6" }, + + {"MID":"66e608bfa6b8448598c0a732a27ddaad","_LINK":"17.7-Shadow-features" }, + + {"MID":"38fa0af0d01e479ba7f5be046bae02eb","_LINK":"38fa0af0d01e479ba7f5be046bae02eb" }, + + {"MID":"c3c6d6fe6e8140ca905d26dbbaae22b3","UID":"SDOC_UG_LIMIT","_LINK":"SDOC_UG_LIMIT" }, + + {"MID":"4fb2645146eb42c1be2152d480b61cfc","UID":"SDOC_UG_LIMIT_RST","_LINK":"SDOC_UG_LIMIT_RST" }, + + {"MID":"f7b039831f4e4c1ea19b45efe71c5365","_LINK":"f7b039831f4e4c1ea19b45efe71c5365" }, + + {"MID":"076c2ae8d627491a85ddcacc132a4bc5","UID":"SDOC_UG_LIMIT_WEB","_LINK":"SDOC_UG_LIMIT_WEB" }, + + {"MID":"911c454331634018b8de24186ae8e742","_LINK":"911c454331634018b8de24186ae8e742" }, + + {"MID":"2a49392c11d246ea8c69375764fc5362","_LINK":"18.2.1-Concurrent-use-of-web-user-interface" }, + + {"MID":"bdee3bde500a40878b70be44f113cf2c","_LINK":"bdee3bde500a40878b70be44f113cf2c" }, + + {"MID":"c5cfb2c7892f4137a3bc0c3136a55a89","_LINK":"19-Known-issues" }, + + {"MID":"9282f2d7218a4f2e967b6df45b0347ab","_LINK":"9282f2d7218a4f2e967b6df45b0347ab" }, + + {"MID":"7e4c91401d244149a1aa502d990f0052","UID":"SDOC_IMPL_2","_LINK":"SDOC_IMPL_2" }, + + {"MID":"ec83d51626ee4e0cbb75c500ee306b06","_LINK":"ec83d51626ee4e0cbb75c500ee306b06" }, + + {"MID":"b55c23b113d54802b5717f7a82a45bd2","_LINK":"20-Appendices" }, + + {"MID":"a72ae310493a42ba8ecdc7de2c42d4f2","UID":"SECTION-UG-FREETEXT-TEXT","_LINK":"SECTION-UG-FREETEXT-TEXT" }, + + {"MID":"2509cce7ee634c7a8878c5e1094b83f2","_LINK":"2509cce7ee634c7a8878c5e1094b83f2" }, + + {"MID":"bbe50de9a4d845a7aa91e32dd9335abe","_LINK":"20.1.1-How-to-migrate-from-FREETEXT-to-TEXT" }, + + {"MID":"5d2f033604824264b1cc05840997c6b8","_LINK":"5d2f033604824264b1cc05840997c6b8" }, + + {"MID":"2ed20461aeb4462b812432c08aa006be","UID":"SECTION-UG-NODE-MIGRATION","_LINK":"SECTION-UG-NODE-MIGRATION" }, + + {"MID":"9d55b69bf789487e814210da06fc4164","_LINK":"9d55b69bf789487e814210da06fc4164" }, + + {"MID":"6f15ef6876ba48598d5560aaf4664ec9","_LINK":"20.2.1-How-to-migrate-from-SECTION-to-SECTION" }, + + {"MID":"19fe5099aa034eddb960cc88953ace0e","_LINK":"19fe5099aa034eddb960cc88953ace0e" }, ], + "strictdoc/docs/strictdoc_02_feature_map.html": [ + + + {"MID":"1696d73230f046d589d2d87dd29d48a9","UID":"SDOC_FEATURE_MAP","_LINK":"SDOC_FEATURE_MAP" }, + + {"MID":"f405431dc66e4d6786be020a62a4e5a3","_LINK":"f405431dc66e4d6786be020a62a4e5a3" }, + + {"MID":"01c07ca347a24181a92a5be6c8431094","UID":"SECTION-FM-SDoc-text-markup","_LINK":"SECTION-FM-SDoc-text-markup" }, + + {"MID":"dfd4d2d77c7247819e476f360ffff639","_LINK":"1.1-Definition" }, + + {"MID":"4c40803dd0f04af88fd722ba6b732270","UID":"SDOC-FEAT-1","_LINK":"SDOC-FEAT-1" }, + + {"MID":"3f422abd9abe4717a995ae4150df12e3","_LINK":"1.2-Use-case" }, + + {"MID":"8ea5b080819e47afa78bc346d671cf30","_LINK":"8ea5b080819e47afa78bc346d671cf30" }, + + {"MID":"fa7b37153def4eb4a6e9dcee079cba2a","_LINK":"1.3-Inspirations" }, + + {"MID":"104b6ff4623f4ac9a965aa789c4cf001","_LINK":"104b6ff4623f4ac9a965aa789c4cf001" }, + + {"MID":"87b1538be72a4ff7945b5bb95f856471","_LINK":"1.4-Screenshots" }, + + {"MID":"f48ae881c5134f77885202b019e124d4","_LINK":"f48ae881c5134f77885202b019e124d4" }, + + {"MID":"27bb9f0875d84d79bb882f6ca98ee1fa","_LINK":"2-HTML-export" }, + + {"MID":"4a0844b5938a488a89ac4b379fc7d956","_LINK":"2.1-Definition" }, + + {"MID":"8e6604c8b27b450cbd21c12dc8572291","UID":"SDOC-FEAT-2","_LINK":"SDOC-FEAT-2" }, + + {"MID":"4e46063e2031418298b55a1861179900","_LINK":"2.2-Screenshots" }, + + {"MID":"586f8745588b483b8e027021d90931ac","_LINK":"586f8745588b483b8e027021d90931ac" }, + + {"MID":"3ef4254efd8547a8adaf37e100f22724","_LINK":"3-Web-based-graphical-user-interface" }, + + {"MID":"bd3e1273cb244616956bddfc22c05638","_LINK":"3.1-Definition" }, + + {"MID":"2c1cde585d5944b5b7b5a0d5f72a98ed","UID":"SDOC-FEAT-3","_LINK":"SDOC-FEAT-3" }, + + {"MID":"6b5989db0e984c42b12440993c885987","_LINK":"3.2-Screenshots" }, + + {"MID":"28ce32fe182c45ce882b65c835620339","_LINK":"28ce32fe182c45ce882b65c835620339" }, + + {"MID":"5ac3e3c690494371bd9de04be5eae0b0","_LINK":"4-Traceability-between-requirements-and-source-code" }, + + {"MID":"538d26fae4644debbc8f244709b8be7b","_LINK":"4.1-Definition" }, + + {"MID":"e14eee106ea443e598378e55401914dc","UID":"SDOC-FEAT-4","_LINK":"SDOC-FEAT-4" }, + + {"MID":"ceec313d9ec040b9bcaaf64967ed6af8","_LINK":"4.2-Screenshots" }, + + {"MID":"363aae23cbb44f30aff42ef6a430ffdf","_LINK":"363aae23cbb44f30aff42ef6a430ffdf" }, + + {"MID":"a436d31c5f324eb0bb6b35f0a6ce598d","_LINK":"5-Document-grammar" }, + + {"MID":"f20223cddcb14e40b45f99045e2d3a3a","_LINK":"5.1-Definition" }, + + {"MID":"0bfcb6144dde487791d4f01164dd7e7c","UID":"SDOC-FEAT-5","_LINK":"SDOC-FEAT-5" }, + + {"MID":"f4763b00f9624cfd8b44f8e571f2aec6","_LINK":"5.2-Screenshots" }, + + {"MID":"3d3d3e2c1c974ed4a2382770225212d7","_LINK":"3d3d3e2c1c974ed4a2382770225212d7" }, + + {"MID":"5b511eac439c4e0a8aa2c60499be88d0","_LINK":"6-Composable-documents" }, + + {"MID":"ab4bc7b948154dbfbd4956d41cda8b75","_LINK":"6.1-Definition" }, + + {"MID":"e7b2d61545eb414f97345ecac25c52cc","UID":"SDOC-FEAT-6","_LINK":"SDOC-FEAT-6" }, + + {"MID":"dd4d9c0d593b46f8a0abfb3fd91356ae","_LINK":"7-Export-to-RST" }, + + {"MID":"ab0e4d27b6334b44bd4b46425842cdbe","_LINK":"7.1-Definition" }, + + {"MID":"dd686a557c3e4e50ba89538a74d39383","UID":"SDOC-FEAT-8","_LINK":"SDOC-FEAT-8" }, + + {"MID":"f0a7041347444f3a822129120faf0fcd","_LINK":"7.2-Screenshots" }, + + {"MID":"45be72764a73440b8abf313bb84420aa","_LINK":"45be72764a73440b8abf313bb84420aa" }, + + {"MID":"2e8e39a2198e4f8aa18a470a892e4d25","_LINK":"8-Export-to-PDF" }, + + {"MID":"381ebcd10ee241aaaa9fe3654870961c","_LINK":"8.1-Definition" }, + + {"MID":"5be4f35be2eb49faae7c33b5a2d83251","UID":"SDOC-FEAT-10","_LINK":"SDOC-FEAT-10" }, + + {"MID":"2d8b04efe57d4c0aa543ed974ace868e","_LINK":"8.2-Screenshots" }, + + {"MID":"15d6e791674c4c0ab54afb6a781f35c9","_LINK":"9-Query-engine-and-search-screen" }, + + {"MID":"91177d3444d9437e87032875ac8fcd7d","_LINK":"9.1-Definition" }, + + {"MID":"9c1e6cb601294891bcaae2b8c9042c59","UID":"SDOC-FEAT-7","_LINK":"SDOC-FEAT-7" }, + + {"MID":"631f1620053342c8a398a1a5d458f873","_LINK":"9.2-Screenshots" }, + + {"MID":"64afc2abdab847439c0c18420db7ba47","_LINK":"64afc2abdab847439c0c18420db7ba47" }, + + {"MID":"3475f60f31b5488aab981fd2b7b12d4c","_LINK":"10-Project-statistics" }, + + {"MID":"9bbcc02a46194852b9616ea4437ce976","_LINK":"10.1-Definition" }, + + {"MID":"2dd0b514a24f43a287a4096d0be0af58","UID":"SDOC-FEAT-11","_LINK":"SDOC-FEAT-11" }, + + {"MID":"7d215ba517fc4aee9dfb381279297cc7","_LINK":"10.2-Screenshots" }, + + {"MID":"bd5a4763e2ca4df497dbaeeff9ffbd71","_LINK":"bd5a4763e2ca4df497dbaeeff9ffbd71" }, + + {"MID":"0b3d7fad0ee643ef83617745c51e8e72","_LINK":"11-Documentation-diff-changelog" }, + + {"MID":"9cdf4d3af1014aea923f77b112e6b1e3","_LINK":"11.1-Definition" }, + + {"MID":"e9da42f8e39b438a8e8eada16b280706","UID":"SDOC-FEAT-12","_LINK":"SDOC-FEAT-12" }, + + {"MID":"c60c6fab5740469aa251bfa3a805f21f","_LINK":"11.2-Screenshots" }, + + {"MID":"1fad67b63724450bb63738cf7e29d051","_LINK":"1fad67b63724450bb63738cf7e29d051" }, + + {"MID":"f655f9a2bde044489233fa03d2891de1","_LINK":"12-ReqIF-support" }, + + {"MID":"1627a126e1184c5e9050206f2f1ab725","_LINK":"12.1-Definition" }, + + {"MID":"2845463f977d4b51ac788c2fc9cbd894","UID":"SDOC-FEAT-13","_LINK":"SDOC-FEAT-13" }, + + {"MID":"e1109c5b3b1c4068a0eace811684f0c0","_LINK":"13-Project-configuration" }, + + {"MID":"3c2a1a37e11c425184042d6dab3f050a","_LINK":"13.1-Definition" }, + + {"MID":"8fc72d79bfe940aba2cf041e263b92df","UID":"SDOC-FEAT-9","_LINK":"SDOC-FEAT-9" }, + + {"MID":"275658296e5342acab72102589096f15","_LINK":"13.2-Screenshots" }, + + {"MID":"100daefc4121415989324de1978d1f59","_LINK":"100daefc4121415989324de1978d1f59" }, ], + "strictdoc/docs/strictdoc_03_faq.html": [ + + + {"MID":"a434bc71a7324efaaed7f5a58e56e544","UID":"SDOC_FAQ","_LINK":"SDOC_FAQ" }, + + {"MID":"5acdfccd5771492f95b70328bd599cf7","_LINK":"5acdfccd5771492f95b70328bd599cf7" }, + + {"MID":"e972c79015524961863e095280a81aab","_LINK":"1-What-is-StrictDoc" }, + + {"MID":"d8d92e4eb8254cbda8dcb37709a9df45","_LINK":"d8d92e4eb8254cbda8dcb37709a9df45" }, + + {"MID":"86827c92ec3a493d8a2382b75b5277a8","_LINK":"2-Resources-about-StrictDoc" }, + + {"MID":"f7083cf6a84e4b858b5e551f8c8a555a","_LINK":"2.1-Presentations" }, + + {"MID":"52342105083e4f80884959d4ca7f4dba","_LINK":"52342105083e4f80884959d4ca7f4dba" }, + + {"MID":"d22b63a405264a6a80d4ab3f9b2bf9c5","_LINK":"2.2-Blog-posts" }, + + {"MID":"b2f5aea77e504ee1b64a67be24bd3c58","_LINK":"b2f5aea77e504ee1b64a67be24bd3c58" }, + + {"MID":"d75bd4d35b4b425b90ec8667ffffe191","_LINK":"2.3-Screencasts-tutorials" }, + + {"MID":"9d23e77aa7f848cb8efca49f5cf41f77","_LINK":"9d23e77aa7f848cb8efca49f5cf41f77" }, + + {"MID":"74235945ec4a4116aecbc883ffba54b7","_LINK":"3-Which-web-server-is-recommended-for-StrictDoc-documentation" }, + + {"MID":"966a5c3babf84fc7a1454b2799d13470","_LINK":"966a5c3babf84fc7a1454b2799d13470" }, + + {"MID":"2e911dedb13348bf827f078bbbca44f4","_LINK":"4-Is-StrictDoc-compatible-with-Sphinx" }, + + {"MID":"ac35a712b4a84f4ab2b61feb15c1550d","_LINK":"ac35a712b4a84f4ab2b61feb15c1550d" }, + + {"MID":"235122d2c10748918c7c6ff736e39d18","UID":"SECTION-FAQ-How-did-the-SDoc-text-language-become-what-it-is","_LINK":"SECTION-FAQ-How-did-the-SDoc-text-language-become-what-it-is" }, + + {"MID":"b3561dd4f8e64f11a251c3471022aece","_LINK":"b3561dd4f8e64f11a251c3471022aece" }, + + {"MID":"1d4220cc9a5444a5bdbaee91c8c50670","_LINK":"6-How-StrictDoc-compares-to-other-tools" }, + + {"MID":"45093addf6ec4772a0289fd958ae61dc","_LINK":"45093addf6ec4772a0289fd958ae61dc" }, + + {"MID":"a4533ae81bfe446295242add9156198e","_LINK":"6.1-Doorstop" }, + + {"MID":"d0c305180d8747a1b70254eaf2c4f201","_LINK":"d0c305180d8747a1b70254eaf2c4f201" }, + + {"MID":"5012af516d18447caa3a2dbce97d3e4a","_LINK":"6.2-Sphinx" }, + + {"MID":"74b6be0dc2bb485987d8731348cdf5ce","_LINK":"74b6be0dc2bb485987d8731348cdf5ce" }, + + {"MID":"515bcae39d1f48eda0b72562871ab15a","_LINK":"6.3-Sphinx-Needs" }, + + {"MID":"602327a9b8654e1eab4bc265e172a63d","_LINK":"602327a9b8654e1eab4bc265e172a63d" }, + + {"MID":"4a77366154d74cf7ab34044bed1cd542","_LINK":"6.4-FRET" }, + + {"MID":"e3eb1be604c6499292339ecbe7620c60","_LINK":"e3eb1be604c6499292339ecbe7620c60" }, + + {"MID":"723731c256fc43dfb315a300f6429945","_LINK":"6.5-Other-tools" }, + + {"MID":"ad24dca8ed9c449ab76c1798f38eb37c","_LINK":"ad24dca8ed9c449ab76c1798f38eb37c" }, + + {"MID":"ff143a18e98144bd86e10e29616c187b","_LINK":"7-How-long-has-the-StrictDoc-project-been-around" }, + + {"MID":"47d60404cf574aebb8c83795589c4137","_LINK":"47d60404cf574aebb8c83795589c4137" }, + + {"MID":"6e5ed6bb36634220a0c583d21c96295f","_LINK":"8-Which-StrictDoc-statistics-are-available" }, + + {"MID":"730bfedff6b24c34a6d6331051fd76d3","_LINK":"730bfedff6b24c34a6d6331051fd76d3" }, ], + "strictdoc/docs/strictdoc_04_release_notes.html": [ + + + {"MID":"4277890572a94f49bda9f32a80e43c06","_LINK":"None-Release-Notes" }, + + {"MID":"0dd2649ec4784c9480eac6f4aef3158b","_LINK":"0dd2649ec4784c9480eac6f4aef3158b" }, + + {"MID":"27d526d90cda48dfa5cbf17df88299ba","_LINK":"1-0-12-1-2025-09-18" }, + + {"MID":"7433a733b3794983b5e17169897c3732","_LINK":"7433a733b3794983b5e17169897c3732" }, + + {"MID":"f2a70deaf8164240951fca48510d7aa4","_LINK":"2-0-12-0-2025-09-17" }, + + {"MID":"a7e0cee58f054a50818f07a02bab7230","_LINK":"a7e0cee58f054a50818f07a02bab7230" }, + + {"MID":"721b576103a748c8a63d3147ec771b95","_LINK":"3-0-11-3-2025-09-07" }, + + {"MID":"e60129ab582946319616595111296e76","_LINK":"e60129ab582946319616595111296e76" }, + + {"MID":"4bd05b9073254db38322102f4bbc5096","_LINK":"4-0-11-2-2025-09-02" }, + + {"MID":"7776a30d522b4b5f9a6e405d20241566","_LINK":"7776a30d522b4b5f9a6e405d20241566" }, + + {"MID":"3e3043021e034cb69dfd9b62eecadbb3","_LINK":"5-0-11-1-2025-09-01" }, + + {"MID":"9a4bbed442b94d72859d70d31282efc1","_LINK":"9a4bbed442b94d72859d70d31282efc1" }, + + {"MID":"f53df5a2e9674373a8f52f1a01b5010d","_LINK":"6-0-11-0-2025-08-26" }, + + {"MID":"177963dca83748d89bfaeb01b3d663a8","_LINK":"177963dca83748d89bfaeb01b3d663a8" }, + + {"MID":"a5ab8dd5bc4045e99299eff729e430e6","_LINK":"7-0-10-1-2025-07-25" }, + + {"MID":"6d0bb97e7ef74fc1ae93cf2a91480e6a","_LINK":"6d0bb97e7ef74fc1ae93cf2a91480e6a" }, + + {"MID":"447ab1fa1d38453ab46e0002e6eb852d","_LINK":"8-0-10-0-2025-07-20" }, + + {"MID":"c536381cea6842f0b77c1aa5ac942ce0","_LINK":"c536381cea6842f0b77c1aa5ac942ce0" }, + + {"MID":"aa45a90bad3f4273ab46dade039ef4c6","_LINK":"9-0-9-4-2025-07-12" }, + + {"MID":"a25dd134646e462ca09e850481bccb2b","_LINK":"a25dd134646e462ca09e850481bccb2b" }, + + {"MID":"eca2ad204c8246a8b0f29a5eb8d7f799","_LINK":"10-0-9-3-2025-07-03" }, + + {"MID":"64706a3d4d9c4beda3597ffa9d92921a","_LINK":"64706a3d4d9c4beda3597ffa9d92921a" }, + + {"MID":"6665c867a01f49f59968ae83d2e928fe","_LINK":"11-0-9-2-2025-06-28" }, + + {"MID":"d56dfede85b84e07bd5ecddc82a576fc","_LINK":"d56dfede85b84e07bd5ecddc82a576fc" }, + + {"MID":"a5ab3ca7210f4c18a465e7686e71c15f","_LINK":"12-0-9-1-2025-06-14" }, + + {"MID":"fbf2c4dcf8154779b3ac914d9ff335c4","_LINK":"fbf2c4dcf8154779b3ac914d9ff335c4" }, + + {"MID":"71b0ec45b5644c5083085c83ef6be95e","_LINK":"13-0-9-0-2025-06-10" }, + + {"MID":"72d1a7e4b54d44e38778d87c74075dc8","_LINK":"72d1a7e4b54d44e38778d87c74075dc8" }, + + {"MID":"d64fb5003399496bbb18ffb6e05efaf4","_LINK":"14-0-8-0-2025-04-13" }, + + {"MID":"a7bd074d2dcd40a6af106e91720d90d3","_LINK":"a7bd074d2dcd40a6af106e91720d90d3" }, + + {"MID":"58b99c15df8445ce9832e24a17588e2c","_LINK":"15-0-7-0-2025-03-03" }, + + {"MID":"c5725f24fcd24d54929e155f6ad47b44","_LINK":"c5725f24fcd24d54929e155f6ad47b44" }, + + {"MID":"ff59e375101145b3bf12465ce2b5fc14","_LINK":"16-0-6-0-2025-02-16" }, + + {"MID":"3ddfc6462bec45b2ac69a1799293c7da","_LINK":"3ddfc6462bec45b2ac69a1799293c7da" }, + + {"MID":"d9e164994bb941ada8aecc73000bd51d","_LINK":"17-0-5-0-2025-01-06" }, + + {"MID":"d0d93f5eb37247079cc182e924dabd14","_LINK":"d0d93f5eb37247079cc182e924dabd14" }, + + {"MID":"e7df8274f42c4e01b02ad840e6b2569c","_LINK":"18-0-4-0-2024-12-25" }, + + {"MID":"f4e63483a11b442993c47b5d0574e89b","_LINK":"f4e63483a11b442993c47b5d0574e89b" }, + + {"MID":"591c1b85c6fb4453aee08e23cb9f257a","_LINK":"19-0-3-0-2024-11-21" }, + + {"MID":"b39a3b3d9f8640e380dad5a2fdb75d79","_LINK":"b39a3b3d9f8640e380dad5a2fdb75d79" }, + + {"MID":"f4cd860f07b1479ba1845bce3c749d68","_LINK":"20-0-2-1-2024-11-10" }, + + {"MID":"26043bba142a4e739dd206e229829202","_LINK":"26043bba142a4e739dd206e229829202" }, + + {"MID":"f76d863243b5410788e457b9c3bf1d2b","_LINK":"21-0-2-0-2024-11-04" }, + + {"MID":"611e8df13d464fd1b22e5026cf3ce531","_LINK":"611e8df13d464fd1b22e5026cf3ce531" }, + + {"MID":"83878b68483f4da2930bfb3712c08aa3","_LINK":"22-0-1-0-2024-11-01" }, + + {"MID":"e37e230c932448798bd9978d4f27f32c","_LINK":"e37e230c932448798bd9978d4f27f32c" }, + + {"MID":"771fb5350bf840e4b30485fe9e8d9452","_LINK":"23-0-0-60-2024-10-26" }, + + {"MID":"65daf4400b464021ba3cb7a06061e651","_LINK":"65daf4400b464021ba3cb7a06061e651" }, + + {"MID":"4538fe1994bc4730b7febe3dc624f704","_LINK":"24-0-0-59-2024-10-13" }, + + {"MID":"e9d5e3f8f6b04647afb95bae6b2f503a","_LINK":"e9d5e3f8f6b04647afb95bae6b2f503a" }, + + {"MID":"306a619f550f4be18db6dbb987df3248","_LINK":"25-0-0-58-2024-06-25" }, + + {"MID":"fd7f4d5b58e54f64aa43349684a675c4","_LINK":"fd7f4d5b58e54f64aa43349684a675c4" }, + + {"MID":"0795627e25e343f3b1b476b673a543ab","_LINK":"26-0-0-57-2024-06-23" }, + + {"MID":"f99185cb69cf47d4bc6e1bd404b67467","_LINK":"f99185cb69cf47d4bc6e1bd404b67467" }, + + {"MID":"cd994052a0354410a550858a1f25d426","_LINK":"27-0-0-56-2024-06-02" }, + + {"MID":"61f17be85ac442a0b70003825d671ced","_LINK":"61f17be85ac442a0b70003825d671ced" }, + + {"MID":"938f1a0fdcc94c5aaa87ddb18b880afe","_LINK":"28-0-0-55-2024-04-28" }, + + {"MID":"00abb07e55534a75a8cfd50d8cfc5732","_LINK":"00abb07e55534a75a8cfd50d8cfc5732" }, + + {"MID":"055ee004b28647629eead8165f198579","_LINK":"29-0-0-54-2024-04-17" }, + + {"MID":"e5bf489ac0684ea4b626574b5439c7dd","_LINK":"e5bf489ac0684ea4b626574b5439c7dd" }, + + {"MID":"87b7b54fa4024959bdcb42ded18f77f2","_LINK":"30-0-0-53-2024-04-01" }, + + {"MID":"2ac296f6326b4481beb7d67b95dbb23c","_LINK":"2ac296f6326b4481beb7d67b95dbb23c" }, + + {"MID":"36a70af62b3e4a5d8bf08d9345fda095","_LINK":"31-0-0-52-2024-03-25" }, + + {"MID":"72d6e38e89cc471b8d3f46ae5654e0e6","_LINK":"72d6e38e89cc471b8d3f46ae5654e0e6" }, + + {"MID":"6c72f8654ea1401ea2a6e4ab82cf76e4","_LINK":"32-0-0-51-2024-03-20" }, + + {"MID":"e81c4206bcb148979762ee3f2aa5c9ba","_LINK":"e81c4206bcb148979762ee3f2aa5c9ba" }, + + {"MID":"413952c14412434fb237c3e909101707","_LINK":"33-0-0-50-2024-03-19" }, + + {"MID":"a9d7c449d1a7400496504926c744e500","_LINK":"a9d7c449d1a7400496504926c744e500" }, + + {"MID":"3a03dd169ffb4fe0b64f48b365a77d2e","_LINK":"34-0-0-49-2024-03-11" }, + + {"MID":"0b8c8f40923744b089845afeb44223e9","_LINK":"0b8c8f40923744b089845afeb44223e9" }, + + {"MID":"a7ed1eaf155d4f588b27ba31ecc88d4d","_LINK":"35-0-0-48-2024-01-24" }, + + {"MID":"6cff333594d6430886053b59b265e806","_LINK":"6cff333594d6430886053b59b265e806" }, + + {"MID":"7a3620eecd1b4bd1a8eba7a373f14723","_LINK":"36-0-0-47-2023-11-20" }, + + {"MID":"c422af340d5b45c48bfe89130e1bba52","_LINK":"c422af340d5b45c48bfe89130e1bba52" }, ], + "strictdoc/docs/strictdoc_05_troubleshooting.html": [ + + + {"MID":"5680165c90f44903b3b1dc1013b8cb59","UID":"SDOC_TROUBLESHOOTING","_LINK":"SDOC_TROUBLESHOOTING" }, + + {"MID":"7e3481d51ad14767b5c97132cad2e0a6","_LINK":"7e3481d51ad14767b5c97132cad2e0a6" }, + + {"MID":"17152ac7de9f4c138b85e2956091206f","_LINK":"1-Caching-issues" }, + + {"MID":"9a32ac13510d401cbc1a608259652c78","_LINK":"9a32ac13510d401cbc1a608259652c78" }, ], + "strictdoc/docs/strictdoc_10_contributing.html": [ + + + {"MID":"1739e662499648d38c210a8aafbb3361","_LINK":"None-Contributing-to-StrictDoc" }, + + {"MID":"13fe652f160d4af69e869c28a155437d","_LINK":"13fe652f160d4af69e869c28a155437d" }, + + {"MID":"ec078ab0d7564738acd2961f72abb2c9","_LINK":"1-Contributor-checklist" }, + + {"MID":"ae0fc6d87b114137ab6da7c9b5647b62","_LINK":"ae0fc6d87b114137ab6da7c9b5647b62" }, + + {"MID":"8b40cc35f3524bc7b4e444321c080837","_LINK":"2-How-can-I-help" }, + + {"MID":"8e346888911649f2a3aaf49763aaef24","_LINK":"2.1-Spread-the-word" }, + + {"MID":"07462b02027a423a85ab68525b78ac67","_LINK":"07462b02027a423a85ab68525b78ac67" }, + + {"MID":"2696c7fda6d440f3a290f202bee08fe6","_LINK":"2.2-ReqIF-users" }, + + {"MID":"9cfbb025525f41c5a195be0e637dffee","_LINK":"9cfbb025525f41c5a195be0e637dffee" }, + + {"MID":"4e1edb97346c43af952c4e74f9defbe8","_LINK":"2.3-TeX-LaTeX-Sphinx-experts" }, + + {"MID":"75ed9362d86748269f62f5920514b7c2","_LINK":"75ed9362d86748269f62f5920514b7c2" }, ], + "strictdoc/docs/strictdoc_11_developer_guide.html": [ + + + {"MID":"4f05169346ab4001b6ff19e8349e8fa7","_LINK":"None-Developer-Guide" }, + + {"MID":"f174b0cdec6b482daa2878c8cc0d02c3","_LINK":"f174b0cdec6b482daa2878c8cc0d02c3" }, + + {"MID":"c069e4c7b4d04dd09eb73f940e947bfd","UID":"DEVGUIDE_GETTING_STARTED","_LINK":"DEVGUIDE_GETTING_STARTED" }, + + {"MID":"7477d397d0ca493f8e47532455d44965","_LINK":"1.1-System-dependencies" }, + + {"MID":"df2e1de12c6b456898c48a36650813d4","_LINK":"df2e1de12c6b456898c48a36650813d4" }, + + {"MID":"b7a21564c30043acbf1733d9b47bd0bc","_LINK":"1.1.1-Windows-specific-Long-Path-support" }, + + {"MID":"8591cf6dece04244824c3ac2a8666ba3","_LINK":"8591cf6dece04244824c3ac2a8666ba3" }, + + {"MID":"89ec30b9f67c489da635be391b0b0e15","_LINK":"1.2-Installing-StrictDoc-from-GitHub-developer-mode" }, + + {"MID":"2407bb186b274a58b97b54b99ba6e86f","_LINK":"2407bb186b274a58b97b54b99ba6e86f" }, + + {"MID":"1814a8c25afc40cba6dca1696cae119b","_LINK":"1.2.1-Development-within-a-virtual-environment" }, + + {"MID":"a3e14b05452b48e2a0d8726fa00a7f00","_LINK":"a3e14b05452b48e2a0d8726fa00a7f00" }, + + {"MID":"90d5d79146e64660a71cd74ac23f6811","_LINK":"2-Invoke-for-development-tasks" }, + + {"MID":"452b3c24463446a497ed6b8fcdb31fef","_LINK":"452b3c24463446a497ed6b8fcdb31fef" }, + + {"MID":"e7ce6a2f13614974b43aba56f6ae7253","_LINK":"3-Main-Check-task" }, + + {"MID":"09258e99cabb4455904b0e2c6687d492","_LINK":"09258e99cabb4455904b0e2c6687d492" }, + + {"MID":"07d9a3c17be8475089844f5eb2cd8299","UID":"DEVGUIDE_PYTHON_CODE","_LINK":"DEVGUIDE_PYTHON_CODE" }, + + {"MID":"3012a69ef5d244d49fcb90c088d4523b","_LINK":"3012a69ef5d244d49fcb90c088d4523b" }, + + {"MID":"6e9707c99199464583f091056d1ae458","UID":"DEVGUIDE_GIT_WORKFLOW","_LINK":"DEVGUIDE_GIT_WORKFLOW" }, + + {"MID":"0e916453596b460b84a89f722354a825","_LINK":"0e916453596b460b84a89f722354a825" }, + + {"MID":"2420b2d7a4624bd99b0f8088dfa9abff","_LINK":"6-Frontend-development" }, + + {"MID":"d818aad87db54985b8933f98528808ab","_LINK":"d818aad87db54985b8933f98528808ab" }, + + {"MID":"73c717608cce4abca2c768f1dbfb705d","_LINK":"7-Running-End-to-End-Web-tests" }, + + {"MID":"da3cbcd69b0545988daeb0f42098830d","_LINK":"da3cbcd69b0545988daeb0f42098830d" }, + + {"MID":"240dc1426d994397ac1401834b4f0ef0","_LINK":"8-Running-integration-tests" }, + + {"MID":"4e307b3b950840bbbe455c1ae3238a6e","_LINK":"4e307b3b950840bbbe455c1ae3238a6e" }, + + {"MID":"7b4bf839b1c44b0d82001060596d8cbf","_LINK":"9-Documentation" }, + + {"MID":"d1f5fde86ebc4103bc4eb0334b1ec919","_LINK":"d1f5fde86ebc4103bc4eb0334b1ec919" }, + + {"MID":"a44e3b99323b4ac291f4b4d3948c3cc0","_LINK":"10-Conventions" }, + + {"MID":"49b7349a3bee478cbe81d8ef25f6245c","_LINK":"49b7349a3bee478cbe81d8ef25f6245c" }, ], + "strictdoc/docs/strictdoc_20_L1_Open_Requirements_Tool.html": [ + + + {"MID":"b7bcca934cd148c3855beb4c85723bc5","_LINK":"None-Requirements-Tool-Specification-L1" }, + + {"MID":"c455f0297eb648c6aa9c6d239f043e0d","_LINK":"c455f0297eb648c6aa9c6d239f043e0d" }, + + {"MID":"668d18a7489043b38dd435933319e8e7","_LINK":"1-Summary-of-user-needs" }, + + {"MID":"7a94c3f55e32498187603400923bfae4","_LINK":"7a94c3f55e32498187603400923bfae4" }, + + {"MID":"1cbe275272994922a942fd088fa969d8","_LINK":"1.1-Free-and-open-source-tool" }, + + {"MID":"3ca29d12a7804f089f5b2aa955e4da59","_LINK":"3ca29d12a7804f089f5b2aa955e4da59" }, + + {"MID":"18136bcb9a5b4226a7a95d27dae49805","_LINK":"1.2-Document-types" }, + + {"MID":"14383453f84747d3abdce6b5dce50c7f","_LINK":"14383453f84747d3abdce6b5dce50c7f" }, + + {"MID":"973b586b8a89411ab4f083fd4c262cf5","UID":"SECTION-RTC-Appendix-A-Document-archetypes","_LINK":"SECTION-RTC-Appendix-A-Document-archetypes" }, + + {"MID":"4a847b9a58444e4a9be056db1f23272c","_LINK":"4a847b9a58444e4a9be056db1f23272c" }, + + {"MID":"52b6175f03d7495a9fa8dbffa36aeff4","UID":"SECTION-LRTS-Workflows","_LINK":"SECTION-LRTS-Workflows" }, + + {"MID":"c537b8fa5b54417eb9dff6478553f6d7","_LINK":"c537b8fa5b54417eb9dff6478553f6d7" }, + + {"MID":"08d547a3b04d485ca1f5a3a94a664ee1","_LINK":"1.4-Target-audience" }, + + {"MID":"847eaab505124e7797bd25c865f9d868","_LINK":"847eaab505124e7797bd25c865f9d868" }, + + {"MID":"f1ffdc7f78134d4b98f3c90e9be40cc7","UID":"SECTION-RTS-Documentation-management","_LINK":"SECTION-RTS-Documentation-management" }, + + {"MID":"0cacb010f5164118adce5dd8512c3e27","_LINK":"0cacb010f5164118adce5dd8512c3e27" }, + + {"MID":"72d0dd23dedd4907b4a8924debe7343b","UID":"SDOC-SSS-3","_LINK":"SDOC-SSS-3" }, + + {"MID":"9841bc9a56d740dd9e7988e52339618c","UID":"SDOC-SSS-91","_LINK":"SDOC-SSS-91" }, + + {"MID":"c4de5f1f46f343b6b4e8dc2a6121b0a2","UID":"SDOC-SSS-51","_LINK":"SDOC-SSS-51" }, + + {"MID":"5236f36f278140539e347bacea921118","UID":"SDOC-SSS-52","_LINK":"SDOC-SSS-52" }, + + {"MID":"8aeecdfa139341588d6ab8c0c3606a9b","UID":"SDOC-SSS-53","_LINK":"SDOC-SSS-53" }, + + {"MID":"d1883b65e17442c0a22987d4ad415cb6","UID":"SDOC-SSS-75","_LINK":"SDOC-SSS-75" }, + + {"MID":"b8d7b22fd8a6478a9a21ecaa60cf0028","UID":"SDOC-SSS-63","_LINK":"SDOC-SSS-63" }, + + {"MID":"3bb5252d75bc486b98e7606fb642bac0","UID":"SDOC-SSS-95","_LINK":"SDOC-SSS-95" }, + + {"MID":"fd227b9cfdc54d7db3134cdb925bde87","UID":"SECTION-RTS-Requirements-management","_LINK":"SECTION-RTS-Requirements-management" }, + + {"MID":"c1e2431cdf2141ca86931f29b9912ff7","_LINK":"c1e2431cdf2141ca86931f29b9912ff7" }, + + {"MID":"6e6f1464c47f4ced8e14c4153f40618b","UID":"SDOC-SSS-4","_LINK":"SDOC-SSS-4" }, + + {"MID":"db42ad4b504e48e0bf51b4d82529a888","UID":"SDOC-SSS-61","_LINK":"SDOC-SSS-61" }, + + {"MID":"a69bae43b3224dfc8439a88451a98395","UID":"SDOC-SSS-62","_LINK":"SDOC-SSS-62" }, + + {"MID":"5f89bdaa6cb74e2a8b9b030b6237b7ef","UID":"SDOC-SSS-64","_LINK":"SDOC-SSS-64" }, + + {"MID":"65575cc8a87e4d4480ccdf89d1fb6d67","UID":"SDOC-SSS-5","_LINK":"SDOC-SSS-5" }, + + {"MID":"06ab121d3c0f4d8c94652323b8f735c6","UID":"SDOC-SSS-70","_LINK":"SDOC-SSS-70" }, + + {"MID":"637c129a91804d8cbe2280c1af14836d","UID":"SDOC-SSS-6","_LINK":"SDOC-SSS-6" }, + + {"MID":"1ce495a5194044d4a7a3dd030f766c9f","UID":"SDOC-SSS-7","_LINK":"SDOC-SSS-7" }, + + {"MID":"44b9d75e50c84d6c8424cfb8a42a0771","UID":"SDOC-SSS-8","_LINK":"SDOC-SSS-8" }, + + {"MID":"387bd5ff0e4a4a97962661e9d7451f19","UID":"SDOC-SSS-71","_LINK":"SDOC-SSS-71" }, + + {"MID":"3348caaed8d44be0928cc6f3c7414094","UID":"SDOC-SSS-89","_LINK":"SDOC-SSS-89" }, + + {"MID":"3850f0b8c8294455bf7be9e2acddbd40","UID":"SDOC-SSS-47","_LINK":"SDOC-SSS-47" }, + + {"MID":"1216cb617c0f495a9c96471ef72bf80f","UID":"SDOC-SSS-57","_LINK":"SDOC-SSS-57" }, + + {"MID":"7c112f3a5885477084396fd26387a864","_LINK":"4-Tool-configurability" }, + + {"MID":"4550038552894ef69b85b281cff60c60","UID":"SDOC-SSS-92","_LINK":"SDOC-SSS-92" }, + + {"MID":"4550b38552894ef69b85b281cff60360","UID":"SDOC-SSS-93","_LINK":"SDOC-SSS-93" }, + + {"MID":"56dabce670be4691b35434bda6d6bb36","UID":"SECTION-SSSS-Performance","_LINK":"SECTION-SSSS-Performance" }, + + {"MID":"68e4b4b1ba78485f8620575ef9e1eaa2","_LINK":"68e4b4b1ba78485f8620575ef9e1eaa2" }, + + {"MID":"a0a95079e3c0438bb3ea9e5af7ddddc9","UID":"SDOC-SSS-13","_LINK":"SDOC-SSS-13" }, + + {"MID":"64b982c9299a4c8eb7031495e940a3f8","UID":"SDOC-SSS-14","_LINK":"SDOC-SSS-14" }, + + {"MID":"d1a483f3e321414b83bb4a39b1283293","_LINK":"6-Data-integrity" }, + + {"MID":"d1323d222a0040b2b6122c59b3c00f06","UID":"SDOC-SSS-94","_LINK":"SDOC-SSS-94" }, + + {"MID":"ec52fa5cc1714b07863eb7da9432be1d","UID":"SECTION-SSSS-Existing-workflows","_LINK":"SECTION-SSSS-Existing-workflows" }, + + {"MID":"33769a5f0ca94b99a4802adc81841037","_LINK":"33769a5f0ca94b99a4802adc81841037" }, + + {"MID":"831debedfa0e4e4c8434021569f96bad","UID":"SDOC-SSS-73","_LINK":"SDOC-SSS-73" }, + + {"MID":"5d412989005e4bc3b5dcfd94675615dc","UID":"SDOC-SSS-56","_LINK":"SDOC-SSS-56" }, + + {"MID":"5eb553abd3944bffbd003e37b3d1c5a5","UID":"SDOC-SSS-28","_LINK":"SDOC-SSS-28" }, + + {"MID":"06f0bb40f76142f4ba59d037aaedc9de","UID":"SDOC-SSS-48","_LINK":"SDOC-SSS-48" }, + + {"MID":"74ffae6279bc4e4095c03d6a247c3599","UID":"SDOC-SSS-29","_LINK":"SDOC-SSS-29" }, + + {"MID":"e0e6d1f883764719919238e4974dccef","UID":"SDOC-SSS-49","_LINK":"SDOC-SSS-49" }, + + {"MID":"ed4bd232bed84eda977263fa03e75b80","UID":"SDOC-SSS-74","_LINK":"SDOC-SSS-74" }, + + {"MID":"85d2f75b2f074f98ace5ceb5288c09b9","UID":"SECTION-RTC-Usability-installation-and-usage","_LINK":"SECTION-RTC-Usability-installation-and-usage" }, + + {"MID":"7e33387f373d427d9e1af522f1e5bbc5","UID":"SDOC-SSS-79","_LINK":"SDOC-SSS-79" }, + + {"MID":"57b01915e4624634909dbb40f5d56697","UID":"SDOC-SSS-80","_LINK":"SDOC-SSS-80" }, + + {"MID":"d3e66072363d4e7ea2e2f9691c094bb2","UID":"SDOC-SSS-81","_LINK":"SDOC-SSS-81" }, + + {"MID":"db495bc2f11f4f96ace60cdb0bef4d66","UID":"SDOC-SSS-82","_LINK":"SDOC-SSS-82" }, + + {"MID":"90562d92b9a7468ba42567bc72cb6ebf","UID":"SDOC-SSS-83","_LINK":"SDOC-SSS-83" }, + + {"MID":"4312ed066d19487eba18ec677430e60f","UID":"SDOC-SSS-84","_LINK":"SDOC-SSS-84" }, + + {"MID":"81858dda65844dcd9640fdd74badecee","UID":"SDOC-SSS-85","_LINK":"SDOC-SSS-85" }, + + {"MID":"ee96cc5f18e14f6894e2775c546240d8","UID":"SDOC-SSS-86","_LINK":"SDOC-SSS-86" }, + + {"MID":"f4b122c5081f4b218aa93a119e372a34","UID":"SDOC-SSS-87","_LINK":"SDOC-SSS-87" }, + + {"MID":"29fa83c59e714d0da8b36a9dc65b0c72","UID":"SECTION-SSSS-Implementation-suggestions","_LINK":"SECTION-SSSS-Implementation-suggestions" }, + + {"MID":"3db06eb066b4441290e06b08405da0a9","UID":"SDOC-SSS-30","_LINK":"SDOC-SSS-30" }, + + {"MID":"e718af582b0345c2b0ddb6e405cb6d9a","UID":"SDOC-SSS-31","_LINK":"SDOC-SSS-31" }, + + {"MID":"cd20e9613a65441a8862143ba6407f61","UID":"SDOC-SSS-32","_LINK":"SDOC-SSS-32" }, + + {"MID":"4475857729e5476cbbb409cc844048fd","UID":"SDOC-SSS-68","_LINK":"SDOC-SSS-68" }, + + {"MID":"5ac2f3010db84466bff854fc323e944c","UID":"SDOC-SSS-33","_LINK":"SDOC-SSS-33" }, + + {"MID":"b76fb774542849a182e2cdd89fdaab3c","UID":"SDOC-SSS-67","_LINK":"SDOC-SSS-67" }, + + {"MID":"871a7a66eb50436997fde692e90e93eb","UID":"SDOC-SSS-69","_LINK":"SDOC-SSS-69" }, + + {"MID":"0556a0b2773d4ee0b44b22773455a600","UID":"SDOC-SSS-90","_LINK":"SDOC-SSS-90" }, + + {"MID":"7bdea1a7cb9e4f6fbcf8497670f87278","UID":"SECTION-RTS-Text-based-requirements-language","_LINK":"SECTION-RTS-Text-based-requirements-language" }, + + {"MID":"dfe5a61ed10149bebcf69d9c5c3b1316","_LINK":"dfe5a61ed10149bebcf69d9c5c3b1316" }, + + {"MID":"f472e85f3398413795234a9298d4af3c","UID":"SDOC-SSS-88","_LINK":"SDOC-SSS-88" }, + + {"MID":"b13bc3dfa7324f238bc06a88baf5edb6","UID":"SDOC-SSS-55","_LINK":"SDOC-SSS-55" }, + + {"MID":"0b47da74f9ec45e6a77573185b95f327","UID":"SDOC-SSS-54","_LINK":"SDOC-SSS-54" }, + + {"MID":"13f3e7765b494237893c1585a7c9dfd0","UID":"SDOC-SSS-34","_LINK":"SDOC-SSS-34" }, + + {"MID":"8b1da9907bc847c989dc807b7dff2a05","UID":"SECTION-RTS-Requirements-and-source-code","_LINK":"SECTION-RTS-Requirements-and-source-code" }, + + {"MID":"4692362c77974fb8bcbeefdc248d0e89","UID":"SDOC-SSS-72","_LINK":"SDOC-SSS-72" }, + + {"MID":"cd9e353e07ae4d50b1aa08999854aa02","UID":"SECTION-RTS-Requirements-exchange-formats-export-import","_LINK":"SECTION-RTS-Requirements-exchange-formats-export-import" }, + + {"MID":"ecc76e0991e44d6282776365496193e6","_LINK":"ecc76e0991e44d6282776365496193e6" }, + + {"MID":"73e70548c2cf415f94adbf1066d5fe4b","UID":"SDOC-SSS-58","_LINK":"SDOC-SSS-58" }, + + {"MID":"c94de32c2c2e454d81c77724a4f29283","UID":"SDOC-SSS-59","_LINK":"SDOC-SSS-59" }, + + {"MID":"f7a600a238e34075ac6b65852492cc2f","UID":"SDOC-SSS-60","_LINK":"SDOC-SSS-60" }, + + {"MID":"efaa3e91045c4d49a70232f09a85ad35","UID":"SECTION-RTS-Requirements-collaboration","_LINK":"SECTION-RTS-Requirements-collaboration" }, + + {"MID":"f9d65df46ff24b0da6cb2bf011f0fc70","UID":"SDOC-SSS-65","_LINK":"SDOC-SSS-65" }, + + {"MID":"29d0374556494f38977ec3c928aa894d","UID":"SDOC-SSS-66","_LINK":"SDOC-SSS-66" }, + + {"MID":"f80a4b7ff03042849c3814c6cd5e5ca4","UID":"SECTION-SSSS-Development-process","_LINK":"SECTION-SSSS-Development-process" }, + + {"MID":"f16ea50f686e412f9c21d8096055bb47","UID":"SDOC-SSS-76","_LINK":"SDOC-SSS-76" }, + + {"MID":"0bebdf77108a499c87e5c0838204c39c","UID":"SDOC-SSS-50","_LINK":"SDOC-SSS-50" }, + + {"MID":"1a39a758930040029db320d8466806c1","UID":"SDOC-SSS-77","_LINK":"SDOC-SSS-77" }, + + {"MID":"8cb5ac6812924f4c8f4afd156b2f021c","UID":"SDOC-SSS-78","_LINK":"SDOC-SSS-78" }, + + {"MID":"e4cd429e9ec04deabfd1fb1381c39e38","UID":"SECTION-SSSS-Licensing-and-distribution","_LINK":"SECTION-SSSS-Licensing-and-distribution" }, + + {"MID":"92bf658bcddb4d2899b78c526fbddf38","_LINK":"92bf658bcddb4d2899b78c526fbddf38" }, + + {"MID":"2f30b94fdc074c8fa5858dfccc6202e8","UID":"SDOC-SSS-38","_LINK":"SDOC-SSS-38" }, + + {"MID":"27b640de8cae44a680b23429c981bee9","UID":"SDOC-SSS-39","_LINK":"SDOC-SSS-39" }, + + {"MID":"1338faad11204073a97212fa0fd11317","UID":"SDOC-SSS-40","_LINK":"SDOC-SSS-40" }, ], + "strictdoc/docs/strictdoc_21_L2_StrictDoc_Requirements.html": [ + + + {"MID":"8a87daf19a7c487f89f396406a974509","_LINK":"None-StrictDoc-Requirements-Specification-L2" }, + + {"MID":"bd8a7931c1e04df9bf08d291488c67aa","_LINK":"1-SDoc-data-model" }, + + {"MID":"e8d5b55055254144b96d1ffeb587ec72","UID":"SDOC-SRS-18","_LINK":"SDOC-SRS-18" }, + + {"MID":"3b31cad22529476db01aca41caf39750","UID":"SDOC-SRS-26","_LINK":"SDOC-SRS-26" }, + + {"MID":"8e2ffffd33af4a969d5bb7ca40cb49a4","UID":"SDOC-SRS-100","_LINK":"SDOC-SRS-100" }, + + {"MID":"2d4020ba06d74571aef7aafe8604215b","UID":"SDOC-SRS-132","_LINK":"SDOC-SRS-132" }, + + {"MID":"f94419a390f2403daa3c4cd0e96f6cb6","UID":"SDOC-SRS-98","_LINK":"SDOC-SRS-98" }, + + {"MID":"435f856197d743c1b9b1dcbe91a22863","UID":"SDOC-SRS-110","_LINK":"SDOC-SRS-110" }, + + {"MID":"abcced8724d743c1b9b1dcbe91a22863","UID":"SDOC-SRS-151","_LINK":"SDOC-SRS-151" }, + + {"MID":"63821c507d584cf985f05904710b9779","UID":"SDOC-SRS-99","_LINK":"SDOC-SRS-99" }, + + {"MID":"899d2f68749b4be1a3124fe4d01de1aa","UID":"SDOC-SRS-135","_LINK":"SDOC-SRS-135" }, + + {"MID":"983653fc26214f46977dc792d65ae65f","UID":"SDOC-SRS-109","_LINK":"SDOC-SRS-109" }, + + {"MID":"fad4cf5dcf4d40518c7c9826fd6bd18a","UID":"SDOC-SRS-31","_LINK":"SDOC-SRS-31" }, + + {"MID":"9930894b08c44f5eba996c0a255e9e3f","UID":"SDOC-SRS-101","_LINK":"SDOC-SRS-101" }, + + {"MID":"fa090bc6976c4275aadbf522da6b920b","UID":"SDOC-SRS-149","_LINK":"SDOC-SRS-149" }, + + {"MID":"54132bc6976c4275aadbf522da6b920b","UID":"SDOC-SRS-150","_LINK":"SDOC-SRS-150" }, + + {"MID":"cbc7f72c6e0b4c858817bf17357105a5","_LINK":"2-SDoc-text-markup" }, + + {"MID":"27a6da48c65445539e536ee034921fb5","UID":"SDOC-SRS-20","_LINK":"SDOC-SRS-20" }, + + {"MID":"1f147feed60245378de2e099facd196d","UID":"SDOC-SRS-136","_LINK":"SDOC-SRS-136" }, + + {"MID":"f48a57c0cd764951a667127ce92fdb12","UID":"SDOC-SRS-127","_LINK":"SDOC-SRS-127" }, + + {"MID":"9df34a7ab79842d99ee68303f334d4eb","UID":"SDOC-SRS-104","_LINK":"SDOC-SRS-104" }, + + {"MID":"8dd6dd3979484dadbe71ac91aab50608","UID":"SDOC-SRS-105","_LINK":"SDOC-SRS-105" }, + + {"MID":"e5e3033c13f24fa083423230da7994f7","UID":"SDOC-SRS-19","_LINK":"SDOC-SRS-19" }, + + {"MID":"b97418ceccff438cb0457ed4bdf1be47","UID":"SDOC-SRS-93","_LINK":"SDOC-SRS-93" }, + + {"MID":"8924c29136944f65ac5efc8b6b90a07b","UID":"SDOC-SRS-21","_LINK":"SDOC-SRS-21" }, + + {"MID":"10d23bfbf4504c2e8c995ae89c500be4","UID":"SDOC-SRS-122","_LINK":"SDOC-SRS-122" }, + + {"MID":"5405c74a9eba4e9193007f01ea98bc85","UID":"SDOC-SRS-22","_LINK":"SDOC-SRS-22" }, + + {"MID":"f871dfc381354daf820cc1e2b94fdb05","UID":"SDOC-SRS-24","_LINK":"SDOC-SRS-24" }, + + {"MID":"b1b4f03d4e404a14be481626803fcfc2","UID":"SDOC-SRS-27","_LINK":"SDOC-SRS-27" }, + + {"MID":"39f117b79cfa4a8485c458280b3d36ce","UID":"SDOC-SRS-23","_LINK":"SDOC-SRS-23" }, + + {"MID":"98628e1db3ca4f52b7632674b69dd657","UID":"SDOC-SRS-25","_LINK":"SDOC-SRS-25" }, + + {"MID":"34b4abc6bb4649de9e56af31c229b0d1","UID":"SECTION-SRS-Graph-database","_LINK":"SECTION-SRS-Graph-database" }, + + {"MID":"2c8d7940a23c443f81a7eb373340894e","UID":"SDOC-SRS-28","_LINK":"SDOC-SRS-28" }, + + {"MID":"272b8933276d4cd48a78fcfa11899573","UID":"SDOC-SRS-29","_LINK":"SDOC-SRS-29" }, + + {"MID":"8330d61fd5b1438fa90f127f88903a0d","UID":"SDOC-SRS-30","_LINK":"SDOC-SRS-30" }, + + {"MID":"07a1e7bef11d4fcca86cd31ee5245d8d","UID":"SDOC-SRS-32","_LINK":"SDOC-SRS-32" }, + + {"MID":"2dad5f293bc442eaa98859c4b5eac0d5","UID":"SDOC-SRS-102","_LINK":"SDOC-SRS-102" }, + + {"MID":"4dce611287954289b1c2a6a15c413611","_LINK":"4-Documentation-tree" }, + + {"MID":"50b2d5d6eb1140f0a2b980203468a5f7","UID":"SDOC-SRS-115","_LINK":"SDOC-SRS-115" }, + + {"MID":"fcbf717009ce4cb2a8def7eb7b377654","UID":"SECTION-SRS-Web-HTML-frontend","_LINK":"SECTION-SRS-Web-HTML-frontend" }, + + {"MID":"a3603184d571449792f5cd752c978686","UID":"SECTION-SRS-General-export-requirements-2","_LINK":"SECTION-SRS-General-export-requirements-2" }, + + {"MID":"37f17fdd57ff47e9b7f4147adaeabde1","UID":"SDOC-SRS-49","_LINK":"SDOC-SRS-49" }, + + {"MID":"37399be2a8ac46889e3437209f9dffa9","UID":"SDOC-SRS-50","_LINK":"SDOC-SRS-50" }, + + {"MID":"f2fd90ad8a2946e7b085233a9c7a8a75","UID":"SDOC-SRS-51","_LINK":"SDOC-SRS-51" }, + + {"MID":"8811f0ef33de4365bc66a602a8c184f6","UID":"SDOC-SRS-48","_LINK":"SDOC-SRS-48" }, + + {"MID":"b99ebebdadb94fec968785429cef9002","UID":"SECTION-SRS-Screen-Project-tree","_LINK":"SECTION-SRS-Screen-Project-tree" }, + + {"MID":"100fa410f4b443e69c48738899cfb5bd","UID":"SDOC-SRS-53","_LINK":"SDOC-SRS-53" }, + + {"MID":"eb04abcfd64146f3b7be509afd1693f9","UID":"SDOC-SRS-107","_LINK":"SDOC-SRS-107" }, + + {"MID":"de7cda1156e043d7999933a2df630a30","UID":"SDOC-SRS-108","_LINK":"SDOC-SRS-108" }, + + {"MID":"185cfc2d1ed4429ebaf0689f70a8cb0a","UID":"SECTION-SRS-Screen-Document-DOC","_LINK":"SECTION-SRS-Screen-Document-DOC" }, + + {"MID":"9c962637ef7f45b6af6c83964b0a06d9","UID":"SDOC-SRS-54","_LINK":"SDOC-SRS-54" }, + + {"MID":"201187122be14ea48d69106d396c6790","UID":"SDOC-SRS-106","_LINK":"SDOC-SRS-106" }, + + {"MID":"13968ae160744e10ba8979eaee2f4f54","UID":"SDOC-SRS-55","_LINK":"SDOC-SRS-55" }, + + {"MID":"edc02a3a97704007b70b93102c76b278","UID":"SDOC-SRS-92","_LINK":"SDOC-SRS-92" }, + + {"MID":"197e00dd3b1d42309250c89de23ea51b","UID":"SDOC-SRS-56","_LINK":"SDOC-SRS-56" }, + + {"MID":"2706ac7ccb8148edb7b200a1b78c25d6","UID":"SDOC-SRS-57","_LINK":"SDOC-SRS-57" }, + + {"MID":"d64f7a0f6ba54672a62634304c54e2d4","UID":"SDOC-SRS-96","_LINK":"SDOC-SRS-96" }, + + {"MID":"956371b6a7824d0b91caa5603faeeae2","UID":"SDOC-SRS-120","_LINK":"SDOC-SRS-120" }, + + {"MID":"738b63b7f55b43eb99f5600bd65f5ba7","UID":"SDOC-SRS-59","_LINK":"SDOC-SRS-59" }, + + {"MID":"2b5d74dafd6d43f4bab4c05ed4da6f0d","UID":"SECTION-SRS-Screen-Table-TBL","_LINK":"SECTION-SRS-Screen-Table-TBL" }, + + {"MID":"e12b4c5b4f13444d99279ab445df5bcc","UID":"SDOC-SRS-62","_LINK":"SDOC-SRS-62" }, + + {"MID":"3c589969eb6a41c19b1dd246dabadbc8","UID":"SECTION-SRS-Screen-Traceability-TR","_LINK":"SECTION-SRS-Screen-Traceability-TR" }, + + {"MID":"35d4c1dd30814c2a80918a90081a88b7","UID":"SDOC-SRS-65","_LINK":"SDOC-SRS-65" }, + + {"MID":"9d337068b6a940b7a98d40697ea200a8","UID":"SECTION-SRS-Screen-Deep-traceability-DTR","_LINK":"SECTION-SRS-Screen-Deep-traceability-DTR" }, + + {"MID":"48bc17d898c94605b17d0267480f57ee","UID":"SDOC-SRS-66","_LINK":"SDOC-SRS-66" }, + + {"MID":"ddf15b3113a54107979752ec192e2ded","_LINK":"5.7-Screen-Project-statistics" }, + + {"MID":"19f7c2d35237423e950584a4a7aa95a7","UID":"SDOC-SRS-97","_LINK":"SDOC-SRS-97" }, + + {"MID":"7968fb7612f64504b5a430ccbffe80f1","UID":"SDOC-SRS-154","_LINK":"SDOC-SRS-154" }, + + {"MID":"d84e3a79b3e443cabe96d7c18ea5d322","_LINK":"5.8-Screen-Document-tree-map" }, + + {"MID":"1520fa85ed7a4f1aa80c49ed858f402f","UID":"SDOC-SRS-157","_LINK":"SDOC-SRS-157" }, + + {"MID":"14ad600308434e6baaeef02d3e90b720","_LINK":"5.9-Screen-Traceability-matrix" }, + + {"MID":"167f6fe66d7247a6bf628ed6c1949275","UID":"SDOC-SRS-112","_LINK":"SDOC-SRS-112" }, + + {"MID":"544f8ffed8604e63a269a3f953681163","_LINK":"5.10-Screen-Project-tree-diff" }, + + {"MID":"aef80cc1a3144564bf23bf3c2a1cfd7f","UID":"SDOC-SRS-111","_LINK":"SDOC-SRS-111" }, + + {"MID":"8380fb39beb04d1c9370733bcfafbc01","_LINK":"5.11-Content-search" }, + + {"MID":"3e85dd6bf05a4cecb8e070fd81aa333a","UID":"SDOC-SRS-155","_LINK":"SDOC-SRS-155" }, + + {"MID":"2d4dc6fc901f4b28aaf414fcfbdfabca","UID":"SECTION-SRS-Requirements-to-source-traceability","_LINK":"SECTION-SRS-Requirements-to-source-traceability" }, + + {"MID":"221b544dab2e4b96be124e1caf84a7c2","UID":"SDOC-SRS-33","_LINK":"SDOC-SRS-33" }, + + {"MID":"2f63252320d546d88462849b521f45ba","_LINK":"6.2-Language-aware-parsing-of-source-code" }, + + {"MID":"79a8080f6e41485f9835ccda067b34bc","UID":"SDOC-SRS-142","_LINK":"SDOC-SRS-142" }, + + {"MID":"85a8080f8441485f9111ccda063b34bc","UID":"SDOC-SRS-146","_LINK":"SDOC-SRS-146" }, + + {"MID":"3456080f1358485f9bacdcda063b34bc","UID":"SDOC-SRS-147","_LINK":"SDOC-SRS-147" }, + + {"MID":"9183080f1358485f9bacdcda0635109","UID":"SDOC-SRS-148","_LINK":"SDOC-SRS-148" }, + + {"MID":"bdeda4b89d82446281e2c7c2eae46708","UID":"SDOC-SRS-143","_LINK":"SDOC-SRS-143" }, + + {"MID":"aefeb5b89d82446281e2c7c2eae12345","UID":"SDOC-SRS-144","_LINK":"SDOC-SRS-144" }, + + {"MID":"5fedbaba39d24da79d074501198ff654","_LINK":"6.5-Forward-linking-from-requirements-to-source-code" }, + + {"MID":"21637d9bec434d32bb6b97ba5a4e70e6","UID":"SDOC-SRS-145","_LINK":"SDOC-SRS-145" }, + + {"MID":"9c1dd6cbabd44ac680985c403cf710a7","_LINK":"6.6-Source-code-markup-Relations" }, + + {"MID":"b9144222a7f3454da2989f494cd9647c","UID":"SDOC-SRS-34","_LINK":"SDOC-SRS-34" }, + + {"MID":"61c25cab106e4283b2c6d64bf1db96b2","UID":"SDOC-SRS-124","_LINK":"SDOC-SRS-124" }, + + {"MID":"61c23c1b106e4583b2c6d64bf1d296ba","UID":"SDOC-SRS-137","_LINK":"SDOC-SRS-137" }, + + {"MID":"86e23c1b106e4adeb2c6d64bf1d29aaa","UID":"SDOC-SRS-140","_LINK":"SDOC-SRS-140" }, + + {"MID":"82345cab106e4283b2c7774bf1db96b1","UID":"SDOC-SRS-139","_LINK":"SDOC-SRS-139" }, + + {"MID":"11c23c1b105e4583b2c6d64bf1d296cc","UID":"SDOC-SRS-138","_LINK":"SDOC-SRS-138" }, + + {"MID":"5ce2ea410dd94927807a258f482483a5","_LINK":"6.7-Source-code-markup-Nodes" }, + + {"MID":"f44df3c6a84f4b4e9201a61023446d9c","UID":"SDOC-SRS-141","_LINK":"SDOC-SRS-141" }, + + {"MID":"3aeb1eea0dba4041a9161a1a8d291999","UID":"SDOC-SRS-35","_LINK":"SDOC-SRS-35" }, + + {"MID":"56ecfb19152e45b09c0e55de2d1d5f05","UID":"SDOC-SRS-36","_LINK":"SDOC-SRS-36" }, + + {"MID":"1acdd6ddf9d24dbaa320230402152226","UID":"SECTION-SRS-Export-import-formats","_LINK":"SECTION-SRS-Export-import-formats" }, + + {"MID":"ce1d174a4d794250931a5ba519583be6","UID":"SECTION-SRS-RST","_LINK":"SECTION-SRS-RST" }, + + {"MID":"2aa615b9cf264e7aad826f4ff466e514","UID":"SDOC-SRS-70","_LINK":"SDOC-SRS-70" }, + + {"MID":"6faffbd1d4cc47ea88c08ef38966e5ec","UID":"SDOC-SRS-71","_LINK":"SDOC-SRS-71" }, + + {"MID":"a906dfb776334b8985f778f0fe1b79e4","UID":"SECTION-SRS-ReqIF","_LINK":"SECTION-SRS-ReqIF" }, + + {"MID":"2bf94015818e4770b2b1947b5fdbb969","UID":"SDOC-SRS-72","_LINK":"SDOC-SRS-72" }, + + {"MID":"bb52b698d7a544aca420d606e4e51356","UID":"SDOC-SRS-73","_LINK":"SDOC-SRS-73" }, + + {"MID":"0ffda51372b641f6ba460bf5ac8b3627","UID":"SECTION-SRS-Excel","_LINK":"SECTION-SRS-Excel" }, + + {"MID":"f7b587f66dda402dbd58768e2740742f","UID":"SDOC-SRS-74","_LINK":"SDOC-SRS-74" }, + + {"MID":"97b587f66dda402dbd58768e2740742f","UID":"SDOC-SRS-152","_LINK":"SDOC-SRS-152" }, + + {"MID":"5aa75848972249d5bec8a40812b99d86","UID":"SDOC-SRS-134","_LINK":"SDOC-SRS-134" }, + + {"MID":"53cfa6296eec45479544e6c5165d7fb9","UID":"SECTION-SRS-Command-line-interface","_LINK":"SECTION-SRS-Command-line-interface" }, + + {"MID":"63373c3875404f1c820add84ea057c73","_LINK":"8.1-General-CLI-requirements" }, + + {"MID":"598647eed3a446fe93e2c54b197f830e","UID":"SDOC-SRS-103","_LINK":"SDOC-SRS-103" }, + + {"MID":"7c6a0efe76b84dcd842ef371796e787e","UID":"SECTION-SRS-Command-Manage","_LINK":"SECTION-SRS-Command-Manage" }, + + {"MID":"bea76afe9bf241eaaf5984b54e00ff76","UID":"SECTION-SRS-Command-Auto-UID","_LINK":"SECTION-SRS-Command-Auto-UID" }, + + {"MID":"7daa1310876a421f8e39f8ac25b99c1d","UID":"SDOC-SRS-85","_LINK":"SDOC-SRS-85" }, + + {"MID":"a0ed7cdd06ec4f0ab6d3ba56cced8c7e","_LINK":"9-Python-API" }, + + {"MID":"a20c5b6a021f4dd5a5a5dda2880c1b87","UID":"SDOC-SRS-125","_LINK":"SDOC-SRS-125" }, + + {"MID":"590121ded2074757aa1720b4d8013426","_LINK":"10-Web-server" }, + + {"MID":"409f462f1f4645c8ad24980727a72521","UID":"SDOC-SRS-126","_LINK":"SDOC-SRS-126" }, + + {"MID":"44eb09d0e5b542d5984d234167e6f337","_LINK":"11-User-experience" }, + + {"MID":"821c01118a9643a188a4ecc5b0cec6a5","UID":"SECTION-SSRS-Strict-mode-by-default","_LINK":"SECTION-SSRS-Strict-mode-by-default" }, + + {"MID":"c4e9dce647654deeab8471d04573f8cb","UID":"SDOC-SRS-6","_LINK":"SDOC-SRS-6" }, + + {"MID":"40e7bd8aabdf4cb889e9d7b01b8729e6","UID":"SECTION-SRS-Configurability","_LINK":"SECTION-SRS-Configurability" }, + + {"MID":"da446e6d3c6d4b13badd4a47bdbcc5a3","UID":"SDOC-SRS-37","_LINK":"SDOC-SRS-37" }, + + {"MID":"a6e70c0619204a26b135d1e5fca45dd7","UID":"SDOC-SRS-39","_LINK":"SDOC-SRS-39" }, + + {"MID":"a8a17295d3e24601891c7d19d5ca67d9","UID":"SDOC-SRS-119","_LINK":"SDOC-SRS-119" }, + + {"MID":"bb14fb2c46f34cf1a82b417b57e95853","UID":"SECTION-SSRS-Performance","_LINK":"SECTION-SSRS-Performance" }, + + {"MID":"acd97ca6b9d2451da53168917d96705e","UID":"SDOC-SRS-1","_LINK":"SDOC-SRS-1" }, + + {"MID":"54602f6d120548c8bbd70d1d25113d53","UID":"SDOC-SRS-95","_LINK":"SDOC-SRS-95" }, + + {"MID":"6fec2f8c0baa4ddba95bee05ad03a785","UID":"SDOC-SRS-2","_LINK":"SDOC-SRS-2" }, + + {"MID":"5f2df631605543d18d57e09bc3791003","UID":"SDOC-SRS-3","_LINK":"SDOC-SRS-3" }, + + {"MID":"d0c4828ed8434613aae844481bbef1ca","UID":"SDOC-SRS-4","_LINK":"SDOC-SRS-4" }, + + {"MID":"4e014d3291ce460c993a0ff334eff4a7","UID":"SDOC-SRS-5","_LINK":"SDOC-SRS-5" }, + + {"MID":"de3a4de44f474784bf233ea7610eac6c","UID":"SECTION-SRS-Quality-requirements","_LINK":"SECTION-SRS-Quality-requirements" }, + + {"MID":"cbee3d3e8a9d440abaa58387c342602c","_LINK":"14.1-General-process" }, + + {"MID":"27319e3866b8423099774d8770950acd","UID":"SDOC-SRS-133","_LINK":"SDOC-SRS-133" }, + + {"MID":"47cec130eb3d4583825355f2635e7011","UID":"SECTION-SRS-Requirements-engineering","_LINK":"SECTION-SRS-Requirements-engineering" }, + + {"MID":"d0ab977bbb494cbf818c92a1cab1bef0","UID":"SDOC-SRS-128","_LINK":"SDOC-SRS-128" }, + + {"MID":"b3fff332c2e342ec9dd372ebd49cb64d","UID":"SDOC-SRS-91","_LINK":"SDOC-SRS-91" }, + + {"MID":"dfbaabc0529f4cc987358ae0563910e9","UID":"SECTION-SRS-Implementation-requirements","_LINK":"SECTION-SRS-Implementation-requirements" }, + + {"MID":"5eb734a4b8944531aa75f0b71ef22d52","UID":"SECTION-SRS-Programming-languages","_LINK":"SECTION-SRS-Programming-languages" }, + + {"MID":"4053ea6c8f6c4c29b89fea0e39912861","UID":"SDOC-SRS-8","_LINK":"SDOC-SRS-8" }, + + {"MID":"131af6457a9f43299306e6dc9fce65a7","UID":"SECTION-SRS-Cross-platform-availability","_LINK":"SECTION-SRS-Cross-platform-availability" }, + + {"MID":"ceee07bdc9bd42fc9a4d0e9b4bfc312f","UID":"SDOC-SRS-9","_LINK":"SDOC-SRS-9" }, + + {"MID":"f8c7586e1b324480ac0bd153e068350d","UID":"SDOC-SRS-10","_LINK":"SDOC-SRS-10" }, + + {"MID":"6af3dbb1e05244579622d3d2a26b5413","UID":"SDOC-SRS-11","_LINK":"SDOC-SRS-11" }, + + {"MID":"852396c217184ba98a0d26b92f36fd04","UID":"SECTION-SRS-Implementation-constraints","_LINK":"SECTION-SRS-Implementation-constraints" }, + + {"MID":"83d5c896004948b48c3597d58267dafa","UID":"SDOC-SRS-89","_LINK":"SDOC-SRS-89" }, + + {"MID":"b22bf982e73246a79a5ba4ae653d21e5","UID":"SDOC-SRS-14","_LINK":"SDOC-SRS-14" }, + + {"MID":"309e4108c1fa47e99d4331af642f3b81","UID":"SDOC-SRS-15","_LINK":"SDOC-SRS-15" }, + + {"MID":"b9cacb97a045418385c7f870b23ac063","UID":"SDOC-SRS-16","_LINK":"SDOC-SRS-16" }, + + {"MID":"70f05a744a0546909dc5b69de88ad41f","UID":"SDOC-SRS-87","_LINK":"SDOC-SRS-87" }, + + {"MID":"7d68821750a54cb5833b608cfc278f47","UID":"SDOC-SRS-88","_LINK":"SDOC-SRS-88" }, + + {"MID":"e4e5b9934e074cdb9567fa197db20a4a","UID":"SECTION-SRS-Coding-constraints","_LINK":"SECTION-SRS-Coding-constraints" }, + + {"MID":"61d96cd1d03e40c48187ee67ac0b65f6","UID":"SDOC-SRS-40","_LINK":"SDOC-SRS-40" }, + + {"MID":"72b5b0c925cf4ef49749b7e466675df1","UID":"SDOC-SRS-41","_LINK":"SDOC-SRS-41" }, + + {"MID":"61e87567580045378d970b327d218cac","UID":"SECTION-SRS-Linting","_LINK":"SECTION-SRS-Linting" }, + + {"MID":"6c0163c56ade49faa353ec06cbd676cd","UID":"SDOC-SRS-42","_LINK":"SDOC-SRS-42" }, + + {"MID":"3029f97a1276499dbbd5d7c71691908e","UID":"SECTION-SRS-Static-analysis","_LINK":"SECTION-SRS-Static-analysis" }, + + {"MID":"2cc9b494ca6248058b6bf63b6e2c1cfa","UID":"SDOC-SRS-43","_LINK":"SDOC-SRS-43" }, + + {"MID":"3aba8ca86d404ba9a063ce125489f35b","UID":"SECTION-SRS-Testing","_LINK":"SECTION-SRS-Testing" }, + + {"MID":"2731c64ab32146eeb4b8167df4a34540","UID":"SDOC-SRS-44","_LINK":"SDOC-SRS-44" }, + + {"MID":"70ef3986cfc4437bbb10288d3b55958c","UID":"SDOC-SRS-45","_LINK":"SDOC-SRS-45" }, + + {"MID":"a42c65f1a53b4c9bb50bc2a1ecb855cb","UID":"SDOC-SRS-46","_LINK":"SDOC-SRS-46" }, + + {"MID":"c90f35ab9a7f4c29a418557151878b9f","UID":"SDOC-SRS-47","_LINK":"SDOC-SRS-47" }, + + {"MID":"83bdb9731e78431cb48d584d279443b4","_LINK":"15-Code-hosting-and-distribution" }, + + {"MID":"dbd186fae9624fd6af8aeb55edfaa43b","UID":"SECTION-SRS-Code-hosting","_LINK":"SECTION-SRS-Code-hosting" }, + + {"MID":"b112c60bc2f34114ab1f3361a1a10a00","UID":"SDOC-SRS-12","_LINK":"SDOC-SRS-12" }, + + {"MID":"bd949cbbe07c4cb29492ea0c8e5c1eec","UID":"SDOC-SRS-118","_LINK":"SDOC-SRS-118" }, ], + "strictdoc/docs/strictdoc_24_development_plan.html": [ + + + {"MID":"e776a9eb7ba0455eb589787028aa0529","_LINK":"None-Development-Plan" }, + + {"MID":"2ba509874f1744cf899643b58a6a2095","_LINK":"2ba509874f1744cf899643b58a6a2095" }, + + {"MID":"24b0bc5efe074a419b441900d30b2876","_LINK":"1-Project-goals" }, + + {"MID":"bdbd5aecefdc41bca1c88b9bce71d512","_LINK":"bdbd5aecefdc41bca1c88b9bce71d512" }, + + {"MID":"f1e3c17829fd4eebbd390fceaeb95619","UID":"SECTION-DP-Project-milestones","_LINK":"SECTION-DP-Project-milestones" }, + + {"MID":"fd9294fdd2b546de993f649767371394","_LINK":"fd9294fdd2b546de993f649767371394" }, + + {"MID":"bf9f3f05501140da8d35cde39493c72f","_LINK":"2.1-The-roadmap-diagram" }, + + {"MID":"53e101acb20e44e89de27f0f77c5f0cb","_LINK":"53e101acb20e44e89de27f0f77c5f0cb" }, + + {"MID":"625560c790a3492eaf45466b787602d7","_LINK":"3-Versioning" }, + + {"MID":"ae2a2af6e3f24f65845c6c37ea7da2e2","_LINK":"ae2a2af6e3f24f65845c6c37ea7da2e2" }, + + {"MID":"ef6bde37e1c54f49afdbd3c6785fd01c","_LINK":"4-Quality" }, + + {"MID":"14d0e79fcadc43f1a1cee2d66ae6b763","_LINK":"14d0e79fcadc43f1a1cee2d66ae6b763" }, + + {"MID":"bc49b1e28fff4386b3bf5788a6686a79","_LINK":"4.1-Linting-of-code" }, + + {"MID":"f6de78d4a95b4970ab55563e18472a92","_LINK":"f6de78d4a95b4970ab55563e18472a92" }, + + {"MID":"38ce17138405490b80e06037b40dc49d","_LINK":"4.2-Static-analysis" }, + + {"MID":"ad63541e341d4648ab59b4a0817a7e3c","_LINK":"ad63541e341d4648ab59b4a0817a7e3c" }, + + {"MID":"2834756354a7420499d0deecfe0ee9c4","_LINK":"4.3-Testing" }, + + {"MID":"ee541e7062704b3f919bcaac2bce2723","_LINK":"ee541e7062704b3f919bcaac2bce2723" }, + + {"MID":"2004550abd54486d9aa5f98282eef6e1","_LINK":"4.4-Traceability-between-StrictDoc-requirements-code-and-tests" }, + + {"MID":"f829d28d2e3d4bcaa38f44ffb1ac0373","_LINK":"f829d28d2e3d4bcaa38f44ffb1ac0373" }, + + {"MID":"30b874ab3eac4d429633537e1e89677f","_LINK":"4.5-Continuous-integration-CI" }, + + {"MID":"85e3915270f144c7a76ccfa0e088d93d","_LINK":"85e3915270f144c7a76ccfa0e088d93d" }, + + {"MID":"2ae3a8822a424a9e8ad6976c2476d109","_LINK":"5-Python-baseline" }, + + {"MID":"081ee6266bab4a3a80f290c95e055a25","_LINK":"081ee6266bab4a3a80f290c95e055a25" }, ], + "strictdoc/docs/strictdoc_25_design.html": [ + + + {"MID":"8e68536f7d4047ada15d3a3043580c4a","UID":"SDOC_DD","_LINK":"SDOC_DD" }, + + {"MID":"2c006e9044074d239db4a45328633657","_LINK":"2c006e9044074d239db4a45328633657" }, + + {"MID":"a74f4e7328714765b5e051768ca8383d","_LINK":"1-Overview" }, + + {"MID":"23937f96ac8e437d81686071cc42d6bf","_LINK":"23937f96ac8e437d81686071cc42d6bf" }, + + {"MID":"f296843b2027457f851a159abafc97e3","_LINK":"2-Building-blocks" }, + + {"MID":"bae7db5591fe418a857af4fb02ad4fec","_LINK":"bae7db5591fe418a857af4fb02ad4fec" }, + + {"MID":"5328a6f4c8544f5e92a91279b5b2c0df","UID":"SECTION-DD-High-level-architecture","_LINK":"SECTION-DD-High-level-architecture" }, + + {"MID":"dcb6369b8d0949f0b06c9ee5b2188b12","_LINK":"dcb6369b8d0949f0b06c9ee5b2188b12" }, + + {"MID":"9b81ed50014c412bb617e90552e58879","_LINK":"4-StrictDoc-command-line-application" }, + + {"MID":"0e0fca629004496084dbe5e217874c36","_LINK":"0e0fca629004496084dbe5e217874c36" }, + + {"MID":"dbcfa8a98e4f4928b0b35938d57dd644","_LINK":"5-StrictDoc-web-application" }, + + {"MID":"f84c0efa27954e44a8b5c1dd3517f90f","_LINK":"f84c0efa27954e44a8b5c1dd3517f90f" }, + + {"MID":"dd2bd75fdffe49a1a0299b804538329c","_LINK":"5.1-The-HTML-Over-the-Wire-Hotwire-architecture" }, + + {"MID":"5577da2187294eb986ec84d9fe26bbdf","_LINK":"5577da2187294eb986ec84d9fe26bbdf" }, + + {"MID":"50f1db3236fa426ea5904974ee724939","_LINK":"6-Static-HTML-search" }, + + {"MID":"5fedd9aac1684adc8fe1f3b87557345d","UID":"SDOC-SRS-156","_LINK":"SDOC-SRS-156" }, + + {"MID":"108af17c077c48bea04557c9d325e924","_LINK":"7-Parsing-SDoc-files" }, + + {"MID":"8a926f0a67424a1eb5db73bb5abca0cd","_LINK":"8a926f0a67424a1eb5db73bb5abca0cd" }, + + {"MID":"4db723c7ae2c4350b2f184783cb638e7","UID":"SECTION-DD-Caching-artifacts","_LINK":"SECTION-DD-Caching-artifacts" }, + + {"MID":"d351d1bf6c2a48d88e9b9bdc50e029ff","_LINK":"d351d1bf6c2a48d88e9b9bdc50e029ff" }, + + {"MID":"4b904b71438c47fcac8aba14df2dd7c0","_LINK":"9-HTML-escaping" }, + + {"MID":"e8a03713c7f644698d9a72cc79b2a299","_LINK":"e8a03713c7f644698d9a72cc79b2a299" }, + + {"MID":"92e8709709614f0f9ccdf702799bf56c","_LINK":"10-ReqIF-interface" }, + + {"MID":"abe8709709614f0f9ccdf702799bf511","UID":"SDOC-SRS-153","_LINK":"SDOC-SRS-153" }, ], + "strictdoc/docs/strictdoc_28_Backlog.html": [ + + + {"MID":"cabef7629dda4f2fb34255d01fea8d5f","_LINK":"None-StrictDoc-Backlog" }, + + {"MID":"35bc1e28ec724aefbcff864c89399877","_LINK":"35bc1e28ec724aefbcff864c89399877" }, + + {"MID":"d1e697dea73144c48a4b88d0d784a517","UID":"SECTION-SB-Open-source-requirements-tool-challenges","_LINK":"SECTION-SB-Open-source-requirements-tool-challenges" }, + + {"MID":"d65fe32e8ea444ceb99c174bfc06df7d","_LINK":"d65fe32e8ea444ceb99c174bfc06df7d" }, + + {"MID":"fc76b207c152433888a86a166f58a425","UID":"SDOC-SRS-13","_LINK":"SDOC-SRS-13" }, + + {"MID":"7614a9f4f7bd4b1c9899995ac3d1e6ad","UID":"SECTION-SB-Backlog","_LINK":"SECTION-SB-Backlog" }, + + {"MID":"e61e7224e27d41ceacad9bf6051bd6f0","UID":"SDOC-BACKLOG-6","_LINK":"SDOC-BACKLOG-6" }, + + {"MID":"07a9636655314912a996ecf5692ae3d5","UID":"SDOC-SRS-86","_LINK":"SDOC-SRS-86" }, + + {"MID":"0425151bf3944e0ebc1c5acabcb35905","UID":"SECTION-SRS-Screen-Project-home","_LINK":"SECTION-SRS-Screen-Project-home" }, + + {"MID":"0a158ddac310483489ef08f618ba8741","UID":"SDOC-SRS-52","_LINK":"SDOC-SRS-52" }, + + {"MID":"1d8a2c3cf8c24e1b92fe212ab7efacf2","_LINK":"2.4-Screen-Traceability-navigator" }, + + {"MID":"dbfe92e9ec3b44e9a3e1c9e89a0e9ef5","UID":"SDOC-SRS-113","_LINK":"SDOC-SRS-113" }, + + {"MID":"2654d541d5ce4cb6ad9fcfe2eaa9abb4","UID":"SECTION-SB-Formal-modeling","_LINK":"SECTION-SB-Formal-modeling" }, + + {"MID":"5958a5c0aad8486b84ad31bc014d4ad3","UID":"SDOC-RMC-27","_LINK":"SDOC-RMC-27" }, + + {"MID":"99e84f704c074cf28de51ce6fb183a66","UID":"SDOC-RMC-29","_LINK":"SDOC-RMC-29" }, + + {"MID":"44b30591f6674ede821324fe527babd9","UID":"SDOC-RMC-55","_LINK":"SDOC-RMC-55" }, + + {"MID":"045aee37b0a24b3cb62cabd8b614aaf7","UID":"SDOC-RMC-28","_LINK":"SDOC-RMC-28" }, + + {"MID":"7e0fd5af118b4f5d8ecbf0d8ef3841e6","UID":"SDOC-RMC-30","_LINK":"SDOC-RMC-30" }, + + {"MID":"061392f275b04c93b664faad9ed4f483","UID":"SECTION-SRS-LaTeX-export","_LINK":"SECTION-SRS-LaTeX-export" }, + + {"MID":"9ec7f471ccb243ea9d86cf2756403b8f","UID":"SDOC-SRS-76","_LINK":"SDOC-SRS-76" }, + + {"MID":"5a0304abcd8a44dab5d032dce6625a5d","UID":"SDOC-BACKLOG-1","_LINK":"SDOC-BACKLOG-1" }, + + {"MID":"162a3da4d9d74c15b5078c9859657ce3","UID":"SDOC-BACKLOG-2","_LINK":"SDOC-BACKLOG-2" }, + + {"MID":"06f16ede826749a389c8d7de9828e0eb","UID":"SDOC-BACKLOG-3","_LINK":"SDOC-BACKLOG-3" }, + + {"MID":"0e69828e1d914e20852aef7cfc4e87c4","UID":"SDOC-BACKLOG-9","_LINK":"SDOC-BACKLOG-9" }, + + {"MID":"34246811634c493dbc771ae4c8bc3b14","UID":"SDOC-BACKLOG-4","_LINK":"SDOC-BACKLOG-4" }, + + {"MID":"3f50a97bc70842dbabeea911f653ef90","UID":"SDOC-BACKLOG-7","_LINK":"SDOC-BACKLOG-7" }, + + {"MID":"72285f9b09de48a29fef8f85431e4a94","UID":"SDOC-BACKLOG-8","_LINK":"SDOC-BACKLOG-8" }, + + {"MID":"bdedcdd91c7f4efb8787b5a82cfec49b","UID":"SDOC-SRS-114","_LINK":"SDOC-SRS-114" }, + + {"MID":"478c38d2cca94107b18fe3dc5b8bfefd","_LINK":"2.15-Multi-user-workflow" }, + + {"MID":"f97dc050ddab4a08bf1689200118faaa","UID":"SDOC-SRS-123","_LINK":"SDOC-SRS-123" }, + + {"MID":"e5b2aff0c1db4b11a8c75c278813300b","UID":"SDOC-SRS-130","_LINK":"SDOC-SRS-130" }, + + {"MID":"440f0bc311b84421a7e8bea9dfd2448b","UID":"SDOC-SRS-131","_LINK":"SDOC-SRS-131" }, + + {"MID":"caecd704ed634d718deff07ba0db7fa6","UID":"SDOC-SRS-116","_LINK":"SDOC-SRS-116" }, + + {"MID":"b74a7a3209d247f6b357513b0cce5000","UID":"SDOC-SRS-121","_LINK":"SDOC-SRS-121" }, + + {"MID":"fa7fd8fd3a694d8e93953fd60e19fdde","UID":"SDOC-SRS-61","_LINK":"SDOC-SRS-61" }, + + {"MID":"7a06d1433e4c4b0eaf55ede14507eac9","UID":"SDOC-SRS-94","_LINK":"SDOC-SRS-94" }, + + {"MID":"45b8513623744776899abfbf99c95286","UID":"SDOC-SRS-58","_LINK":"SDOC-SRS-58" }, + + {"MID":"c02a65f2716f4a86b8f83e3a7b4cd31c","UID":"SDOC-SRS-60","_LINK":"SDOC-SRS-60" }, + + {"MID":"7aa5316dd056465e97044e221412290b","UID":"SDOC-SRS-63","_LINK":"SDOC-SRS-63" }, + + {"MID":"229378602d1847cb83c61ab3e97bb22b","UID":"SDOC-SRS-64","_LINK":"SDOC-SRS-64" }, + + {"MID":"b3472533d57145bc9790724efe56075c","_LINK":"2.24-Screen-Impact-analysis" }, + + {"MID":"f5feb38777064d9db939279d32f79130","UID":"SDOC-SRS-117","_LINK":"SDOC-SRS-117" }, + + {"MID":"a7c504087df04cedb5541d83ed988e9b","UID":"SDOC-SRS-75","_LINK":"SDOC-SRS-75" }, + + {"MID":"4a49c79a1bdd4d4aae5f62bc1d2a56d0","UID":"SECTION-SB-Backlog-Web-based-user-interface","_LINK":"SECTION-SB-Backlog-Web-based-user-interface" }, + + {"MID":"d10cddd846de4961bbb0bc8b8c6550d3","_LINK":"d10cddd846de4961bbb0bc8b8c6550d3" }, + + {"MID":"866a55f2e47b477ebcd52ea57da4f037","UID":"SECTION-SB-Backlog-Nice-to-have","_LINK":"SECTION-SB-Backlog-Nice-to-have" }, + + {"MID":"d02e104a518c4627ac918a33ad165c96","_LINK":"d02e104a518c4627ac918a33ad165c96" }, + + {"MID":"eb1fab2bbea541c8b00f70780326c9fc","UID":"SECTION-SB-Backlog-Technical-debt","_LINK":"SECTION-SB-Backlog-Technical-debt" }, + + {"MID":"85e94802132048d29ca73cf2096c6758","_LINK":"85e94802132048d29ca73cf2096c6758" }, + + {"MID":"6ef8c2ad683d46af948674727c7a52b4","UID":"SECTION-SB-Open-questions","_LINK":"SECTION-SB-Open-questions" }, + + {"MID":"717df91623014fb98ba34ab8173b94ab","UID":"SECTION-SB-One-or-many-input-sdoc-trees","_LINK":"SECTION-SB-One-or-many-input-sdoc-trees" }, + + {"MID":"b778a18ede89464c936be2b2af492e24","_LINK":"b778a18ede89464c936be2b2af492e24" }, ], + "strictdoc/docs/strictdoc_30_credits.html": [ + + + {"MID":"d92baa6b8cf745a2ab6be86616b06277","_LINK":"None-Credits" }, + + {"MID":"18cf0af34c194590b3eb391c2ef94074","_LINK":"18cf0af34c194590b3eb391c2ef94074" }, + + {"MID":"c89b5ba9c6dc427497647b64048a2c98","_LINK":"1-Contributions-to-StrictDoc" }, + + {"MID":"e6de83e242bc4aea96622a2e7585f135","_LINK":"e6de83e242bc4aea96622a2e7585f135" }, + + {"MID":"4d59a5df65d547e685cb491937793f69","_LINK":"2-Open-source-software" }, + + {"MID":"4428ac9ec3504dda9f1e1e845ffb3c75","_LINK":"4428ac9ec3504dda9f1e1e845ffb3c75" }, + + {"MID":"2da7450f8b804a7a8494921486bd4d8e","_LINK":"3-Hosting-and-Continuous-Integration" }, + + {"MID":"5b702e6f9b714e458b059912ca8e196a","_LINK":"5b702e6f9b714e458b059912ca8e196a" }, + + {"MID":"43ccb6448b9d4b65a2c40df54d1707aa","_LINK":"4-Free-and-commercial-IDEs-by-JetBrains" }, + + {"MID":"b4729e592d104fe3818c8949615ffef9","_LINK":"b4729e592d104fe3818c8949615ffef9" }, ], + "strictdoc/docs_extra/DO178_requirements.html": [ + + + {"MID":"b3727ea826c24d42a84e88983e7c1e24","_LINK":"None-Technical-Note-DO-178C-requirements-tool-requirements" }, + + {"MID":"fc77215d50134f17aa242b4df7310099","_LINK":"fc77215d50134f17aa242b4df7310099" }, + + {"MID":"6be4dbe02b4346df9849eaa6fe077e0e","UID":"SECTION-DR-Already-implemented-features","_LINK":"SECTION-DR-Already-implemented-features" }, + + {"MID":"af14393406e64ab6b2911eefa4358f33","UID":"DO178-1","_LINK":"DO178-1" }, + + {"MID":"0a19cae3756a4d2f9f0be9e57c44a8fa","UID":"DO178-2","_LINK":"DO178-2" }, + + {"MID":"dd075d6cd4fb4ad2b731a9b8068efade","UID":"DO178-14","_LINK":"DO178-14" }, + + {"MID":"03346e43cd0b43529cc3b958a00d8d35","UID":"DO178-3","_LINK":"DO178-3" }, + + {"MID":"4f0809d49292413eb621a47a8b405bc7","UID":"DO178-4","_LINK":"DO178-4" }, + + {"MID":"c74bba92cec34bf7b1046689c2dfc54c","UID":"DO178-5","_LINK":"DO178-5" }, + + {"MID":"8b73ce9efcc840b2be272d5aaef08e70","UID":"DO178-6","_LINK":"DO178-6" }, + + {"MID":"8c654ff659a94cceb0c7a52bc8ae4073","UID":"DO178-8","_LINK":"DO178-8" }, + + {"MID":"cfdb3867a4a64b3eb9405e998c11ae72","UID":"DO178-7","_LINK":"DO178-7" }, + + {"MID":"40d4a56263d0420691dbde5d7889895a","UID":"DO178-13","_LINK":"DO178-13" }, + + {"MID":"8bd26640450f448c8228464223c21373","UID":"DO178-9","_LINK":"DO178-9" }, + + {"MID":"09f3e3faa1b84aafb19f26e22ab06c95","UID":"SECTION-DR-Needs-discussion","_LINK":"SECTION-DR-Needs-discussion" }, + + {"MID":"db4336a8f119443cae6a70d74a0601da","UID":"DO178-19","_LINK":"DO178-19" }, + + {"MID":"7d18b7674050495c935dc4669dfb260e","UID":"DO178-15","_LINK":"DO178-15" }, + + {"MID":"b0c13e71074242ab91d6344514b82657","UID":"DO178-10","_LINK":"DO178-10" }, + + {"MID":"7e8a475859084e9d95eda3f2c1381ae7","UID":"DO178-11","_LINK":"DO178-11" }, + + {"MID":"23c11790799c4457804bcb9d6288931e","UID":"DO178-12","_LINK":"DO178-12" }, + + {"MID":"a1fb2f7cd7cd4c8eb33c3407a791fd88","UID":"DO178-16","_LINK":"DO178-16" }, + + {"MID":"756503f4934a4aed81dfe090b2538e2a","UID":"DO178-17","_LINK":"DO178-17" }, + + {"MID":"8120b4f6be4e4ecd83c0b69a3424e0be","UID":"DO178-18","_LINK":"DO178-18" }, ], + "strictdoc/docs_extra/Zephyr_requirements.html": [ + + + {"MID":"2e7deb9808bb42879e7463b1d7ac9bba","_LINK":"None-Technical-Note-Zephyr-requirements-tool-requirements" }, + + {"MID":"68e87901205c4b0dbb6a0071479330b5","UID":"ZEP-1","_LINK":"ZEP-1" }, + + {"MID":"36415af620f645388af4dc51c2a19b97","UID":"ZEP-2","_LINK":"ZEP-2" }, + + {"MID":"7a9e26240f5c4fe29143cf3348ac3d3b","UID":"ZEP-3","_LINK":"ZEP-3" }, + + {"MID":"a5928d6204a84d02a3c367d6dbc54092","UID":"ZEP-4","_LINK":"ZEP-4" }, + + {"MID":"55bbe693d12d4e00956d41a837ef15ef","UID":"ZEP-5","_LINK":"ZEP-5" }, + + {"MID":"b58c387f3a2d4cf6a540e279dbe49e47","UID":"ZEP-6","_LINK":"ZEP-6" }, + + {"MID":"17a141add32f4d7696b5a74b6507b9aa","UID":"ZEP-7","_LINK":"ZEP-7" }, + + {"MID":"d0d258d8640443578c7ec508c014b50d","UID":"ZEP-8","_LINK":"ZEP-8" }, + + {"MID":"611546bf2409417e8844f93854d54268","UID":"ZEP-9","_LINK":"ZEP-9" }, + + {"MID":"f1f6da87da5c49d1b8c3441b14863975","UID":"ZEP-10","_LINK":"ZEP-10" }, + + {"MID":"55cbd9b658164414adb0c13b0446e3f7","UID":"ZEP-11","_LINK":"ZEP-11" }, + + {"MID":"a2930225168147808e6554a4da49aeb4","UID":"ZEP-12","_LINK":"ZEP-12" }, + + {"MID":"2614d6673a594da995df672e8943839e","UID":"ZEP-13","_LINK":"ZEP-13" }, + + {"MID":"a92a28ce3bcb4b3590f36e778a604ccb","UID":"ZEP-14","_LINK":"ZEP-14" }, + + {"MID":"b74919612283422a9e139418a20926c6","UID":"ZEP-15","_LINK":"ZEP-15" }, ], +}; diff --git a/tests/fuzz/20_L1_Open_Requirements_Tool_202511/assets/project_tree.css b/tests/fuzz/20_L1_Open_Requirements_Tool_202511/assets/project_tree.css new file mode 100644 index 0000000..940d06b --- /dev/null +++ b/tests/fuzz/20_L1_Open_Requirements_Tool_202511/assets/project_tree.css @@ -0,0 +1,218 @@ + +/* tree */ +.project_tree { + position: relative; + /* width: -moz-fit-content; + width: fit-content; */ + /* margin: 0 auto; */ +} + +.project_tree details>summary { + list-style: none; +} + +.project_tree summary::-webkit-details-marker { + display: none +} + +/* folder */ + +.project_tree-folder { + border: var(--base-border); + border-radius: calc(0.5 * var(--base-rhythm)); + overflow: hidden; + position: relative; + padding-left: var(--base-rhythm); + padding-right: var(--base-rhythm); + background: var(--color-bg-secondary); +} + +.project_tree-folder[open] { + background: transparent; +} + +.project_tree-folder.source { + padding-right: 0; + border-right: none; + border-bottom: none; +} + +.project_tree-folder summary { + position: relative; + cursor: pointer; + user-select: none; +} + +.project_tree-folder-title { + font-weight: bold; + display: flex; + column-gap: calc(0.5 * var(--base-rhythm)); + font-size: 0.85rem; + line-height: 1.2; + color: #666; + padding: calc(0.5 * var(--base-rhythm)); + position: relative; +} + +.project_tree-folder-title::before, +.project_tree-folder-title::after { + content: ''; + position: absolute; + top: 0; + right: -20px; + left: -20px; + bottom: 0; +} + +.project_tree-folder-title::after { + background: var(--color-bg-secondary); +} + +.project_tree-folder summary:hover > .project_tree-folder-title::before { + background: var(--color-bg-secondary); +} + +.project_tree-folder summary:hover > .project_tree-folder-title, +.project_tree-folder summary:hover::after { + color: var(--color-fg-accent); +} + +/* .icon_collapse_expand is class inside SVG */ +.project_tree-folder-title .icon_collapse_expand { + margin-left: auto; +} +/* .collapsed and .expanded are classes inside SVG .icon_collapse */ +.project_tree-folder > summary .icon_collapse_expand .collapsed { + display: initial; +} +.project_tree-folder > summary .icon_collapse_expand .expanded { + display: none; +} +.project_tree-folder[open] > summary .icon_collapse_expand .expanded { + display: initial; +} +.project_tree-folder[open] > summary .icon_collapse_expand .collapsed { + display: none; +} + +.project_tree-folder-content { + display: flex; + flex-direction: column; + justify-content: space-between; + gap: var(--base-rhythm); + padding-top: var(--base-rhythm); + padding-bottom: var(--base-rhythm); +} + +/* file */ + +.project_tree-file { + display: flex; + column-gap: var(--base-rhythm); + padding: calc(0.5 * var(--base-rhythm)); + border-radius: calc(0.5 * var(--base-rhythm)); + transition: background .3s; + border: 1px solid rgba(255,255,255,0); +} + +.project_tree-file:not([href]):hover { + border: 1px solid rgba(255,255,255,0.75); + background: rgba(0,0,0,0.01); +} + +.project_tree-file[href]:hover { + background: rgba(255,255,255,0.5); +} + +.project_tree-file[href]:hover .project_tree-file-icon, +.project_tree-file[href]:hover .project_tree-file-title { + color: var(--color-fg-accent); +} + +.project_tree-file-details { + display: block; +} + +.project_tree-file-icon { + line-height: 0; +} + +.project_tree-file-title { + font-size: 1em; + font-weight: 500; + line-height: 1.2; + transition: color .2s; +} + +.project_tree-file-icon, +.project_tree-file-title { + color: var(--color-fg-secondary); +} + +.project_tree-file[href] .project_tree-file-icon, +.project_tree-file[href] .project_tree-file-title { + color: var(--color-fg-main); +} + +.project_tree-file-name { + font-size: 0.85rem; + line-height: 1.2; + color: var(--color-fg-secondary); + margin-top: 0.25rem; +} + +.project_tree-file-aside { + margin-left: auto; +} + +/* dashboard */ + +.dashboard { + display: flex; + align-items: flex-start; + gap: var(--base-rhythm); +} + +.dashboard-main { + flex-grow: 1; +} + +.dashboard-aside { + width: 30%; + /* max-width: 300px; */ + font-size: 0.75rem; + display: flex; + gap: var(--base-rhythm); + flex-direction: column; +} + +.dashboard-block:empty { + display: none; +} + +.dashboard-block { + border: var(--base-border); + border-radius: calc(0.5 * var(--base-rhythm)); + overflow: hidden; + position: relative; + padding: var(--base-rhythm); +} + +.dashboard-block-title { + font-weight: 600; + position: relative; + top: calc(-1 * var(--base-rhythm)); + color: var(--color-fg-secondary); + padding: calc(0.5 * var(--base-rhythm)) 0; +} + +.dashboard-block-title::after { + content: ''; + position: absolute; + top: -20px; + left: -20px; + right: -20px; + bottom: 0; + background-color: rgba(0,0,0,0.025); + border-bottom: var(--base-border); +} diff --git a/tests/fuzz/20_L1_Open_Requirements_Tool_202511/assets/project_tree.js b/tests/fuzz/20_L1_Open_Requirements_Tool_202511/assets/project_tree.js new file mode 100644 index 0000000..79a48c9 --- /dev/null +++ b/tests/fuzz/20_L1_Open_Requirements_Tool_202511/assets/project_tree.js @@ -0,0 +1,373 @@ +const FRAME_SELECTOR = '#frame_project_tree'; +const SWITCH_SELECTOR = '#project_tree_controls'; +const FRAGMENT_ATTR = 'included-document'; +const FRAGMENT_SELECTOR = `.project_tree-file[${FRAGMENT_ATTR}]`; +const FILE_SELECTOR = `.project_tree-file`; +const FOLDER_SELECTOR = `.project_tree-folder`; + +// This class Switch was taken +// from strictdoc/export/html/_static/source-code-coverage.js +// and improved a bit: +class Switch { + constructor({ + callback, + labelText, + checked, + componentClass, + colorOn, + colorOff, + size, + stroke, + units, + position, + topPosition, + leftPosition, + rightPosition, + bottomPosition, + dataTestID, + }) { + this.colorOn = colorOn || 'rgb(242, 100, 42)'; + this.colorOff = colorOff || 'rgb(200, 200, 200)'; + this.labelInitialText = labelText || ''; + this.checked = (checked === false) ? false : true; + + this.dataTestID = dataTestID || 'std-switch'; + this.componentClass = componentClass || 'std-switch-scc'; + this.size = size || 0.75; + this.stroke = stroke || 0.25; + this.units = units || 'rem'; + + this.topPosition = topPosition || 'unset', + this.leftPosition = leftPosition || 'unset', + this.rightPosition = rightPosition || 'unset', + this.bottomPosition = bottomPosition || 'unset', + this.position = position || 'static', + + this.controlLabelTextSpan = document.createElement('span'); + this.callback = callback; + } + + create() { + const block = document.createElement('div'); + block.classList.add(this.componentClass); + const label = document.createElement('label'); + label.classList.add(`${this.componentClass}__label`); + const input = document.createElement('input'); + input.classList.add(`${this.componentClass}__input`); + input.type = 'checkbox'; + input.checked = this.checked; + const slider = document.createElement('span'); + slider.classList.add(`${this.componentClass}__slider`); + + label.append(input, slider, this.controlLabelTextSpan); + block.append(label); + + this.insertStyle(); + this.updateLabelText(this.labelInitialText); + input.addEventListener('change', () => this.callback(input.checked)); + + label.setAttribute('data-testid', this.dataTestID); + + return block; + } + + updateLabelText(text) { + this.controlLabelTextSpan.innerHTML = text; + } + + insertStyle() { + + const css = ` + .${this.componentClass} { + display: inline-block; + line-height: 0; + position: ${this.position}; + top: ${this.topPosition}; + left: ${this.leftPosition}; + right: ${this.rightPosition}; + bottom: ${this.bottomPosition}; + } + .${this.componentClass}__label { + display: inline-flex; + column-gap: 8px; + line-height: ${this.size * 1.6}${this.units}; + line-height: 1.25; + align-items: flex-start; + justify-content: flex-start; + user-select: none; + cursor: pointer; + font-size: small; + } + .${this.componentClass}__input { + opacity: 0; + width: 0; + height: 0; + position: absolute; + } + .${this.componentClass}__slider { + position: relative; + cursor: pointer; + background-color: ${this.colorOff}; + -webkit-transition: .4s; + transition: .4s; + display: inline-block; + width: ${this.size * 2 + this.stroke * 2}${this.units}; + min-width: ${this.size * 2 + this.stroke * 2}${this.units}; + height: ${this.size + this.stroke * 2}${this.units}; + margin-right: ${this.size * 0.5}${this.units}; + border-radius: ${this.size * 0.5 + this.stroke}${this.units}; + } + .${this.componentClass}__slider::before { + position: absolute; + content: ""; + height: ${this.size}${this.units}; + width: ${this.size}${this.units}; + left: ${this.stroke}${this.units}; + bottom: ${this.stroke}${this.units}; + background-color: white; + -webkit-transition: .4s; + transition: .4s; + border-radius: 50%; + } + input:checked + .${this.componentClass}__slider { + background-color: ${this.colorOn}; + } + input:focus + .${this.componentClass}__slider { + box-shadow: 0 0 1px ${this.colorOn}; + } + input:checked + .${this.componentClass}__slider::before { + -webkit-transform: translateX(${this.size}${this.units}); + -ms-transform: translateX(${this.size}${this.units}); + transform: translateX(${this.size}${this.units}); + } + `; + + const head = document.querySelector('head'); + const style = document.createElement('style'); + style.append(document.createTextNode(css)); + style.setAttribute("data-slider-styles", ''); + head.append(style); + } + +} + +class ProjectTree { + constructor({ + mutatingFrame, + controlTarget + }) { + this.mutatingFrame = mutatingFrame; + this.controlTarget = controlTarget; + this.fragments = []; + + this.control; + this.controlElement; + + this.state = { + fragmentVisibility: { + _sessionStorageItemName: 'projectTreeFragmentVisibility', + initial: 'hide', + current: null, + } + }; + } + + init() { + // console.log('First time call.'); + + console.assert(this.mutatingFrame, `mutatingFrame not found on the page`); + this._addMutationObserver(); + + this._initStateAndStorage(); + + // * this.controlTarget may not be present on the page, for example, in the case of static export. + // console.assert(this.controlTarget, `controlTarget not found on the page`); + + this._createControl(); + this._addControl(); + + this.updateFragmentsAndControl(); + } + + getCurrentFragmentVisibilityBool() { + return (this.state.fragmentVisibility.current === 'show') ? true : false; + } + + updateFragmentsAndControl() { + // Update list of fragment elements (files + folders) and toggle visibility + const fileFragments = this._getFileFragments(); + const folderFragments = this._getFoldersWithOnlyFragments(); + + // Store both individual file fragments and folders that contain only fragments + this.fragments = [...fileFragments, ...folderFragments]; + + // Only show number of file fragments (folders are excluded from count) + this._updateControl(fileFragments.length); + this._updateFragmentsVisibility(this.getCurrentFragmentVisibilityBool()); + } + + _getFileFragments() { + // Find all file elements that are marked as included fragments + return [...this.mutatingFrame.querySelectorAll(FRAGMENT_SELECTOR)]; + } + + _getFoldersWithOnlyFragments() { + // Find folders that contain only fragment files and no other files + const folders = [...this.mutatingFrame.querySelectorAll(FOLDER_SELECTOR)]; + return folders.filter(folder => { + // Consider only those folders if it contains only fragment files + const fragCount = folder.querySelectorAll(FRAGMENT_SELECTOR).length; + const fileCount = folder.querySelectorAll(FILE_SELECTOR).length; + return fragCount > 0 && fragCount === fileCount; + }); + } + + _getFragments() { + let fragments = [...this.mutatingFrame.querySelectorAll(FRAGMENT_SELECTOR)]; + const folders = this._prepareFragmentsFolders(fragments); + return fragments.concat(folders); + } + + _prepareFragmentsFolders(fragments) { + // Temporary solution. + // It is possible to optimize the search to reduce the number of runs. + + const result = []; + // .project_tree-folder > .project_tree-folder-content > .project_tree-file === fragment + const folders = [...this.mutatingFrame.querySelectorAll(FOLDER_SELECTOR)]; + folders.forEach(folder => { + const frag = folder.querySelectorAll(FRAGMENT_SELECTOR); + const file = folder.querySelectorAll(FILE_SELECTOR); + if (frag.length === file.length) { + result.push(folder); + } + }); + return result + } + + _updateFragmentsVisibility(bool) { + const display = bool ? '' : 'none'; + this.fragments.forEach(element => { + element.style.display = display; + }) + } + + _updateControl(num) { + this.control.updateLabelText(`Show ${num} fragment${num > 1 ? 's' : ''} included in other documents in the Project document tree.`) + + if (num) { + this._addControl(); + } else { + this._removeControl(); + } + } + + toggleFragmentsVisibility(checked) { + this._updateFragmentsVisibility(checked); + + if (checked) { + this._updateCurrentState('show', 'fragmentVisibility'); + this._setSessionStorageItem('show', 'fragmentVisibility'); + } else { + this._updateCurrentState('hide', 'fragmentVisibility'); + this._setSessionStorageItem('hide', 'fragmentVisibility'); + } + } + + _initStateAndStorage(option = 'fragmentVisibility') { + const storage = this._getSessionStorageItem(option); + + if (storage) { + this._updateCurrentState(storage); + } else { + this._updateCurrentState(this.state[option].initial); + this._setSessionStorageItem(this.state[option].initial); + } + } + + _updateCurrentState(value, option = 'fragmentVisibility') { + this.state[option].current = value; + } + + _setSessionStorageItem(nextState, option = 'fragmentVisibility') { + sessionStorage.setItem(this.state[option]._sessionStorageItemName, nextState); + } + + _getSessionStorageItem(option = 'fragmentVisibility') { + const storage = sessionStorage.getItem(this.state[option]._sessionStorageItemName); + return storage; + } + + _createControl() { + this.control = new Switch( + { + labelText: `Show fragments`, // * This text will be updated later. + dataTestID: 'show-hide-fragments-toggler', + size: 0.5, + stroke: 0.175, + // position: 'absolute', + // topPosition: '16px', + // leftPosition: 0, + // checked: false, + checked: this.getCurrentFragmentVisibilityBool(), + callback: (checked) => this.toggleFragmentsVisibility(checked), + } + ); + this.controlElement = this.control.create(); + } + + _addControl() { + // * this.controlTarget may not be present on the page, for example, in the case of static export. + this.controlTarget && this.controlTarget.append(this.controlElement); + } + + _removeControl() { + this.controlElement.remove(); + } + + _addMutationObserver() { + // console.log('Mutation observer added for', this.mutatingFrame); + + new MutationObserver((mutationsList, observer) => { + for (let mutation of mutationsList) { + if (mutation.type === 'childList') { + // When re-rendering the frame content, + // the array of tracked elements should be updated + // and their visibility should be set according + // to the current settings available in the State. + this.updateFragmentsAndControl(); + } + } + }).observe( + this.mutatingFrame, + { + childList: true, + // subtree: true + } + ); + } +} + +window.addEventListener("DOMContentLoaded", function(){ + + const controlTarget = document.querySelector(SWITCH_SELECTOR); + // * This element may not be present on the page, for example, in the case of static export. + // if (!controlTarget) { + // console.error(`Selector "${SWITCH_SELECTOR}" not found on the page`); + // return; + // } + + const mutatingFrame = document.querySelector(FRAME_SELECTOR); + if (!mutatingFrame) { + console.error(`Selector "${FRAME_SELECTOR}" not found on the page`); + return; + } + + const projectTree = new ProjectTree({ + mutatingFrame, + controlTarget + }); + + projectTree.init(); + +},false); diff --git a/tests/fuzz/20_L1_Open_Requirements_Tool_202511/assets/requirement-tree.css b/tests/fuzz/20_L1_Open_Requirements_Tool_202511/assets/requirement-tree.css new file mode 100644 index 0000000..b0aa443 --- /dev/null +++ b/tests/fuzz/20_L1_Open_Requirements_Tool_202511/assets/requirement-tree.css @@ -0,0 +1,283 @@ +:root { + --requirement-tree-line-width: 1px; + --requirement-tree-line-color: var(--color-border); + --requirement-tree-margin: var(--tree-gap); + --requirement-tree-downward-margin: calc(var(--requirement-tree-margin)*.5); + --requirement-tree-downward-string-margin: calc(var(--requirement-tree-downward-margin)*0.25); + --requirement-tree-line: var(--requirement-tree-line-width) solid; + --requirement-tree-downward-line: var(--requirement-tree-line-color) var(--requirement-tree-line-width) solid; + --requirement-tree-arrow-size: calc(var(--requirement-tree-margin)/4); + /* var(--color-fg-contrast) */ + /* var(--color-accent) */ +} + +/* requirement-tree */ + +.requirement-tree { + position: relative; + + list-style: none; + padding: 0; + margin: 0; + box-sizing: border-box; + + display: flex; + justify-content: flex-start; + flex-direction: column; + align-content: flex-start; + + /* default aka right direction */ + align-items: flex-start; + margin-left: var(--requirement-tree-margin); +} + +.requirement-tree_right.requirement-tree { + align-items: flex-start; + margin-right: 0; + margin-left: var(--requirement-tree-margin); +} + +.requirement-tree_left.requirement-tree { + align-items: flex-end; + margin-left: 0; + margin-right: var(--requirement-tree-margin); +} + +.requirement-tree_branch { + position: relative; + + display: flex; + justify-content: center; + align-items: flex-start; + align-content: stretch; + margin-bottom: var(--requirement-tree-margin); +} + +.requirement-tree_left .requirement-tree_branch { + flex-direction: row-reverse; +} + +.requirement-tree_branch:last-child { + margin-bottom: 0; +} + +.requirement-tree_node { + display: flex; + flex-direction: column; + align-items: stretch; + + /* for width restrictions in deep trace */ + max-width: var(--card-width); +} + +[data-viewtype="requirements-coverage"] .requirement-tree_node { + width: calc(var(--card-width)*0.75); +} + + /* arrows */ + +.requirement-tree_right.requirement-tree::before { + /* arrow from children, show only in right tree */ + content: ''; + position: absolute; + width: var(--requirement-tree-arrow-size); + height: var(--requirement-tree-arrow-size); + top: var(--requirement-tree-margin); + left: calc(var(--requirement-tree-margin)*(-1) - var(--requirement-tree-line-width)*0.5); + box-sizing: border-box; + border-top: var(--requirement-tree-line); + border-left: var(--requirement-tree-line); + transform-origin: top left; + transform: rotate(-45deg); + z-index: 2; +} + +.requirement-tree_left .requirement-tree_node::before { + /* arrow to parents inside tree */ + content: ''; + position: absolute; + width: var(--requirement-tree-arrow-size); + height: var(--requirement-tree-arrow-size); + top: var(--requirement-tree-margin); + right: calc(var(--requirement-tree-arrow-size)*(-1)); + box-sizing: border-box; + border-top: var(--requirement-tree-line); + border-left: var(--requirement-tree-line); + transform-origin: top left; + transform: rotate(-45deg); + z-index: 2; +} + +/* corner connector to node */ + +.requirement-tree_branch::before { + content: ''; + position: absolute; + width: calc(var(--requirement-tree-margin)*0.5); + height: var(--requirement-tree-margin); + top: 0; + box-sizing: border-box; + /* default aka right direction */ + border-bottom: var(--requirement-tree-line); + border-left: var(--requirement-tree-line); + left: calc(var(--requirement-tree-margin)*(-1)*0.5); +} + +.requirement-tree_right .requirement-tree_branch::before { + right: unset; + left: calc(var(--requirement-tree-margin)*(-1)*0.5); + border: none; + border-bottom: var(--requirement-tree-line); + border-left: var(--requirement-tree-line); +} + +.requirement-tree_left .requirement-tree_branch::before { + left: unset; + right: calc(var(--requirement-tree-margin)*(-1)*0.5); + border: none; + border-bottom: var(--requirement-tree-line); + border-right: var(--requirement-tree-line); +} + +/* horizontal line to top node instead corner */ + +.requirement-tree_branch:first-child::before { + width: var(--requirement-tree-margin); + border: none; + border-bottom: var(--requirement-tree-line); + /* default aka right direction */ + right: unset; + left: calc(var(--requirement-tree-margin)*(-1)); +} + +.requirement-tree_right .requirement-tree_branch:first-child::before { + right: unset; + left: calc(var(--requirement-tree-margin)*(-1)); +} + +.requirement-tree_left .requirement-tree_branch:first-child::before { + left: unset; + right: calc(var(--requirement-tree-margin)*(-1)); +} + +/* vertical line */ + +.requirement-tree_branch::after { + content: ''; + position: absolute; + width: calc(var(--requirement-tree-margin)*0.5); + top: var(--requirement-tree-margin); + bottom: calc(var(--requirement-tree-margin)*(-1)); + box-sizing: border-box; + /* default aka right direction */ + border: none; + border-left: var(--requirement-tree-line); + left: calc(var(--requirement-tree-margin)*(-1)*0.5); +} + +.requirement-tree_right .requirement-tree_branch::after { + border: none; + border-left: var(--requirement-tree-line); + right: unset; + left: calc(var(--requirement-tree-margin)*(-1)*0.5); +} + +.requirement-tree_left .requirement-tree_branch::after { + border: none; + border-right: var(--requirement-tree-line); + left: unset; + right: calc(var(--requirement-tree-margin)*(-1)*0.5); +} + +/* vertical line on last node is not showing */ +.requirement-tree_branch:last-child:after { + content: none; +} + +/* downward */ + +.requirement-tree_downward { + list-style: none; + padding: 0; + margin: 0; + margin-left: var(--requirement-tree-downward-margin); + box-sizing: border-box; + + font-size: .75rem; + line-height: 1.2; +} + +.requirement-tree_downward_node { + position: relative; + border-left: var(--requirement-tree-downward-line); +} + +.requirement-tree_downward .requirement-tree_downward_node { + padding-top: calc(var(--requirement-tree-downward-margin)*0.5); +} + +.requirement-tree_downward .requirement-tree_downward_node:first-child { + padding-top: var(--requirement-tree-downward-margin); +} + +.requirement-tree_downward .requirement-tree_downward_node:last-child { + border-color: transparent; +} + +.requirement-tree_downward .requirement-tree_downward_node::before { + position: absolute; + content: ''; + width: var(--requirement-tree-downward-margin); + border-bottom: var(--requirement-tree-downward-line); + box-sizing: border-box; + top: 0; + left: calc(var(--requirement-tree-line-width)*(-1)); + bottom: calc(100% - var(--requirement-tree-downward-margin)*0.5 - 0.75rem); +} + +.requirement-tree_downward .requirement-tree_downward_node:last-child::before { + border-left: var(--requirement-tree-downward-line); +} + +.requirement-tree_downward .requirement-tree_downward_node:first-child::before { + bottom: calc(100% - var(--requirement-tree-downward-margin)*1 - 0.75rem); +} + +.requirement-tree_downward_node .requirement-tree_downward_item { + position: relative; + /* margin-left: calc(var(--requirement-tree-downward-margin) + var(--requirement-tree-downward-string-margin)); */ + margin-left: calc(var(--requirement-tree-downward-margin) - 0.25rem); + + border-radius: 4px; + overflow: clip; + border: 1px solid var(--requirement-tree-line-color); + background-color: var(--color-bg-main); + + overflow-wrap: break-word; +} + +.requirement-tree_downward_item span, +.requirement-tree_downward_item a { + padding: 4px 6px; + display: inline-block; + width: 100%; + overflow-wrap: break-word; +} + +.requirement-tree_downward_item a { + color: var(--color-accent); + text-decoration: none; +} + +.requirement-tree_downward_item a:hover { + text-decoration: underline; +} + +/* hover */ + +/* .requirement-tree_node:hover + .requirement-tree::before, +.requirement-tree_node:hover + .requirement-tree .requirement-tree::before, +.requirement-tree_node:hover + .requirement-tree .requirement-tree_branch::before, +.requirement-tree_node:hover + .requirement-tree .requirement-tree_branch::after { + border-width: 2px; +} */ diff --git a/tests/fuzz/20_L1_Open_Requirements_Tool_202511/assets/requirement__temporary.css b/tests/fuzz/20_L1_Open_Requirements_Tool_202511/assets/requirement__temporary.css new file mode 100644 index 0000000..24122fe --- /dev/null +++ b/tests/fuzz/20_L1_Open_Requirements_Tool_202511/assets/requirement__temporary.css @@ -0,0 +1,138 @@ +/* TODO: should be revised and then moved to a permanent location or removed */ + +/* requirement__title */ + +.requirement__title { + margin: 0; + line-height: 1.6; + font-weight: bold; +} + +/* requirement: parent / child / file */ + +ul.requirement__link { + font-size: .85rem; + line-height: 1.4; + list-style: none; + padding: 0; +} + +[data-viewtype="source-file"] ul.requirement__link li { + margin-top: 0.5rem; +} + +.requirement__link a, +.requirement__link li > span { + display: inline-block; + position: relative; + margin-left: 1.5rem; + width: calc(100% - 1.5rem); + overflow-wrap: break-word; +} + +.requirement__link a::before, +.requirement__link li > span::before { + color: #808080; + position: absolute; + left: -1.5rem; +} + +.requirement__link a:link, +.requirement__link a:visited { + color: var(--color-fg-contrast); + text-decoration: none; +} + +.requirement__link a:hover { + /* color: var(--color-fg-accent); */ + text-decoration: underline; +} + +/* .requirement__link a::before { + content: '\2014'; +} */ + +a.requirement__link-file::before, +.requirement__link-file::before { + content: ''; +} + +a.requirement__link-parent::before { + content: '\2190'; +} + +a.requirement__link-child::before { + content: '\2192'; +} + +.requirement__link-external::before { + content: '\21D6'; +} + +.requirement__parent-uid, +.requirement__child-uid { + position: relative; + font-weight: bold; +} + +/* switch (injected by JS) */ + +.std-switch { + display: flex; + align-items: center; + justify-content: flex-start; + + user-select: none; + cursor: pointer; +} + +.std-switch input { + opacity: 0; + width: 0; + height: 0; + position: absolute; +} + +.std-switch_slider { + + position: relative; + cursor: pointer; + background-color: #ccc; + -webkit-transition: .4s; + transition: .4s; + + display: inline-block; + width: 44px; + height: 26px; + margin-right: 10px; + + border-radius: 22px; +} + +.std-switch_slider::before { + position: absolute; + content: ""; + height: 18px; + width: 18px; + left: 4px; + bottom: 4px; + background-color: white; + -webkit-transition: .4s; + transition: .4s; + + border-radius: 50%; +} + +input:checked + .std-switch_slider { + background-color: rgb(100, 222, 50); +} + +input:focus + .std-switch_slider { + box-shadow: 0 0 1px rgb(100, 222, 50); +} + +input:checked + .std-switch_slider:before { + -webkit-transform: translateX(18px); + -ms-transform: translateX(18px); + transform: translateX(18px); +} diff --git a/tests/fuzz/20_L1_Open_Requirements_Tool_202511/assets/resizable_bar.js b/tests/fuzz/20_L1_Open_Requirements_Tool_202511/assets/resizable_bar.js new file mode 100644 index 0000000..098482c --- /dev/null +++ b/tests/fuzz/20_L1_Open_Requirements_Tool_202511/assets/resizable_bar.js @@ -0,0 +1,602 @@ +// Expected in element: +// js-resizable_bar="name" +// data-state="open|closed" +// data-position="left|right" + +class ResizableBar { + constructor({ + barAttribute, + barOpenMinWidth, + barOpenMaxWidthRatio, + // styles + barPadding, + barPaddingBottom, + barClosedWidth, + barHandlerWidth, + barColorMain, + barColorBackground, + barColorActive, + barColorBorder, + barColorScrollbarTrack, + barColorScrollbarThumb, + }) { + this.barAttribute = barAttribute || 'js-resizable_bar'; + this.barOpenMaxWidthRatio = barOpenMaxWidthRatio || 0.2; + this.barOpenMinWidth = barOpenMinWidth || 77; + // styles + this.barPadding = barPadding || 'calc(var(--base-rhythm, 8px)*2)'; + this.barPaddingBottom = barPaddingBottom || 'calc(var(--base-rhythm, 8px)*8)'; + this.barClosedWidth = barClosedWidth || 12; + this.barHandlerWidth = barHandlerWidth || 8; + this.barColorMain = barColorMain || 'var(--color-fg-main, Black)'; + this.barColorBackground = barColorBackground || 'var(--color-bg-main, White)'; + this.barColorActive = barColorActive || 'var(--color-fg-accent, currentColor)'; + this.barColorBorder = barColorBorder || 'var(--color-border, rgba(0,0,0,0.1))'; + this.barColorScrollbarTrack = barColorScrollbarTrack || 'var(--scrollbarBG, transparent)'; + this.barColorScrollbarThumb = barColorScrollbarThumb || 'var(--thumbBG, rgba(0,0,0,.05))'; + + // state + this.state = { + current: { + id: null, + direction: null, // 1||-1 + pageX: null + } + }; + + this.initialState = 'open'; + this.initialWidth = `${this.barOpenMaxWidthRatio * 100}vw`; + this.initialStyle = ``; + + // subscribe + const _this = this; + this._mouseDownHandler = (e) => {_this._onMouseDown(e)}; + this._mouseMoveHandler = (e) => {_this._onMouseMove(e)}; + this._mouseUpHandler = (e) => {_this._onMouseUp(e)}; + this._toggleHandler = (e) => {_this._toggle(e)}; + } + + init() { + this._insertInitialBarStyle(); + this._insertInitialPreloaderStyle(); + } + + render() { + this._renderBars(); + this._insertBarStyle(); + this._insertPreloaderStyle(); + } + + // render + + _renderBars() { + [...document.querySelectorAll(`[${this.barAttribute}]`)] + .forEach((bar) => { + const id = bar.getAttribute(this.barAttribute); + const position = bar.dataset.position; + const direction = (bar.dataset.position === 'left') ? 1 : -1;; + const state = this._sessionStorageGetItem(id, 'state') || this.initialState; + const width = this._sessionStorageGetItem(id, 'width'); + + // Read data from element and from Storage + // and set to State: + this._setState({ + id: id, + element: bar, + position: position, + direction: direction, + state: state, + width: width, + }); + + // Update Bar with data from Storage: + this._updateBar(id); + + // Wrap the bar content in the created scrolling element: + const wrapper = this._createScrollableWrapper(id); + [ ...bar.childNodes ].forEach(child => wrapper.appendChild(child)); + bar.appendChild(wrapper); + + // Add control elements: + bar.append(this._createHandler(id)); + + // Add testID: + this._addTestID(bar, id, 'bar'); + }); + } + + _adjustWidth(width) { + let adjustedWidth = width; + const max = window.innerWidth * this.barOpenMaxWidthRatio; + if(width && width < this.barOpenMinWidth) { + adjustedWidth = this.barOpenMinWidth + } + if(width && width > max) { + adjustedWidth = null; + } + return adjustedWidth; + } + + // session storage + + _sessionStorageGet() { + return JSON.parse(sessionStorage.getItem('resizableBarStorage')) + } + + _sessionStorageSet(obj) { + const string = JSON.stringify(obj); + sessionStorage.setItem('resizableBarStorage', string) + } + + _sessionStorageGetItem(id, item) { + const storage = this._sessionStorageGet(); + const value = (storage && storage[id]) ? storage[id][item] : null; + return value; + } + + _sessionStorageSetItem(id, item, value) { + let storage = this._sessionStorageGet() || {}; + storage = { + ...storage, + [id]: { + ...storage[id], + [item]: value, + }, + }; + this._sessionStorageSet(storage); + } + + // state + + _setState({ + id, + element, + state, + position, + direction, + width, + }) { + this.state[id] = { + element: element, + state: state, + position: position, + direction: direction, + width: width, + }; + } + + _updateState({ + id, + element, + state, + position, + direction, + width, + }) { + console.assert(id, '_updateState(): ID must be provided'); + if (!this.state[id]) { this.state[id] = {} } + if (element) { this.state[id].element = element; } + if (state) { this.state[id].state = state; } + if (position) { this.state[id].position = position; } + if (direction) { this.state[id].direction = direction; } + if (width || width===null) { this.state[id].width = width; } + } + + // current + + _updateCurrent(e) { + if (e.type == "mousedown") { + // When we start a new resize, we update the currents: + this.state.current.id = e.target.dataset.content; + this.state.current.pageX = e.pageX; + this.state.current.startWidth = this.state[this.state.current.id].element.offsetWidth; + } else { + // e.type == "mouseup" + // At the end of the resize: + this.state.current.id = null; + this.state.current.pageX = null; + this.state.current.startWidth = null; + } + } + + // elements + + _addTestID(element, id, attr) { + element.dataset.testid = `${id}-${attr}`; + } + + _updateBar(id) { + const barState = this.state[id]; + const bar = this.state[id].element; + bar.dataset.position = barState.position; + bar.dataset.state = barState.state; + this._updateBarWidth(bar, barState.width); + } + + _updateBarWidth(bar, width) { + // If there is no specific width, set the maximum. + // The style tag cannot be left empty to override the preloaded styles + // set for each bar with the width taken from the storage + // before the page is rendered. + bar.style.width = width ? `${width}px` : `${this.barOpenMaxWidthRatio * 100}vw`; + } + + _createHandler(id) { + const handler = document.createElement('div'); + handler.setAttribute(`${this.barAttribute}-handler`, ''); + handler.dataset.content = id; + handler.style[this.state[id].position] = 'unset'; // 'left | right' + + const border = document.createElement('div'); + border.setAttribute(`${this.barAttribute}-border`, ''); + border.dataset.content = id; + border.title = `Resize ${id}`; + border.addEventListener('mousedown', this._mouseDownHandler); + + const button = document.createElement('div'); + button.setAttribute(`${this.barAttribute}-button`, ''); + button.dataset.content = id; + button.title = `Toggle ${id}`; + button.addEventListener('mousedown', this._toggleHandler); + + // Add testIDs: + this._addTestID(border, id, 'handler-border'); + this._addTestID(button, id, 'handler-button'); + + handler.append(border, button); + return handler; + } + + _createScrollableWrapper(id, direction = 'y') { + const wrapper = document.createElement('div'); + wrapper.setAttribute(`${this.barAttribute}-scroll`, direction); + wrapper.dataset.content = id; + return wrapper; + } + + // event listeners + + _onMouseDown(e) { + // Init resizing + if (e.button == 0) { + e.preventDefault(); + this._updateCurrent(e); + window.addEventListener('mousemove', this._mouseMoveHandler); + window.addEventListener('mouseup', this._mouseUpHandler); + } + } + + _onMouseMove(e) { + // Resizing + const currentId = this.state.current.id; + const currentX = this.state.current.pageX; + const currentStartWidth = this.state.current.startWidth; + const currentBar = this.state[currentId].element; + + // todo: _onMouseUp() does not always stop execution of requestAnimationFrame, + // and after the correct width is calculated in _adjustWidth (20vw), + // the extreme width from this function can be set again (for ex.: 789px). + // requestAnimationFrame(() => { + + // currentId exists if a resize has been initiated + if (currentId) { + + // Resize + // * delta: the distance traveled by the mouse, starting from the initial point + const delta = e.pageX - currentX; + // * this.state[currentId].direction = 1 || -1 + // * w: current bar width + this.state.current.width = currentStartWidth + this.state[currentId].direction * delta; + + // Rendering the change in width of the bar: + // currentBar.style.width = w + 'px'; + this._updateBarWidth(currentBar, this.state.current.width); + + // Close/Open + if (this.state.current.width < this.barOpenMinWidth) { + if (this.state[currentId].state == 'open') { + this._close(currentId); + } + } else { + if (this.state[currentId].state == 'closed') { + this._open(currentId); + } + } + } + + // }) + } + + _onMouseUp(e) { + // Clean up after work + window.removeEventListener('mousemove', this._mouseMoveHandler); + window.removeEventListener('mouseup', this._mouseUpHandler); + + const currentWidth = this._adjustWidth(this.state.current.width); + this._updateState({ id: this.state.current.id, width: currentWidth }); + this._sessionStorageSetItem(this.state.current.id, 'width', currentWidth); // WRITE DATA TO STORAGE + this._updateBar(this.state.current.id); + this._updateCurrent(e); + } + + _toggle(e) { + if (e.button == 0) { + const id = e.target.dataset.content; + this.state[id].state = this.state[id].state === 'open' + ? 'closed' + : 'open'; + this._sessionStorageSetItem(id, 'state', this.state[id].state); + this._updateBar(id); + } + } + + _open(id) { + this.state[id].state = 'open'; + this._sessionStorageSetItem(id, 'state', this.state[id].state); + this._updateBar(id); + } + + _close(id) { + this.state[id].state = 'closed'; + this._sessionStorageSetItem(id, 'state', this.state[id].state); + this._updateBar(id); + } + + // styles + + _insertStyle(css, attr) { + const style = document.createElement('style'); + style.setAttribute(`${this.barAttribute}-${attr}`, ''); + style.textContent = css; + document.head.append(style); + } + + _insertInitialBarStyle() { + const storage = this._sessionStorageGet(); + + let initStyle = `[${this.barAttribute}]{width:${this.barOpenMaxWidthRatio * 100}vw}`; + + // Add styles based on data from Storage: + for (let id in storage) { + const w = (storage[id].state == 'closed') ? this.barClosedWidth : storage[id].width; + // If w = null (this is possible if so in the storage), + // the base style will be in effect: + w && (initStyle += `[${this.barAttribute}="${id}"]{width:${w}px}`); + } + + this._insertStyle(initStyle, 'initial-style') + } + + _insertBarStyle() { + let barStyle = ` +[${this.barAttribute}] { + position: relative; + height: 100%; + max-width: ${this.barOpenMaxWidthRatio * 100}vw; +} + +[${this.barAttribute}]:hover { + z-index: 22; +} + +[${this.barAttribute}][data-state="open"] { + min-width: ${this.barOpenMinWidth}px; + pointer-events: auto; +} +[${this.barAttribute}][data-state="closed"] { + max-width: ${this.barClosedWidth}px; + min-width: ${this.barClosedWidth}px; + pointer-events: none; + transition: .5s; +} + +[${this.barAttribute}][data-position="left"] { + border-left: none; +} +[${this.barAttribute}][data-position="right"] { + border-right: none; +} + +[${this.barAttribute}-handler] { + pointer-events: auto; + position: absolute; + top: 0; + bottom: 0; + left: 0; + right: 0; + z-index: 10; + width: ${this.barHandlerWidth}px; + color: ${this.barColorActive}; +} + +[${this.barAttribute}-border] { + pointer-events: auto; + position: absolute; + top: 0; + bottom: 0; + left: 0; + right: 0; + width: ${this.barHandlerWidth}px; + background: transparent; + transition: .3s; + cursor: col-resize; +} + +[${this.barAttribute}][data-state="closed"] [${this.barAttribute}-border] { + cursor: e-resize; +} + +[${this.barAttribute}-border]::before { + content: ''; + position: absolute; + top: 0; + bottom: 0; + left: 0; + right: 0; + width: 1px; + background: ${this.barColorBorder}; + transition: .3s; +} + +[${this.barAttribute}-border]::after { + content: ''; + position: absolute; + top: 0; + bottom: 0; + left: 0; + right: 0; + width: ${0.5 * this.barHandlerWidth}px; + background: transparent; + transition: .3s; +} + +[${this.barAttribute}][data-position="left"] [${this.barAttribute}-border]::before { + right: 0; + left: unset; +} +[${this.barAttribute}][data-position="left"] [${this.barAttribute}-border]::after { + right: ${-0.25 * this.barHandlerWidth}px; + left: unset; +} + +[${this.barAttribute}][data-position="right"] [${this.barAttribute}-border]::before { + right: unset; + left: 0; +} +[${this.barAttribute}][data-position="right"] [${this.barAttribute}-border]::after { + right: unset; + left: ${-0.25 * this.barHandlerWidth}px; +} + +[${this.barAttribute}-border]:hover::after { + background: ${this.barColorActive}; +} + +[${this.barAttribute}-button] { + cursor: pointer; + position: absolute; + z-index: 2; + left: 0; + right: 0; + top: ${-1 * this.barHandlerWidth}px; + box-sizing: border-box; + width: ${2 * this.barHandlerWidth}px; + height: ${2 * this.barHandlerWidth}px; + font-size: ${1.5 * this.barHandlerWidth}px; + font-weight: bold; + border-radius: 50%; + border-width: 1px; + border-style: solid; + border-color: ${this.barColorBackground}; + background: ${this.barColorBackground}; + color: ${this.barColorActive}; + transition: .3s; +} + +[data-position="right"] [${this.barAttribute}-button] { + right: 0; + left: unset; +} +[data-position="left"] [${this.barAttribute}-button] { + left: 0; + right: unset; +} + +[${this.barAttribute}-button]:hover { + color: ${this.barColorMain}; + border-color: ${this.barColorMain}; +} + +/* ❮❯ */ +[${this.barAttribute}-button]::after { + content: '❮'; + position: absolute; + display: flex; + align-items: center; + justify-content: center; + left: 0; + right: 0; + top: ${-1 * this.barHandlerWidth}px; + bottom: ${-1 * this.barHandlerWidth}px; +} +[${this.barAttribute}][data-state="open"] [${this.barAttribute}-button]::after, +[${this.barAttribute}][data-state="open"][data-position="left"] [${this.barAttribute}-button]::after, +[${this.barAttribute}][data-state="closed"][data-position="right"] [${this.barAttribute}-button]::after { + content: '❮'; +} +[${this.barAttribute}][data-state="closed"] [${this.barAttribute}-button]::after, +[${this.barAttribute}][data-state="closed"][data-position="left"] [${this.barAttribute}-button]::after, +[${this.barAttribute}][data-state="open"][data-position="right"] [${this.barAttribute}-button]::after { + content: '❯'; +} + +[${this.barAttribute}-scroll] { + height: 100%; + overflow-x: hidden; + overflow-y: scroll; + /*padding: ${this.barPadding}; + padding-bottom: ${this.barPaddingBottom};*/ + scrollbar-color: ${this.barColorScrollbarTrack} ${this.barColorScrollbarTrack}; +} +[${this.barAttribute}-scroll='y'] { + overflow-x: hidden; + overflow-y: scroll; +} +[${this.barAttribute}-scroll]:hover { + scrollbar-color: ${this.barColorScrollbarThumb} ${this.barColorScrollbarTrack}; +} +[${this.barAttribute}-scroll]::-webkit-scrollbar-thumb { + background-color: ${this.barColorScrollbarTrack}; +} +[${this.barAttribute}-scroll]:hover::-webkit-scrollbar-thumb { + background-color: ${this.barColorScrollbarThumb} +} + +[data-state="closed"] [${this.barAttribute}-scroll] { + display: none; +} +`; + + this._insertStyle(barStyle, 'style'); + } + + _insertInitialPreloaderStyle() { + let style = ` + aside { + /* for a pseudo preloader [js-resizable_bar]::after, affects: layout_tree,layout_toc */ + position: relative; + } + [${this.barAttribute}]::after { + display: flex; + align-items: center; + justify-content: center; + content: ''; + position: absolute; + left: 0; right: 0; top: 0; bottom: 0; + z-index: 2; + background-color: ${this.barColorBackground}; + } + `; + + this._insertStyle(style, 'initial-preloader-style'); + } + + _insertPreloaderStyle() { + let style = ` + [${this.barAttribute}]::after { + opacity: 0; + transition: .3s; + pointer-events: none; + } + `; + + this._insertStyle(style, 'preloader-style'); + } +} + +const resizableBar = new ResizableBar({}); +resizableBar.init(); + +window.addEventListener("load", function () { + resizableBar.render(); +}); diff --git a/tests/fuzz/20_L1_Open_Requirements_Tool_202511/assets/source_coverage_screen.css b/tests/fuzz/20_L1_Open_Requirements_Tool_202511/assets/source_coverage_screen.css new file mode 100644 index 0000000..16df13a --- /dev/null +++ b/tests/fuzz/20_L1_Open_Requirements_Tool_202511/assets/source_coverage_screen.css @@ -0,0 +1,283 @@ +.project_coverage { + width: 100%; + /* font-size: 0.875rem; */ +} + +.project_coverage thead { + position: sticky; + z-index: 1; + top: calc(-1 * var(--base-gap)); /* compensate .main top padding when scrolling */ +} + +.project_coverage thead th { + /* adding a background prevents the th-button background from working */ + vertical-align: bottom; + border-bottom: var(--base-border); + font-size: 0.875em; +} + +.project_coverage thead::before { + content: ''; + position: absolute; + z-index: -1; + left: calc(-1 * var(--base-gap)); /* compensate .main padding to overlap content */ + right: calc(-1 * var(--base-gap)); /* compensate .main padding to overlap content */ + top: calc(-1 * var(--base-gap)); /* compensate .main padding to overlap content */ + bottom: 0; + background: var(--color-bg-main); +} + +.project_coverage th, +.project_coverage td { + padding: calc(0.5 * var(--base-rhythm)) var(--base-rhythm); + border-right: var(--base-border); + position: relative; +} + +.project_coverage td { + text-align: right; +} + +.project_coverage th:last-child, +.project_coverage td:last-child { + border-right: none; +} + +.project_coverage tr:first-child th:first-child, /* tr compensate colspan */ +.project_coverage td:first-child { + width: auto; + text-align: left; +} + +.project_coverage td:not(:first-child) { + width: 1px; + white-space: nowrap; +} + +.project_coverage tr.project_coverage-file:hover { + background: rgba(255,255,255,0.3); +} + +.project_coverage tr.project_coverage-folder td { + padding-right: 0; +} + +.project_coverage-folder-title { + /* font-weight: bold; */ + display: flex; + align-items: flex-end; + column-gap: calc(0.5 * var(--base-rhythm)); + /* font-size: 0.75em; */ + line-height: 1; + color: #999; + padding: calc(0.5 * var(--base-rhythm)); + position: relative; + background: var(--color-bg-secondary); +} + +.project_coverage-file-link { + display: flex; + /* column-gap: var(--base-rhythm); */ + padding: calc(0.5 * var(--base-rhythm)); + border-radius: calc(0.5 * var(--base-rhythm)); + transition: background .3s; + border: 1px solid rgba(255,255,255,0); +} + +.project_coverage-file-icon {} + +.project_coverage-file-title { + font-size: 1em; + font-weight: 500; + line-height: 1.2; + transition: color .2s; + display: flex; + column-gap: var(--base-rhythm); +} + +.project_coverage-file-path { + font-size: 0.75em; + color: var(--color-fg-secondary); + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + position: absolute; + left: 0; + right: 0; + bottom: 0; + display: none; +} + +.project_coverage-file-details { + position: relative; + flex-grow: 1; +} + +.project_coverage-file-link:not([href]):hover { + /* color: gray; */ + /* all this file have no links -> they are red */ +} + +.project_coverage-file_uncovered .project_coverage-file-link, +.project_coverage-file_uncovered .project_coverage-file-title { + color: var(--color-danger); + font-weight: 400 !important; +} + +.project_coverage-file-link[href]:hover .project_coverage-file-icon, +.project_coverage-file-link[href]:hover .project_coverage-file-title { + color: var(--color-fg-accent); +} + +.project_coverage-file-link[href] .project_coverage-file-icon, +.project_coverage-file-link[href] .project_coverage-file-title { + color: var(--color-fg-main); +} + +/* cols */ + +.project_coverage-col-line { + background-color: rgba(255, 155, 0, 0.05) !important; +} + +.project_coverage-col-func { + background-color: rgba(175, 0, 255, 0.05) !important; +} + +.sorted_col.project_coverage-col-line, +th.project_coverage-col-line { + background-color: rgba(255, 155, 0, 0.15) !important; +} + +.sorted_col.project_coverage-col-func, +th.project_coverage-col-func { + background-color: rgba(175, 0, 255, 0.15) !important; +} + +/* value_extended */ + +.value_extended[data-ext]::after { +content: attr(data-ext); +margin-left: 2px; +font-size: 0.85em; +font-weight: 200; +} + +/* project_coverage-sort_handler */ + +.project_coverage-sort_handler { + cursor: pointer; +} + +.project_coverage-sort_handler::after { + content: '○'; + display: block; +} + +.project_coverage-sort_handler::before { + content: ''; + cursor: pointer; + position: absolute; + inset: 0; + z-index: -1; +} + +.project_coverage-sort_handler:hover { + color: var(--color-blue); +} + +.project_coverage-sort_handler:hover::before { + border: 3px solid white; +} + +.project_coverage-sort_handler[sorted]::before { + border: 3px solid white; +} + +/* ▿ ▵ △ ▽ ▾ ▴ */ + +.project_coverage-sort_handler:hover::after { + /* the first act: desc */ + content: '▽'; +} + +.project_coverage-sort_handler[sorted='asc']::after { + content: '▴'; +} + +.project_coverage-sort_handler[sorted='dsc']::after { + content: '▾'; +} + +.project_coverage-sort_handler[sorted='dsc']:hover::after { + content: '△'; +} + +.project_coverage-sort_handler[sorted='asc']:hover::after { + content: '▽'; +} + +/* sort_reset */ + +.project_coverage-sort_reset { + display: flex; + flex-direction: column; + gap: var(--base-padding); +} + +.project_coverage-sort_reset::after { + content: ' '; + display: block; +} + +/* sorted sort_reset */ + +.project_coverage.sorted .project_coverage-sort_reset { + cursor: pointer; +} + +.project_coverage.sorted .project_coverage-sort_reset::after { + content: 'reset sorting'; + font-weight: 400; + text-align: right; + color: var(--color-fg-accent); +} + +.project_coverage.sorted .project_coverage-sort_reset::before { + content: ''; + cursor: pointer; + position: absolute; + inset: 0; + z-index: -1; +} + +.project_coverage.sorted .project_coverage-sort_reset:hover::before { + background-color: rgb(255 255 255 / 50%); +} + +/* sorted mods */ + +.project_coverage.sorted .project_coverage-folder, +.project_coverage.sorted .project_coverage-file-indent { + display: none; +} + +.project_coverage.sorted .project_coverage-file-path { + display: block; +} + +.project_coverage.sorted .project_coverage-file-details { + position: relative; + flex-grow: 1; + padding: 0 0 var(--base-padding) 0; +} + +/* color */ + +.color-secondary { + color: var(--color-fg-secondary); +} + +.color-uncovered { + color: var(--color-danger); +} diff --git a/tests/fuzz/20_L1_Open_Requirements_Tool_202511/assets/source_coverage_screen.js b/tests/fuzz/20_L1_Open_Requirements_Tool_202511/assets/source_coverage_screen.js new file mode 100644 index 0000000..64fbc86 --- /dev/null +++ b/tests/fuzz/20_L1_Open_Requirements_Tool_202511/assets/source_coverage_screen.js @@ -0,0 +1,73 @@ +// @relation(SDOC-SRS-35, scope=file) + +document.addEventListener("DOMContentLoaded", () => { + const table = document.querySelector(".project_coverage"); + const tbody = table.querySelector("tbody"); + const originalRows = Array.from(tbody.rows); + let sortState = null; + + const handlers = table.querySelectorAll(".project_coverage-sort_handler"); + const colgroupCols = table.querySelectorAll("colgroup col"); + + handlers.forEach((handler) => { + handler.addEventListener("click", () => { + + const dataId = handler.getAttribute("data-id"); + // We expect: + // - Each .project_coverage-sort_handler has a unique data-id + // - Each in sortable rows has matching data-id and data-value + // - contains matching the same data-id + + console.assert(dataId, "Missing data-id on sort handler."); + if (!dataId) return; + + const isSameColumn = sortState && sortState.id === dataId; + const asc = isSameColumn ? !sortState.asc : false; + + const rows = Array.from(tbody.rows); + const validRows = rows.filter(row => + row.classList.contains("project_coverage-file") && + row.querySelector(`td[data-id="${dataId}"]`) + ); + if (validRows.length === 0) return; + + validRows.sort((a, b) => { + const aCell = a.querySelector(`td[data-id="${dataId}"]`); + const bCell = b.querySelector(`td[data-id="${dataId}"]`); + const aVal = parseFloat(aCell?.dataset.value || '0'); + const bVal = parseFloat(bCell?.dataset.value || '0'); + return asc ? aVal - bVal : bVal - aVal; + }); + + tbody.innerHTML = ""; + validRows.forEach(row => tbody.appendChild(row)); + table.classList.add("sorted"); + + handlers.forEach(h => h.removeAttribute("sorted")); + handler.setAttribute("sorted", asc ? "asc" : "dsc"); + + sortState = { id: dataId, asc }; + + // Highlight active + colgroupCols.forEach(col => col.classList.remove("sorted_col")); + const activeCol = table.querySelector(`colgroup col[data-id="${dataId}"]`); + if (activeCol) { + activeCol.classList.add("sorted_col"); + } else { + console.warn(`No found for data-id="${dataId}"`); + } + }); + }); + + const resetter = table.querySelector(".project_coverage-sort_reset"); + if (resetter) { + resetter.addEventListener("click", () => { + tbody.innerHTML = ""; + originalRows.forEach(row => tbody.appendChild(row)); + table.classList.remove("sorted"); + handlers.forEach(h => h.removeAttribute("sorted")); + colgroupCols.forEach(col => col.classList.remove("sorted_col")); + sortState = null; + }); + } +}); diff --git a/tests/fuzz/20_L1_Open_Requirements_Tool_202511/assets/source_file_screen.css b/tests/fuzz/20_L1_Open_Requirements_Tool_202511/assets/source_file_screen.css new file mode 100644 index 0000000..06a393a --- /dev/null +++ b/tests/fuzz/20_L1_Open_Requirements_Tool_202511/assets/source_file_screen.css @@ -0,0 +1,410 @@ +* { + margin: 0; + padding: 0; +} + +[data-viewtype="source-file"] { + --source-line: 1px solid rgba(0, 0, 0, .05); +} + +[data-viewtype="source-file"] .layout { + grid-template-columns: + fit-content(var(--base-gap)) + minmax(222px, 22%) /* replaced: fit-content(20%) */ + fit-content(20%) + minmax(0, 1fr) + fit-content(20%) + auto; + grid-template-areas: + "nav header header header header aside" + "nav tree bar_left main bar_right aside" + "nav footer footer footer footer aside"; +} + +/* left panel */ + +.source-file__aside { + display: flex; + flex-direction: column; + height: 100%; +} + +/* left scrollable panel */ + +.source-file__refer { /* wrapper */ + position: relative; + height: 100%; + overflow-y: hidden; +} + +.source-file__toc { + position: relative; + height: 100%; + overflow-y: scroll; + padding: calc(var(--base-padding)*2) + calc(var(--base-padding) / 2) + calc(var(--base-padding)*4) + calc(var(--base-padding) / 1); + + font-size: var(--font-size-sm); + + transition: margin-left .5s; + scrollbar-color: transparent var(--scrollbarBG); +} + +.source-file__toc:hover { + scrollbar-color: var(--thumbBG) var(--scrollbarBG); +} + +.source-file__toc::-webkit-scrollbar-thumb { + background-color: transparent; +} + +.source-file__toc:hover::-webkit-scrollbar-thumb { + background-color: var(--thumbBG) +} + +.source-file__toc-range { + background-color: var(--color-highlight-secondary); + border-radius: var(--base-rhythm); + margin-bottom: var(--tree-gap); +} + +.source-file__toc-range-header { + padding: var(--base-rhythm); +} + +.source-file__toc-range-node { + border-top: 2px solid var(--color-bg-main); +} + +.source-file__toc-node { + border-top: 1px solid var(--color-border); + margin-bottom: 2rem; +} + +/* SDOC-NODE in SOURCE */ + +.source-file__requirement { + position: relative; + padding: var(--base-rhythm) 0; + transition: background-color 0.3s ease-in, border-color 0.3s ease-in; + font-size: var(--font-size-xsm); + display: flex; + flex-direction: column; + align-items: stretch; + gap: 0.25rem; +} + +.source-file__requirement-links { + display: flex; + flex-direction: column; + align-items: stretch; + gap: 0.25rem; + margin-top: var(--base-rhythm); +} + +.source-file__requirement-info { + word-break: break-word; +} + +.source-file__requirement-uid { + display: block; + position: relative; + font-weight: bold; + word-break: break-word; +} + +.source-file__requirement details { + display: flex; + flex-direction: column; + align-items: stretch; + gap: 0.25rem; +} + +.source-file__requirement summary { + list-style: none; + display: flex; + justify-content: space-between; + align-items: center; + cursor: pointer; + position: relative; + gap: var(--base-rhythm); + align-items: flex-start; +} + +.source-file__requirement summary::-webkit-details-marker { + display: none; +} + +.source-file__requirement summary::after { + content: "➕"; + user-select: none; + padding: calc(var(--base-rhythm) / 2); + margin-left: auto; + transition: transform 0.2s; + font-size: 8px; + color: var(--color-link); +} + +.source-file__requirement:hover summary::after { + color: var(--color-hover); +} + +.source-file__requirement details[open] summary::after { + content: "➖"; +} + +.source-file__requirement-header { + display: flex; + gap: var(--base-rhythm); + align-items: flex-start; /* to push action icon to top */ +} + +/* requirement_file */ + +[data-viewtype="source-file"] .requirement_file li { + position: relative; +} + +[data-viewtype="source-file"] .requirement_file li > a, +[data-viewtype="source-file"] .requirement_file li > span { + display: inline-block; + padding: .15rem .25rem .15rem 0; + line-height: 1; +} + +[data-viewtype="source-file"] .current_file_pseudolink { + font-weight: bold; + color: #808080; +} + +/* CODE */ + +.source-file__source { + /* position: relative; */ + position: absolute; + inset: 0px; + left: var(--tree-gap); + overflow: auto; + + padding: var(--tree-gap) 0 calc(var(--tree-gap)*10); + transition: box-shadow 0.3s ease-in; +} + +.source-file__source .sdoc-table_key_value { + /* font-size: var(--font-size-sm); */ + font-size: 14px; + min-width: 100%; + margin-bottom: var(--tree-gap); +} + +.source { + display: grid; + grid-template-columns: + minmax(min-content, max-content) + minmax(0, 1fr); /* issue#1370 https://css-tricks.com/preventing-a-grid-blowout/ */ + gap: 0 0; + place-items: stretch stretch; + transition: transform 0.3s ease-in; + position: relative; + + min-width: 100%; + width: max-content; + + font-size: 14px; + z-index: 1; +} + +.source_highlight { + position: absolute; + left: 0; + right: 0; + background-color: var(--color-highlight); + z-index: -1; + transition: height 0.3s ease-in, top 0.3s ease-in; +} + +/* source__range */ + +.source__range { + grid-column: 1 / -1; + display: contents; +} + +.source__range-closer { + grid-column: 1 / -1; + /* margin-bottom: var(--tree-gap); */ + margin-bottom: var(--base-padding); +} + +.source__range-closer-label { + display: flex; + border-radius: 0 0 6px 6px; + column-gap: calc(var(--base-rhythm)* 1); + background-color: var(--color-highlight-secondary); + padding: var(--base-rhythm); +} + +.source__range-closer-label .source__range-definition::before { + content: 'End of'; + display: inline-block; + margin-right: 6px; +} + +.focus .source__range:not(.active), +.focus .source__range-closer:not(.active) { + display: none; +} + +.source__range-cell { + background-color: var(--color-highlight-secondary); +} + +.source__range-header { + grid-column: 1 / -1; + background-color: var(--color-highlight-secondary); + padding: var(--base-rhythm); + margin-top: var(--tree-gap); + border-top-left-radius: var(--base-rhythm); +} + +.source__range-button { + display: flex; + flex-direction: column; + align-items: stretch; + justify-content: center; + height: 100%; +} + +.source__range-banner { + padding: 0 1rem 1rem 0; +} + +.source__range.collapsed .source__range-banner { + display: none; +} + +ul.source__range-titles-list { + margin: var(--base-rhythm); + list-style-type: none; +} + +.source__range.collapsed .source__range-titles-list { + display: block; +} + +.source__range.expanded .source__range-titles-list { + display: none; +} + +.source__range-title-icon { + cursor: help; +} + +/* sdoc-node-content */ + +.source__range-banner sdoc-node-content[node-view="table"] sdoc-node-field-label { + background-color: var(--requirement-bg-light-color); +} + +/* source__range-handler */ + +.source__range-handler { + display: flex; + justify-content: center; + align-items: center; + cursor: pointer; + height: 100%; + color: var(--color-link); +} + +.source__range-handler:hover { + color: var(--color-hover); +} + +.source__range .source__range-handler .expanded, +.source__range.expanded .source__range-handler .collapsed { + display: none; +} + +.source__range .source__range-handler .collapsed, +.source__range.expanded .source__range-handler .expanded { + display: unset; +} + +/* source__line */ + +.source__line { + grid-column: 1 / -1; + display: contents; +} + +.source.coverage .source__line.covered > div { + background: rgba(75, 255, 0, 0.2); +} + +.source__line.highlighted > div { + background: var(--color-highlight) !important; +} + +/* line-number */ + +.source__line-number { + grid-column: 1 / 2; + padding: 0.25rem 1rem 0.25rem 2rem; + text-align: right; + border-bottom: var(--source-line); + color: var(--color-fg-secondary); + background: var(--color-bg-contrast); + user-select: none; + position: relative; +} + +/* line content */ + +.source__line-content { + grid-column: 2 / 3; + padding: 0.25rem 1rem 0.25rem .5rem; + border-bottom: var(--source-line); + border-left: var(--source-line); + background: var(--color-bg-contrast); +} + +/* pointers */ + +.source__range-pointer { + display: inline-block; + padding-left: calc(var(--base-rhythm)* 1); + padding-right: calc(var(--base-rhythm)* 1); + border-radius: 6px; +} + +.source__range-pointer.active { + background-color: var(--color-highlight) !important; + color: var(--color-fg-contrast) !important; +} + +.focus .source__range-pointer.active { + outline: solid 3px yellow; +} + +.source__range-pointer .source__range-pointer_description { + font-weight: normal; + word-break: break-word; +} + +.source__range-definition { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + max-width: 100%; + min-width: 0; + display: inline-block; +} + +/* Pygments */ + +.highlight { + background: transparent !important; +} diff --git a/tests/fuzz/20_L1_Open_Requirements_Tool_202511/assets/source_file_screen.js b/tests/fuzz/20_L1_Open_Requirements_Tool_202511/assets/source_file_screen.js new file mode 100644 index 0000000..8e47e20 --- /dev/null +++ b/tests/fuzz/20_L1_Open_Requirements_Tool_202511/assets/source_file_screen.js @@ -0,0 +1,642 @@ +// @relation(SDOC-SRS-36, scope=file) + +const __log = (topic, ...payload) => { + console.log(`%c ${topic} `, 'background:yellow;color:black', + ...payload + ); +} + +class SimpleTabs { + constructor(tabsContainer, tabContentSelector = "sdoc-tab-content") { + this.tabsContainer = tabsContainer; + this.tabContents = document.querySelectorAll(tabContentSelector); + this.tabs = tabsContainer.querySelectorAll("sdoc-tab"); + this._init(); + } + + _init() { + this.tabs.forEach((tab) => { + tab.addEventListener("click", () => { + this.activateTab(tab.innerText.trim()); + }); + }); + + // Activate the tab marked as active, or the first one + const activeTab = [...this.tabs].find((t) => t.hasAttribute("active")) || this.tabs[0]; + this.activateTab(activeTab.innerText.trim()); + } + + activateTab(tabName) { + this.tabs.forEach((tab) => { + if (tab.innerText.trim() === tabName) { + tab.setAttribute("active", ""); + } else { + tab.removeAttribute("active"); + } + }); + + this.tabContents.forEach((content) => { + if (content.id === tabName) { + content.setAttribute("active", ""); + } else { + content.removeAttribute("active"); + } + }); + } +} + +class Switch { + constructor({ + callback, + labelText, + checked, + componentClass, + colorOn, + colorOff, + size, + stroke, + units, + alignRight, + }) { + this.colorOn = colorOn || 'rgb(100, 200, 50)'; + this.colorOff = colorOff || 'rgb(200, 200, 200)'; + this.labelText = labelText || ''; + this.checked = checked || false; // todo: replace true/false with strings + + this.componentClass = componentClass || 'std-switch-scc'; + this.size = size || 0.75; + this.stroke = stroke || 0.25; + this.units = units || 'rem'; + this.alignRight = alignRight || true; + + this.callback = callback; + } + + create() { + const block = document.createElement('div'); + block.classList.add(this.componentClass); + const label = document.createElement('label'); + label.classList.add(`${this.componentClass}__label`); + const input = document.createElement('input'); + input.classList.add(`${this.componentClass}__input`); + input.type = 'checkbox'; + input.checked = this.checked; + const slider = document.createElement('span'); + slider.classList.add(`${this.componentClass}__slider`); + const text = document.createElement('span'); + text.innerHTML = this.labelText; + + input.addEventListener('change', () => this.callback(input.checked)); + + label.append(input, slider, text); + block.append(label); + this.insertStyle(); + + return block; + } + + insertStyle() { + + const css = ` + .${this.componentClass} { + display: inline-block; + line-height: 0; + } + .${this.componentClass}__label { + display: inline-flex; + gap: ${this.size * 0.5}${this.units}; + font-size: ${this.size * 1.5}${this.units}; /* 0.75rem; */ + line-height: ${this.size}${this.units}; + align-items: center; + justify-content: flex-start; + user-select: none; + cursor: pointer; + flex-direction: ${this.alignRight ? "row-reverse" : "row"}; + text-align: ${this.alignRight ? "right" : "left"}; + } + .${this.componentClass}__input { + opacity: 0; + width: 0; + height: 0; + position: absolute; + } + .${this.componentClass}__slider { + position: relative; + cursor: pointer; + background-color: ${this.colorOff}; + -webkit-transition: .4s; + transition: .4s; + display: inline-block; + width: ${this.size * 2 + this.stroke * 2}${this.units}; + height: ${this.size + this.stroke * 2}${this.units}; + border-radius: ${this.size * 0.5 + this.stroke}${this.units}; + } + .${this.componentClass}__slider::before { + position: absolute; + content: ""; + height: ${this.size}${this.units}; + width: ${this.size}${this.units}; + left: ${this.stroke}${this.units}; + bottom: ${this.stroke}${this.units}; + background-color: white; + -webkit-transition: .4s; + transition: .4s; + border-radius: 50%; + } + input:checked + .${this.componentClass}__slider { + background-color: ${this.colorOn}; + } + input:focus + .${this.componentClass}__slider { + box-shadow: 0 0 1px ${this.colorOn}; + } + input:checked + .${this.componentClass}__slider::before { + -webkit-transform: translateX(${this.size}${this.units}); + -ms-transform: translateX(${this.size}${this.units}); + transform: translateX(${this.size}${this.units}); + } + `; + + const head = document.querySelector('head'); + const style = document.createElement('style'); + style.append(document.createTextNode(css)); + style.setAttribute("data-slider-styles", ''); + head.append(style); + } + +} + +class Dom { + constructor({ + sourceId, + sourceContainerId, + referContainerId, + hashSplitter, + strictdocPointerSelector, + strictdocRequirementSelector, + strictdocRangeBannerSelector, + strictdocRangeBannerHeaderSelector, + strictdocRangeHandlerSelector, + strictdocRangeCloserSelector, + strictdocLineSelector, + strictdocLineNumberSelector, + strictdocLineContentSelector, + strictdocLineRangeSelector, + strictdocSourceFilterClass, + filteredClass, + coveredClass, + activeClass, + collapsedClass, + expandedClass, + coverageClass, + highlightClass, + focusClass, + }) { + + // CONSTANTS + this.sourceId = sourceId || 'source'; + this.sourceContainerId = sourceContainerId || 'sourceContainer'; + this.referContainerId = referContainerId || 'referContainer'; + this.hashSplitter = hashSplitter || '#'; + + // STRICTDOC SPECIFIC + this.strictdocPointerSelector = strictdocPointerSelector || '.source__range-pointer'; + this.strictdocRequirementSelector = strictdocRequirementSelector || '.source-file__requirement'; + this.strictdocRangeBannerSelector = strictdocRangeBannerSelector || '.source__range'; + this.strictdocRangeBannerHeaderSelector = strictdocRangeBannerHeaderSelector || '.source__range-header'; + this.strictdocRangeHandlerSelector = strictdocRangeHandlerSelector || '.source__range-handler'; + this.strictdocRangeCloserSelector = strictdocRangeCloserSelector || '.source__range-closer'; + this.strictdocLineSelector = strictdocLineSelector || '.source__line'; + this.strictdocLineNumberSelector = strictdocLineNumberSelector || '.source__line-number'; + this.strictdocLineContentSelector = strictdocLineContentSelector || '.source__line-content'; + this.strictdocLineRangeSelector = strictdocLineRangeSelector || '.source__line-ranges'; + this.strictdocSourceFilterClass = strictdocSourceFilterClass || 'source__filter'; + this.filteredClass = filteredClass || 'filtered'; + this.activeClass = activeClass || 'active'; + this.coveredClass = coveredClass || 'covered'; + this.coverageClass = coverageClass || 'coverage'; + this.collapsedClass = collapsedClass || 'collapsed'; + this.expandedClass = expandedClass || 'expanded'; + this.highlightClass = highlightClass || 'highlighted'; + this.focusClass = focusClass || 'focus'; + + // elements + this.sourceContainer; + this.referContainer; + this.source; + this.lines = {}; + this.requirements = {}; + this.ranges = {}; + this.closers = {}; + + // state + this.active = { + range: null, + pointers: [], + labels: [], + focus: false, + }; + } + + prepare() { + this._prepareSourceContainer(); + this._prepareReferContainer(); + this._prepareSource(); + + this._prepareLines(); + this._prepareRanges(); + this._updateLinesWithRanges(); + this._updateRangesWithRequirements(); + this._updateRangesWithHandlers(); + this._updateRangesWithBanners(); + this._updateRangesWithClosers(); + + // console.log('this.lines', this.lines); + // console.log('this.ranges', this.ranges); + // console.log('this.closers', this.closers); + // console.log('this.requirements', this.requirements); + // console.log('this.active', this.active); + } + + useLocationHash() { + const [_, reqId, rangeBegin, rangeEnd] = window.location.hash.split(this.hashSplitter); + const rangeAlias = rangeBegin ? this._generateRangeAlias(rangeBegin, rangeEnd) : undefined; + + this.changeActive({ + rangeBegin, + rangeEnd, + rangeAlias, + pointers: rangeAlias ? this.ranges[rangeAlias].pointers : null, + labels: (reqId && rangeAlias) ? this.ranges[rangeAlias][reqId] : null, + }); + + this.highlightRange(); + } + + changeActive = ({ + rangeBegin, + rangeEnd, + rangeAlias, + pointers, + labels, + }) => { + + // remove old 'active' + this.active.pointers?.forEach(pointer => pointer?.classList.remove(this.activeClass)); + this.active.labels?.forEach(label => label?.classList.remove(this.activeClass)); + if (this.active.rangeAlias) { + this.ranges[this.active.rangeAlias].banner.classList.remove(this.activeClass); + + const closer = this.closers?.[this.active.rangeEnd]; + console.assert( + closer, + "Closer must not be null. One known way of getting this error is " + + "when a closing function/range marker is missing and not registered " + + "with the file traceability info." + ); + + closer.classList.remove(this.activeClass); + } + + // make changes to state + this.active.pointers = pointers; + this.active.labels = labels; + this.active.rangeBegin = rangeBegin; + this.active.rangeEnd = rangeEnd; + this.active.rangeAlias = rangeAlias; + + // add new 'active' + this.active.pointers?.forEach(pointer => pointer.classList.add(this.activeClass)); + this.active.labels?.forEach(label => label.classList.add(this.activeClass)); + this.ranges[rangeAlias]?.banner.classList.add(this.activeClass); + this.closers[this.active.rangeEnd]?.classList.add(this.activeClass); + } + + toggleRangeBannerVisibility(handler) { + const banner = handler.closest(this.strictdocRangeBannerSelector); + + if (banner.classList.contains(this.expandedClass)) { + banner.classList.remove(this.expandedClass); + banner.classList.add(this.collapsedClass); + } else { + banner.classList.add(this.expandedClass); + banner.classList.remove(this.collapsedClass); + } + } + + toggleCoverageVisibility(toggler) { + if (toggler) { + this.source.classList.add(this.coverageClass); + } else { + this.source.classList.remove(this.coverageClass); + } + } + + highlightRange() { + this.scrollToActiveRangeIfNeeded(); + + const begin = parseInt(this.active.rangeBegin, 10); + const end = parseInt(this.active.rangeEnd, 10); + + for (var key in this.lines) { + this.lines[key].line.classList.remove(this.highlightClass); + if (key >= begin && key <= end) { + this.lines[key].line.classList.add(this.highlightClass); + } + } + } + + scrollToActiveRangeIfNeeded() { + // scroll to highlighted, do not scroll to top when unset highlighting: + if (this.active.rangeAlias) { + const activeRange = this.ranges[this.active.rangeAlias]?.bannerHeader || 0; + requestAnimationFrame(() => { + this.scrollTo(activeRange); + }); + } + } + + scrollTo(element) { + const top = element.offsetTop || 0; + this.sourceContainer.scrollTo({ + top: top, + behavior: 'smooth', + }); + } + + _prepareSource() { + this.source = document.getElementById(this.sourceId); + this.source.style.position = 'relative'; + this.source.style.zIndex = '1'; + } + + _prepareSourceContainer() { + this.sourceContainer = document.getElementById('sourceContainer'); + } + + _prepareReferContainer() { + this.referContainer = document.getElementById('referContainer'); + } + + _prepareLines() { + this.lines = [...document.querySelectorAll(this.strictdocLineSelector)] + .reduce((acc, line) => { + const lineNumber = line.querySelector(this.strictdocLineNumberSelector); + const lineContent = line.querySelector(this.strictdocLineContentSelector); + acc[line.dataset.line] = { + line: line, + lineNumber: lineNumber, + lineContent: lineContent, + ranges: [] + }; + return acc + }, {}); + } + + _getRangePart(hash) { + const parts = hash.split(this.hashSplitter); + return `${this.hashSplitter}${parts[parts.length - 2]}${this.hashSplitter}${parts[parts.length - 1]}`; + }; + + _prepareRanges() { + [...document.querySelectorAll(this.strictdocPointerSelector)] + .map(pointer => { + const thisFileOrOther = pointer.dataset.traceabilityFileType; + // consider only references to the current file: + if (thisFileOrOther === "other_file") { + return; + } + + const rangeBegin = pointer.dataset.begin; + const rangeEnd = pointer.dataset.end; + const rangeReq = pointer.dataset.reqid; + + const range = this._generateRangeAlias(rangeBegin, rangeEnd); + + if (!this.ranges[range]) { + // add new range + this.ranges[range] = {}; + this.ranges[range].pointers = []; + + this.ranges[range].beginLine = this.lines[rangeBegin].lineNumber; + this.ranges[range].endLine = this.lines[rangeEnd].lineNumber; + this.ranges[range].begin = rangeBegin; + this.ranges[range].end = rangeEnd; + } + + // todo pointers? + if (rangeReq) { + + // add pointer from code + (this.ranges[range][rangeReq] ??= []).push(pointer); + + } else { + + // add pointer from menu + this.ranges[range].pointers.push(pointer); + } + + pointer.addEventListener("click", (event) => { + const targetHash = `#${rangeReq || ""}${this.hashSplitter}${rangeBegin}${this.hashSplitter}${rangeEnd}`; + const currentHash = window.location.hash; + + const isSameHash = currentHash === targetHash; + // Buttons linked to requirements include an ID in the hash, + // while buttons in the source code do not. + // Therefore, only the range part of the hash (e.g., #3#10) + // should be compared to identify the currently active range. + const isSameRange = this._getRangePart(currentHash) === `${this.hashSplitter}${rangeBegin}${this.hashSplitter}${rangeEnd}`; + + const isModifierPressed = event.metaKey || event.ctrlKey; + + const targetBannerHeader = this.ranges[range]?.bannerHeader; + const topBefore = targetBannerHeader?.getBoundingClientRect().top; + + // Modifier click + if (isModifierPressed) { + event.preventDefault(); // cancel opening a new browser tab + + if (isSameRange) { + this._toggleFocus(); + this._compensateSourceContainerScrollPosition(targetBannerHeader, topBefore); + } else { + // Sets the URL hash to activate a specific range without reloading the page: + window.location.hash = targetHash; + this.useLocationHash(); + this._activateFocus(); + this._compensateSourceContainerScrollPosition(targetBannerHeader, topBefore); + } + return; + } + + // Normal click + if (isSameRange) { + // active → reset + event.preventDefault(); + // Removes hash from URL without reloading or adding history entry: + history.replaceState(null, '', window.location.pathname); + this.useLocationHash(); + this._clearFocus(); + this._compensateSourceContainerScrollPosition(targetBannerHeader, topBefore); + } else { + // inactive → just reset the focus, + // the basic functionality via URL will work itself out + this._clearFocus(); + this._compensateSourceContainerScrollPosition(targetBannerHeader, topBefore); + } + }); + + }); + } + + _compensateSourceContainerScrollPosition(element, topBefore) { + // Compensates scroll to keep the given element in the same viewport position. + // Expects the element and its initial .getBoundingClientRect().top value as input. + // Measures element's top offset relative to the viewport before and after DOM changes. + // Uses requestAnimationFrame to wait for DOM/layout updates before measuring again. + // Calculates delta = after - before, i.e. how much the element moved visually. + // Scrolls by that delta to restore the element's original viewport position. + requestAnimationFrame(() => { + const topAfter = element?.getBoundingClientRect().top; + const delta = topAfter - topBefore; + this.sourceContainer.scrollBy({ top: delta }); + }); + } + + _toggleFocus() { + if (this.active.focus) { + this.sourceContainer.classList.remove(this.focusClass); + this.referContainer.classList.remove(this.focusClass); + } else { + this.sourceContainer.classList.add(this.focusClass); + this.referContainer.classList.add(this.focusClass); + } + this.active.focus = !this.active.focus; + } + + _activateFocus() { + if (!this.active.focus) { + this.active.focus = true; + this.sourceContainer.classList.add(this.focusClass); + this.referContainer.classList.add(this.focusClass); + } + } + + _clearFocus() { + if (this.active.focus) { + this.active.focus = false; + this.sourceContainer.classList.remove(this.focusClass); + this.referContainer.classList.remove(this.focusClass); + } + } + + _updateRangesWithRequirements() { + const requirements = [...document.querySelectorAll(this.strictdocRequirementSelector)]; + requirements.forEach(requirement => { + + const rangeBegin = requirement.dataset.begin; + const rangeEnd = requirement.dataset.end; + const rangeReq = requirement.dataset.reqid; + + if (rangeEnd && rangeBegin) { + const range = this._generateRangeAlias(rangeBegin, rangeEnd); + console.assert(this.ranges[range], "The range must be registered:", range); + + (this.ranges[range].requirements ??= {})[rangeReq] = {}; + this.ranges[range].requirements[rangeReq].begin = rangeBegin; + this.ranges[range].requirements[rangeReq].end = rangeEnd; + this.ranges[range].requirements[rangeReq].element = requirement; + } else { + this.requirements[rangeReq] = requirement; + } + + + }) + } + + _updateRangesWithHandlers() { + const handlers = [...document.querySelectorAll(this.strictdocRangeHandlerSelector)]; + handlers.forEach(handler => { + + const rangeBegin = handler.dataset.begin; + const rangeEnd = handler.dataset.end; + const range = this._generateRangeAlias(rangeBegin, rangeEnd); + + console.assert(this.ranges[range], "The range must be registered:", range); + + this.ranges[range].handler = handler; + handler.addEventListener("click", event => this.toggleRangeBannerVisibility(event.currentTarget)); + }) + } + + _updateRangesWithBanners() { + const banners = [...document.querySelectorAll(this.strictdocRangeBannerSelector)]; + banners.forEach(banner => { + + const rangeBegin = banner.dataset.begin; + const rangeEnd = banner.dataset.end; + + if (rangeBegin && rangeEnd) { + const range = this._generateRangeAlias(rangeBegin, rangeEnd); + console.assert(this.ranges[range], "The range must be registered:", range); + this.ranges[range].banner = banner; + this.ranges[range].bannerHeader = banner.querySelector(this.strictdocRangeBannerHeaderSelector); // 'source__range-header' + } + }) + } + + _updateRangesWithClosers() { + const closers = [...document.querySelectorAll(this.strictdocRangeCloserSelector)]; + closers.forEach(closer => { + + const rangeEnd = closer.dataset.end; + + if (rangeEnd) { + this.closers[rangeEnd] = closer; + } + }) + } + + _updateLinesWithRanges() { + Object.entries(this.ranges).forEach(([key, value]) => { + const begin = parseInt(value.begin, 10); + const end = parseInt(value.end, 10); + + for (let i = begin; i <= end; i++) { + (this.lines[i].ranges ??= []).push(key); + (this.lines[i].pointers ??= []).push(...value.pointers); + + console.assert(this.lines[i].lineNumber, `The line ${i} must be registered.`); + this.lines[i].lineNumber.classList.add(this.strictdocSourceFilterClass); + this.lines[i].line.classList.add(this.coveredClass); + } + }); + } + + _generateRangeAlias(begin, end) { return `${begin}${this.hashSplitter}${end}` }; +} + +const dom = new Dom({}); + +window.addEventListener("load", function () { + dom.prepare(); + dom.useLocationHash(); + + const switcher = new Switch( + { + labelText: 'Show coverage', + size: 0.5, + stroke: 0.2, + checked: false, + callback: (checked) => dom.toggleCoverageVisibility(checked), + } + ); + document.getElementById('sourceCodeCoverageSwitch').append(switcher.create()); + + const tabsContainer = document.querySelector("sdoc-tabs"); + if (tabsContainer) { + new SimpleTabs(tabsContainer); + } + +}); + +window.addEventListener("hashchange", () => dom.useLocationHash()); diff --git a/tests/fuzz/20_L1_Open_Requirements_Tool_202511/assets/stable_uri_forwarder.js b/tests/fuzz/20_L1_Open_Requirements_Tool_202511/assets/stable_uri_forwarder.js new file mode 100644 index 0000000..fcabf7f --- /dev/null +++ b/tests/fuzz/20_L1_Open_Requirements_Tool_202511/assets/stable_uri_forwarder.js @@ -0,0 +1,99 @@ +// stable_uri_forwarder.js +// +// This script is included from the project's index.html page (in static HTML export) +// It checks for a known MID or UID in the #anchor, and then +// redirects to the referenced section or node within the project. +// +// Example: +// http://strictdoc.company.com/#SDOC_UG will redirect to +// http://strictdoc.company.com/strictdoc/docs/strictdoc_01_user_guide.html#SDOC_UG +// +// Thanks to this mechanism, it becomes possible to export stable links to +// nodes/requirements/sections for integration with external tools. +// The external links remain stable, even if the node/requirement/section is moved +// within the project. + + +// Resolve the MID / UID to the correct page / anchor using the projectMap. +function resolveStableUriRedirectUsingProjectMap(anchor) { + for (const [page, nodes] of Object.entries(projectMap)) { + for (const node of nodes) { + if (node['_LINK'].toLowerCase() === anchor.toLowerCase()) { + window.location.replace(page + "#" + node['_LINK']); + return; + } + + const nodeHasUID = 'UID' in node && typeof node['UID'] === 'string'; + if (nodeHasUID && node['UID'].toLowerCase() === anchor.toLowerCase()) { + window.location.replace(page + "#" + node['_LINK']); + return; + } + + const nodeHasMID = 'MID' in node && typeof node['MID'] === 'string'; + if (nodeHasMID && node['MID'].toLowerCase() === anchor.toLowerCase()) { + window.location.replace(page + "#" + node['_LINK']); + return; + } + } + } +} + +// Dynamically load the projectMap an resolve MID / UID. +function loadProjectMapAndResolveStableUriRedirect(anchor) { + + // ProjectMap is loaded, no need to load it again. + if (typeof projectMap !== 'undefined') { + resolveStableUriRedirectUsingProjectMap(anchor); + return; + } + + // Get script URL and derive from it the url of project_map.js + const scriptUrl = new URL(document.getElementById("stable_uri_forwarder").src, window.location + .href) + const projectMapUrl = new URL('project_map.js', scriptUrl).href; + + // Dynamically load project map and resolve + const script = document.createElement("script"); + script.src = projectMapUrl; + script.onload = () => resolveStableUriRedirectUsingProjectMap(anchor); + script.onerror = () => { + console.error(`Failed to load project map from ${projectMapUrl}`); + }; + document.head.appendChild(script); +} + +function processStableUriRedirect(anchor) { + const exportType = document.querySelector('meta[name="strictdoc-export-type"]')?.content; + + if (exportType === 'webserver') { + // For the web server, we let the main_router.py dynamically forward UID to node. + window.location.replace("/UID/" + anchor); + } else if (exportType === 'static') { + // For static exports, we use the project_map.js. + loadProjectMapAndResolveStableUriRedirect(anchor) + } +} + + +// In case an anchor is present at content load time. +document.addEventListener("DOMContentLoaded", () => { + const anchorParam = new URLSearchParams(window.location.search).get("a"); + if (anchorParam) { + processStableUriRedirect(anchorParam) + return + } + + // for legacy link support + const anchorHash = window.location.hash.substring(1); + if (anchorHash) { + processStableUriRedirect(anchorHash) + } +}); + +// In case an anchor is added manually afterwards (eases testing). +window.addEventListener("hashchange", () => { + const anchor = window.location.hash.substring(1); + if (anchor) { + processStableUriRedirect(anchor) + } +}); diff --git a/tests/fuzz/20_L1_Open_Requirements_Tool_202511/assets/stimulus_umd.min.js b/tests/fuzz/20_L1_Open_Requirements_Tool_202511/assets/stimulus_umd.min.js new file mode 100644 index 0000000..0df2936 --- /dev/null +++ b/tests/fuzz/20_L1_Open_Requirements_Tool_202511/assets/stimulus_umd.min.js @@ -0,0 +1,5 @@ +!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).Stimulus={})}(this,function(e){"use strict";class K{constructor(e,t,r){this.eventTarget=e,this.eventName=t,this.eventOptions=r,this.unorderedBindings=new Set}connect(){this.eventTarget.addEventListener(this.eventName,this,this.eventOptions)}disconnect(){this.eventTarget.removeEventListener(this.eventName,this,this.eventOptions)}bindingConnected(e){this.unorderedBindings.add(e)}bindingDisconnected(e){this.unorderedBindings.delete(e)}handleEvent(e){var t=function(t){{if("immediatePropagationStopped"in t)return t;{let e=t.stopImmediatePropagation;return Object.assign(t,{immediatePropagationStopped:!1,stopImmediatePropagation(){this.immediatePropagationStopped=!0,e.call(this)}})}}}(e);for(const r of this.bindings){if(t.immediatePropagationStopped)break;r.handleEvent(t)}}hasBindings(){return 0{e=e.index,t=t.index;return ee.connect()))}stop(){this.started&&(this.started=!1,this.eventListeners.forEach(e=>e.disconnect()))}get eventListeners(){return Array.from(this.eventListenerMaps.values()).reduce((e,t)=>e.concat(Array.from(t.values())),[])}bindingConnected(e){this.fetchEventListenerForBinding(e).bindingConnected(e)}bindingDisconnected(e,t=!1){this.fetchEventListenerForBinding(e).bindingDisconnected(e),t&&this.clearEventListenersForBinding(e)}handleError(e,t,r={}){this.application.handleError(e,"Error "+t,r)}clearEventListenersForBinding(e){var t=this.fetchEventListenerForBinding(e);t.hasBindings()||(t.disconnect(),this.removeMappedEventListenerFor(e))}removeMappedEventListenerFor(e){var{eventTarget:e,eventName:t,eventOptions:r}=e,s=this.fetchEventListenerMapForEventTarget(e),t=this.cacheKey(t,r);s.delete(t),0==s.size&&this.eventListenerMaps.delete(e)}fetchEventListenerForBinding(e){var{eventTarget:e,eventName:t,eventOptions:r}=e;return this.fetchEventListener(e,t,r)}fetchEventListener(e,t,r){var s=this.fetchEventListenerMapForEventTarget(e),i=this.cacheKey(t,r);let n=s.get(i);return n||(n=this.createEventListener(e,t,r),s.set(i,n)),n}createEventListener(e,t,r){e=new K(e,t,r);return this.started&&e.connect(),e}fetchEventListenerMapForEventTarget(e){let t=this.eventListenerMaps.get(e);return t||(t=new Map,this.eventListenerMaps.set(e,t)),t}cacheKey(e,t){const r=[e];return Object.keys(t).sort().forEach(e=>{r.push((t[e]?"":"!")+e)}),r.join(":")}}const j={stop({event:e,value:t}){return t&&e.stopPropagation(),!0},prevent({event:e,value:t}){return t&&e.preventDefault(),!0},self({event:e,value:t,element:r}){return!t||r===e.target}},$=/^(?:(?:([^.]+?)\+)?(.+?)(?:\.(.+?))?(?:@(window|document))?->)?(.+?)(?:#([^:]+?))(?::(.+))?$/;function U(e){var t,e=e.trim().match($)||[];let r=e[2],s=e[3];return s&&!["keydown","keyup","keypress"].includes(r)&&(r+="."+s,s=""),{eventTarget:"window"==(t=e[4])?window:"document"==t?document:void 0,eventName:r,eventOptions:e[7]?e[7].split(":").reduce((e,t)=>Object.assign(e,{[t.replace(/^!/,"")]:!/^!/.test(t)}),{}):{},identifier:e[5],methodName:e[6],keyFilter:e[1]||s}}function a(e){return e.replace(/(?:[_-])([a-z0-9])/g,(e,t)=>t.toUpperCase())}function s(e){return a(e.replace(/--/g,"-").replace(/__/g,"_"))}function l(e){return e.charAt(0).toUpperCase()+e.slice(1)}function h(e){return e.replace(/([A-Z])/g,(e,t)=>"-"+t.toLowerCase())}function c(e){return null!=e}function u(e,t){return Object.prototype.hasOwnProperty.call(e,t)}const o=["meta","ctrl","alt","shift"];class P{constructor(e,t,r,s){this.element=e,this.index=t,this.eventTarget=r.eventTarget||e,this.eventName=r.eventName||function(e){var t=e.tagName.toLowerCase();if(t in i)return i[t](e)}(e)||n("missing event name"),this.eventOptions=r.eventOptions||{},this.identifier=r.identifier||n("missing identifier"),this.methodName=r.methodName||n("missing method name"),this.keyFilter=r.keyFilter||"",this.schema=s}static forToken(e,t){return new this(e.element,e.index,U(e.content),t)}toString(){var e=this.keyFilter?"."+this.keyFilter:"",t=this.eventTargetName?"@"+this.eventTargetName:"";return this.eventName+e+t+`->${this.identifier}#`+this.methodName}shouldIgnoreKeyboardEvent(e){var t;return!!this.keyFilter&&(t=this.keyFilter.split("+"),!!this.keyFilterDissatisfied(e,t)||!!(t=t.filter(e=>!o.includes(e))[0])&&(u(this.keyMappings,t)||n("contains unknown key filter: "+this.keyFilter),this.keyMappings[t].toLowerCase()!==e.key.toLowerCase()))}shouldIgnoreMouseEvent(e){var t;return!!this.keyFilter&&(t=[this.keyFilter],!!this.keyFilterDissatisfied(e,t))}get params(){var e,t,r={},s=new RegExp(`^data-${this.identifier}-(.+)-param$`,"i");for({name:e,value:t}of Array.from(this.element.attributes)){var i=e.match(s),i=i&&i[1];i&&(r[a(i)]=function(t){try{return JSON.parse(t)}catch(e){return t}}(t))}return r}get eventTargetName(){return(e=this.eventTarget)==window?"window":e==document?"document":void 0;var e}get keyMappings(){return this.schema.keyMappings}keyFilterDissatisfied(e,t){var[r,s,i,n]=o.map(e=>t.includes(e));return e.metaKey!==r||e.ctrlKey!==s||e.altKey!==i||e.shiftKey!==n}}const i={a:()=>"click",button:()=>"click",form:()=>"submit",details:()=>"toggle",input:e=>"submit"==e.getAttribute("type")?"click":"input",select:()=>"change",textarea:()=>"input"};function n(e){throw new Error(e)}class R{constructor(e,t){this.context=e,this.action=t}get index(){return this.action.index}get eventTarget(){return this.action.eventTarget}get eventOptions(){return this.action.eventOptions}get identifier(){return this.context.identifier}handleEvent(e){var t=this.prepareActionEvent(e);this.willBeInvokedByEvent(e)&&this.applyEventModifiers(t)&&this.invokeWithEvent(t)}get eventName(){return this.action.eventName}get method(){var e=this.controller[this.methodName];if("function"==typeof e)return e;throw new Error(`Action "${this.action}" references undefined method "${this.methodName}"`)}applyEventModifiers(e){var t,r,s,i=this.action.element,n=this.context.application.actionDescriptorFilters,o=this.context.controller;let a=!0;for([t,r]of Object.entries(this.eventOptions))t in n&&(s=n[t],a=a&&s({name:t,value:r,event:e,element:i,controller:o}));return a}prepareActionEvent(e){return Object.assign(e,{params:this.action.params})}invokeWithEvent(t){var{target:r,currentTarget:s}=t;try{this.method.call(this.controller,t),this.context.logDebugActivity(this.methodName,{event:t,target:r,currentTarget:s,action:this.methodName})}catch(e){var{identifier:r,controller:s,element:i,index:n}=this;this.context.handleError(e,`invoking action "${this.action}"`,{identifier:r,controller:s,element:i,index:n,event:t})}}willBeInvokedByEvent(e){var t=e.target;return!(e instanceof KeyboardEvent&&this.action.shouldIgnoreKeyboardEvent(e)||e instanceof MouseEvent&&this.action.shouldIgnoreMouseEvent(e))&&(this.element===t||(t instanceof Element&&this.element.contains(t)?this.scope.containsElement(t):this.scope.containsElement(this.action.element)))}get controller(){return this.context.controller}get methodName(){return this.action.methodName}get element(){return this.scope.element}get scope(){return this.context.scope}}class d{constructor(e,t){this.mutationObserverInit={attributes:!0,childList:!0,subtree:!0},this.element=e,this.started=!1,this.delegate=t,this.elements=new Set,this.mutationObserver=new MutationObserver(e=>this.processMutations(e))}start(){this.started||(this.started=!0,this.mutationObserver.observe(this.element,this.mutationObserverInit),this.refresh())}pause(e){this.started&&(this.mutationObserver.disconnect(),this.started=!1),e(),this.started||(this.mutationObserver.observe(this.element,this.mutationObserverInit),this.started=!0)}stop(){this.started&&(this.mutationObserver.takeRecords(),this.mutationObserver.disconnect(),this.started=!1)}refresh(){if(this.started){var e=new Set(this.matchElementsInTree());for(const t of Array.from(this.elements))e.has(t)||this.removeElement(t);for(const r of Array.from(e))this.addElement(r)}}processMutations(e){if(this.started)for(const t of e)this.processMutation(t)}processMutation(e){"attributes"==e.type?this.processAttributeChange(e.target,e.attributeName):"childList"==e.type&&(this.processRemovedNodes(e.removedNodes),this.processAddedNodes(e.addedNodes))}processAttributeChange(e,t){this.elements.has(e)?this.delegate.elementAttributeChanged&&this.matchElement(e)?this.delegate.elementAttributeChanged(e,t):this.removeElement(e):this.matchElement(e)&&this.addElement(e)}processRemovedNodes(e){for(const r of Array.from(e)){var t=this.elementFromNode(r);t&&this.processTree(t,this.removeElement)}}processAddedNodes(e){for(const r of Array.from(e)){var t=this.elementFromNode(r);t&&this.elementIsActive(t)&&this.processTree(t,this.addElement)}}matchElement(e){return this.delegate.matchElement(e)}matchElementsInTree(e=this.element){return this.delegate.matchElementsInTree(e)}processTree(e,t){for(const r of this.matchElementsInTree(e))t.call(this,r)}elementFromNode(e){if(e.nodeType==Node.ELEMENT_NODE)return e}elementIsActive(e){return e.isConnected==this.element.isConnected&&this.element.contains(e)}addElement(e){this.elements.has(e)||this.elementIsActive(e)&&(this.elements.add(e),this.delegate.elementMatched)&&this.delegate.elementMatched(e)}removeElement(e){this.elements.has(e)&&(this.elements.delete(e),this.delegate.elementUnmatched)&&this.delegate.elementUnmatched(e)}}class m{constructor(e,t,r){this.attributeName=t,this.delegate=r,this.elementObserver=new d(e,this)}get element(){return this.elementObserver.element}get selector(){return`[${this.attributeName}]`}start(){this.elementObserver.start()}pause(e){this.elementObserver.pause(e)}stop(){this.elementObserver.stop()}refresh(){this.elementObserver.refresh()}get started(){return this.elementObserver.started}matchElement(e){return e.hasAttribute(this.attributeName)}matchElementsInTree(e){var t=this.matchElement(e)?[e]:[],e=Array.from(e.querySelectorAll(this.selector));return t.concat(e)}elementMatched(e){this.delegate.elementMatchedAttribute&&this.delegate.elementMatchedAttribute(e,this.attributeName)}elementUnmatched(e){this.delegate.elementUnmatchedAttribute&&this.delegate.elementUnmatchedAttribute(e,this.attributeName)}elementAttributeChanged(e,t){this.delegate.elementAttributeValueChanged&&this.attributeName==t&&this.delegate.elementAttributeValueChanged(e,t)}}function r(e,t,r){p(e,t).add(r)}function g(e,t,r){p(e,t).delete(r),f(e,t)}function p(e,t){let r=e.get(t);return r||(r=new Set,e.set(t,r)),r}function f(e,t){var r=e.get(t);null!=r&&0==r.size&&e.delete(t)}class v{constructor(){this.valuesByKey=new Map}get keys(){return Array.from(this.valuesByKey.keys())}get values(){return Array.from(this.valuesByKey.values()).reduce((e,t)=>e.concat(Array.from(t)),[])}get size(){return Array.from(this.valuesByKey.values()).reduce((e,t)=>e+t.size,0)}add(e,t){r(this.valuesByKey,e,t)}delete(e,t){g(this.valuesByKey,e,t)}has(e,t){e=this.valuesByKey.get(e);return null!=e&&e.has(t)}hasKey(e){return this.valuesByKey.has(e)}hasValue(t){return Array.from(this.valuesByKey.values()).some(e=>e.has(t))}getValuesForKey(e){e=this.valuesByKey.get(e);return e?Array.from(e):[]}getKeysForValue(t){return Array.from(this.valuesByKey).filter(([,e])=>e.has(t)).map(([e])=>e)}}class z extends v{constructor(){super(),this.keysByValue=new Map}get values(){return Array.from(this.keysByValue.keys())}add(e,t){super.add(e,t),r(this.keysByValue,t,e)}delete(e,t){super.delete(e,t),g(this.keysByValue,t,e)}hasValue(e){return this.keysByValue.has(e)}getKeysForValue(e){e=this.keysByValue.get(e);return e?Array.from(e):[]}}class b{constructor(e,t,r,s){this._selector=t,this.details=s,this.elementObserver=new d(e,this),this.delegate=r,this.matchesByElement=new v}get started(){return this.elementObserver.started}get selector(){return this._selector}set selector(e){this._selector=e,this.refresh()}start(){this.elementObserver.start()}pause(e){this.elementObserver.pause(e)}stop(){this.elementObserver.stop()}refresh(){this.elementObserver.refresh()}get element(){return this.elementObserver.element}matchElement(e){var t=this.selector;return!!t&&(t=e.matches(t),this.delegate.selectorMatchElement?t&&this.delegate.selectorMatchElement(e,this.details):t)}matchElementsInTree(e){var t,r=this.selector;return r?(t=this.matchElement(e)?[e]:[],e=Array.from(e.querySelectorAll(r)).filter(e=>this.matchElement(e)),t.concat(e)):[]}elementMatched(e){var t=this.selector;t&&this.selectorMatched(e,t)}elementUnmatched(e){for(const t of this.matchesByElement.getKeysForValue(e))this.selectorUnmatched(e,t)}elementAttributeChanged(e,t){var r,s,i=this.selector;i&&(r=this.matchElement(e),s=this.matchesByElement.has(i,e),r&&!s?this.selectorMatched(e,i):!r&&s&&this.selectorUnmatched(e,i))}selectorMatched(e,t){this.delegate.selectorMatched(e,t,this.details),this.matchesByElement.add(t,e)}selectorUnmatched(e,t){this.delegate.selectorUnmatched(e,t,this.details),this.matchesByElement.delete(t,e)}}class y{constructor(e,t){this.element=e,this.delegate=t,this.started=!1,this.stringMap=new Map,this.mutationObserver=new MutationObserver(e=>this.processMutations(e))}start(){this.started||(this.started=!0,this.mutationObserver.observe(this.element,{attributes:!0,attributeOldValue:!0}),this.refresh())}stop(){this.started&&(this.mutationObserver.takeRecords(),this.mutationObserver.disconnect(),this.started=!1)}refresh(){if(this.started)for(const e of this.knownAttributeNames)this.refreshAttribute(e,null)}processMutations(e){if(this.started)for(const t of e)this.processMutation(t)}processMutation(e){var t=e.attributeName;t&&this.refreshAttribute(t,e.oldValue)}refreshAttribute(e,t){var r=this.delegate.getStringMapKeyForAttribute(e);if(null!=r){this.stringMap.has(e)||this.stringMapKeyAdded(r,e);var s=this.element.getAttribute(e);if(this.stringMap.get(e)!=s&&this.stringMapValueChanged(s,r,t),null==s){const t=this.stringMap.get(e);this.stringMap.delete(e),t&&this.stringMapKeyRemoved(r,e,t)}else this.stringMap.set(e,s)}}stringMapKeyAdded(e,t){this.delegate.stringMapKeyAdded&&this.delegate.stringMapKeyAdded(e,t)}stringMapValueChanged(e,t,r){this.delegate.stringMapValueChanged&&this.delegate.stringMapValueChanged(e,t,r)}stringMapKeyRemoved(e,t,r){this.delegate.stringMapKeyRemoved&&this.delegate.stringMapKeyRemoved(e,t,r)}get knownAttributeNames(){return Array.from(new Set(this.currentAttributeNames.concat(this.recordedAttributeNames)))}get currentAttributeNames(){return Array.from(this.element.attributes).map(e=>e.name)}get recordedAttributeNames(){return Array.from(this.stringMap.keys())}}class O{constructor(e,t,r){this.attributeObserver=new m(e,t,this),this.delegate=r,this.tokensByElement=new v}get started(){return this.attributeObserver.started}start(){this.attributeObserver.start()}pause(e){this.attributeObserver.pause(e)}stop(){this.attributeObserver.stop()}refresh(){this.attributeObserver.refresh()}get element(){return this.attributeObserver.element}get attributeName(){return this.attributeObserver.attributeName}elementMatchedAttribute(e){this.tokensMatched(this.readTokensForElement(e))}elementAttributeValueChanged(e){var[e,t]=this.refreshTokensForElement(e);this.tokensUnmatched(e),this.tokensMatched(t)}elementUnmatchedAttribute(e){this.tokensUnmatched(this.tokensByElement.getValuesForKey(e))}tokensMatched(e){e.forEach(e=>this.tokenMatched(e))}tokensUnmatched(e){e.forEach(e=>this.tokenUnmatched(e))}tokenMatched(e){this.delegate.tokenMatched(e),this.tokensByElement.add(e.element,e)}tokenUnmatched(e){this.delegate.tokenUnmatched(e),this.tokensByElement.delete(e.element,e)}refreshTokensForElement(e){var r,s,t=this.tokensByElement.getValuesForKey(e),e=this.readTokensForElement(e),i=(r=t,s=e,i=Math.max(r.length,s.length),Array.from({length:i},(e,t)=>[r[t],s[t]]).findIndex(([e,t])=>{return t=t,!((e=e)&&t&&e.index==t.index&&e.content==t.content)}));return-1==i?[[],[]]:[t.slice(i),e.slice(i)]}readTokensForElement(e){var r,s,t=this.attributeName,i=e.getAttribute(t)||"";return r=e,s=t,i.trim().split(/\s+/).filter(e=>e.length).map((e,t)=>({element:r,attributeName:s,content:e,index:t}))}}class A{constructor(e,t,r){this.tokenListObserver=new O(e,t,this),this.delegate=r,this.parseResultsByToken=new WeakMap,this.valuesByTokenByElement=new WeakMap}get started(){return this.tokenListObserver.started}start(){this.tokenListObserver.start()}stop(){this.tokenListObserver.stop()}refresh(){this.tokenListObserver.refresh()}get element(){return this.tokenListObserver.element}get attributeName(){return this.tokenListObserver.attributeName}tokenMatched(e){var t=e.element,r=this.fetchParseResultForToken(e).value;r&&(this.fetchValuesByTokenForElement(t).set(e,r),this.delegate.elementMatchedValue(t,r))}tokenUnmatched(e){var t=e.element,r=this.fetchParseResultForToken(e).value;r&&(this.fetchValuesByTokenForElement(t).delete(e),this.delegate.elementUnmatchedValue(t,r))}fetchParseResultForToken(e){let t=this.parseResultsByToken.get(e);return t||(t=this.parseToken(e),this.parseResultsByToken.set(e,t)),t}fetchValuesByTokenForElement(e){let t=this.valuesByTokenByElement.get(e);return t||(t=new Map,this.valuesByTokenByElement.set(e,t)),t}parseToken(e){try{return{value:this.delegate.parseValueForToken(e)}}catch(e){return{error:e}}}}class _{constructor(e,t){this.context=e,this.delegate=t,this.bindingsByAction=new Map}start(){this.valueListObserver||(this.valueListObserver=new A(this.element,this.actionAttribute,this),this.valueListObserver.start())}stop(){this.valueListObserver&&(this.valueListObserver.stop(),delete this.valueListObserver,this.disconnectAllActions())}get element(){return this.context.element}get identifier(){return this.context.identifier}get actionAttribute(){return this.schema.actionAttribute}get schema(){return this.context.schema}get bindings(){return Array.from(this.bindingsByAction.values())}connectAction(e){var t=new R(this.context,e);this.bindingsByAction.set(e,t),this.delegate.bindingConnected(t)}disconnectAction(e){var t=this.bindingsByAction.get(e);t&&(this.bindingsByAction.delete(e),this.delegate.bindingDisconnected(t))}disconnectAllActions(){this.bindings.forEach(e=>this.delegate.bindingDisconnected(e,!0)),this.bindingsByAction.clear()}parseValueForToken(e){e=P.forToken(e,this.schema);if(e.identifier==this.identifier)return e}elementMatchedValue(e,t){this.connectAction(t)}elementUnmatchedValue(e,t){this.disconnectAction(t)}}class q{constructor(e,t){this.context=e,this.receiver=t,this.stringMapObserver=new y(this.element,this),this.valueDescriptorMap=this.controller.valueDescriptorMap}start(){this.stringMapObserver.start(),this.invokeChangedCallbacksForDefaultValues()}stop(){this.stringMapObserver.stop()}get element(){return this.context.element}get controller(){return this.context.controller}getStringMapKeyForAttribute(e){if(e in this.valueDescriptorMap)return this.valueDescriptorMap[e].name}stringMapKeyAdded(e,t){t=this.valueDescriptorMap[t];this.hasValue(e)||this.invokeChangedCallback(e,t.writer(this.receiver[e]),t.writer(t.defaultValue))}stringMapValueChanged(e,t,r){var s=this.valueDescriptorNameMap[t];null!==e&&(null===r&&(r=s.writer(s.defaultValue)),this.invokeChangedCallback(t,e,r))}stringMapKeyRemoved(e,t,r){var s=this.valueDescriptorNameMap[e];this.hasValue(e)?this.invokeChangedCallback(e,s.writer(this.receiver[e]),r):this.invokeChangedCallback(e,s.writer(s.defaultValue),r)}invokeChangedCallbacksForDefaultValues(){for(var{key:e,name:t,defaultValue:r,writer:s}of this.valueDescriptors)null==r||this.controller.data.has(e)||this.invokeChangedCallback(t,s(r),void 0)}invokeChangedCallback(t,r,s){var i=this.receiver[t+"Changed"];if("function"==typeof i){t=this.valueDescriptorNameMap[t];try{var n=t.reader(r);let e=s;s&&(e=t.reader(s)),i.call(this.receiver,n,e)}catch(e){throw e instanceof TypeError&&(e.message=`Stimulus Value "${this.context.identifier}.${t.name}" - `+e.message),e}}}get valueDescriptors(){let t=this.valueDescriptorMap;return Object.keys(t).map(e=>t[e])}get valueDescriptorNameMap(){const t={};return Object.keys(this.valueDescriptorMap).forEach(e=>{e=this.valueDescriptorMap[e];t[e.name]=e}),t}hasValue(e){e="has"+l(this.valueDescriptorNameMap[e].name);return this.receiver[e]}}class W{constructor(e,t){this.context=e,this.delegate=t,this.targetsByName=new v}start(){this.tokenListObserver||(this.tokenListObserver=new O(this.element,this.attributeName,this),this.tokenListObserver.start())}stop(){this.tokenListObserver&&(this.disconnectAllTargets(),this.tokenListObserver.stop(),delete this.tokenListObserver)}tokenMatched({element:e,content:t}){this.scope.containsElement(e)&&this.connectTarget(e,t)}tokenUnmatched({element:e,content:t}){this.disconnectTarget(e,t)}connectTarget(e,t){var r;this.targetsByName.has(t,e)||(this.targetsByName.add(t,e),null!=(r=this.tokenListObserver)&&r.pause(()=>this.delegate.targetConnected(e,t)))}disconnectTarget(e,t){var r;this.targetsByName.has(t,e)&&(this.targetsByName.delete(t,e),null!=(r=this.tokenListObserver))&&r.pause(()=>this.delegate.targetDisconnected(e,t))}disconnectAllTargets(){for(const e of this.targetsByName.keys)for(const t of this.targetsByName.getValuesForKey(e))this.disconnectTarget(t,e)}get attributeName(){return`data-${this.context.identifier}-target`}get element(){return this.context.element}get scope(){return this.context.scope}}function E(e,r){e=t(e);return Array.from(e.reduce((t,e)=>(function(e,t){e=e[t];return Array.isArray(e)?e:[]}(e,r).forEach(e=>t.add(e)),t),new Set))}function J(e,r){return t(e).reduce((e,t)=>(e.push(...function(e,t){const r=e[t];return r?Object.keys(r).map(e=>[e,r[e]]):[]}(t,r)),e),[])}function t(e){for(var t=[];e;)t.push(e),e=Object.getPrototypeOf(e);return t.reverse()}class H{constructor(e,t){this.started=!1,this.context=e,this.delegate=t,this.outletsByName=new v,this.outletElementsByName=new v,this.selectorObserverMap=new Map,this.attributeObserverMap=new Map}start(){this.started||(this.outletDefinitions.forEach(e=>{this.setupSelectorObserverForOutlet(e),this.setupAttributeObserverForOutlet(e)}),this.started=!0,this.dependentContexts.forEach(e=>e.refresh()))}refresh(){this.selectorObserverMap.forEach(e=>e.refresh()),this.attributeObserverMap.forEach(e=>e.refresh())}stop(){this.started&&(this.started=!1,this.disconnectAllOutlets(),this.stopSelectorObservers(),this.stopAttributeObservers())}stopSelectorObservers(){0e.stop()),this.selectorObserverMap.clear())}stopAttributeObservers(){0e.stop()),this.attributeObserverMap.clear())}selectorMatched(e,t,{outletName:r}){var s=this.getOutlet(e,r);s&&this.connectOutlet(s,e,r)}selectorUnmatched(e,t,{outletName:r}){var s=this.getOutletFromMap(e,r);s&&this.disconnectOutlet(s,e,r)}selectorMatchElement(e,{outletName:t}){var r=this.selector(t),s=this.hasOutlet(e,t),t=e.matches(`[${this.schema.controllerAttribute}~=${t}]`);return!!r&&s&&t&&e.matches(r)}elementMatchedAttribute(e,t){t=this.getOutletNameFromOutletAttributeName(t);t&&this.updateSelectorObserverForOutlet(t)}elementAttributeValueChanged(e,t){t=this.getOutletNameFromOutletAttributeName(t);t&&this.updateSelectorObserverForOutlet(t)}elementUnmatchedAttribute(e,t){t=this.getOutletNameFromOutletAttributeName(t);t&&this.updateSelectorObserverForOutlet(t)}connectOutlet(e,t,r){var s;this.outletElementsByName.has(r,t)||(this.outletsByName.add(r,e),this.outletElementsByName.add(r,t),null!=(s=this.selectorObserverMap.get(r))&&s.pause(()=>this.delegate.outletConnected(e,t,r)))}disconnectOutlet(e,t,r){var s;this.outletElementsByName.has(r,t)&&(this.outletsByName.delete(r,e),this.outletElementsByName.delete(r,t),null!=(s=this.selectorObserverMap.get(r)))&&s.pause(()=>this.delegate.outletDisconnected(e,t,r))}disconnectAllOutlets(){for(const e of this.outletElementsByName.keys)for(const t of this.outletElementsByName.getValuesForKey(e))for(const r of this.outletsByName.getValuesForKey(e))this.disconnectOutlet(r,t,e)}updateSelectorObserverForOutlet(e){var t=this.selectorObserverMap.get(e);t&&(t.selector=this.selector(e))}setupSelectorObserverForOutlet(e){var t=this.selector(e),t=new b(document.body,t,this,{outletName:e});this.selectorObserverMap.set(e,t),t.start()}setupAttributeObserverForOutlet(e){var t=this.attributeNameForOutletName(e),t=new m(this.scope.element,t,this);this.attributeObserverMap.set(e,t),t.start()}selector(e){return this.scope.outlets.getSelectorForOutletName(e)}attributeNameForOutletName(e){return this.scope.schema.outletAttributeForScope(this.identifier,e)}getOutletNameFromOutletAttributeName(t){return this.outletDefinitions.find(e=>this.attributeNameForOutletName(e)===t)}get outletDependencies(){const r=new v;return this.router.modules.forEach(t=>{E(t.definition.controllerConstructor,"outlets").forEach(e=>r.add(e,t.identifier))}),r}get outletDefinitions(){return this.outletDependencies.getKeysForValue(this.identifier)}get dependentControllerIdentifiers(){return this.outletDependencies.getValuesForKey(this.identifier)}get dependentContexts(){const t=this.dependentControllerIdentifiers;return this.router.contexts.filter(e=>t.includes(e.identifier))}hasOutlet(e,t){return!!this.getOutlet(e,t)||!!this.getOutletFromMap(e,t)}getOutlet(e,t){return this.application.getControllerForElementAndIdentifier(e,t)}getOutletFromMap(t,e){return this.outletsByName.getValuesForKey(e).find(e=>e.element===t)}get scope(){return this.context.scope}get schema(){return this.context.schema}get identifier(){return this.context.identifier}get application(){return this.context.application}get router(){return this.application.router}}class M{constructor(e,t){this.logDebugActivity=(e,t={})=>{var{identifier:r,controller:s,element:i}=this;t=Object.assign({identifier:r,controller:s,element:i},t),this.application.logDebugActivity(this.identifier,e,t)},this.module=e,this.scope=t,this.controller=new e.controllerConstructor(this),this.bindingObserver=new _(this,this.dispatcher),this.valueObserver=new q(this,this.controller),this.targetObserver=new W(this,this),this.outletObserver=new H(this,this);try{this.controller.initialize(),this.logDebugActivity("initialize")}catch(e){this.handleError(e,"initializing controller")}}connect(){this.bindingObserver.start(),this.valueObserver.start(),this.targetObserver.start(),this.outletObserver.start();try{this.controller.connect(),this.logDebugActivity("connect")}catch(e){this.handleError(e,"connecting controller")}}refresh(){this.outletObserver.refresh()}disconnect(){try{this.controller.disconnect(),this.logDebugActivity("disconnect")}catch(e){this.handleError(e,"disconnecting controller")}this.outletObserver.stop(),this.targetObserver.stop(),this.valueObserver.stop(),this.bindingObserver.stop()}get application(){return this.module.application}get identifier(){return this.module.identifier}get schema(){return this.application.schema}get dispatcher(){return this.application.dispatcher}get element(){return this.scope.element}get parentElement(){return this.element.parentElement}handleError(e,t,r={}){var{identifier:s,controller:i,element:n}=this;r=Object.assign({identifier:s,controller:i,element:n},r),this.application.handleError(e,"Error "+t,r)}targetConnected(e,t){this.invokeControllerMethod(t+"TargetConnected",e)}targetDisconnected(e,t){this.invokeControllerMethod(t+"TargetDisconnected",e)}outletConnected(e,t,r){this.invokeControllerMethod(s(r)+"OutletConnected",e,t)}outletDisconnected(e,t,r){this.invokeControllerMethod(s(r)+"OutletDisconnected",e,t)}invokeControllerMethod(e,...t){var r=this.controller;"function"==typeof r[e]&&r[e](...t)}}function Z(e){return r=E(n=e=e,"blessings"),r=r.reduce((e,t)=>{var r=t(n);for(const i in r){var s=e[i]||{};e[i]=Object.assign(s,r[i])}return e},{}),t=Q(e),e=function(s,i){return G(i).reduce((e,t)=>{var r=function(e,t,r){var e=Object.getOwnPropertyDescriptor(e,r),s=e&&"value"in e;if(!s)return s=Object.getOwnPropertyDescriptor(t,r).value,e&&(s.get=e.get||s.get,s.set=e.set||s.set),s}(s,i,t);return r&&Object.assign(e,{[t]:r}),e},{})}(e.prototype,r),Object.defineProperties(t.prototype,e),t;var t,n,r}const G="function"==typeof Object.getOwnPropertySymbols?e=>[...Object.getOwnPropertyNames(e),...Object.getOwnPropertySymbols(e)]:Object.getOwnPropertyNames,Q=(()=>{function e(e){function t(){return Reflect.construct(e,arguments,new.target)}return t.prototype=Object.create(e.prototype,{constructor:{value:t}}),Reflect.setPrototypeOf(t,e),t}try{return(t=e(function(){this.a.call(this)})).prototype.a=function(){},new t,e}catch(e){return e=>class extends e{}}var t})();class X{constructor(e,t){this.application=e,this.definition={identifier:t.identifier,controllerConstructor:Z(t.controllerConstructor)},this.contextsByScope=new WeakMap,this.connectedContexts=new Set}get identifier(){return this.definition.identifier}get controllerConstructor(){return this.definition.controllerConstructor}get contexts(){return Array.from(this.connectedContexts)}connectContextForScope(e){e=this.fetchContextForScope(e);this.connectedContexts.add(e),e.connect()}disconnectContextForScope(e){e=this.contextsByScope.get(e);e&&(this.connectedContexts.delete(e),e.disconnect())}fetchContextForScope(e){let t=this.contextsByScope.get(e);return t||(t=new M(this,e),this.contextsByScope.set(e,t)),t}}class Y{constructor(e){this.scope=e}has(e){return this.data.has(this.getDataKey(e))}get(e){return this.getAll(e)[0]}getAll(e){return(this.data.get(this.getDataKey(e))||"").match(/[^\s]+/g)||[]}getAttributeName(e){return this.data.getAttributeNameForKey(this.getDataKey(e))}getDataKey(e){return e+"-class"}get data(){return this.scope.data}}class ee{constructor(e){this.scope=e}get element(){return this.scope.element}get identifier(){return this.scope.identifier}get(e){e=this.getAttributeNameForKey(e);return this.element.getAttribute(e)}set(e,t){var r=this.getAttributeNameForKey(e);return this.element.setAttribute(r,t),this.get(e)}has(e){e=this.getAttributeNameForKey(e);return this.element.hasAttribute(e)}delete(e){return!!this.has(e)&&(e=this.getAttributeNameForKey(e),this.element.removeAttribute(e),!0)}getAttributeNameForKey(e){return`data-${this.identifier}-`+h(e)}}class te{constructor(e){this.warnedKeysByObject=new WeakMap,this.logger=e}warn(e,t,r){let s=this.warnedKeysByObject.get(e);s||(s=new Set,this.warnedKeysByObject.set(e,s)),s.has(t)||(s.add(t),this.logger.warn(r,e))}}function w(e,t){return`[${e}~="${t}"]`}class re{constructor(e){this.scope=e}get element(){return this.scope.element}get identifier(){return this.scope.identifier}get schema(){return this.scope.schema}has(e){return null!=this.find(e)}find(...e){return e.reduce((e,t)=>e||this.findTarget(t)||this.findLegacyTarget(t),void 0)}findAll(...e){return e.reduce((e,t)=>[...e,...this.findAllTargets(t),...this.findAllLegacyTargets(t)],[])}findTarget(e){e=this.getSelectorForTargetName(e);return this.scope.findElement(e)}findAllTargets(e){e=this.getSelectorForTargetName(e);return this.scope.findAllElements(e)}getSelectorForTargetName(e){return w(this.schema.targetAttributeForScope(this.identifier),e)}findLegacyTarget(e){var t=this.getLegacySelectorForTargetName(e);return this.deprecate(this.scope.findElement(t),e)}findAllLegacyTargets(t){var e=this.getLegacySelectorForTargetName(t);return this.scope.findAllElements(e).map(e=>this.deprecate(e,t))}getLegacySelectorForTargetName(e){e=this.identifier+"."+e;return w(this.schema.targetAttribute,e)}deprecate(e,t){var r,s,i;return e&&(r=this.identifier,s=this.schema.targetAttribute,i=this.schema.targetAttributeForScope(r),this.guide.warn(e,"target:"+t,`Please replace ${s}="${r}.${t}" with ${i}="${t}". `+`The ${s} attribute is deprecated and will be removed in a future version of Stimulus.`)),e}get guide(){return this.scope.guide}}class se{constructor(e,t){this.scope=e,this.controllerElement=t}get element(){return this.scope.element}get identifier(){return this.scope.identifier}get schema(){return this.scope.schema}has(e){return null!=this.find(e)}find(...e){return e.reduce((e,t)=>e||this.findOutlet(t),void 0)}findAll(...e){return e.reduce((e,t)=>[...e,...this.findAllOutlets(t)],[])}getSelectorForOutletName(e){e=this.schema.outletAttributeForScope(this.identifier,e);return this.controllerElement.getAttribute(e)}findOutlet(e){var t=this.getSelectorForOutletName(e);if(t)return this.findElement(t,e)}findAllOutlets(e){var t=this.getSelectorForOutletName(e);return t?this.findAllElements(t,e):[]}findElement(t,r){return this.scope.queryElements(t).filter(e=>this.matchesElement(e,t,r))[0]}findAllElements(t,r){return this.scope.queryElements(t).filter(e=>this.matchesElement(e,t,r))}matchesElement(e,t,r){var s=e.getAttribute(this.scope.schema.controllerAttribute)||"";return e.matches(t)&&s.split(" ").includes(r)}}class k{constructor(e,t,r,s){this.targets=new re(this),this.classes=new Y(this),this.data=new ee(this),this.containsElement=e=>e.closest(this.controllerSelector)===this.element,this.schema=e,this.element=t,this.identifier=r,this.guide=new te(s),this.outlets=new se(this.documentScope,t)}findElement(e){return this.element.matches(e)?this.element:this.queryElements(e).find(this.containsElement)}findAllElements(e){return[...this.element.matches(e)?[this.element]:[],...this.queryElements(e).filter(this.containsElement)]}queryElements(e){return Array.from(this.element.querySelectorAll(e))}get controllerSelector(){return w(this.schema.controllerAttribute,this.identifier)}get isDocumentScope(){return this.element===document.documentElement}get documentScope(){return this.isDocumentScope?this:new k(this.schema,document.documentElement,this.identifier,this.guide.logger)}}class ie{constructor(e,t,r){this.element=e,this.schema=t,this.delegate=r,this.valueListObserver=new A(this.element,this.controllerAttribute,this),this.scopesByIdentifierByElement=new WeakMap,this.scopeReferenceCounts=new WeakMap}start(){this.valueListObserver.start()}stop(){this.valueListObserver.stop()}get controllerAttribute(){return this.schema.controllerAttribute}parseValueForToken(e){var{element:e,content:t}=e;return this.parseValueForElementAndIdentifier(e,t)}parseValueForElementAndIdentifier(e,t){var r=this.fetchScopesByIdentifierForElement(e);let s=r.get(t);return s||(s=this.delegate.createScopeForElementAndIdentifier(e,t),r.set(t,s)),s}elementMatchedValue(e,t){var r=(this.scopeReferenceCounts.get(t)||0)+1;this.scopeReferenceCounts.set(t,r),1==r&&this.delegate.scopeConnected(t)}elementUnmatchedValue(e,t){var r=this.scopeReferenceCounts.get(t);r&&(this.scopeReferenceCounts.set(t,r-1),1==r)&&this.delegate.scopeDisconnected(t)}fetchScopesByIdentifierForElement(e){let t=this.scopesByIdentifierByElement.get(e);return t||(t=new Map,this.scopesByIdentifierByElement.set(e,t)),t}}class ne{constructor(e){this.application=e,this.scopeObserver=new ie(this.element,this.schema,this),this.scopesByIdentifier=new v,this.modulesByIdentifier=new Map}get element(){return this.application.element}get schema(){return this.application.schema}get logger(){return this.application.logger}get controllerAttribute(){return this.schema.controllerAttribute}get modules(){return Array.from(this.modulesByIdentifier.values())}get contexts(){return this.modules.reduce((e,t)=>e.concat(t.contexts),[])}start(){this.scopeObserver.start()}stop(){this.scopeObserver.stop()}loadDefinition(e){this.unloadIdentifier(e.identifier);var t=new X(this.application,e),t=(this.connectModule(t),e.controllerConstructor.afterLoad);t&&t.call(e.controllerConstructor,e.identifier,this.application)}unloadIdentifier(e){e=this.modulesByIdentifier.get(e);e&&this.disconnectModule(e)}getContextForElementAndIdentifier(t,e){e=this.modulesByIdentifier.get(e);if(e)return e.contexts.find(e=>e.element==t)}proposeToConnectScopeForElementAndIdentifier(e,t){var r=this.scopeObserver.parseValueForElementAndIdentifier(e,t);r?this.scopeObserver.elementMatchedValue(r.element,r):console.error(`Couldn't find or create scope for identifier: "${t}" and element:`,e)}handleError(e,t,r){this.application.handleError(e,t,r)}createScopeForElementAndIdentifier(e,t){return new k(this.schema,e,t,this.logger)}scopeConnected(e){this.scopesByIdentifier.add(e.identifier,e);var t=this.modulesByIdentifier.get(e.identifier);t&&t.connectContextForScope(e)}scopeDisconnected(e){this.scopesByIdentifier.delete(e.identifier,e);var t=this.modulesByIdentifier.get(e.identifier);t&&t.disconnectContextForScope(e)}connectModule(t){this.modulesByIdentifier.set(t.identifier,t),this.scopesByIdentifier.getValuesForKey(t.identifier).forEach(e=>t.connectContextForScope(e))}disconnectModule(t){this.modulesByIdentifier.delete(t.identifier),this.scopesByIdentifier.getValuesForKey(t.identifier).forEach(e=>t.disconnectContextForScope(e))}}const N={controllerAttribute:"data-controller",actionAttribute:"data-action",targetAttribute:"data-target",targetAttributeForScope:e=>`data-${e}-target`,outletAttributeForScope:(e,t)=>`data-${e}-${t}-outlet`,keyMappings:Object.assign(Object.assign({enter:"Enter",tab:"Tab",esc:"Escape",space:" ",up:"ArrowUp",down:"ArrowDown",left:"ArrowLeft",right:"ArrowRight",home:"Home",end:"End",page_up:"PageUp",page_down:"PageDown"},F("abcdefghijklmnopqrstuvwxyz".split("").map(e=>[e,e]))),F("0123456789".split("").map(e=>[e,e])))};function F(e){return e.reduce((e,[t,r])=>Object.assign(Object.assign({},e),{[t]:r}),{})}class oe{constructor(e=document.documentElement,t=N){this.logger=console,this.debug=!1,this.logDebugActivity=(e,t,r={})=>{this.debug&&this.logFormattedMessage(e,t,r)},this.element=e,this.schema=t,this.dispatcher=new I(this),this.router=new ne(this),this.actionDescriptorFilters=Object.assign({},j)}static start(e,t){e=new this(e,t);return e.start(),e}async start(){await new Promise(e=>{"loading"==document.readyState?document.addEventListener("DOMContentLoaded",()=>e()):e()}),this.logDebugActivity("application","starting"),this.dispatcher.start(),this.router.start(),this.logDebugActivity("application","start")}stop(){this.logDebugActivity("application","stopping"),this.dispatcher.stop(),this.router.stop(),this.logDebugActivity("application","stop")}register(e,t){this.load({identifier:e,controllerConstructor:t})}registerActionOption(e,t){this.actionDescriptorFilters[e]=t}load(e,...t){(Array.isArray(e)?e:[e,...t]).forEach(e=>{e.controllerConstructor.shouldLoad&&this.router.loadDefinition(e)})}unload(e,...t){(Array.isArray(e)?e:[e,...t]).forEach(e=>this.router.unloadIdentifier(e))}get controllers(){return this.router.contexts.map(e=>e.controller)}getControllerForElementAndIdentifier(e,t){e=this.router.getContextForElementAndIdentifier(e,t);return e?e.controller:null}handleError(e,t,r){this.logger.error(`%s + +%o + +%o`,t,e,r),null!=(r=window.onerror)&&r.call(window,t,"",0,0,e)}logFormattedMessage(e,t,r={}){r=Object.assign({application:this},r),this.logger.groupCollapsed(e+" #"+t),this.logger.log("details:",Object.assign({},r)),this.logger.groupEnd()}}function B(e,t,r){return e.application.getControllerForElementAndIdentifier(t,r)}function C(e,t,r){var s=B(e,t,r);return s||(e.application.router.proposeToConnectScopeForElementAndIdentifier(t,r),B(e,t,r))||void 0}function T([s,n],o){{let{token:e,typeDefinition:i}=o={controller:o,token:s,typeDefinition:n},t=h(e)+"-value",r=function(e){var{controller:e,token:t,typeDefinition:r}=e,s={controller:e,token:t,typeObject:r},s=function(e){var{controller:t,token:r,typeObject:s}=e,i=c(s.type),n=c(s.default),o=i&&n,a=i&&!n,i=!i&&n,n=S(s.type),e=x(e.typeObject.default);if(a)return n;if(i)return e;if(n===e)return o?n:void 0;throw new Error(`The specified default value for the Stimulus Value "${t?t+"."+r:r}" must match the defined type "${n}". The provided default value of "${s.default}" is of type "${e}".`)}(s),i=x(r),n=S(r),s=s||i||n;if(s)return s;i=e?e+"."+r:t;throw new Error(`Unknown value type "${i}" for "${t}" value`)}(o);return{type:r,key:t,name:a(t),get defaultValue(){var e=i;if(t=S(e))return D[t];var t=u(e,"default"),r=u(e,"type"),s=e;if(t)return s.default;if(r){t=s.type,r=S(t);if(r)return D[r]}return e},get hasCustomDefaultValue(){return void 0!==x(i)},reader:ae[r],writer:L[r]||L.default}}}function S(e){switch(e){case Array:return"array";case Boolean:return"boolean";case Number:return"number";case Object:return"object";case String:return"string"}}function x(e){switch(typeof e){case"boolean":return"boolean";case"number":return"number";case"string":return"string"}return Array.isArray(e)?"array":"[object Object]"===Object.prototype.toString.call(e)?"object":void 0}const D={get array(){return[]},boolean:!1,number:0,get object(){return{}},string:""},ae={array(e){var t=JSON.parse(e);if(Array.isArray(t))return t;throw new TypeError(`expected value of type "array" but instead got value "${e}" of type "${x(t)}"`)},boolean(e){return!("0"==e||"false"==String(e).toLowerCase())},number(e){return Number(e.replace(/_/g,""))},object(e){var t=JSON.parse(e);if(null===t||"object"!=typeof t||Array.isArray(t))throw new TypeError(`expected value of type "object" but instead got value "${e}" of type "${x(t)}"`);return t},string(e){return e}},L={default:function(e){return""+e},array:le,object:le};function le(e){return JSON.stringify(e)}class V{constructor(e){this.context=e}static get shouldLoad(){return!0}static afterLoad(e,t){}get application(){return this.context.application}get scope(){return this.context.scope}get element(){return this.scope.element}get identifier(){return this.scope.identifier}get targets(){return this.scope.targets}get outlets(){return this.scope.outlets}get classes(){return this.scope.classes}get data(){return this.scope.data}initialize(){}connect(){}disconnect(){}dispatch(e,{target:t=this.element,detail:r={},prefix:s=this.identifier,bubbles:i=!0,cancelable:n=!0}={}){s=new CustomEvent(s?s+":"+e:e,{detail:r,bubbles:i,cancelable:n});return t.dispatchEvent(s),s}}V.blessings=[function(e){return E(e,"classes").reduce((e,t)=>{return Object.assign(e,{[(r=t)+"Class"]:{get(){var e=this.classes;if(e.has(r))return e.get(r);throw e=e.getAttributeName(r),new Error(`Missing attribute "${e}"`)}},[r+"Classes"]:{get(){return this.classes.getAll(r)}},[`has${l(r)}Class`]:{get(){return this.classes.has(r)}}});var r},{})},function(e){return E(e,"targets").reduce((e,t)=>{return Object.assign(e,{[(r=t)+"Target"]:{get(){var e=this.targets.find(r);if(e)return e;throw new Error(`Missing target element "${r}" for "${this.identifier}" controller`)}},[r+"Targets"]:{get(){return this.targets.findAll(r)}},[`has${l(r)}Target`]:{get(){return this.targets.has(r)}}});var r},{})},function(e){const t=J(e,"values");return t.reduce((e,t)=>Object.assign(e,function(e,t){let r=T(e,t),{key:s,name:i,reader:n,writer:o}=r;return{[i]:{get(){var e=this.data.get(s);return null!==e?n(e):r.defaultValue},set(e){void 0===e?this.data.delete(s):this.data.set(s,o(e))}},["has"+l(i)]:{get(){return this.data.has(s)||r.hasCustomDefaultValue}}}}(t)),{valueDescriptorMap:{get(){return t.reduce((e,t)=>{var t=T(t,this.identifier),r=this.data.getAttributeNameForKey(t.key);return Object.assign(e,{[r]:t})},{})}}})},function(e){return E(e,"outlets").reduce((e,t)=>{return Object.assign(e,{[(e=s(r=t))+"Outlet"]:{get(){var e=this.outlets.find(r),t=this.outlets.getSelectorForOutletName(r);if(e){e=C(this,e,r);if(e)return e;throw new Error(`The provided outlet element is missing an outlet controller "${r}" instance for host controller "${this.identifier}"`)}throw new Error(`Missing outlet element "${r}" for host controller "${this.identifier}". Stimulus couldn't find a matching outlet element using selector "${t}".`)}},[e+"Outlets"]:{get(){var e=this.outlets.findAll(r);return 0{var t=C(this,e,r);if(t)return t;console.warn(`The provided outlet element is missing an outlet controller "${r}" instance for host controller "${this.identifier}"`,e)}).filter(e=>e):[]}},[e+"OutletElement"]:{get(){var e=this.outlets.find(r),t=this.outlets.getSelectorForOutletName(r);if(e)return e;throw new Error(`Missing outlet element "${r}" for host controller "${this.identifier}". Stimulus couldn't find a matching outlet element using selector "${t}".`)}},[e+"OutletElements"]:{get(){return this.outlets.findAll(r)}},[`has${l(e)}Outlet`]:{get(){return this.outlets.has(r)}}});var r},{})}],V.targets=[],V.outlets=[],V.values={},e.Application=oe,e.AttributeObserver=m,e.Context=M,e.Controller=V,e.ElementObserver=d,e.IndexedMultimap=z,e.Multimap=v,e.SelectorObserver=b,e.StringMapObserver=y,e.TokenListObserver=O,e.ValueListObserver=A,e.add=r,e.defaultSchema=N,e.del=g,e.fetch=p,e.prune=f,Object.defineProperty(e,"__esModule",{value:!0})}); diff --git a/tests/fuzz/20_L1_Open_Requirements_Tool_202511/assets/table_vew.css b/tests/fuzz/20_L1_Open_Requirements_Tool_202511/assets/table_vew.css new file mode 100644 index 0000000..263332e --- /dev/null +++ b/tests/fuzz/20_L1_Open_Requirements_Tool_202511/assets/table_vew.css @@ -0,0 +1,79 @@ +/* ultimate table view */ + +.content-view-table { + border-collapse: collapse; + width: 100%; + background-color: var(--color-bg-contrast); +} + +/* .content-view-table td, +.content-view-table th, */ +.content-view-th, +.content-view-td { + padding: .5rem 1rem; + border: 1px solid #666; + vertical-align: top; +} + +.content-view-th { + text-align: left; + text-transform: uppercase; + background-color: var(--color-border); + font-size: .85em; + font-weight: bold; +} + +.content-view-td-type, +.content-view-td-meta { + background-color: var(--color-bg-main); + font-size: .85em; +} + +.content-view-td-type { + background-color: var(--color-bg-main); + text-align: left; + font-weight: normal; + text-transform: uppercase; +} + +.content-view-table div.document { + /* TODO */ + display: block; + max-width: 900px; +} + +.content-view-table p:first-child { + margin-top: 0; +} + +.content-view-table p:last-child { + margin-bottom: 0; +} + +.content-view-td:empty { + width: 1%; +} + +.content-view-td-content:not(:empty) { + min-width: 333px; + width: 30%; +} + +.content-view-td-title:not(:empty) { + font-weight: bold; + min-width: 264px; +} + +.content-view-td-related:not(:empty) { + min-width: 264px; +} + +.content-view-td-related, +.content-view-td-related > ul.requirement__link { + font-size: .85rem; + margin-top: 0; +} + +.content-view-td-related > ul.requirement__link:last-child { + margin-bottom: 0; +} diff --git a/tests/fuzz/20_L1_Open_Requirements_Tool_202511/assets/toc_highlighting.js b/tests/fuzz/20_L1_Open_Requirements_Tool_202511/assets/toc_highlighting.js new file mode 100644 index 0000000..fb96016 --- /dev/null +++ b/tests/fuzz/20_L1_Open_Requirements_Tool_202511/assets/toc_highlighting.js @@ -0,0 +1,291 @@ +const TOC_HIGHLIGHT_DEBUG = false; + +const TOC_FRAME_SELECTOR = 'turbo-frame#frame-toc'; // updating +const TOC_LIST_SELECTOR = 'ul#toc'; +const TOC_ELEMENT_SELECTOR = 'a'; +const CONTENT_FRAME_SELECTOR = 'turbo-frame#frame_document_content'; // replacing => parentNode is needed +const CONTENT_ELEMENT_SELECTOR = 'sdoc-anchor'; + +let tocHighlightingState = { + data: {}, + links: null, + anchors: null, + contentFrameTop: undefined, + closerForFolder: {}, + folderSet: new Set(), +}; + +function resetState() { + tocHighlightingState.data = {}; + tocHighlightingState.links = null; + tocHighlightingState.anchors = null; + tocHighlightingState.closerForFolder = {}; + tocHighlightingState.folderSet = new Set(); +} + +window.addEventListener("hashchange", handleHashChange); +window.addEventListener("load",function(){ + + // * Frames are stable and we define them once. + const tocFrame = document.querySelector(TOC_FRAME_SELECTOR); + const tocList = tocFrame ? tocFrame.querySelector(TOC_LIST_SELECTOR) : null; + const contentFrame = document.querySelector(CONTENT_FRAME_SELECTOR)?.parentNode; + + if(!tocFrame || !tocList || !contentFrame) { return } + + // ! depends on TOC markup + tocHighlightingState.contentFrameTop = contentFrame.offsetParent + ? contentFrame.offsetTop + : contentFrame.parentNode.offsetTop; + + const anchorObserver = new IntersectionObserver( + handleIntersect, + { + root: null, + rootMargin: "0px", + }); + + // * Then we will refresh when the TOC tree is updated& + // * The content in the tocFrame frame will mutate: + const mutatingFrame = tocFrame; + new MutationObserver(function (mutationsList, observer) { + // * Use requestAnimationFrame to put highlightTOC + // * at the end of the event queue and to ensure + // * the code runs after all DOM changes have been applied. + requestAnimationFrame(() => { + highlightTOC(tocFrame, contentFrame, anchorObserver); + }) + }).observe( + mutatingFrame, + { + // * We're looking at an updatable frame (mutates its contents, + // * and we don't care what mutations were made inside): + childList: true, + // attributes: true, + // characterData: true, + // subtree: true + } + ); + + // * Call for the first time only if the TOC actually contains items. + if (tocList && tocList.querySelector(TOC_ELEMENT_SELECTOR)) { + highlightTOC(tocFrame, contentFrame, anchorObserver); + } + +},false); + +function highlightTOC(tocFrame, contentFrame, anchorObserver) { + + resetState(); + processLinkList(tocFrame); + processAnchorList(contentFrame, anchorObserver); + handleHashChange(); + + TOC_HIGHLIGHT_DEBUG && console.log(tocHighlightingState); +} + +function handleHashChange() { + const hash = window.location.hash; + const match = hash.match(/#(.*)/); + const fragment = match ? match[1] : null; + + // Guard: no links collected yet (e.g., empty TOC or init race) + if (!tocHighlightingState.links || typeof tocHighlightingState.links.forEach !== 'function') { + return; + } + + tocHighlightingState.links.forEach(link => { + targetItem(link, false) + }); + // * When updating the hash + // * and there's a fragment, + fragment + // * and the corresponding link-anchor pair is registered, + && tocHighlightingState.data[fragment] + // * highlight the corresponding link. + && targetItem(tocHighlightingState.data[fragment].link) +} + +function processLinkList(tocFrame) { + // * Collects all links in the TOC + tocHighlightingState.links = tocFrame.querySelectorAll(TOC_ELEMENT_SELECTOR); + if (!tocHighlightingState.links || tocHighlightingState.links.length === 0) { + return; + } + tocHighlightingState.links.length + && tocHighlightingState.links.forEach(link => { + const id = link.getAttribute('anchor'); + tocHighlightingState.data[id] = { + 'link': link, + ...tocHighlightingState.data[id] + } + + // ! depends on TOC markup + // is link in collapsible node and precedes the UL + // ! expected UL or null + const ul = link.nextSibling; + + if (ul && ul.nodeName === 'UL') { + // register folder + tocHighlightingState.folderSet.add(id); + + // register closer + const lastLink = findDeepestLastChild(ul); + const lastAnchor = lastLink.getAttribute('anchor'); + + + if (!tocHighlightingState.closerForFolder[lastAnchor]) { + tocHighlightingState.closerForFolder[lastAnchor] = []; + } + tocHighlightingState.closerForFolder[lastAnchor].push(id); + } + }); +} + +function processAnchorList(contentFrame, anchorObserver) { + anchorObserver.disconnect(); // FIXME don`t work: have to hack at #rootBounds_null + + // * Collects all anchors in the document + tocHighlightingState.anchors = null; + tocHighlightingState.anchors = contentFrame.querySelectorAll(CONTENT_ELEMENT_SELECTOR); + tocHighlightingState.anchors.length + && tocHighlightingState.anchors.forEach(anchor => { + const id = anchor.id; + tocHighlightingState.data[id] = { + 'anchor': anchor, + ...tocHighlightingState.data[id] + }; + // * Adds an observer for the position of the anchor + anchorObserver.observe(anchor); + }); +} + +function handleIntersect(entries, observer) { + + entries.forEach((entry) => { + + // #rootBounds_null + // rootBounds: null + // after frame reload and before init + if(!entry.rootBounds) { + return + } + + const anchor = entry.target.id; + // * For anchors that go into the viewport, + // * finds the corresponding links + const link = tocHighlightingState.data[anchor].link; + + // * if there is no menu item for the section in the TOC + if(!link) { + return + } + + if (entry.isIntersecting) { //! entry.intersectionRatio > 0 -- it happens to be equal to zero at the intersection! + + TOC_HIGHLIGHT_DEBUG && console.group('🔶', entry.isIntersecting, entry.intersectionRatio, anchor, entry.intersectionRect.height); + // * and highlights them in the TOC, + fireItem(link) + + // * semi-highlights folder in the TOC, + if (tocHighlightingState.folderSet.has(anchor)) { + TOC_HIGHLIGHT_DEBUG && console.log('🔴', anchor, '(visible folder)', ); + fireFolder(link) + } + + // * semi-highlights closer`s parent folder in the TOC, + if (tocHighlightingState.closerForFolder[anchor]) { + tocHighlightingState.closerForFolder[anchor].forEach(id => { + TOC_HIGHLIGHT_DEBUG && console.log(`🔴`, id, `(from ${anchor})`); + fireFolder(tocHighlightingState.data[id].link) + }) + } + TOC_HIGHLIGHT_DEBUG && console.groupEnd(); + + } else { + + TOC_HIGHLIGHT_DEBUG && console.group('🔹', entry.isIntersecting, entry.intersectionRatio, anchor); + // * or cancels highlighting for the rest of the links. + fireItem(link, false); + + if( + // * If the node goes down ⬇️ off the screen + entry.boundingClientRect.bottom >= entry.rootBounds.bottom + // * and it's a folder + && tocHighlightingState.folderSet.has(anchor) + ) { + // ** remove highlights from folder in the TOC + TOC_HIGHLIGHT_DEBUG && console.log(`⚫ ⬇️`, anchor); + fireFolder(link, false) + } + + if( + // * If the node goes up ⬆️ off the screen + entry.boundingClientRect.y < tocHighlightingState.contentFrameTop + // * and this is the last child of the section + && tocHighlightingState.closerForFolder[anchor] + ) { + // * When the LAST CHILD of the section disappears + // * over the upper boundary ( < tocHighlightingState.contentFrameTop), + // * strictly speaking, this occurs when the lower bound disappears: + // * entry.boundingClientRect.bottom. + // * But we will use the upper bound, entry.boundingClientRect.y + // * which will be less than or equal to the lower bound. + + // ** remove highlights from closer`s parent folder in the TOC + tocHighlightingState.closerForFolder[anchor].forEach(id => { + TOC_HIGHLIGHT_DEBUG && console.log(`⚫ ⬆️`, id,); + fireFolder(tocHighlightingState.data[id].link, false) + }); + } + + TOC_HIGHLIGHT_DEBUG && console.groupEnd(); + } + }); + +} + +function findDeepestLastChild(element) { + // ! depends on TOC markup + // ul > li > div + a + ul > ... + // ul > li > a + // > li > a <---------------*** + if (element.nodeName === 'A') { + return element; + } + const children = element.children; + if (children && children.length > 0) { + return findDeepestLastChild([...children].at(-1)) + } else { + return element; + } +} + +function targetItem(element, on = true) { + if (!element) { return } // Guard against race conditions: + // hashchange or intersection events may fire + // before the TOC is fully built, resulting in undefined link elements. + if(on) { + element.setAttribute('targeted', ''); + } else { + element.removeAttribute('targeted'); + } +} + +function fireItem(element, on = true) { + if (!element) { return } // Guard against race conditions + if(on) { + element.setAttribute('intersected', ''); + } else { + element.removeAttribute('intersected'); + } +} + +function fireFolder(element, on = true) { + if (!element) { return } // Guard against race conditions + if(on) { + element.setAttribute('parented', ''); + } else { + element.removeAttribute('parented'); + } +} diff --git a/tests/fuzz/20_L1_Open_Requirements_Tool_202511/assets/traceability_matrix_screen.css b/tests/fuzz/20_L1_Open_Requirements_Tool_202511/assets/traceability_matrix_screen.css new file mode 100644 index 0000000..6c9c279 --- /dev/null +++ b/tests/fuzz/20_L1_Open_Requirements_Tool_202511/assets/traceability_matrix_screen.css @@ -0,0 +1,148 @@ +.traceability_matrix { + --traceability_matrix-thead-height: calc(4 * var(--base-rhythm)); + + + display: table; + border-collapse: collapse; +} + +.traceability_matrix__anchor { + /* for Fixed Headers + Section Anchors */ + scroll-snap-margin-top: var(--base-gap); + scroll-margin-top: var(--base-gap); +} + +.traceability_matrix .traceability_matrix__thead { + position: sticky; + top: calc(-1 * var(--traceability_matrix-thead-height)); + z-index: 2; + background-color: var(--color-bg-main); +} + +.traceability_matrix .traceability_matrix__thead::before { + content: ''; + position: absolute; + top: calc(-1 * var(--base-gap)); + left: calc(-1 * var(--base-gap)); + right: calc(-1 * var(--base-gap)); + bottom: 0; + z-index: 0; + background-color: var(--color-bg-main); +} + +.traceability_matrix__thead th { + height: var(--traceability_matrix-thead-height); + text-align: left; + vertical-align: middle; + line-height: 1; + position: relative; + background-color: var(--color-bg-secondary-invert); + padding-left: var(--base-rhythm); + border: 1px solid var(--color-border); +} + +.traceability_matrix td { + border: 1px solid var(--color-border); + background-color: var(--color-bg-contrast); + vertical-align: top; +} + +td.traceability_matrix__document { + position: sticky; + padding: 0; + top: 0; + z-index: 1; +} + +.traceability_matrix__placeholder { + padding: var(--base-rhythm); + text-align: center; + font-size: var(--font-size-sm); +} + +td.traceability_matrix__null { + height: calc(4 * var(--base-rhythm)); + background-color: var(--color-bg-main); + border: none; +} + +.traceability_matrix__document_line { + display: flex; + align-items: center; + justify-content: flex-start; + column-gap: calc(2 * var(--base-rhythm)); + font-size: var(--font-size-l); + font-weight: 600; + line-height: 1.2; + width: 100%; + + padding: var(--base-rhythm); + padding-top: calc(1.5 * var(--base-rhythm)); + + border-bottom: 1px solid var(--color-border); +} + +.traceability_matrix__document_stat { + margin-left: auto; +} + +.traceability_matrix__requirement { + display: block; + position: relative; + font-size: var(--font-size-sm); + line-height: 1.2; + padding: var(--base-rhythm); +} + +.traceability_matrix__file { + position: relative; + font-size: var(--font-size-sm); + line-height: 1.2; + margin-top: var(--base-rhythm); + margin-bottom: var(--base-rhythm); + padding-right: var(--base-rhythm); +} + +.traceability_matrix__file a { + /* overflow-wrap: break-word; */ + overflow-wrap: anywhere; + color: var(--color-link); + text-decoration: none; +} + +.traceability_matrix__file a:hover { + color: var(--color-hover); +} + +[with_relation] { + padding-left: calc(4 * var(--base-rhythm)); +} + +[with_relation]::before { + content: ''; + font-size: var(--font-size); + line-height: 1; + position: absolute; + left: var(--base-rhythm); +} + +.traceability_matrix__requirement[with_relation]::before { + top: var(--base-rhythm); +} + +.traceability_matrix__file[with_relation]::before { + top: 0 +} + +/* ← → */ +[with_relation="parent"]::before { + content: '→'; +} + +[with_relation="child"]::before { + content: '←'; +} + +[with_relation="file"]::before { + content: '→'; +} diff --git a/tests/fuzz/20_L1_Open_Requirements_Tool_202511/assets/traceability_matrix_screen.js b/tests/fuzz/20_L1_Open_Requirements_Tool_202511/assets/traceability_matrix_screen.js new file mode 100644 index 0000000..9be993e --- /dev/null +++ b/tests/fuzz/20_L1_Open_Requirements_Tool_202511/assets/traceability_matrix_screen.js @@ -0,0 +1,48 @@ +// @relation(SDOC-SRS-112, scope=file) + +const SELECTOR = '[js-requirements-coverage]'; + +const __log = (topic, ...payload) => { + console.log(`%c ${topic} `, 'background:yellow;color:black', + ...payload + ); +} + +function markSame(newUid, state, arr) { + // __log('state', state.currentUid); + state.currentUid && arr[state.currentUid].forEach(element => element.classList.remove('highlighted')); + state.currentUid = newUid; + arr[newUid].forEach(element => element.classList.add('highlighted')); + +} + +window.addEventListener("load", function () { + // __log( + // 'requirements-coverage', + // 'test' + // ) + + const state = { + currentUid: null + } + + // https://stackoverflow.com/questions/32249997/how-to-check-if-data-attribute-exist-with-plain-javascript/32250073 + const reqs = [...document.querySelectorAll(SELECTOR)] + .reduce((acc, req) => { + if (req.hasAttribute('data-uid')) { + req.addEventListener('click', () => markSame(uid, state, reqs)); + const uid = req.dataset.uid; + acc[uid] = acc[uid] ? [...acc[uid], req] : [req]; + } else { + req.classList.add('nouid') + acc.nouid.push(req); + } + return acc + }, { nouid: [] }); + + // __log( + // '--', + // reqs + // ) + +}); diff --git a/tests/fuzz/20_L1_Open_Requirements_Tool_202511/assets/tree(not_in_use).css b/tests/fuzz/20_L1_Open_Requirements_Tool_202511/assets/tree(not_in_use).css new file mode 100644 index 0000000..acd2a13 --- /dev/null +++ b/tests/fuzz/20_L1_Open_Requirements_Tool_202511/assets/tree(not_in_use).css @@ -0,0 +1,146 @@ +.std-tree { + --tree-font-size: 16px; + --tree-line-height: calc(var(--tree-font-size)*1.25); + --tree-level-padding: calc(var(--tree-font-size)*1.5); + --tree-cell-padding: calc(var(--tree-font-size)*0.5); + + --tree-level-line-width: 1px; + + --tree-color-line: #808080; + --tree-color-fg: var(--color-fg-main, #444444); + --tree-color-bg: var(--color-bg-main, #F2F5F9); + --tree-color-row-hover-bg: var(--color-bg-contrast, #FFFFFF); + --tree-btn-bg-color: var(--color-bg-contrast, #FFFFFF); + + --tree-half-sell-height: calc( var(--tree-line-height)/2 + var(--tree-cell-padding) ); + + --tree-ico-size: var(--tree-font-size, 1rem); + + --tree-max-width: 800px; +} + + +/* tree */ +.std-tree { + font-size: var(--tree-font-size, 1rem); + line-height: var(--tree-line-height, 1.25); + color: var(--tree-color-fg, #444444); + + position: relative; + width: -moz-fit-content; + width: fit-content; + /* margin: 0 auto; */ +} + +.std-tree ul { + list-style: none; + width: 100%; + padding: 0; + margin: 0; +} + +.std-tree li { + position: relative; + padding-left: var(--tree-level-padding); +} + +.std-tree ul ul > li::before { + content: ''; + position: absolute; + left: calc(var(--tree-ico-size)*0.5 - var(--tree-level-padding)); + top: calc(var(--tree-ico-size)*(-0.5)); + height: 100%; + border-left: 1px solid var(--tree-color-line); +} + +.std-tree ul > li:last-child::before { + height: calc(var(--tree-ico-size) + var(--tree-cell-padding)); +} + +.std-tree_ico { + display: inline-flex; + align-items: center; + + position: absolute; + left: 0; + top: calc(var(--tree-cell-padding)); +} + + ul ul .std-tree_ico::before { + content: ''; + position: absolute; + left: calc(var(--tree-ico-size)*0.5 - var(--tree-level-padding)); + right: 100%; + border-bottom: 1px solid var(--tree-color-line); + } + + .std-tree_ico svg { + display: inline-block; + width: var(--tree-ico-size); + height: var(--tree-ico-size); + + fill: var(--tree-color-line); + } + + .std-tree_row { + padding-top: var(--tree-cell-padding); + padding-bottom: var(--tree-cell-padding); + padding-left: var(--tree-cell-padding); + + display: flex; + justify-content: space-between; + } + + .std-tree_folder { + font-weight: bold; + display: flex; + } + + .std-tree_file { + order: 1; + flex-grow: 1; + } + + .std-tree_file__title { + font-size: 1.25em; + color: #000; + transition: color .2s ease; + } + + .std-tree_file-link:hover .std-tree_file__title { + color: var(--color-fg-accent); + } + + .std-tree_file__name { + font-size: 0.85rem; + line-height: 1.2; + color: var(--color-fg-secondary); + margin-top: 0.25rem; + } + + .std-tree_desc { + position: relative; + top: -2px; + color: #000; + } + + .std-tree_name { + font-size: 0.85rem; + line-height: 1.2; + color: #666; + } + + .std-tree_aside { + width: -moz-fit-content; + width: fit-content; + padding-left: 20px; + display: flex; + justify-content: space-between; + align-items: flex-start; + order: 3; + } + + .std-tree_file-link { + position: relative; + top: -2px; + } diff --git a/tests/fuzz/20_L1_Open_Requirements_Tool_202511/assets/turbo.min.js b/tests/fuzz/20_L1_Open_Requirements_Tool_202511/assets/turbo.min.js new file mode 100644 index 0000000..ee8fdf3 --- /dev/null +++ b/tests/fuzz/20_L1_Open_Requirements_Tool_202511/assets/turbo.min.js @@ -0,0 +1,24 @@ +!function(){if(void 0!==window.Reflect&&void 0!==window.customElements&&!window.customElements.polyfillWrapFlushCallback){const e=HTMLElement,t=function(){return Reflect.construct(e,[],this.constructor)};window.HTMLElement=t,HTMLElement.prototype=e.prototype,HTMLElement.prototype.constructor=HTMLElement,Object.setPrototypeOf(HTMLElement,e)}}(),function(e){function r(e,t,s){throw new e("Failed to execute 'requestSubmit' on 'HTMLFormElement': "+t+".",s)}"function"!=typeof e.requestSubmit&&(e.requestSubmit=function(e){var t,s;e?(s=this,(t=e)instanceof HTMLElement||r(TypeError,"parameter 1 is not of type 'HTMLElement'"),"submit"!=t.type&&r(TypeError,"The specified element is not a submit button"),t.form!=s&&r(DOMException,"The specified element is not owned by this form element","NotFoundError"),e.click()):((e=document.createElement("input")).type="submit",e.hidden=!0,this.appendChild(e),e.click(),this.removeChild(e))})}(HTMLFormElement.prototype);const submittersByForm=new WeakMap;function findSubmitterFromClickTarget(e){e=e instanceof Element?e:e instanceof Node?e.parentElement:null,e=e?e.closest("input, button"):null;return"submit"==(null==e?void 0:e.type)?e:null}function clickCaptured(e){e=findSubmitterFromClickTarget(e.target);e&&e.form&&submittersByForm.set(e.form,e)}var FrameLoadingStyle,FetchMethod,FormSubmissionState,FormEnctype,TimingMetric,VisitState,SystemStatusCode,PageStage;!function(){if(!("submitter"in Event.prototype)){let e;if("SubmitEvent"in window&&/Apple Computer/.test(navigator.vendor))e=window.SubmitEvent.prototype;else{if("SubmitEvent"in window)return;e=window.Event.prototype}addEventListener("click",clickCaptured,!0),Object.defineProperty(e,"submitter",{get(){if("submit"==this.type&&this.target instanceof HTMLFormElement)return submittersByForm.get(this.target)}})}}(),function(e){e.eager="eager",e.lazy="lazy"}(FrameLoadingStyle=FrameLoadingStyle||{});class FrameElement extends HTMLElement{constructor(){super(),this.loaded=Promise.resolve(),this.delegate=new FrameElement.delegateConstructor(this)}static get observedAttributes(){return["disabled","loading","src"]}connectedCallback(){this.delegate.connect()}disconnectedCallback(){this.delegate.disconnect()}reload(){var e=this["src"];this.src=null,this.src=e}attributeChangedCallback(e){"loading"==e?this.delegate.loadingStyleChanged():"src"==e?this.delegate.sourceURLChanged():this.delegate.disabledChanged()}get src(){return this.getAttribute("src")}set src(e){e?this.setAttribute("src",e):this.removeAttribute("src")}get loading(){return frameLoadingStyleFromString(this.getAttribute("loading")||"")}set loading(e){e?this.setAttribute("loading",e):this.removeAttribute("loading")}get disabled(){return this.hasAttribute("disabled")}set disabled(e){e?this.setAttribute("disabled",""):this.removeAttribute("disabled")}get autoscroll(){return this.hasAttribute("autoscroll")}set autoscroll(e){e?this.setAttribute("autoscroll",""):this.removeAttribute("autoscroll")}get complete(){return!this.delegate.isLoading}get isActive(){return this.ownerDocument===document&&!this.isPreview}get isPreview(){var e;return null==(e=null==(e=this.ownerDocument)?void 0:e.documentElement)?void 0:e.hasAttribute("data-turbo-preview")}}function frameLoadingStyleFromString(e){return"lazy"!==e.toLowerCase()?FrameLoadingStyle.eager:FrameLoadingStyle.lazy}function expandURL(e){return new URL(e.toString(),document.baseURI)}function getAnchor(e){return e.hash?e.hash.slice(1):(e=e.href.match(/#(.*)$/))?e[1]:void 0}function getAction(e,t){return expandURL((null==t?void 0:t.getAttribute("formaction"))||e.getAttribute("action")||e.action)}function getExtension(e){return(getLastPathComponent(e).match(/\.[^.]*$/)||[])[0]||""}function isHTML(e){return!!getExtension(e).match(/^(?:|\.(?:htm|html|xhtml))$/)}function isPrefixedBy(e,t){t=getPrefix(t);return e.href===expandURL(t).href||e.href.startsWith(t)}function locationIsVisitable(e,t){return isPrefixedBy(e,t)&&isHTML(e)}function getRequestURL(e){var t=getAnchor(e);return null!=t?e.href.slice(0,-(t.length+1)):e.href}function toCacheKey(e){return getRequestURL(e)}function urlsAreEqual(e,t){return expandURL(e).href==expandURL(t).href}function getPathComponents(e){return e.pathname.split("/").slice(1)}function getLastPathComponent(e){return getPathComponents(e).slice(-1)[0]}function getPrefix(e){return addTrailingSlash(e.origin+e.pathname)}function addTrailingSlash(e){return e.endsWith("/")?e:e+"/"}class FetchResponse{constructor(e){this.response=e}get succeeded(){return this.response.ok}get failed(){return!this.succeeded}get clientError(){return 400<=this.statusCode&&this.statusCode<=499}get serverError(){return 500<=this.statusCode&&this.statusCode<=599}get redirected(){return this.response.redirected}get location(){return expandURL(this.response.url)}get isHTML(){return this.contentType&&this.contentType.match(/^(?:text\/([^\s;,]+\b)?html|application\/xhtml\+xml)\b/)}get statusCode(){return this.response.status}get contentType(){return this.header("Content-Type")}get responseText(){return this.response.clone().text()}get responseHTML(){return this.isHTML?this.response.clone().text():Promise.resolve(void 0)}header(e){return this.response.headers.get(e)}}function dispatch(e,{target:t,cancelable:s,detail:r}={}){e=new CustomEvent(e,{cancelable:s,bubbles:!0,detail:r});return(t&&t.isConnected?t:document.documentElement).dispatchEvent(e),e}function nextAnimationFrame(){return new Promise(e=>requestAnimationFrame(()=>e()))}function nextEventLoopTick(){return new Promise(e=>setTimeout(()=>e(),0))}function nextMicrotask(){return Promise.resolve()}function parseHTMLDocument(e=""){return(new DOMParser).parseFromString(e,"text/html")}function unindent(e,...t){e=interpolate(e,t).replace(/^\n/,"").split("\n"),t=e[0].match(/^\s+/);const s=t?t[0].length:0;return e.map(e=>e.slice(s)).join("\n")}function interpolate(e,r){return e.reduce((e,t,s)=>{return e+t+(null==r[s]?"":r[s])},"")}function uuid(){return Array.apply(null,{length:36}).map((e,t)=>8==t||13==t||18==t||23==t?"-":14==t?"4":(19==t?Math.floor(4*Math.random())+8:Math.floor(15*Math.random())).toString(16)).join("")}function getAttribute(t,...e){for(const s of e.map(e=>null==e?void 0:e.getAttribute(t)))if("string"==typeof s)return s;return null}function markAsBusy(...e){for(const t of e)"turbo-frame"==t.localName&&t.setAttribute("busy",""),t.setAttribute("aria-busy","true")}function clearBusyState(...e){for(const t of e)"turbo-frame"==t.localName&&t.removeAttribute("busy"),t.removeAttribute("aria-busy")}function fetchMethodFromString(e){switch(e.toLowerCase()){case"get":return FetchMethod.get;case"post":return FetchMethod.post;case"put":return FetchMethod.put;case"patch":return FetchMethod.patch;case"delete":return FetchMethod.delete}}!function(e){e[e.get=0]="get",e[e.post=1]="post",e[e.put=2]="put",e[e.patch=3]="patch",e[e.delete=4]="delete"}(FetchMethod=FetchMethod||{});class FetchRequest{constructor(e,t,s,r=new URLSearchParams,i=null){this.abortController=new AbortController,this.resolveRequestPromise=e=>{},this.delegate=e,this.method=t,this.headers=this.defaultHeaders,this.body=r,this.url=s,this.target=i}get location(){return this.url}get params(){return this.url.searchParams}get entries(){return this.body?Array.from(this.body.entries()):[]}cancel(){this.abortController.abort()}async perform(){var e,t,s=this["fetchOptions"];null!=(t=(e=this.delegate).prepareHeadersForRequest)&&t.call(e,this.headers,this),await this.allowRequestToBeIntercepted(s);try{this.delegate.requestStarted(this);var r=await fetch(this.url.href,s);return await this.receive(r)}catch(e){if("AbortError"!==e.name)throw this.delegate.requestErrored(this,e),e}finally{this.delegate.requestFinished(this)}}async receive(e){e=new FetchResponse(e);return dispatch("turbo:before-fetch-response",{cancelable:!0,detail:{fetchResponse:e},target:this.target}).defaultPrevented?this.delegate.requestPreventedHandlingResponse(this,e):e.succeeded?this.delegate.requestSucceededWithResponse(this,e):this.delegate.requestFailedWithResponse(this,e),e}get fetchOptions(){var e;return{method:FetchMethod[this.method].toUpperCase(),credentials:"same-origin",headers:this.headers,redirect:"follow",body:this.isIdempotent?null:this.body,signal:this.abortSignal,referrer:null==(e=this.delegate.referrer)?void 0:e.href}}get defaultHeaders(){return{Accept:"text/html, application/xhtml+xml"}}get isIdempotent(){return this.method==FetchMethod.get}get abortSignal(){return this.abortController.signal}async allowRequestToBeIntercepted(e){var t=new Promise(e=>this.resolveRequestPromise=e);dispatch("turbo:before-fetch-request",{cancelable:!0,detail:{fetchOptions:e,url:this.url,resume:this.resolveRequestPromise},target:this.target}).defaultPrevented&&await t}}class AppearanceObserver{constructor(e,t){this.started=!1,this.intersect=e=>{e=e.slice(-1)[0];null!=e&&e.isIntersecting&&this.delegate.elementAppearedInViewport(this.element)},this.delegate=e,this.element=t,this.intersectionObserver=new IntersectionObserver(this.intersect)}start(){this.started||(this.started=!0,this.intersectionObserver.observe(this.element))}stop(){this.started&&(this.started=!1,this.intersectionObserver.unobserve(this.element))}}class StreamMessage{constructor(e){this.templateElement=document.createElement("template"),this.templateElement.innerHTML=e}static wrap(e){return"string"==typeof e?new this(e):e}get fragment(){var e=document.createDocumentFragment();for(const t of this.foreignElements)e.appendChild(document.importNode(t,!0));return e}get foreignElements(){return this.templateChildren.reduce((e,t)=>"turbo-stream"==t.tagName.toLowerCase()?[...e,t]:e,[])}get templateChildren(){return Array.from(this.templateElement.content.children)}}function formEnctypeFromString(e){switch(e.toLowerCase()){case FormEnctype.multipart:return FormEnctype.multipart;case FormEnctype.plain:return FormEnctype.plain;default:return FormEnctype.urlEncoded}}StreamMessage.contentType="text/vnd.turbo-stream.html",function(e){e[e.initialized=0]="initialized",e[e.requesting=1]="requesting",e[e.waiting=2]="waiting",e[e.receiving=3]="receiving",e[e.stopping=4]="stopping",e[e.stopped=5]="stopped"}(FormSubmissionState=FormSubmissionState||{}),function(e){e.urlEncoded="application/x-www-form-urlencoded",e.multipart="multipart/form-data",e.plain="text/plain"}(FormEnctype=FormEnctype||{});class FormSubmission{constructor(e,t,s,r=!1){this.state=FormSubmissionState.initialized,this.delegate=e,this.formElement=t,this.submitter=s,this.formData=buildFormData(t,s),this.location=expandURL(this.action),this.method==FetchMethod.get&&mergeFormDataEntries(this.location,[...this.body.entries()]),this.fetchRequest=new FetchRequest(this,this.method,this.location,this.body,this.formElement),this.mustRedirect=r}static confirmMethod(e,t){return confirm(e)}get method(){var e;return fetchMethodFromString(((null==(e=this.submitter)?void 0:e.getAttribute("formmethod"))||this.formElement.getAttribute("method")||"").toLowerCase())||FetchMethod.get}get action(){var e,t="string"==typeof this.formElement.action?this.formElement.action:null;return(null==(e=this.submitter)?void 0:e.getAttribute("formaction"))||this.formElement.getAttribute("action")||t||""}get body(){return this.enctype==FormEnctype.urlEncoded||this.method==FetchMethod.get?new URLSearchParams(this.stringFormData):this.formData}get enctype(){var e;return formEnctypeFromString((null==(e=this.submitter)?void 0:e.getAttribute("formenctype"))||this.formElement.enctype)}get isIdempotent(){return this.fetchRequest.isIdempotent}get stringFormData(){return[...this.formData].reduce((e,[t,s])=>e.concat("string"==typeof s?[[t,s]]:[]),[])}get confirmationMessage(){return this.formElement.getAttribute("data-turbo-confirm")}get needsConfirmation(){return null!==this.confirmationMessage}async start(){var{initialized:e,requesting:t}=FormSubmissionState;if(this.needsConfirmation&&!FormSubmission.confirmMethod(this.confirmationMessage,this.formElement))return;if(this.state==e)return this.state=t,this.fetchRequest.perform()}stop(){var{stopping:e,stopped:t}=FormSubmissionState;if(this.state!=e&&this.state!=t)return this.state=e,this.fetchRequest.cancel(),!0}prepareHeadersForRequest(e,t){t.isIdempotent||((t=getCookieValue(getMetaContent("csrf-param"))||getMetaContent("csrf-token"))&&(e["X-CSRF-Token"]=t),e.Accept=[StreamMessage.contentType,e.Accept].join(", "))}requestStarted(e){var t;this.state=FormSubmissionState.waiting,null!=(t=this.submitter)&&t.setAttribute("disabled",""),dispatch("turbo:submit-start",{target:this.formElement,detail:{formSubmission:this}}),this.delegate.formSubmissionStarted(this)}requestPreventedHandlingResponse(e,t){this.result={success:t.succeeded,fetchResponse:t}}requestSucceededWithResponse(e,t){t.clientError||t.serverError?this.delegate.formSubmissionFailedWithResponse(this,t):this.requestMustRedirect(e)&&responseSucceededWithoutRedirect(t)?(e=new Error("Form responses must redirect to another location"),this.delegate.formSubmissionErrored(this,e)):(this.state=FormSubmissionState.receiving,this.result={success:!0,fetchResponse:t},this.delegate.formSubmissionSucceededWithResponse(this,t))}requestFailedWithResponse(e,t){this.result={success:!1,fetchResponse:t},this.delegate.formSubmissionFailedWithResponse(this,t)}requestErrored(e,t){this.result={success:!1,error:t},this.delegate.formSubmissionErrored(this,t)}requestFinished(e){var t;this.state=FormSubmissionState.stopped,null!=(t=this.submitter)&&t.removeAttribute("disabled"),dispatch("turbo:submit-end",{target:this.formElement,detail:Object.assign({formSubmission:this},this.result)}),this.delegate.formSubmissionFinished(this)}requestMustRedirect(e){return!e.isIdempotent&&this.mustRedirect}}function buildFormData(e,t){var e=new FormData(e),s=null==t?void 0:t.getAttribute("name"),t=null==t?void 0:t.getAttribute("value");return s&&null!=t&&e.get(s)!=t&&e.append(s,t),e}function getCookieValue(t){var e;return null!=t&&(e=(document.cookie?document.cookie.split("; "):[]).find(e=>e.startsWith(t)))&&(e=e.split("=").slice(1).join("="))?decodeURIComponent(e):void 0}function getMetaContent(e){e=document.querySelector(`meta[name="${e}"]`);return e&&e.content}function responseSucceededWithoutRedirect(e){return 200==e.statusCode&&!e.redirected}function mergeFormDataEntries(e,t){var s,r,i=new URLSearchParams;for([s,r]of t)r instanceof File||i.append(s,r);return e.search=i.toString(),e}class Snapshot{constructor(e){this.element=e}get children(){return[...this.element.children]}hasAnchor(e){return null!=this.getElementForAnchor(e)}getElementForAnchor(e){return e?this.element.querySelector(`[id='${e}'], a[name='${e}']`):null}get isConnected(){return this.element.isConnected}get firstAutofocusableElement(){return this.element.querySelector("[autofocus]")}get permanentElements(){return[...this.element.querySelectorAll("[id][data-turbo-permanent]")]}getPermanentElementById(e){return this.element.querySelector(`#${e}[data-turbo-permanent]`)}getPermanentElementMapForSnapshot(e){var t={};for(const i of this.permanentElements){var s=i["id"],r=e.getPermanentElementById(s);r&&(t[s]=[i,r])}return t}}class FormInterceptor{constructor(e,t){this.submitBubbled=e=>{var t,s=e.target;!e.defaultPrevented&&s instanceof HTMLFormElement&&s.closest("turbo-frame, html")==this.element&&"dialog"!=((null==(t=e.submitter||void 0)?void 0:t.getAttribute("formmethod"))||s.method)&&this.delegate.shouldInterceptFormSubmission(s,t)&&(e.preventDefault(),e.stopImmediatePropagation(),this.delegate.formSubmissionIntercepted(s,t))},this.delegate=e,this.element=t}start(){this.element.addEventListener("submit",this.submitBubbled)}stop(){this.element.removeEventListener("submit",this.submitBubbled)}}class View{constructor(e,t){this.resolveRenderPromise=e=>{},this.resolveInterceptionPromise=e=>{},this.delegate=e,this.element=t}scrollToAnchor(e){e=this.snapshot.getElementForAnchor(e);e?(this.scrollToElement(e),this.focusElement(e)):this.scrollToPosition({x:0,y:0})}scrollToAnchorFromLocation(e){this.scrollToAnchor(getAnchor(e))}scrollToElement(e){e.scrollIntoView()}focusElement(e){e instanceof HTMLElement&&(e.hasAttribute("tabindex")?e.focus():(e.setAttribute("tabindex","-1"),e.focus(),e.removeAttribute("tabindex")))}scrollToPosition({x:e,y:t}){this.scrollRoot.scrollTo(e,t)}scrollToTop(){this.scrollToPosition({x:0,y:0})}get scrollRoot(){return window}async render(e){var{isPreview:t,shouldRender:s,newSnapshot:r}=e;if(s)try{this.renderPromise=new Promise(e=>this.resolveRenderPromise=e),this.renderer=e,this.prepareToRenderSnapshot(e);var i=new Promise(e=>this.resolveInterceptionPromise=e);this.delegate.allowsImmediateRender(r,this.resolveInterceptionPromise)||await i,await this.renderSnapshot(e),this.delegate.viewRenderedSnapshot(r,t),this.finishRenderingSnapshot(e)}finally{delete this.renderer,this.resolveRenderPromise(void 0),delete this.renderPromise}else this.invalidate()}invalidate(){this.delegate.viewInvalidated()}prepareToRenderSnapshot(e){this.markAsPreview(e.isPreview),e.prepareToRender()}markAsPreview(e){e?this.element.setAttribute("data-turbo-preview",""):this.element.removeAttribute("data-turbo-preview")}async renderSnapshot(e){await e.render()}finishRenderingSnapshot(e){e.finishRendering()}}class FrameView extends View{invalidate(){this.element.innerHTML=""}get snapshot(){return new Snapshot(this.element)}}class LinkInterceptor{constructor(e,t){this.clickBubbled=e=>{this.respondsToEventTarget(e.target)?this.clickEvent=e:delete this.clickEvent},this.linkClicked=e=>{this.clickEvent&&this.respondsToEventTarget(e.target)&&e.target instanceof Element&&this.delegate.shouldInterceptLinkClick(e.target,e.detail.url)&&(this.clickEvent.preventDefault(),e.preventDefault(),this.delegate.linkClickIntercepted(e.target,e.detail.url)),delete this.clickEvent},this.willVisit=()=>{delete this.clickEvent},this.delegate=e,this.element=t}start(){this.element.addEventListener("click",this.clickBubbled),document.addEventListener("turbo:click",this.linkClicked),document.addEventListener("turbo:before-visit",this.willVisit)}stop(){this.element.removeEventListener("click",this.clickBubbled),document.removeEventListener("turbo:click",this.linkClicked),document.removeEventListener("turbo:before-visit",this.willVisit)}respondsToEventTarget(e){e=e instanceof Element?e:e instanceof Node?e.parentElement:null;return e&&e.closest("turbo-frame, html")==this.element}}class Bardo{constructor(e){this.permanentElementMap=e}static preservingPermanentElements(e,t){e=new this(e);e.enter(),t(),e.leave()}enter(){for(const t in this.permanentElementMap){var[,e]=this.permanentElementMap[t];this.replaceNewPermanentElementWithPlaceholder(e)}}leave(){for(const t in this.permanentElementMap){var[e]=this.permanentElementMap[t];this.replaceCurrentPermanentElementWithClone(e),this.replacePlaceholderWithPermanentElement(e)}}replaceNewPermanentElementWithPlaceholder(e){var t=createPlaceholderForPermanentElement(e);e.replaceWith(t)}replaceCurrentPermanentElementWithClone(e){var t=e.cloneNode(!0);e.replaceWith(t)}replacePlaceholderWithPermanentElement(e){var t=this.getPlaceholderById(e.id);null!=t&&t.replaceWith(e)}getPlaceholderById(t){return this.placeholders.find(e=>e.content==t)}get placeholders(){return[...document.querySelectorAll("meta[name=turbo-permanent-placeholder][content]")]}}function createPlaceholderForPermanentElement(e){var t=document.createElement("meta");return t.setAttribute("name","turbo-permanent-placeholder"),t.setAttribute("content",e.id),t}class Renderer{constructor(e,t,s,r=!0){this.currentSnapshot=e,this.newSnapshot=t,this.isPreview=s,this.willRender=r,this.promise=new Promise((e,t)=>this.resolvingFunctions={resolve:e,reject:t})}get shouldRender(){return!0}prepareToRender(){}finishRendering(){this.resolvingFunctions&&(this.resolvingFunctions.resolve(),delete this.resolvingFunctions)}createScriptElement(e){var t;return"false"==e.getAttribute("data-turbo-eval")?e:(t=document.createElement("script"),this.cspNonce&&(t.nonce=this.cspNonce),t.textContent=e.textContent,t.async=!1,copyElementAttributes(t,e),t)}preservingPermanentElements(e){Bardo.preservingPermanentElements(this.permanentElementMap,e)}focusFirstAutofocusableElement(){var e=this.connectedSnapshot.firstAutofocusableElement;elementIsFocusable(e)&&e.focus()}get connectedSnapshot(){return this.newSnapshot.isConnected?this.newSnapshot:this.currentSnapshot}get currentElement(){return this.currentSnapshot.element}get newElement(){return this.newSnapshot.element}get permanentElementMap(){return this.currentSnapshot.getPermanentElementMapForSnapshot(this.newSnapshot)}get cspNonce(){var e;return null==(e=document.head.querySelector('meta[name="csp-nonce"]'))?void 0:e.getAttribute("content")}}function copyElementAttributes(e,t){for(var{name:s,value:r}of[...t.attributes])e.setAttribute(s,r)}function elementIsFocusable(e){return e&&"function"==typeof e.focus}class FrameRenderer extends Renderer{get shouldRender(){return!0}async render(){await nextAnimationFrame(),this.preservingPermanentElements(()=>{this.loadFrameElement()}),this.scrollFrameIntoView(),await nextAnimationFrame(),this.focusFirstAutofocusableElement(),await nextAnimationFrame(),this.activateScriptElements()}loadFrameElement(){var e=document.createRange(),e=(e.selectNodeContents(this.currentElement),e.deleteContents(),this.newElement),t=null==(t=e.ownerDocument)?void 0:t.createRange();t&&(t.selectNodeContents(e),this.currentElement.appendChild(t.extractContents()))}scrollFrameIntoView(){if(this.currentElement.autoscroll||this.newElement.autoscroll){var e=this.currentElement.firstElementChild,t=readScrollLogicalPosition(this.currentElement.getAttribute("data-autoscroll-block"),"end");if(e)return e.scrollIntoView({block:t}),!0}return!1}activateScriptElements(){for(const t of this.newScriptElements){var e=this.createScriptElement(t);t.replaceWith(e)}}get newScriptElements(){return this.currentElement.querySelectorAll("script")}}function readScrollLogicalPosition(e,t){return"end"==e||"start"==e||"center"==e||"nearest"==e?e:t}class ProgressBar{constructor(){this.hiding=!1,this.value=0,this.visible=!1,this.trickle=()=>{this.setValue(this.value+Math.random()/100)},this.stylesheetElement=this.createStylesheetElement(),this.progressElement=this.createProgressElement(),this.installStylesheetElement(),this.setValue(0)}static get defaultCSS(){return unindent` + .turbo-progress-bar { + position: fixed; + display: block; + top: 0; + left: 0; + height: 3px; + background: #0076ff; + z-index: 9999; + transition: + width ${ProgressBar.animationDuration}ms ease-out, + opacity ${ProgressBar.animationDuration/2}ms ${ProgressBar.animationDuration/2}ms ease-in; + transform: translate3d(0, 0, 0); + } + `}show(){this.visible||(this.visible=!0,this.installProgressElement(),this.startTrickling())}hide(){this.visible&&!this.hiding&&(this.hiding=!0,this.fadeProgressElement(()=>{this.uninstallProgressElement(),this.stopTrickling(),this.visible=!1,this.hiding=!1}))}setValue(e){this.value=e,this.refresh()}installStylesheetElement(){document.head.insertBefore(this.stylesheetElement,document.head.firstChild)}installProgressElement(){this.progressElement.style.width="0",this.progressElement.style.opacity="1",document.documentElement.insertBefore(this.progressElement,document.body),this.refresh()}fadeProgressElement(e){this.progressElement.style.opacity="0",setTimeout(e,1.5*ProgressBar.animationDuration)}uninstallProgressElement(){this.progressElement.parentNode&&document.documentElement.removeChild(this.progressElement)}startTrickling(){this.trickleInterval||(this.trickleInterval=window.setInterval(this.trickle,ProgressBar.animationDuration))}stopTrickling(){window.clearInterval(this.trickleInterval),delete this.trickleInterval}refresh(){requestAnimationFrame(()=>{this.progressElement.style.width=10+90*this.value+"%"})}createStylesheetElement(){var e=document.createElement("style");return e.type="text/css",e.textContent=ProgressBar.defaultCSS,e}createProgressElement(){var e=document.createElement("div");return e.className="turbo-progress-bar",e}}ProgressBar.animationDuration=300;class HeadSnapshot extends Snapshot{constructor(){super(...arguments),this.detailsByOuterHTML=this.children.filter(e=>!elementIsNoscript(e)).map(e=>elementWithoutNonce(e)).reduce((e,t)=>{var s=t["outerHTML"],r=s in e?e[s]:{type:elementType(t),tracked:elementIsTracked(t),elements:[]};return Object.assign(Object.assign({},e),{[s]:Object.assign(Object.assign({},r),{elements:[...r.elements,t]})})},{})}get trackedElementSignature(){return Object.keys(this.detailsByOuterHTML).filter(e=>this.detailsByOuterHTML[e].tracked).join("")}getScriptElementsNotInSnapshot(e){return this.getElementsMatchingTypeNotInSnapshot("script",e)}getStylesheetElementsNotInSnapshot(e){return this.getElementsMatchingTypeNotInSnapshot("stylesheet",e)}getElementsMatchingTypeNotInSnapshot(t,s){return Object.keys(this.detailsByOuterHTML).filter(e=>!(e in s.detailsByOuterHTML)).map(e=>this.detailsByOuterHTML[e]).filter(({type:e})=>e==t).map(({elements:[e]})=>e)}get provisionalElements(){return Object.keys(this.detailsByOuterHTML).reduce((e,t)=>{var{type:t,tracked:s,elements:r}=this.detailsByOuterHTML[t];return null!=t||s?1{var[t]=this.detailsByOuterHTML[t]["elements"];return elementIsMetaElementWithName(t,s)?t:e},void 0)}}function elementType(e){return elementIsScript(e)?"script":elementIsStylesheet(e)?"stylesheet":void 0}function elementIsTracked(e){return"reload"==e.getAttribute("data-turbo-track")}function elementIsScript(e){return"script"==e.tagName.toLowerCase()}function elementIsNoscript(e){return"noscript"==e.tagName.toLowerCase()}function elementIsStylesheet(e){var t=e.tagName.toLowerCase();return"style"==t||"link"==t&&"stylesheet"==e.getAttribute("rel")}function elementIsMetaElementWithName(e,t){return"meta"==e.tagName.toLowerCase()&&e.getAttribute("name")==t}function elementWithoutNonce(e){return e.hasAttribute("nonce")&&e.setAttribute("nonce",""),e}class PageSnapshot extends Snapshot{constructor(e,t){super(e),this.headSnapshot=t}static fromHTMLString(e=""){return this.fromDocument(parseHTMLDocument(e))}static fromElement(e){return this.fromDocument(e.ownerDocument)}static fromDocument({head:e,body:t}){return new this(t,new HeadSnapshot(e))}clone(){return new PageSnapshot(this.element.cloneNode(!0),this.headSnapshot)}get headElement(){return this.headSnapshot.element}get rootLocation(){var e;return expandURL(null!=(e=this.getSetting("root"))?e:"/")}get cacheControlValue(){return this.getSetting("cache-control")}get isPreviewable(){return"no-preview"!=this.cacheControlValue}get isCacheable(){return"no-cache"!=this.cacheControlValue}get isVisitable(){return"reload"!=this.getSetting("visit-control")}getSetting(e){return this.headSnapshot.getMetaValue("turbo-"+e)}}!function(e){e.visitStart="visitStart",e.requestStart="requestStart",e.requestEnd="requestEnd",e.visitEnd="visitEnd"}(TimingMetric=TimingMetric||{}),function(e){e.initialized="initialized",e.started="started",e.canceled="canceled",e.failed="failed",e.completed="completed"}(VisitState=VisitState||{});const defaultOptions={action:"advance",historyChanged:!1,visitCachedSnapshot:()=>{},willRender:!0};!function(e){e[e.networkFailure=0]="networkFailure",e[e.timeoutFailure=-1]="timeoutFailure",e[e.contentTypeMismatch=-2]="contentTypeMismatch"}(SystemStatusCode=SystemStatusCode||{});class Visit{constructor(e,t,s,r={}){this.identifier=uuid(),this.timingMetrics={},this.followedRedirect=!1,this.historyChanged=!1,this.scrolled=!1,this.snapshotCached=!1,this.state=VisitState.initialized,this.delegate=e,this.location=t,this.restorationIdentifier=s||uuid();var{action:e,historyChanged:t,referrer:s,snapshotHTML:r,response:i,visitCachedSnapshot:n,willRender:o}=Object.assign(Object.assign({},defaultOptions),r);this.action=e,this.historyChanged=t,this.referrer=s,this.snapshotHTML=r,this.response=i,this.isSamePage=this.delegate.locationWithActionIsSamePage(this.location,this.action),this.visitCachedSnapshot=n,this.willRender=o,this.scrolled=!o}get adapter(){return this.delegate.adapter}get view(){return this.delegate.view}get history(){return this.delegate.history}get restorationData(){return this.history.getRestorationDataForIdentifier(this.restorationIdentifier)}get silent(){return this.isSamePage}start(){this.state==VisitState.initialized&&(this.recordTimingMetric(TimingMetric.visitStart),this.state=VisitState.started,this.adapter.visitStarted(this),this.delegate.visitStarted(this))}cancel(){this.state==VisitState.started&&(this.request&&this.request.cancel(),this.cancelRender(),this.state=VisitState.canceled)}complete(){this.state==VisitState.started&&(this.recordTimingMetric(TimingMetric.visitEnd),this.state=VisitState.completed,this.adapter.visitCompleted(this),this.delegate.visitCompleted(this),this.followRedirect())}fail(){this.state==VisitState.started&&(this.state=VisitState.failed,this.adapter.visitFailed(this))}changeHistory(){var e;this.historyChanged||(e=this.location.href===(null==(e=this.referrer)?void 0:e.href)?"replace":this.action,e=this.getHistoryMethodForAction(e),this.history.update(e,this.location,this.restorationIdentifier),this.historyChanged=!0)}issueRequest(){this.hasPreloadedResponse()?this.simulateRequest():this.shouldIssueRequest()&&!this.request&&(this.request=new FetchRequest(this,FetchMethod.get,this.location),this.request.perform())}simulateRequest(){this.response&&(this.startRequest(),this.recordResponse(),this.finishRequest())}startRequest(){this.recordTimingMetric(TimingMetric.requestStart),this.adapter.visitRequestStarted(this)}recordResponse(e=this.response){(this.response=e)&&(e=e["statusCode"],isSuccessful(e)?this.adapter.visitRequestCompleted(this):this.adapter.visitRequestFailedWithStatusCode(this,e))}finishRequest(){this.recordTimingMetric(TimingMetric.requestEnd),this.adapter.visitRequestFinished(this)}loadResponse(){if(this.response){const{statusCode:e,responseHTML:t}=this.response;this.render(async()=>{this.cacheSnapshot(),this.view.renderPromise&&await this.view.renderPromise,isSuccessful(e)&&null!=t?(await this.view.renderPage(PageSnapshot.fromHTMLString(t),!1,this.willRender),this.adapter.visitRendered(this),this.complete()):(await this.view.renderError(PageSnapshot.fromHTMLString(t)),this.adapter.visitRendered(this),this.fail())})}}getCachedSnapshot(){var e=this.view.getCachedSnapshotForLocation(this.location)||this.getPreloadedSnapshot();if(e&&(!getAnchor(this.location)||e.hasAnchor(getAnchor(this.location)))&&("restore"==this.action||e.isPreviewable))return e}getPreloadedSnapshot(){if(this.snapshotHTML)return PageSnapshot.fromHTMLString(this.snapshotHTML)}hasCachedSnapshot(){return null!=this.getCachedSnapshot()}loadCachedSnapshot(){const e=this.getCachedSnapshot();if(e){const t=this.shouldIssueRequest();this.render(async()=>{this.cacheSnapshot(),this.isSamePage?this.adapter.visitRendered(this):(this.view.renderPromise&&await this.view.renderPromise,await this.view.renderPage(e,t,this.willRender),this.adapter.visitRendered(this),t||this.complete())})}}followRedirect(){var e;this.redirectedToLocation&&!this.followedRedirect&&null!=(e=this.response)&&e.redirected&&(this.adapter.visitProposedToLocation(this.redirectedToLocation,{action:"replace",response:this.response}),this.followedRedirect=!0)}goToSamePageAnchor(){this.isSamePage&&this.render(async()=>{this.cacheSnapshot(),this.adapter.visitRendered(this)})}requestStarted(){this.startRequest()}requestPreventedHandlingResponse(e,t){}async requestSucceededWithResponse(e,t){var s=await t.responseHTML,{redirected:r,statusCode:i}=t;null==s?this.recordResponse({statusCode:SystemStatusCode.contentTypeMismatch,redirected:r}):(this.redirectedToLocation=t.redirected?t.location:void 0,this.recordResponse({statusCode:i,responseHTML:s,redirected:r}))}async requestFailedWithResponse(e,t){var s=await t.responseHTML,{redirected:t,statusCode:r}=t;null==s?this.recordResponse({statusCode:SystemStatusCode.contentTypeMismatch,redirected:t}):this.recordResponse({statusCode:r,responseHTML:s,redirected:t})}requestErrored(e,t){this.recordResponse({statusCode:SystemStatusCode.networkFailure,redirected:!1})}requestFinished(){this.finishRequest()}performScroll(){this.scrolled||("restore"==this.action?this.scrollToRestoredPosition()||this.scrollToAnchor()||this.view.scrollToTop():this.scrollToAnchor()||this.view.scrollToTop(),this.isSamePage&&this.delegate.visitScrolledToSamePageLocation(this.view.lastRenderedLocation,this.location),this.scrolled=!0)}scrollToRestoredPosition(){var e=this.restorationData["scrollPosition"];if(e)return this.view.scrollToPosition(e),!0}scrollToAnchor(){var e=getAnchor(this.location);if(null!=e)return this.view.scrollToAnchor(e),!0}recordTimingMetric(e){this.timingMetrics[e]=(new Date).getTime()}getTimingMetrics(){return Object.assign({},this.timingMetrics)}getHistoryMethodForAction(e){switch(e){case"replace":return history.replaceState;case"advance":case"restore":return history.pushState}}hasPreloadedResponse(){return"object"==typeof this.response}shouldIssueRequest(){return!this.isSamePage&&("restore"==this.action?!this.hasCachedSnapshot():this.willRender)}cacheSnapshot(){this.snapshotCached||(this.view.cacheSnapshot().then(e=>e&&this.visitCachedSnapshot(e)),this.snapshotCached=!0)}async render(e){this.cancelRender(),await new Promise(e=>{this.frame=requestAnimationFrame(()=>e())}),await e(),delete this.frame,this.performScroll()}cancelRender(){this.frame&&(cancelAnimationFrame(this.frame),delete this.frame)}}function isSuccessful(e){return 200<=e&&e<300}class BrowserAdapter{constructor(e){this.progressBar=new ProgressBar,this.showProgressBar=()=>{this.progressBar.show()},this.session=e}visitProposedToLocation(e,t){this.navigator.startVisit(e,uuid(),t)}visitStarted(e){e.loadCachedSnapshot(),e.issueRequest(),e.changeHistory(),e.goToSamePageAnchor()}visitRequestStarted(e){this.progressBar.setValue(0),e.hasCachedSnapshot()||"restore"!=e.action?this.showVisitProgressBarAfterDelay():this.showProgressBar()}visitRequestCompleted(e){e.loadResponse()}visitRequestFailedWithStatusCode(e,t){switch(t){case SystemStatusCode.networkFailure:case SystemStatusCode.timeoutFailure:case SystemStatusCode.contentTypeMismatch:return this.reload();default:return e.loadResponse()}}visitRequestFinished(e){this.progressBar.setValue(1),this.hideVisitProgressBar()}visitCompleted(e){}pageInvalidated(){this.reload()}visitFailed(e){}visitRendered(e){}formSubmissionStarted(e){this.progressBar.setValue(0),this.showFormProgressBarAfterDelay()}formSubmissionFinished(e){this.progressBar.setValue(1),this.hideFormProgressBar()}showVisitProgressBarAfterDelay(){this.visitProgressBarTimeout=window.setTimeout(this.showProgressBar,this.session.progressBarDelay)}hideVisitProgressBar(){this.progressBar.hide(),null!=this.visitProgressBarTimeout&&(window.clearTimeout(this.visitProgressBarTimeout),delete this.visitProgressBarTimeout)}showFormProgressBarAfterDelay(){null==this.formProgressBarTimeout&&(this.formProgressBarTimeout=window.setTimeout(this.showProgressBar,this.session.progressBarDelay))}hideFormProgressBar(){this.progressBar.hide(),null!=this.formProgressBarTimeout&&(window.clearTimeout(this.formProgressBarTimeout),delete this.formProgressBarTimeout)}reload(){window.location.reload()}get navigator(){return this.session.navigator}}class CacheObserver{constructor(){this.started=!1}start(){this.started||(this.started=!0,addEventListener("turbo:before-cache",this.removeStaleElements,!1))}stop(){this.started&&(this.started=!1,removeEventListener("turbo:before-cache",this.removeStaleElements,!1))}removeStaleElements(){for(const e of[...document.querySelectorAll('[data-turbo-cache="false"]')])e.remove()}}class FormSubmitObserver{constructor(e){this.started=!1,this.submitCaptured=()=>{removeEventListener("submit",this.submitBubbled,!1),addEventListener("submit",this.submitBubbled,!1)},this.submitBubbled=e=>{var t,s;e.defaultPrevented||(t=e.target instanceof HTMLFormElement?e.target:void 0,s=e.submitter||void 0,t&&"dialog"!=((null==s?void 0:s.getAttribute("formmethod"))||t.getAttribute("method"))&&this.delegate.willSubmitForm(t,s)&&(e.preventDefault(),this.delegate.formSubmitted(t,s)))},this.delegate=e}start(){this.started||(addEventListener("submit",this.submitCaptured,!0),this.started=!0)}stop(){this.started&&(removeEventListener("submit",this.submitCaptured,!0),this.started=!1)}}class FrameRedirector{constructor(e){this.element=e,this.linkInterceptor=new LinkInterceptor(this,e),this.formInterceptor=new FormInterceptor(this,e)}start(){this.linkInterceptor.start(),this.formInterceptor.start()}stop(){this.linkInterceptor.stop(),this.formInterceptor.stop()}shouldInterceptLinkClick(e,t){return this.shouldRedirect(e)}linkClickIntercepted(e,t){var s=this.findFrameElement(e);s&&s.delegate.linkClickIntercepted(e,t)}shouldInterceptFormSubmission(e,t){return this.shouldSubmit(e,t)}formSubmissionIntercepted(e,t){var s=this.findFrameElement(e,t);s&&(s.removeAttribute("reloadable"),s.delegate.formSubmissionIntercepted(e,t))}shouldSubmit(e,t){var s=getAction(e,t),r=this.element.ownerDocument.querySelector('meta[name="turbo-root"]'),r=expandURL(null!=(r=null==r?void 0:r.content)?r:"/");return this.shouldRedirect(e,t)&&locationIsVisitable(s,r)}shouldRedirect(e,t){t=this.findFrameElement(e,t);return!!t&&t!=e.closest("turbo-frame")}findFrameElement(e,t){t=(null==t?void 0:t.getAttribute("data-turbo-frame"))||e.getAttribute("data-turbo-frame");if(t&&"_top"!=t){e=this.element.querySelector(`#${t}:not([disabled])`);if(e instanceof FrameElement)return e}}}class History{constructor(e){this.restorationIdentifier=uuid(),this.restorationData={},this.started=!1,this.pageLoaded=!1,this.onPopState=e=>{this.shouldHandlePopState()&&(e=(e.state||{})["turbo"],e)&&(this.location=new URL(window.location.href),e=e["restorationIdentifier"],this.restorationIdentifier=e,this.delegate.historyPoppedToLocationWithRestorationIdentifier(this.location,e))},this.onPageLoad=async e=>{await nextMicrotask(),this.pageLoaded=!0},this.delegate=e}start(){this.started||(addEventListener("popstate",this.onPopState,!1),addEventListener("load",this.onPageLoad,!1),this.started=!0,this.replace(new URL(window.location.href)))}stop(){this.started&&(removeEventListener("popstate",this.onPopState,!1),removeEventListener("load",this.onPageLoad,!1),this.started=!1)}push(e,t){this.update(history.pushState,e,t)}replace(e,t){this.update(history.replaceState,e,t)}update(e,t,s=uuid()){e.call(history,{turbo:{restorationIdentifier:s}},"",t.href),this.location=t,this.restorationIdentifier=s}getRestorationDataForIdentifier(e){return this.restorationData[e]||{}}updateRestorationData(e){var t=this["restorationIdentifier"],s=this.restorationData[t];this.restorationData[t]=Object.assign(Object.assign({},s),e)}assumeControlOfScrollRestoration(){var e;this.previousScrollRestoration||(this.previousScrollRestoration=null!=(e=history.scrollRestoration)?e:"auto",history.scrollRestoration="manual")}relinquishControlOfScrollRestoration(){this.previousScrollRestoration&&(history.scrollRestoration=this.previousScrollRestoration,delete this.previousScrollRestoration)}shouldHandlePopState(){return this.pageIsLoaded()}pageIsLoaded(){return this.pageLoaded||"complete"==document.readyState}}class LinkClickObserver{constructor(e){this.started=!1,this.clickCaptured=()=>{removeEventListener("click",this.clickBubbled,!1),addEventListener("click",this.clickBubbled,!1)},this.clickBubbled=e=>{var t,s;this.clickEventIsSignificant(e)&&(t=e.composedPath&&e.composedPath()[0]||e.target,t=this.findLinkFromClickTarget(t))&&(s=this.getLocationForLink(t),this.delegate.willFollowLinkToLocation(t,s))&&(e.preventDefault(),this.delegate.followedLinkToLocation(t,s))},this.delegate=e}start(){this.started||(addEventListener("click",this.clickCaptured,!0),this.started=!0)}stop(){this.started&&(removeEventListener("click",this.clickCaptured,!0),this.started=!1)}clickEventIsSignificant(e){return!(e.target&&e.target.isContentEditable||e.defaultPrevented||1{var e=this["readyState"];"interactive"==e?this.pageIsInteractive():"complete"==e&&this.pageIsComplete()},this.pageWillUnload=()=>{this.delegate.pageWillUnload()},this.delegate=e}start(){this.started||(this.stage==PageStage.initial&&(this.stage=PageStage.loading),document.addEventListener("readystatechange",this.interpretReadyState,!1),addEventListener("pagehide",this.pageWillUnload,!1),this.started=!0)}stop(){this.started&&(document.removeEventListener("readystatechange",this.interpretReadyState,!1),removeEventListener("pagehide",this.pageWillUnload,!1),this.started=!1)}pageIsInteractive(){this.stage==PageStage.loading&&(this.stage=PageStage.interactive,this.delegate.pageBecameInteractive())}pageIsComplete(){this.pageIsInteractive(),this.stage==PageStage.interactive&&(this.stage=PageStage.complete,this.delegate.pageLoaded())}get readyState(){return document.readyState}}class ScrollObserver{constructor(e){this.started=!1,this.onScroll=()=>{this.updatePosition({x:window.pageXOffset,y:window.pageYOffset})},this.delegate=e}start(){this.started||(addEventListener("scroll",this.onScroll,!1),this.onScroll(),this.started=!0)}stop(){this.started&&(removeEventListener("scroll",this.onScroll,!1),this.started=!1)}updatePosition(e){this.delegate.scrollPositionChanged(e)}}class StreamObserver{constructor(e){this.sources=new Set,this.started=!1,this.inspectFetchResponse=e=>{var t=fetchResponseFromEvent(e);t&&fetchResponseIsStream(t)&&(e.preventDefault(),this.receiveMessageResponse(t))},this.receiveMessageEvent=e=>{this.started&&"string"==typeof e.data&&this.receiveMessageHTML(e.data)},this.delegate=e}start(){this.started||(this.started=!0,addEventListener("turbo:before-fetch-response",this.inspectFetchResponse,!1))}stop(){this.started&&(this.started=!1,removeEventListener("turbo:before-fetch-response",this.inspectFetchResponse,!1))}connectStreamSource(e){this.streamSourceIsConnected(e)||(this.sources.add(e),e.addEventListener("message",this.receiveMessageEvent,!1))}disconnectStreamSource(e){this.streamSourceIsConnected(e)&&(this.sources.delete(e),e.removeEventListener("message",this.receiveMessageEvent,!1))}streamSourceIsConnected(e){return this.sources.has(e)}async receiveMessageResponse(e){e=await e.responseHTML;e&&this.receiveMessageHTML(e)}receiveMessageHTML(e){this.delegate.receivedMessageFromStream(new StreamMessage(e))}}function fetchResponseFromEvent(e){e=null==(e=e.detail)?void 0:e.fetchResponse;if(e instanceof FetchResponse)return e}function fetchResponseIsStream(e){return(null!=(e=e.contentType)?e:"").startsWith(StreamMessage.contentType)}class ErrorRenderer extends Renderer{async render(){this.replaceHeadAndBody(),this.activateScriptElements()}replaceHeadAndBody(){var{documentElement:e,head:t,body:s}=document;e.replaceChild(this.newHead,t),e.replaceChild(this.newElement,s)}activateScriptElements(){for(const s of this.scriptElements){var e,t=s.parentNode;t&&(e=this.createScriptElement(s),t.replaceChild(e,s))}}get newHead(){return this.newSnapshot.headSnapshot.element}get scriptElements(){return[...document.documentElement.querySelectorAll("script")]}}class PageRenderer extends Renderer{get shouldRender(){return this.newSnapshot.isVisitable&&this.trackedElementsAreIdentical}prepareToRender(){this.mergeHead()}async render(){this.willRender&&this.replaceBody()}finishRendering(){super.finishRendering(),this.isPreview||this.focusFirstAutofocusableElement()}get currentHeadSnapshot(){return this.currentSnapshot.headSnapshot}get newHeadSnapshot(){return this.newSnapshot.headSnapshot}get newElement(){return this.newSnapshot.element}mergeHead(){this.copyNewHeadStylesheetElements(),this.copyNewHeadScriptElements(),this.removeCurrentHeadProvisionalElements(),this.copyNewHeadProvisionalElements()}replaceBody(){this.preservingPermanentElements(()=>{this.activateNewBody(),this.assignNewBody()})}get trackedElementsAreIdentical(){return this.currentHeadSnapshot.trackedElementSignature==this.newHeadSnapshot.trackedElementSignature}copyNewHeadStylesheetElements(){for(const e of this.newHeadStylesheetElements)document.head.appendChild(e)}copyNewHeadScriptElements(){for(const e of this.newHeadScriptElements)document.head.appendChild(this.createScriptElement(e))}removeCurrentHeadProvisionalElements(){for(const e of this.currentHeadProvisionalElements)document.head.removeChild(e)}copyNewHeadProvisionalElements(){for(const e of this.newHeadProvisionalElements)document.head.appendChild(e)}activateNewBody(){document.adoptNode(this.newElement),this.activateNewBodyScriptElements()}activateNewBodyScriptElements(){for(const t of this.newBodyScriptElements){var e=this.createScriptElement(t);t.replaceWith(e)}}assignNewBody(){document.body&&this.newElement instanceof HTMLBodyElement?document.body.replaceWith(this.newElement):document.documentElement.appendChild(this.newElement)}get newHeadStylesheetElements(){return this.newHeadSnapshot.getStylesheetElementsNotInSnapshot(this.currentHeadSnapshot)}get newHeadScriptElements(){return this.newHeadSnapshot.getScriptElementsNotInSnapshot(this.currentHeadSnapshot)}get currentHeadProvisionalElements(){return this.currentHeadSnapshot.provisionalElements}get newHeadProvisionalElements(){return this.newHeadSnapshot.provisionalElements}get newBodyScriptElements(){return this.newElement.querySelectorAll("script")}}class SnapshotCache{constructor(e){this.keys=[],this.snapshots={},this.size=e}has(e){return toCacheKey(e)in this.snapshots}get(e){var t;if(this.has(e))return t=this.read(e),this.touch(e),t}put(e,t){return this.write(e,t),this.touch(e),t}clear(){this.snapshots={}}read(e){return this.snapshots[toCacheKey(e)]}write(e,t){this.snapshots[toCacheKey(e)]=t}touch(e){var e=toCacheKey(e),t=this.keys.indexOf(e);-1s.remove())):s.addEventListener("submit",()=>s.remove()),document.body.appendChild(s),dispatch("submit",{cancelable:!0,target:s})}return!1}allowsVisitingLocationWithAction(e,t){return this.locationWithActionIsSamePage(e,t)||this.applicationAllowsVisitingLocation(e)}visitProposedToLocation(e,t){extendURLWithDeprecatedProperties(e),this.adapter.visitProposedToLocation(e,t)}visitStarted(e){extendURLWithDeprecatedProperties(e.location),e.silent||this.notifyApplicationAfterVisitingLocation(e.location,e.action)}visitCompleted(e){this.notifyApplicationAfterPageLoad(e.getTimingMetrics())}locationWithActionIsSamePage(e,t){return this.navigator.locationWithActionIsSamePage(e,t)}visitScrolledToSamePageLocation(e,t){this.notifyApplicationAfterVisitingSamePageLocation(e,t)}willSubmitForm(e,t){var s=getAction(e,t);return this.elementDriveEnabled(e)&&(!t||this.elementDriveEnabled(t))&&locationIsVisitable(expandURL(s),this.snapshot.rootLocation)}formSubmitted(e,t){this.navigator.submitForm(e,t)}pageBecameInteractive(){this.view.lastRenderedLocation=this.location,this.notifyApplicationAfterPageLoad()}pageLoaded(){this.history.assumeControlOfScrollRestoration()}pageWillUnload(){this.history.relinquishControlOfScrollRestoration()}receivedMessageFromStream(e){this.renderStreamMessage(e)}viewWillCacheSnapshot(){var e;null!=(e=this.navigator.currentVisit)&&e.silent||this.notifyApplicationBeforeCachingSnapshot()}allowsImmediateRender({element:e},t){return!this.notifyApplicationBeforeRender(e,t).defaultPrevented}viewRenderedSnapshot(e,t){this.view.lastRenderedLocation=this.history.location,this.notifyApplicationAfterRender()}viewInvalidated(){this.adapter.pageInvalidated()}frameLoaded(e){this.notifyApplicationAfterFrameLoad(e)}frameRendered(e,t){this.notifyApplicationAfterFrameRender(e,t)}applicationAllowsFollowingLinkToLocation(e,t){return!this.notifyApplicationAfterClickingLinkToLocation(e,t).defaultPrevented}applicationAllowsVisitingLocation(e){return!this.notifyApplicationBeforeVisitingLocation(e).defaultPrevented}notifyApplicationAfterClickingLinkToLocation(e,t){return dispatch("turbo:click",{target:e,detail:{url:t.href},cancelable:!0})}notifyApplicationBeforeVisitingLocation(e){return dispatch("turbo:before-visit",{detail:{url:e.href},cancelable:!0})}notifyApplicationAfterVisitingLocation(e,t){return markAsBusy(document.documentElement),dispatch("turbo:visit",{detail:{url:e.href,action:t}})}notifyApplicationBeforeCachingSnapshot(){return dispatch("turbo:before-cache")}notifyApplicationBeforeRender(e,t){return dispatch("turbo:before-render",{detail:{newBody:e,resume:t},cancelable:!0})}notifyApplicationAfterRender(){return dispatch("turbo:render")}notifyApplicationAfterPageLoad(e={}){return clearBusyState(document.documentElement),dispatch("turbo:load",{detail:{url:this.location.href,timing:e}})}notifyApplicationAfterVisitingSamePageLocation(e,t){dispatchEvent(new HashChangeEvent("hashchange",{oldURL:e.toString(),newURL:t.toString()}))}notifyApplicationAfterFrameLoad(e){return dispatch("turbo:frame-load",{target:e})}notifyApplicationAfterFrameRender(e,t){return dispatch("turbo:frame-render",{detail:{fetchResponse:e},target:t,cancelable:!0})}elementDriveEnabled(e){e=null==e?void 0:e.closest("[data-turbo]");return this.drive?!e||"false"!=e.getAttribute("data-turbo"):!!e&&"true"==e.getAttribute("data-turbo")}getActionForLink(e){e=e.getAttribute("data-turbo-action");return isAction(e)?e:"advance"}getTargetFrameForLink(e){var t=e.getAttribute("data-turbo-frame");return t||((t=e.closest("turbo-frame"))?t.id:void 0)}get snapshot(){return this.view.snapshot}}function extendURLWithDeprecatedProperties(e){Object.defineProperties(e,deprecatedLocationPropertyDescriptors)}const deprecatedLocationPropertyDescriptors={absoluteURL:{get(){return this.toString()}}},session=new Session,navigator$1=session["navigator"];function start(){session.start()}function registerAdapter(e){session.registerAdapter(e)}function visit(e,t){session.visit(e,t)}function connectStreamSource(e){session.connectStreamSource(e)}function disconnectStreamSource(e){session.disconnectStreamSource(e)}function renderStreamMessage(e){session.renderStreamMessage(e)}function clearCache(){session.clearCache()}function setProgressBarDelay(e){session.setProgressBarDelay(e)}function setConfirmMethod(e){FormSubmission.confirmMethod=e}var Turbo=Object.freeze({__proto__:null,navigator:navigator$1,session:session,PageRenderer:PageRenderer,PageSnapshot:PageSnapshot,start:start,registerAdapter:registerAdapter,visit:visit,connectStreamSource:connectStreamSource,disconnectStreamSource:disconnectStreamSource,renderStreamMessage:renderStreamMessage,clearCache:clearCache,setProgressBarDelay:setProgressBarDelay,setConfirmMethod:setConfirmMethod});class FrameController{constructor(e){this.fetchResponseLoaded=e=>{},this.currentFetchRequest=null,this.resolveVisitPromise=()=>{},this.connected=!1,this.hasBeenLoaded=!1,this.settingSourceURL=!1,this.element=e,this.view=new FrameView(this,this.element),this.appearanceObserver=new AppearanceObserver(this,this.element),this.linkInterceptor=new LinkInterceptor(this,this.element),this.formInterceptor=new FormInterceptor(this,this.element)}connect(){this.connected||(this.connected=!0,this.reloadable=!1,this.loadingStyle==FrameLoadingStyle.lazy&&this.appearanceObserver.start(),this.linkInterceptor.start(),this.formInterceptor.start(),this.sourceURLChanged())}disconnect(){this.connected&&(this.connected=!1,this.appearanceObserver.stop(),this.linkInterceptor.stop(),this.formInterceptor.stop())}disabledChanged(){this.loadingStyle==FrameLoadingStyle.eager&&this.loadSourceURL()}sourceURLChanged(){this.loadingStyle!=FrameLoadingStyle.eager&&!this.hasBeenLoaded||this.loadSourceURL()}loadingStyleChanged(){this.loadingStyle==FrameLoadingStyle.lazy?this.appearanceObserver.start():(this.appearanceObserver.stop(),this.loadSourceURL())}async loadSourceURL(){if(!this.settingSourceURL&&this.enabled&&this.isActive&&(this.reloadable||this.sourceURL!=this.currentURL)){var t=this.currentURL;if(this.currentURL=this.sourceURL,this.sourceURL)try{this.element.loaded=this.visit(expandURL(this.sourceURL)),this.appearanceObserver.stop(),await this.element.loaded,this.hasBeenLoaded=!0}catch(e){throw this.currentURL=t,e}}}async loadResponse(e){(e.redirected||e.succeeded&&e.isHTML)&&(this.sourceURL=e.response.url);try{var t,s,r,i=await e.responseHTML;i&&(t=parseHTMLDocument(i)["body"],s=new Snapshot(await this.extractForeignFrameElement(t)),r=new FrameRenderer(this.view.snapshot,s,!1,!1),this.view.renderPromise&&await this.view.renderPromise,await this.view.render(r),session.frameRendered(e,this.element),session.frameLoaded(this.element),this.fetchResponseLoaded(e))}catch(e){console.error(e),this.view.invalidate()}finally{this.fetchResponseLoaded=()=>{}}}elementAppearedInViewport(e){this.loadSourceURL()}shouldInterceptLinkClick(e,t){return!e.hasAttribute("data-turbo-method")&&this.shouldInterceptNavigation(e)}linkClickIntercepted(e,t){this.reloadable=!0,this.navigateFrame(e,t)}shouldInterceptFormSubmission(e,t){return this.shouldInterceptNavigation(e,t)}formSubmissionIntercepted(e,t){this.formSubmission&&this.formSubmission.stop(),this.reloadable=!1,this.formSubmission=new FormSubmission(this,e,t);e=this.formSubmission.fetchRequest;this.prepareHeadersForRequest(e.headers,e),this.formSubmission.start()}prepareHeadersForRequest(e,t){e["Turbo-Frame"]=this.id}requestStarted(e){markAsBusy(this.element)}requestPreventedHandlingResponse(e,t){this.resolveVisitPromise()}async requestSucceededWithResponse(e,t){await this.loadResponse(t),this.resolveVisitPromise()}requestFailedWithResponse(e,t){console.error(t),this.resolveVisitPromise()}requestErrored(e,t){console.error(t),this.resolveVisitPromise()}requestFinished(e){clearBusyState(this.element)}formSubmissionStarted({formElement:e}){markAsBusy(e,this.findFrameElement(e))}formSubmissionSucceededWithResponse(e,t){var s=this.findFrameElement(e.formElement,e.submitter);this.proposeVisitIfNavigatedWithAction(s,e.formElement,e.submitter),s.delegate.loadResponse(t)}formSubmissionFailedWithResponse(e,t){this.element.delegate.loadResponse(t)}formSubmissionErrored(e,t){console.error(t)}formSubmissionFinished({formElement:e}){clearBusyState(e,this.findFrameElement(e))}allowsImmediateRender(e,t){return!0}viewRenderedSnapshot(e,t){}viewInvalidated(){}async visit(e){const t=new FetchRequest(this,FetchMethod.get,e,new URLSearchParams,this.element);return null!=(e=this.currentFetchRequest)&&e.cancel(),this.currentFetchRequest=t,new Promise(e=>{this.resolveVisitPromise=()=>{this.resolveVisitPromise=()=>{},this.currentFetchRequest=null,e()},t.perform()})}navigateFrame(e,t,s){var r=this.findFrameElement(e,s);this.proposeVisitIfNavigatedWithAction(r,e,s),r.setAttribute("reloadable",""),r.src=t}proposeVisitIfNavigatedWithAction(s,e,t){const r=getAttribute("data-turbo-action",t,e,s);if(isAction(r)){const i=new SnapshotSubstitution(s)["visitCachedSnapshot"];s.delegate.fetchResponseLoaded=e=>{var t;s.src&&({statusCode:e,redirected:t}=e,e={statusCode:e,redirected:t,responseHTML:s.ownerDocument.documentElement.outerHTML},session.visit(s.src,{action:r,response:e,visitCachedSnapshot:i,willRender:!1}))}}}findFrameElement(e,t){return null!=(t=getFrameElementById(getAttribute("data-turbo-frame",t,e)||this.element.getAttribute("target")))?t:this.element}async extractForeignFrameElement(e){var t,s=CSS.escape(this.id);try{if(t=activateElement(e.querySelector("turbo-frame#"+s),this.currentURL))return t;if(t=activateElement(e.querySelector(`turbo-frame[src][recurse~=${s}]`),this.currentURL))return await t.loaded,await this.extractForeignFrameElement(t);console.error(`Response has no matching element`)}catch(e){console.error(e)}return new FrameElement}formActionIsVisitable(e,t){return locationIsVisitable(expandURL(getAction(e,t)),this.rootLocation)}shouldInterceptNavigation(e,t){var s=getAttribute("data-turbo-frame",t,e)||this.element.getAttribute("target");if(e instanceof HTMLFormElement&&!this.formActionIsVisitable(e,t))return!1;if(!this.enabled||"_top"==s)return!1;if(s){s=getFrameElementById(s);if(s)return!s.disabled}return!(!session.elementDriveEnabled(e)||t&&!session.elementDriveEnabled(t))}get id(){return this.element.id}get enabled(){return!this.element.disabled}get sourceURL(){if(this.element.src)return this.element.src}get reloadable(){return this.findFrameElement(this.element).hasAttribute("reloadable")}set reloadable(e){var t=this.findFrameElement(this.element);e?t.setAttribute("reloadable",""):t.removeAttribute("reloadable")}set sourceURL(e){this.settingSourceURL=!0,this.element.src=null!=e?e:null,this.currentURL=this.element.src,this.settingSourceURL=!1}get loadingStyle(){return this.element.loading}get isLoading(){return void 0!==this.formSubmission||void 0!==this.resolveVisitPromise()}get isActive(){return this.element.isActive&&this.connected}get rootLocation(){var e=this.element.ownerDocument.querySelector('meta[name="turbo-root"]');return expandURL(null!=(e=null==e?void 0:e.content)?e:"/")}}class SnapshotSubstitution{constructor(e){this.visitCachedSnapshot=({element:e})=>{var{id:t,clone:s}=this;null!=(e=e.querySelector("#"+t))&&e.replaceWith(s)},this.clone=e.cloneNode(!0),this.id=e.id}}function getFrameElementById(e){if(null!=e){e=document.getElementById(e);if(e instanceof FrameElement)return e}}function activateElement(e,t){if(e){var s=e.getAttribute("src");if(null!=s&&null!=t&&urlsAreEqual(s,t))throw new Error(`Matching element has a source URL which references itself`);if((e=e.ownerDocument!==document?document.importNode(e,!0):e)instanceof FrameElement)return e.connectedCallback(),e.disconnectedCallback(),e}}const StreamActions={after(){this.targetElements.forEach(e=>{var t;return null==(t=e.parentElement)?void 0:t.insertBefore(this.templateContent,e.nextSibling)})},append(){this.removeDuplicateTargetChildren(),this.targetElements.forEach(e=>e.append(this.templateContent))},before(){this.targetElements.forEach(e=>{var t;return null==(t=e.parentElement)?void 0:t.insertBefore(this.templateContent,e)})},prepend(){this.removeDuplicateTargetChildren(),this.targetElements.forEach(e=>e.prepend(this.templateContent))},remove(){this.targetElements.forEach(e=>e.remove())},replace(){this.targetElements.forEach(e=>e.replaceWith(this.templateContent))},update(){this.targetElements.forEach(e=>{e.innerHTML="",e.append(this.templateContent)})}};class StreamElement extends HTMLElement{async connectedCallback(){try{await this.render()}catch(e){console.error(e)}finally{this.disconnect()}}async render(){var e;return null!=(e=this.renderPromise)?e:this.renderPromise=(async()=>{this.dispatchEvent(this.beforeRenderEvent)&&(await nextAnimationFrame(),this.performAction())})()}disconnect(){try{this.remove()}catch(e){}}removeDuplicateTargetChildren(){this.duplicateChildren.forEach(e=>e.remove())}get duplicateChildren(){var e,t=this.targetElements.flatMap(e=>[...e.children]).filter(e=>!!e.id);const s=[...null==(e=this.templateContent)?void 0:e.children].filter(e=>!!e.id).map(e=>e.id);return t.filter(e=>s.includes(e.id))}get performAction(){if(this.action){var e=StreamActions[this.action];if(e)return e;this.raise("unknown action")}this.raise("action attribute is missing")}get targetElements(){return this.target?this.targetElementsById:this.targets?this.targetElementsByQuery:void this.raise("target or targets attribute is missing")}get templateContent(){return this.templateElement.content.cloneNode(!0)}get templateElement(){if(this.firstElementChild instanceof HTMLTemplateElement)return this.firstElementChild;this.raise("first child element must be a