diff --git a/searx/static/themes/simple/js/keyboard.min.js b/searx/static/themes/simple/js/keyboard.min.js index 2d4fc2963..122af71d5 100644 --- a/searx/static/themes/simple/js/keyboard.min.js +++ b/searx/static/themes/simple/js/keyboard.min.js @@ -1,2 +1,2 @@ -import{a as e,i as t,n,o as r}from"./searxng.core.min.js";var i={Escape:{key:`ESC`,fun:e=>f(e),des:`remove focus from the focused input`,cat:`Control`},c:{key:`c`,fun:()=>S(),des:`copy url of the selected result to the clipboard`,cat:`Results`},h:{key:`h`,fun:()=>x(o),des:`toggle help window`,cat:`Other`},i:{key:`i`,fun:()=>v(),des:`focus on the search input`,cat:`Control`},n:{key:`n`,fun:()=>m(),des:`go to next page`,cat:`Results`},o:{key:`o`,fun:()=>y(!1),des:`open search result`,cat:`Results`},p:{key:`p`,fun:()=>h(),des:`go to previous page`,cat:`Results`},r:{key:`r`,fun:()=>d(),des:`reload page from the server`,cat:`Control`},t:{key:`t`,fun:()=>y(!0),des:`open the result in a new tab`,cat:`Results`}},a={default:{ArrowLeft:{key:`←`,fun:()=>u(`up`)(),des:`select previous search result`,cat:`Results`},ArrowRight:{key:`→`,fun:()=>u(`down`)(),des:`select next search result`,cat:`Results`},...i},vim:{b:{key:`b`,fun:()=>g(-window.innerHeight),des:`scroll one page up`,cat:`Navigation`},d:{key:`d`,fun:()=>g(window.innerHeight/2),des:`scroll half a page down`,cat:`Navigation`},f:{key:`f`,fun:()=>g(window.innerHeight),des:`scroll one page down`,cat:`Navigation`},g:{key:`g`,fun:()=>_(-document.body.scrollHeight,`top`),des:`scroll to the top of the page`,cat:`Navigation`},j:{key:`j`,fun:()=>u(`down`)(),des:`select next search result`,cat:`Results`},k:{key:`k`,fun:()=>u(`up`)(),des:`select previous search result`,cat:`Results`},u:{key:`u`,fun:()=>g(-window.innerHeight/2),des:`scroll half a page up`,cat:`Navigation`},v:{key:`v`,fun:()=>_(document.body.scrollHeight,`bottom`),des:`scroll to the bottom of the page`,cat:`Navigation`},y:{key:`y`,fun:()=>S(),des:`copy url of the selected result to the clipboard`,cat:`Results`},...i}},o=r.hotkeys&&r.hotkeys in a?a[r.hotkeys]:a.default,s=e=>(e?.closest(`.detail, .result`))?.classList.contains(`detail`)??!1,c=e=>e?.closest(`.result`)??void 0,l=e=>e?.classList.contains(`result-images`)??!1,u=t=>(n,r)=>{let i=t,a=document.querySelector(`.result[data-vim-selected]`);if(!a){if(a=document.querySelector(`.result`),!a)return;(t===`down`||t===`up`)&&(i=a)}let o=Array.from(document.querySelectorAll(`.result`)),s;if(typeof i!=`string`)s=i;else switch(i){case`visible`:{let e=document.documentElement.scrollTop||document.body.scrollTop,t=e+document.documentElement.clientHeight;for(let n of o){let r=n.offsetTop;if(r+n.clientHeight<=t&&r>e){s=n;break}}break}case`down`:s=o[o.indexOf(a)+1]||a;break;case`up`:s=o[o.indexOf(a)-1]||a;break;case`bottom`:s=o.at(-1);break;case`top`:default:s=o[0]}s&&(a.removeAttribute(`data-vim-selected`),s.setAttribute(`data-vim-selected`,`true`),r||(s.querySelector(`h3 a`)||s.querySelector(`a`))?.focus(),n||e.scrollPageToSelected?.())},d=()=>{document.location.reload()},f=t=>{let n=t.target?.tagName?.toLowerCase();document.activeElement&&(n===`input`||n===`select`||n===`textarea`)?document.activeElement.blur():e.closeDetail?.()},p=e=>{let t=document.querySelector(e);t&&t.click()},m=()=>{p(`nav#pagination .next_page button[type="submit"]`)},h=()=>{p(`nav#pagination .previous_page button[type="submit"]`)};e.scrollPageToSelected=()=>{let e=document.querySelector(`.result[data-vim-selected]`);if(!e)return;let t=document.documentElement.scrollTop||document.body.scrollTop,n=document.documentElement.clientHeight,r=e.offsetTop,i=r+e.clientHeight;if(!e.previousElementSibling&&ir-120?window.scroll(window.scrollX,r-120):t+n{window.scrollBy(0,e),u(`visible`)()},_=(e,t)=>{window.scrollTo(0,e),u(t)()},v=()=>{window.scrollTo(0,0);let e=document.querySelector(`#q`);if(e&&(e.focus(),e.setSelectionRange)){let t=e.value.length;e.setSelectionRange(t,t)}},y=e=>{let t=document.querySelector(`.result[data-vim-selected] h3 a`);if(t||=document.querySelector(`.result[data-vim-selected] > a`),!t)return;let n=t.getAttribute(`href`);n&&(e?window.open(n):window.location.href=n)},b=(e,t)=>{let n={};for(let e of Object.values(t)){let t=e.cat;n[t]??=[],n[t].push(e)}let r=Object.keys(n).sort((e,t)=>(n[t]?.length??0)-(n[e]?.length??0)),i=`×`;i+=`

How to navigate SearXNG with hotkeys

`,i+=``;for(let[e,t]of r.entries()){let a=n[t];if(!a||a.length===0)continue;let o=e%2==0,s=e===r.length-1;o&&(i+=``),i+=``,(!o||s)&&(i+=``)}i+=`
`,i+=`

${t}

`,i+=`
    `;for(let e of a)i+=`
  • ${e.key} ${e.des}
  • `;i+=`
`,i+=`
`,e.innerHTML=i},x=e=>{let t=document.querySelector(`#vim-hotkeys-help`);if(t)t.classList.toggle(`invisible`);else{t=Object.assign(document.createElement(`div`),{id:`vim-hotkeys-help`,className:`dialog-modal`}),b(t,e);let n=document.getElementsByTagName(`body`)[0];n&&n.appendChild(t)}},S=async()=>{let e=document.querySelector(`.result[data-vim-selected] h3 a`);n(e);let t=e.getAttribute(`href`);t&&await navigator.clipboard.writeText(t)};t(`click`,`.result`,function(t){if(!s(t.target)){u(this)(!0,!0);let n=c(t.target);n&&l(n)&&(t.preventDefault(),e.selectImage?.(n))}}),t(`focus`,`.result a`,t=>{if(!s(t.target)){let n=c(t.target);n&&!n.hasAttribute(`data-vim-selected`)&&u(n)(!0),n&&l(n)&&(t.preventDefault(),e.selectImage?.(n))}},{capture:!0}),t(`keydown`,document,e=>{if(Object.hasOwn(o,e.key)&&!e.ctrlKey&&!e.altKey&&!e.shiftKey&&!e.metaKey){let t=e.target?.tagName?.toLowerCase();e.key===`Escape`?o[e.key]?.fun(e):(e.target===document.body||t===`a`||t===`button`)&&(e.preventDefault(),o[e.key]?.fun(e))}}),e.selectNext=u(`down`),e.selectPrevious=u(`up`); +import{a as e,i as t,n,o as r}from"./searxng.core.min.js";var i={Escape:{key:`ESC`,fun:e=>f(e),des:`remove focus from the focused input`,cat:`Control`},c:{key:`c`,fun:()=>S(),des:`copy url of the selected result to the clipboard`,cat:`Results`},h:{key:`h`,fun:()=>x(o),des:`toggle help window`,cat:`Other`},i:{key:`i`,fun:()=>v(),des:`focus on the search input`,cat:`Control`},n:{key:`n`,fun:()=>m(),des:`go to next page`,cat:`Results`},o:{key:`o`,fun:()=>y(!1),des:`open search result`,cat:`Results`},p:{key:`p`,fun:()=>h(),des:`go to previous page`,cat:`Results`},r:{key:`r`,fun:()=>d(),des:`reload page from the server`,cat:`Control`},t:{key:`t`,fun:()=>y(!0),des:`open the result in a new tab`,cat:`Results`}},a={default:{ArrowLeft:{key:`←`,fun:()=>u(`up`)(),des:`select previous search result`,cat:`Results`},ArrowRight:{key:`→`,fun:()=>u(`down`)(),des:`select next search result`,cat:`Results`},...i},vim:{b:{key:`b`,fun:()=>g(-window.innerHeight),des:`scroll one page up`,cat:`Navigation`},d:{key:`d`,fun:()=>g(window.innerHeight/2),des:`scroll half a page down`,cat:`Navigation`},f:{key:`f`,fun:()=>g(window.innerHeight),des:`scroll one page down`,cat:`Navigation`},g:{key:`g`,fun:()=>_(-document.body.scrollHeight,`top`),des:`scroll to the top of the page`,cat:`Navigation`},j:{key:`j`,fun:()=>u(`down`)(),des:`select next search result`,cat:`Results`},k:{key:`k`,fun:()=>u(`up`)(),des:`select previous search result`,cat:`Results`},u:{key:`u`,fun:()=>g(-window.innerHeight/2),des:`scroll half a page up`,cat:`Navigation`},v:{key:`v`,fun:()=>_(document.body.scrollHeight,`bottom`),des:`scroll to the bottom of the page`,cat:`Navigation`},y:{key:`y`,fun:()=>S(),des:`copy url of the selected result to the clipboard`,cat:`Results`},...i}},o=r.hotkeys&&r.hotkeys in a?a[r.hotkeys]:a.default,s=e=>(e?.closest(`.detail, .result`))?.classList.contains(`detail`)??!1,c=e=>e?.closest(`.result`)??void 0,l=e=>e?.classList.contains(`result-images`)??!1,u=t=>(n,r)=>{let i=t,a=document.querySelector(`.result[data-vim-selected]`);if(!a){if(a=document.querySelector(`.result`),!a)return;(t===`down`||t===`up`)&&(i=a)}let o=Array.from(document.querySelectorAll(`.result`)),s;if(typeof i!=`string`)s=i;else switch(i){case`visible`:{let e=document.documentElement.scrollTop||document.body.scrollTop,t=e+document.documentElement.clientHeight;for(let n of o){let r=n.offsetTop;if(r+n.clientHeight<=t&&r>e){s=n;break}}break}case`down`:s=o[o.indexOf(a)+1]||a;break;case`up`:s=o[o.indexOf(a)-1]||a;break;case`bottom`:s=o.at(-1);break;case`top`:default:s=o[0]}s&&(a.removeAttribute(`data-vim-selected`),s.setAttribute(`data-vim-selected`,`true`),r||(s.querySelector(`h3 a`)||s.querySelector(`a`))?.focus(),n||e.scrollPageToSelected?.())},d=()=>{document.location.reload()},f=t=>{let n=t.target?.tagName?.toLowerCase();document.activeElement&&(n===`input`||n===`select`||n===`textarea`)?document.activeElement.blur():e.closeDetail?.()},p=e=>{let t=document.querySelector(e);t&&t.click()},m=()=>{p(`nav#pagination .next_page button[type="submit"]`)},h=()=>{p(`nav#pagination .previous_page button[type="submit"]`)};e.scrollPageToSelected=()=>{let e=document.querySelector(`.result[data-vim-selected]`);if(!e)return;let t=document.documentElement.scrollTop||document.body.scrollTop,n=document.documentElement.clientHeight,r=e.offsetTop,i=r+e.clientHeight;if(!e.previousElementSibling&&ir-120?window.scroll(window.scrollX,r-120):t+n{window.scrollBy(0,e),u(`visible`)()},_=(e,t)=>{window.scrollTo(0,e),u(t)()},v=()=>{window.scrollTo(0,0);let e=document.querySelector(`#q`);if(e&&(e.focus(),e.setSelectionRange)){let t=e.value.length;e.setSelectionRange(t,t)}},y=e=>{let t=document.querySelector(`.result[data-vim-selected] h3 a`);if(t||=document.querySelector(`.result[data-vim-selected] > a`),!t)return;let n=t.getAttribute(`href`);n&&(e?window.open(n):window.location.href=n)},b=(e,t)=>{let n={};for(let e of Object.values(t)){let t=e.cat;n[t]??=[],n[t].push(e)}let r=Object.keys(n).sort((e,t)=>(n[t]?.length??0)-(n[e]?.length??0)),i=`×`;i+=`

How to navigate SearXNG with hotkeys

`,i+=``;for(let[e,t]of r.entries()){let a=n[t];if(!a||a.length===0)continue;let o=e%2==0,s=e===r.length-1;o&&(i+=``),i+=``,(!o||s)&&(i+=``)}i+=`
`,i+=`

${t}

`,i+=`
    `;for(let e of a)i+=`
  • ${e.key} ${e.des}
  • `;i+=`
`,i+=`
`,e.innerHTML=i},x=e=>{let t=document.querySelector(`#vim-hotkeys-help`);if(t)t.classList.toggle(`invisible`);else{t=Object.assign(document.createElement(`div`),{id:`vim-hotkeys-help`,className:`dialog-modal`}),b(t,e);let n=document.getElementsByTagName(`body`)[0];n&&n.appendChild(t)}},S=async()=>{let e=document.querySelector(`.result[data-vim-selected]`);if(!e)return;let t=e.querySelector(`a`);n(t);let r=t.getAttribute(`href`);if(r)if(window.isSecureContext)await navigator.clipboard.writeText(r);else{let e=window.getSelection();if(e){let n=document.createElement(`span`);n.textContent=r,t.appendChild(n);let i=document.createRange();i.selectNodeContents(n),e.removeAllRanges(),e.addRange(i),document.execCommand(`copy`),n.remove()}}};t(`click`,`.result`,function(t){if(!s(t.target)){u(this)(!0,!0);let n=c(t.target);n&&l(n)&&(t.preventDefault(),e.selectImage?.(n))}}),t(`focus`,`.result a`,t=>{if(!s(t.target)){let n=c(t.target);n&&!n.hasAttribute(`data-vim-selected`)&&u(n)(!0),n&&l(n)&&(t.preventDefault(),e.selectImage?.(n))}},{capture:!0}),t(`keydown`,document,e=>{if(Object.hasOwn(o,e.key)&&!e.ctrlKey&&!e.altKey&&!e.shiftKey&&!e.metaKey){let t=e.target?.tagName?.toLowerCase();e.key===`Escape`?o[e.key]?.fun(e):(e.target===document.body||t===`a`||t===`button`)&&(e.preventDefault(),o[e.key]?.fun(e))}}),e.selectNext=u(`down`),e.selectPrevious=u(`up`); //# sourceMappingURL=keyboard.min.js.map \ No newline at end of file diff --git a/searx/static/themes/simple/js/keyboard.min.js.map b/searx/static/themes/simple/js/keyboard.min.js.map index 1b102b872..5b33efb4d 100644 --- a/searx/static/themes/simple/js/keyboard.min.js.map +++ b/searx/static/themes/simple/js/keyboard.min.js.map @@ -1 +1 @@ -{"version":3,"file":"keyboard.min.js","names":["baseKeyBinding: Record","keyBindingLayouts: Record>","keyBindings: Record","next: HTMLElement | undefined","categories: Record"],"sources":["../../../../../client/simple/src/js/main/keyboard.ts"],"sourcesContent":["// SPDX-License-Identifier: AGPL-3.0-or-later\n\nimport { assertElement, listen, mutable, settings } from \"../core/toolkit.ts\";\n\nexport type KeyBindingLayout = \"default\" | \"vim\";\n\ntype KeyBinding = {\n key: string;\n fun: (event: KeyboardEvent) => void;\n des: string;\n cat: string;\n};\n\ntype HighlightResultElement = \"down\" | \"up\" | \"visible\" | \"bottom\" | \"top\";\n\n/* common base for layouts */\nconst baseKeyBinding: Record = {\n Escape: {\n key: \"ESC\",\n fun: (event: KeyboardEvent) => removeFocus(event),\n des: \"remove focus from the focused input\",\n cat: \"Control\"\n },\n c: {\n key: \"c\",\n fun: () => copyURLToClipboard(),\n des: \"copy url of the selected result to the clipboard\",\n cat: \"Results\"\n },\n h: {\n key: \"h\",\n fun: () => toggleHelp(keyBindings),\n des: \"toggle help window\",\n cat: \"Other\"\n },\n i: {\n key: \"i\",\n fun: () => searchInputFocus(),\n des: \"focus on the search input\",\n cat: \"Control\"\n },\n n: {\n key: \"n\",\n fun: () => GoToNextPage(),\n des: \"go to next page\",\n cat: \"Results\"\n },\n o: {\n key: \"o\",\n fun: () => openResult(false),\n des: \"open search result\",\n cat: \"Results\"\n },\n p: {\n key: \"p\",\n fun: () => GoToPreviousPage(),\n des: \"go to previous page\",\n cat: \"Results\"\n },\n r: {\n key: \"r\",\n fun: () => reloadPage(),\n des: \"reload page from the server\",\n cat: \"Control\"\n },\n t: {\n key: \"t\",\n fun: () => openResult(true),\n des: \"open the result in a new tab\",\n cat: \"Results\"\n }\n};\n\nconst keyBindingLayouts: Record> = {\n // SearXNG layout\n default: {\n ArrowLeft: {\n key: \"←\",\n fun: () => highlightResult(\"up\")(),\n des: \"select previous search result\",\n cat: \"Results\"\n },\n ArrowRight: {\n key: \"→\",\n fun: () => highlightResult(\"down\")(),\n des: \"select next search result\",\n cat: \"Results\"\n },\n ...baseKeyBinding\n },\n\n // Vim-like keyboard layout\n vim: {\n b: {\n key: \"b\",\n fun: () => scrollPage(-window.innerHeight),\n des: \"scroll one page up\",\n cat: \"Navigation\"\n },\n d: {\n key: \"d\",\n fun: () => scrollPage(window.innerHeight / 2),\n des: \"scroll half a page down\",\n cat: \"Navigation\"\n },\n f: {\n key: \"f\",\n fun: () => scrollPage(window.innerHeight),\n des: \"scroll one page down\",\n cat: \"Navigation\"\n },\n g: {\n key: \"g\",\n fun: () => scrollPageTo(-document.body.scrollHeight, \"top\"),\n des: \"scroll to the top of the page\",\n cat: \"Navigation\"\n },\n j: {\n key: \"j\",\n fun: () => highlightResult(\"down\")(),\n des: \"select next search result\",\n cat: \"Results\"\n },\n k: {\n key: \"k\",\n fun: () => highlightResult(\"up\")(),\n des: \"select previous search result\",\n cat: \"Results\"\n },\n u: {\n key: \"u\",\n fun: () => scrollPage(-window.innerHeight / 2),\n des: \"scroll half a page up\",\n cat: \"Navigation\"\n },\n v: {\n key: \"v\",\n fun: () => scrollPageTo(document.body.scrollHeight, \"bottom\"),\n des: \"scroll to the bottom of the page\",\n cat: \"Navigation\"\n },\n y: {\n key: \"y\",\n fun: () => copyURLToClipboard(),\n des: \"copy url of the selected result to the clipboard\",\n cat: \"Results\"\n },\n ...baseKeyBinding\n }\n};\n\nconst keyBindings: Record =\n settings.hotkeys && settings.hotkeys in keyBindingLayouts\n ? keyBindingLayouts[settings.hotkeys]\n : keyBindingLayouts.default;\n\nconst isElementInDetail = (element?: HTMLElement): boolean => {\n const ancestor = element?.closest(\".detail, .result\");\n return ancestor?.classList.contains(\"detail\") ?? false;\n};\n\nconst getResultElement = (element?: HTMLElement): HTMLElement | undefined => {\n return element?.closest(\".result\") ?? undefined;\n};\n\nconst isImageResult = (resultElement?: HTMLElement): boolean => {\n return resultElement?.classList.contains(\"result-images\") ?? false;\n};\n\nconst highlightResult =\n (which: HighlightResultElement | HTMLElement) =>\n (noScroll?: boolean, keepFocus?: boolean): void => {\n let effectiveWhich = which;\n let current = document.querySelector(\".result[data-vim-selected]\");\n if (!current) {\n // no selection : choose the first one\n current = document.querySelector(\".result\");\n if (!current) {\n // no first one : there are no results\n return;\n }\n // replace up/down actions by selecting first one\n if (which === \"down\" || which === \"up\") {\n effectiveWhich = current;\n }\n }\n\n const results = Array.from(document.querySelectorAll(\".result\"));\n\n let next: HTMLElement | undefined;\n\n if (typeof effectiveWhich !== \"string\") {\n next = effectiveWhich;\n } else {\n switch (effectiveWhich) {\n case \"visible\": {\n const top = document.documentElement.scrollTop || document.body.scrollTop;\n const bot = top + document.documentElement.clientHeight;\n\n for (const element of results) {\n const etop = element.offsetTop;\n const ebot = etop + element.clientHeight;\n if (ebot <= bot && etop > top) {\n next = element;\n break;\n }\n }\n break;\n }\n case \"down\":\n next = results[results.indexOf(current) + 1] || current;\n break;\n case \"up\":\n next = results[results.indexOf(current) - 1] || current;\n break;\n case \"bottom\":\n next = results.at(-1);\n break;\n // biome-ignore lint/complexity/noUselessSwitchCase: fallthrough is intended\n case \"top\":\n default:\n next = results[0];\n }\n }\n\n if (next) {\n current.removeAttribute(\"data-vim-selected\");\n next.setAttribute(\"data-vim-selected\", \"true\");\n\n if (!keepFocus) {\n const link = next.querySelector(\"h3 a\") || next.querySelector(\"a\");\n link?.focus();\n }\n\n if (!noScroll) {\n mutable.scrollPageToSelected?.();\n }\n }\n };\n\nconst reloadPage = (): void => {\n document.location.reload();\n};\n\nconst removeFocus = (event: KeyboardEvent): void => {\n const target = event.target as HTMLElement;\n const tagName = target?.tagName?.toLowerCase();\n\n if (document.activeElement && (tagName === \"input\" || tagName === \"select\" || tagName === \"textarea\")) {\n (document.activeElement as HTMLElement).blur();\n } else {\n mutable.closeDetail?.();\n }\n};\n\nconst pageButtonClick = (css_selector: string): void => {\n const button = document.querySelector(css_selector);\n if (button) {\n button.click();\n }\n};\n\nconst GoToNextPage = (): void => {\n pageButtonClick('nav#pagination .next_page button[type=\"submit\"]');\n};\n\nconst GoToPreviousPage = (): void => {\n pageButtonClick('nav#pagination .previous_page button[type=\"submit\"]');\n};\n\nmutable.scrollPageToSelected = (): void => {\n const sel = document.querySelector(\".result[data-vim-selected]\");\n if (!sel) return;\n\n const wtop = document.documentElement.scrollTop || document.body.scrollTop;\n const height = document.documentElement.clientHeight;\n const etop = sel.offsetTop;\n const ebot = etop + sel.clientHeight;\n const offset = 120;\n\n // first element ?\n if (!sel.previousElementSibling && ebot < height) {\n // set to the top of page if the first element\n // is fully included in the viewport\n window.scroll(window.scrollX, 0);\n return;\n }\n\n if (wtop > etop - offset) {\n window.scroll(window.scrollX, etop - offset);\n } else {\n const wbot = wtop + height;\n if (wbot < ebot + offset) {\n window.scroll(window.scrollX, ebot - height + offset);\n }\n }\n};\n\nconst scrollPage = (amount: number): void => {\n window.scrollBy(0, amount);\n highlightResult(\"visible\")();\n};\n\nconst scrollPageTo = (position: number, nav: HighlightResultElement): void => {\n window.scrollTo(0, position);\n highlightResult(nav)();\n};\n\nconst searchInputFocus = (): void => {\n window.scrollTo(0, 0);\n\n const q = document.querySelector(\"#q\");\n if (q) {\n q.focus();\n\n if (q.setSelectionRange) {\n const len = q.value.length;\n\n q.setSelectionRange(len, len);\n }\n }\n};\n\nconst openResult = (newTab: boolean): void => {\n let link = document.querySelector(\".result[data-vim-selected] h3 a\");\n if (!link) {\n link = document.querySelector(\".result[data-vim-selected] > a\");\n }\n if (!link) return;\n\n const url = link.getAttribute(\"href\");\n if (url) {\n if (newTab) {\n window.open(url);\n } else {\n window.location.href = url;\n }\n }\n};\n\nconst initHelpContent = (divElement: HTMLElement, keyBindings: typeof baseKeyBinding): void => {\n const categories: Record = {};\n\n for (const binding of Object.values(keyBindings)) {\n const cat = binding.cat;\n categories[cat] ??= [];\n categories[cat].push(binding);\n }\n\n const sortedCategoryKeys = Object.keys(categories).sort(\n (a, b) => (categories[b]?.length ?? 0) - (categories[a]?.length ?? 0)\n );\n\n let html = '×';\n html += \"

How to navigate SearXNG with hotkeys

\";\n html += \"\";\n\n for (const [i, categoryKey] of sortedCategoryKeys.entries()) {\n const bindings = categories[categoryKey];\n if (!bindings || bindings.length === 0) continue;\n\n const isFirst = i % 2 === 0;\n const isLast = i === sortedCategoryKeys.length - 1;\n\n if (isFirst) {\n html += \"\";\n }\n\n html += \"\";\n\n if (!isFirst || isLast) {\n html += \"\";\n }\n }\n\n html += \"
\";\n html += `

${categoryKey}

`;\n html += '
    ';\n\n for (const binding of bindings) {\n html += `
  • ${binding.key} ${binding.des}
  • `;\n }\n\n html += \"
\";\n html += \"
\";\n\n divElement.innerHTML = html;\n};\n\nconst toggleHelp = (keyBindings: typeof baseKeyBinding): void => {\n let helpPanel = document.querySelector(\"#vim-hotkeys-help\");\n if (helpPanel) {\n // toggle hidden\n helpPanel.classList.toggle(\"invisible\");\n } else {\n // first call\n helpPanel = Object.assign(document.createElement(\"div\"), {\n id: \"vim-hotkeys-help\",\n className: \"dialog-modal\"\n });\n initHelpContent(helpPanel, keyBindings);\n const body = document.getElementsByTagName(\"body\")[0];\n if (body) {\n body.appendChild(helpPanel);\n }\n }\n};\n\nconst copyURLToClipboard = async (): Promise => {\n const currentUrlElement = document.querySelector(\".result[data-vim-selected] h3 a\");\n assertElement(currentUrlElement);\n\n const url = currentUrlElement.getAttribute(\"href\");\n if (url) {\n await navigator.clipboard.writeText(url);\n }\n};\n\nlisten(\"click\", \".result\", function (this: HTMLElement, event: PointerEvent) {\n if (!isElementInDetail(event.target as HTMLElement)) {\n highlightResult(this)(true, true);\n\n const resultElement = getResultElement(event.target as HTMLElement);\n\n if (resultElement && isImageResult(resultElement)) {\n event.preventDefault();\n mutable.selectImage?.(resultElement);\n }\n }\n});\n\n// FIXME: Focus might also trigger Pointer event ^^^\nlisten(\n \"focus\",\n \".result a\",\n (event: FocusEvent) => {\n if (!isElementInDetail(event.target as HTMLElement)) {\n const resultElement = getResultElement(event.target as HTMLElement);\n\n if (resultElement && !resultElement.hasAttribute(\"data-vim-selected\")) {\n highlightResult(resultElement)(true);\n }\n\n if (resultElement && isImageResult(resultElement)) {\n event.preventDefault();\n mutable.selectImage?.(resultElement);\n }\n }\n },\n { capture: true }\n);\n\nlisten(\"keydown\", document, (event: KeyboardEvent) => {\n // check for modifiers so we don't break browser's hotkeys\n if (Object.hasOwn(keyBindings, event.key) && !event.ctrlKey && !event.altKey && !event.shiftKey && !event.metaKey) {\n const tagName = (event.target as HTMLElement)?.tagName?.toLowerCase();\n\n if (event.key === \"Escape\") {\n keyBindings[event.key]?.fun(event);\n } else if (event.target === document.body || tagName === \"a\" || tagName === \"button\") {\n event.preventDefault();\n keyBindings[event.key]?.fun(event);\n }\n }\n});\n\nmutable.selectNext = highlightResult(\"down\");\nmutable.selectPrevious = highlightResult(\"up\");\n"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"keyboard.min.js","names":["baseKeyBinding: Record","keyBindingLayouts: Record>","keyBindings: Record","next: HTMLElement | undefined","categories: Record"],"sources":["../../../../../client/simple/src/js/main/keyboard.ts"],"sourcesContent":["// SPDX-License-Identifier: AGPL-3.0-or-later\n\nimport { assertElement, listen, mutable, settings } from \"../core/toolkit.ts\";\n\nexport type KeyBindingLayout = \"default\" | \"vim\";\n\ntype KeyBinding = {\n key: string;\n fun: (event: KeyboardEvent) => void;\n des: string;\n cat: string;\n};\n\ntype HighlightResultElement = \"down\" | \"up\" | \"visible\" | \"bottom\" | \"top\";\n\n/* common base for layouts */\nconst baseKeyBinding: Record = {\n Escape: {\n key: \"ESC\",\n fun: (event: KeyboardEvent) => removeFocus(event),\n des: \"remove focus from the focused input\",\n cat: \"Control\"\n },\n c: {\n key: \"c\",\n fun: () => copyURLToClipboard(),\n des: \"copy url of the selected result to the clipboard\",\n cat: \"Results\"\n },\n h: {\n key: \"h\",\n fun: () => toggleHelp(keyBindings),\n des: \"toggle help window\",\n cat: \"Other\"\n },\n i: {\n key: \"i\",\n fun: () => searchInputFocus(),\n des: \"focus on the search input\",\n cat: \"Control\"\n },\n n: {\n key: \"n\",\n fun: () => GoToNextPage(),\n des: \"go to next page\",\n cat: \"Results\"\n },\n o: {\n key: \"o\",\n fun: () => openResult(false),\n des: \"open search result\",\n cat: \"Results\"\n },\n p: {\n key: \"p\",\n fun: () => GoToPreviousPage(),\n des: \"go to previous page\",\n cat: \"Results\"\n },\n r: {\n key: \"r\",\n fun: () => reloadPage(),\n des: \"reload page from the server\",\n cat: \"Control\"\n },\n t: {\n key: \"t\",\n fun: () => openResult(true),\n des: \"open the result in a new tab\",\n cat: \"Results\"\n }\n};\n\nconst keyBindingLayouts: Record> = {\n // SearXNG layout\n default: {\n ArrowLeft: {\n key: \"←\",\n fun: () => highlightResult(\"up\")(),\n des: \"select previous search result\",\n cat: \"Results\"\n },\n ArrowRight: {\n key: \"→\",\n fun: () => highlightResult(\"down\")(),\n des: \"select next search result\",\n cat: \"Results\"\n },\n ...baseKeyBinding\n },\n\n // Vim-like keyboard layout\n vim: {\n b: {\n key: \"b\",\n fun: () => scrollPage(-window.innerHeight),\n des: \"scroll one page up\",\n cat: \"Navigation\"\n },\n d: {\n key: \"d\",\n fun: () => scrollPage(window.innerHeight / 2),\n des: \"scroll half a page down\",\n cat: \"Navigation\"\n },\n f: {\n key: \"f\",\n fun: () => scrollPage(window.innerHeight),\n des: \"scroll one page down\",\n cat: \"Navigation\"\n },\n g: {\n key: \"g\",\n fun: () => scrollPageTo(-document.body.scrollHeight, \"top\"),\n des: \"scroll to the top of the page\",\n cat: \"Navigation\"\n },\n j: {\n key: \"j\",\n fun: () => highlightResult(\"down\")(),\n des: \"select next search result\",\n cat: \"Results\"\n },\n k: {\n key: \"k\",\n fun: () => highlightResult(\"up\")(),\n des: \"select previous search result\",\n cat: \"Results\"\n },\n u: {\n key: \"u\",\n fun: () => scrollPage(-window.innerHeight / 2),\n des: \"scroll half a page up\",\n cat: \"Navigation\"\n },\n v: {\n key: \"v\",\n fun: () => scrollPageTo(document.body.scrollHeight, \"bottom\"),\n des: \"scroll to the bottom of the page\",\n cat: \"Navigation\"\n },\n y: {\n key: \"y\",\n fun: () => copyURLToClipboard(),\n des: \"copy url of the selected result to the clipboard\",\n cat: \"Results\"\n },\n ...baseKeyBinding\n }\n};\n\nconst keyBindings: Record =\n settings.hotkeys && settings.hotkeys in keyBindingLayouts\n ? keyBindingLayouts[settings.hotkeys]\n : keyBindingLayouts.default;\n\nconst isElementInDetail = (element?: HTMLElement): boolean => {\n const ancestor = element?.closest(\".detail, .result\");\n return ancestor?.classList.contains(\"detail\") ?? false;\n};\n\nconst getResultElement = (element?: HTMLElement): HTMLElement | undefined => {\n return element?.closest(\".result\") ?? undefined;\n};\n\nconst isImageResult = (resultElement?: HTMLElement): boolean => {\n return resultElement?.classList.contains(\"result-images\") ?? false;\n};\n\nconst highlightResult =\n (which: HighlightResultElement | HTMLElement) =>\n (noScroll?: boolean, keepFocus?: boolean): void => {\n let effectiveWhich = which;\n let current = document.querySelector(\".result[data-vim-selected]\");\n if (!current) {\n // no selection : choose the first one\n current = document.querySelector(\".result\");\n if (!current) {\n // no first one : there are no results\n return;\n }\n // replace up/down actions by selecting first one\n if (which === \"down\" || which === \"up\") {\n effectiveWhich = current;\n }\n }\n\n const results = Array.from(document.querySelectorAll(\".result\"));\n\n let next: HTMLElement | undefined;\n\n if (typeof effectiveWhich !== \"string\") {\n next = effectiveWhich;\n } else {\n switch (effectiveWhich) {\n case \"visible\": {\n const top = document.documentElement.scrollTop || document.body.scrollTop;\n const bot = top + document.documentElement.clientHeight;\n\n for (const element of results) {\n const etop = element.offsetTop;\n const ebot = etop + element.clientHeight;\n if (ebot <= bot && etop > top) {\n next = element;\n break;\n }\n }\n break;\n }\n case \"down\":\n next = results[results.indexOf(current) + 1] || current;\n break;\n case \"up\":\n next = results[results.indexOf(current) - 1] || current;\n break;\n case \"bottom\":\n next = results.at(-1);\n break;\n // biome-ignore lint/complexity/noUselessSwitchCase: fallthrough is intended\n case \"top\":\n default:\n next = results[0];\n }\n }\n\n if (next) {\n current.removeAttribute(\"data-vim-selected\");\n next.setAttribute(\"data-vim-selected\", \"true\");\n\n if (!keepFocus) {\n const link = next.querySelector(\"h3 a\") || next.querySelector(\"a\");\n link?.focus();\n }\n\n if (!noScroll) {\n mutable.scrollPageToSelected?.();\n }\n }\n };\n\nconst reloadPage = (): void => {\n document.location.reload();\n};\n\nconst removeFocus = (event: KeyboardEvent): void => {\n const target = event.target as HTMLElement;\n const tagName = target?.tagName?.toLowerCase();\n\n if (document.activeElement && (tagName === \"input\" || tagName === \"select\" || tagName === \"textarea\")) {\n (document.activeElement as HTMLElement).blur();\n } else {\n mutable.closeDetail?.();\n }\n};\n\nconst pageButtonClick = (css_selector: string): void => {\n const button = document.querySelector(css_selector);\n if (button) {\n button.click();\n }\n};\n\nconst GoToNextPage = (): void => {\n pageButtonClick('nav#pagination .next_page button[type=\"submit\"]');\n};\n\nconst GoToPreviousPage = (): void => {\n pageButtonClick('nav#pagination .previous_page button[type=\"submit\"]');\n};\n\nmutable.scrollPageToSelected = (): void => {\n const sel = document.querySelector(\".result[data-vim-selected]\");\n if (!sel) return;\n\n const wtop = document.documentElement.scrollTop || document.body.scrollTop;\n const height = document.documentElement.clientHeight;\n const etop = sel.offsetTop;\n const ebot = etop + sel.clientHeight;\n const offset = 120;\n\n // first element ?\n if (!sel.previousElementSibling && ebot < height) {\n // set to the top of page if the first element\n // is fully included in the viewport\n window.scroll(window.scrollX, 0);\n return;\n }\n\n if (wtop > etop - offset) {\n window.scroll(window.scrollX, etop - offset);\n } else {\n const wbot = wtop + height;\n if (wbot < ebot + offset) {\n window.scroll(window.scrollX, ebot - height + offset);\n }\n }\n};\n\nconst scrollPage = (amount: number): void => {\n window.scrollBy(0, amount);\n highlightResult(\"visible\")();\n};\n\nconst scrollPageTo = (position: number, nav: HighlightResultElement): void => {\n window.scrollTo(0, position);\n highlightResult(nav)();\n};\n\nconst searchInputFocus = (): void => {\n window.scrollTo(0, 0);\n\n const q = document.querySelector(\"#q\");\n if (q) {\n q.focus();\n\n if (q.setSelectionRange) {\n const len = q.value.length;\n\n q.setSelectionRange(len, len);\n }\n }\n};\n\nconst openResult = (newTab: boolean): void => {\n let link = document.querySelector(\".result[data-vim-selected] h3 a\");\n if (!link) {\n link = document.querySelector(\".result[data-vim-selected] > a\");\n }\n if (!link) return;\n\n const url = link.getAttribute(\"href\");\n if (url) {\n if (newTab) {\n window.open(url);\n } else {\n window.location.href = url;\n }\n }\n};\n\nconst initHelpContent = (divElement: HTMLElement, keyBindings: typeof baseKeyBinding): void => {\n const categories: Record = {};\n\n for (const binding of Object.values(keyBindings)) {\n const cat = binding.cat;\n categories[cat] ??= [];\n categories[cat].push(binding);\n }\n\n const sortedCategoryKeys = Object.keys(categories).sort(\n (a, b) => (categories[b]?.length ?? 0) - (categories[a]?.length ?? 0)\n );\n\n let html = '×';\n html += \"

How to navigate SearXNG with hotkeys

\";\n html += \"\";\n\n for (const [i, categoryKey] of sortedCategoryKeys.entries()) {\n const bindings = categories[categoryKey];\n if (!bindings || bindings.length === 0) continue;\n\n const isFirst = i % 2 === 0;\n const isLast = i === sortedCategoryKeys.length - 1;\n\n if (isFirst) {\n html += \"\";\n }\n\n html += \"\";\n\n if (!isFirst || isLast) {\n html += \"\";\n }\n }\n\n html += \"
\";\n html += `

${categoryKey}

`;\n html += '
    ';\n\n for (const binding of bindings) {\n html += `
  • ${binding.key} ${binding.des}
  • `;\n }\n\n html += \"
\";\n html += \"
\";\n\n divElement.innerHTML = html;\n};\n\nconst toggleHelp = (keyBindings: typeof baseKeyBinding): void => {\n let helpPanel = document.querySelector(\"#vim-hotkeys-help\");\n if (helpPanel) {\n // toggle hidden\n helpPanel.classList.toggle(\"invisible\");\n } else {\n // first call\n helpPanel = Object.assign(document.createElement(\"div\"), {\n id: \"vim-hotkeys-help\",\n className: \"dialog-modal\"\n });\n initHelpContent(helpPanel, keyBindings);\n const body = document.getElementsByTagName(\"body\")[0];\n if (body) {\n body.appendChild(helpPanel);\n }\n }\n};\n\nconst copyURLToClipboard = async (): Promise => {\n const selectedResult = document.querySelector(\".result[data-vim-selected]\");\n if (!selectedResult) return;\n\n const resultAnchor = selectedResult.querySelector(\"a\");\n assertElement(resultAnchor);\n\n const url = resultAnchor.getAttribute(\"href\");\n if (url) {\n if (window.isSecureContext) {\n await navigator.clipboard.writeText(url);\n } else {\n const selection = window.getSelection();\n if (selection) {\n const node = document.createElement(\"span\");\n node.textContent = url;\n resultAnchor.appendChild(node);\n\n const range = document.createRange();\n range.selectNodeContents(node);\n selection.removeAllRanges();\n selection.addRange(range);\n document.execCommand(\"copy\");\n node.remove();\n }\n }\n }\n};\n\nlisten(\"click\", \".result\", function (this: HTMLElement, event: PointerEvent) {\n if (!isElementInDetail(event.target as HTMLElement)) {\n highlightResult(this)(true, true);\n\n const resultElement = getResultElement(event.target as HTMLElement);\n\n if (resultElement && isImageResult(resultElement)) {\n event.preventDefault();\n mutable.selectImage?.(resultElement);\n }\n }\n});\n\n// FIXME: Focus might also trigger Pointer event ^^^\nlisten(\n \"focus\",\n \".result a\",\n (event: FocusEvent) => {\n if (!isElementInDetail(event.target as HTMLElement)) {\n const resultElement = getResultElement(event.target as HTMLElement);\n\n if (resultElement && !resultElement.hasAttribute(\"data-vim-selected\")) {\n highlightResult(resultElement)(true);\n }\n\n if (resultElement && isImageResult(resultElement)) {\n event.preventDefault();\n mutable.selectImage?.(resultElement);\n }\n }\n },\n { capture: true }\n);\n\nlisten(\"keydown\", document, (event: KeyboardEvent) => {\n // check for modifiers so we don't break browser's hotkeys\n if (Object.hasOwn(keyBindings, event.key) && !event.ctrlKey && !event.altKey && !event.shiftKey && !event.metaKey) {\n const tagName = (event.target as HTMLElement)?.tagName?.toLowerCase();\n\n if (event.key === \"Escape\") {\n keyBindings[event.key]?.fun(event);\n } else if (event.target === document.body || tagName === \"a\" || tagName === \"button\") {\n event.preventDefault();\n keyBindings[event.key]?.fun(event);\n }\n }\n});\n\nmutable.selectNext = highlightResult(\"down\");\nmutable.selectPrevious = highlightResult(\"up\");\n"],"mappings":"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"} \ No newline at end of file diff --git a/searx/static/themes/simple/js/preferences.min.js b/searx/static/themes/simple/js/preferences.min.js index 72c615caf..f4adf6723 100644 --- a/searx/static/themes/simple/js/preferences.min.js +++ b/searx/static/themes/simple/js/preferences.min.js @@ -1,2 +1,2 @@ -import{i as e,o as t,r as n}from"./searxng.core.min.js";var r,i=async()=>{if(!r){try{r=await(await n(`GET`,`engine_descriptions.json`)).json()}catch(e){console.error(`Error fetching engineDescriptions:`,e)}if(r)for(let[e,[n,i]]of Object.entries(r)){let r=document.querySelectorAll(`[data-engine-name="${e}"] .engine-description`),a=` (${t.translations?.Source}: ${i})`;for(let e of r)e.innerHTML=n+a}}},a=(e,t)=>{for(let n of t)n.offsetParent&&(n.checked=!e)},o=document.querySelectorAll(`[data-engine-name]`);for(let t of o)e(`mouseenter`,t,i);var s=document.querySelectorAll(`tbody input[type=checkbox][class~=checkbox-onoff]`),c=document.querySelectorAll(`.enable-all-engines`);for(let t of c)e(`click`,t,()=>a(!0,s));var l=document.querySelectorAll(`.disable-all-engines`);for(let t of l)e(`click`,t,()=>a(!1,s));var u=document.querySelector(`#copy-hash`);u&&e(`click`,u,async e=>{e.preventDefault();let{copiedText:t,hash:n}=u.dataset;if(t&&n)try{await navigator.clipboard.writeText(n),u.innerText=t}catch(e){console.error(`Failed to copy hash:`,e)}}); +import{i as e,n as t,o as n,r}from"./searxng.core.min.js";var i,a=async()=>{if(!i){try{i=await(await r(`GET`,`engine_descriptions.json`)).json()}catch(e){console.error(`Error fetching engineDescriptions:`,e)}if(i)for(let[e,[t,r]]of Object.entries(i)){let i=document.querySelectorAll(`[data-engine-name="${e}"] .engine-description`),a=` (${n.translations?.Source}: ${r})`;for(let e of i)e.innerHTML=t+a}}},o=(e,t)=>{for(let n of t)n.offsetParent&&(n.checked=!e)},s=document.querySelectorAll(`[data-engine-name]`);for(let t of s)e(`mouseenter`,t,a);var c=document.querySelectorAll(`tbody input[type=checkbox][class~=checkbox-onoff]`),l=document.querySelectorAll(`.enable-all-engines`);for(let t of l)e(`click`,t,()=>o(!0,c));var u=document.querySelectorAll(`.disable-all-engines`);for(let t of u)e(`click`,t,()=>o(!1,c));e(`click`,`#copy-hash`,async function(){let e=this.parentElement?.querySelector(`pre`);if(t(e),window.isSecureContext)await navigator.clipboard.writeText(e.innerText);else{let t=window.getSelection();if(t){let n=document.createRange();n.selectNodeContents(e),t.removeAllRanges(),t.addRange(n),document.execCommand(`copy`)}}let n=this.dataset.copiedText;n&&(this.innerText=n)}); //# sourceMappingURL=preferences.min.js.map \ No newline at end of file diff --git a/searx/static/themes/simple/js/preferences.min.js.map b/searx/static/themes/simple/js/preferences.min.js.map index c41e0d494..b4659500b 100644 --- a/searx/static/themes/simple/js/preferences.min.js.map +++ b/searx/static/themes/simple/js/preferences.min.js.map @@ -1 +1 @@ -{"version":3,"file":"preferences.min.js","names":["engineDescriptions: Record | undefined","engineElements: NodeListOf","engineToggles: NodeListOf","enableAllEngines: NodeListOf","disableAllEngines: NodeListOf","copyHashButton: HTMLElement | null"],"sources":["../../../../../client/simple/src/js/main/preferences.ts"],"sourcesContent":["// SPDX-License-Identifier: AGPL-3.0-or-later\n\nimport { http, listen, settings } from \"../core/toolkit.ts\";\n\nlet engineDescriptions: Record | undefined;\n\nconst loadEngineDescriptions = async (): Promise => {\n if (engineDescriptions) return;\n try {\n const res = await http(\"GET\", \"engine_descriptions.json\");\n engineDescriptions = await res.json();\n } catch (error) {\n console.error(\"Error fetching engineDescriptions:\", error);\n }\n if (!engineDescriptions) return;\n\n for (const [engine_name, [description, source]] of Object.entries(engineDescriptions)) {\n const elements = document.querySelectorAll(`[data-engine-name=\"${engine_name}\"] .engine-description`);\n const sourceText = ` (${settings.translations?.Source}: ${source})`;\n\n for (const element of elements) {\n element.innerHTML = description + sourceText;\n }\n }\n};\n\nconst toggleEngines = (enable: boolean, engineToggles: NodeListOf): void => {\n for (const engineToggle of engineToggles) {\n // check if element visible, so that only engines of the current category are modified\n if (engineToggle.offsetParent) {\n engineToggle.checked = !enable;\n }\n }\n};\n\nconst engineElements: NodeListOf = document.querySelectorAll(\"[data-engine-name]\");\nfor (const engineElement of engineElements) {\n listen(\"mouseenter\", engineElement, loadEngineDescriptions);\n}\n\nconst engineToggles: NodeListOf = document.querySelectorAll(\n \"tbody input[type=checkbox][class~=checkbox-onoff]\"\n);\n\nconst enableAllEngines: NodeListOf = document.querySelectorAll(\".enable-all-engines\");\nfor (const engine of enableAllEngines) {\n listen(\"click\", engine, () => toggleEngines(true, engineToggles));\n}\n\nconst disableAllEngines: NodeListOf = document.querySelectorAll(\".disable-all-engines\");\nfor (const engine of disableAllEngines) {\n listen(\"click\", engine, () => toggleEngines(false, engineToggles));\n}\n\nconst copyHashButton: HTMLElement | null = document.querySelector(\"#copy-hash\");\nif (copyHashButton) {\n listen(\"click\", copyHashButton, async (event: Event) => {\n event.preventDefault();\n\n const { copiedText, hash } = copyHashButton.dataset;\n if (!(copiedText && hash)) return;\n\n try {\n await navigator.clipboard.writeText(hash);\n copyHashButton.innerText = copiedText;\n } catch (error) {\n console.error(\"Failed to copy hash:\", error);\n }\n });\n}\n"],"mappings":"wDAIA,IAAIA,EAEE,EAAyB,SAA2B,CACpD,MACJ,IAAI,CAEF,EAAqB,MADT,MAAM,EAAK,MAAO,2BAA2B,EAC1B,MAAM,OAC9B,EAAO,CACd,QAAQ,MAAM,qCAAsC,EAAM,CAEvD,KAEL,IAAK,GAAM,CAAC,EAAa,CAAC,EAAa,MAAY,OAAO,QAAQ,EAAmB,CAAE,CACrF,IAAM,EAAW,SAAS,iBAA8B,sBAAsB,EAAY,wBAAwB,CAC5G,EAAa,QAAQ,EAAS,cAAc,OAAO,SAAS,EAAO,OAEzE,IAAK,IAAM,KAAW,EACpB,EAAQ,UAAY,EAAc,KAKlC,GAAiB,EAAiB,IAAsD,CAC5F,IAAK,IAAM,KAAgB,EAErB,EAAa,eACf,EAAa,QAAU,CAAC,IAKxBC,EAA0C,SAAS,iBAA8B,qBAAqB,CAC5G,IAAK,IAAM,KAAiB,EAC1B,EAAO,aAAc,EAAe,EAAuB,CAG7D,IAAMC,EAA8C,SAAS,iBAC3D,oDACD,CAEKC,EAA4C,SAAS,iBAA8B,sBAAsB,CAC/G,IAAK,IAAM,KAAU,EACnB,EAAO,QAAS,MAAc,EAAc,GAAM,EAAc,CAAC,CAGnE,IAAMC,EAA6C,SAAS,iBAA8B,uBAAuB,CACjH,IAAK,IAAM,KAAU,EACnB,EAAO,QAAS,MAAc,EAAc,GAAO,EAAc,CAAC,CAGpE,IAAMC,EAAqC,SAAS,cAA2B,aAAa,CACxF,GACF,EAAO,QAAS,EAAgB,KAAO,IAAiB,CACtD,EAAM,gBAAgB,CAEtB,GAAM,CAAE,aAAY,QAAS,EAAe,QACtC,MAAc,EAEpB,GAAI,CACF,MAAM,UAAU,UAAU,UAAU,EAAK,CACzC,EAAe,UAAY,QACpB,EAAO,CACd,QAAQ,MAAM,uBAAwB,EAAM,GAE9C"} \ No newline at end of file +{"version":3,"file":"preferences.min.js","names":["engineDescriptions: Record | undefined","engineElements: NodeListOf","engineToggles: NodeListOf","enableAllEngines: NodeListOf","disableAllEngines: NodeListOf"],"sources":["../../../../../client/simple/src/js/main/preferences.ts"],"sourcesContent":["// SPDX-License-Identifier: AGPL-3.0-or-later\n\nimport { assertElement, http, listen, settings } from \"../core/toolkit.ts\";\n\nlet engineDescriptions: Record | undefined;\n\nconst loadEngineDescriptions = async (): Promise => {\n if (engineDescriptions) return;\n try {\n const res = await http(\"GET\", \"engine_descriptions.json\");\n engineDescriptions = await res.json();\n } catch (error) {\n console.error(\"Error fetching engineDescriptions:\", error);\n }\n if (!engineDescriptions) return;\n\n for (const [engine_name, [description, source]] of Object.entries(engineDescriptions)) {\n const elements = document.querySelectorAll(`[data-engine-name=\"${engine_name}\"] .engine-description`);\n const sourceText = ` (${settings.translations?.Source}: ${source})`;\n\n for (const element of elements) {\n element.innerHTML = description + sourceText;\n }\n }\n};\n\nconst toggleEngines = (enable: boolean, engineToggles: NodeListOf): void => {\n for (const engineToggle of engineToggles) {\n // check if element visible, so that only engines of the current category are modified\n if (engineToggle.offsetParent) {\n engineToggle.checked = !enable;\n }\n }\n};\n\nconst engineElements: NodeListOf = document.querySelectorAll(\"[data-engine-name]\");\nfor (const engineElement of engineElements) {\n listen(\"mouseenter\", engineElement, loadEngineDescriptions);\n}\n\nconst engineToggles: NodeListOf = document.querySelectorAll(\n \"tbody input[type=checkbox][class~=checkbox-onoff]\"\n);\n\nconst enableAllEngines: NodeListOf = document.querySelectorAll(\".enable-all-engines\");\nfor (const engine of enableAllEngines) {\n listen(\"click\", engine, () => toggleEngines(true, engineToggles));\n}\n\nconst disableAllEngines: NodeListOf = document.querySelectorAll(\".disable-all-engines\");\nfor (const engine of disableAllEngines) {\n listen(\"click\", engine, () => toggleEngines(false, engineToggles));\n}\n\nlisten(\"click\", \"#copy-hash\", async function (this: HTMLElement) {\n const target = this.parentElement?.querySelector(\"pre\");\n assertElement(target);\n\n if (window.isSecureContext) {\n await navigator.clipboard.writeText(target.innerText);\n } else {\n const selection = window.getSelection();\n if (selection) {\n const range = document.createRange();\n range.selectNodeContents(target);\n selection.removeAllRanges();\n selection.addRange(range);\n document.execCommand(\"copy\");\n }\n }\n\n const copiedText = this.dataset.copiedText;\n if (copiedText) {\n this.innerText = copiedText;\n }\n});\n"],"mappings":"0DAIA,IAAIA,EAEE,EAAyB,SAA2B,CACpD,MACJ,IAAI,CAEF,EAAqB,MADT,MAAM,EAAK,MAAO,2BAA2B,EAC1B,MAAM,OAC9B,EAAO,CACd,QAAQ,MAAM,qCAAsC,EAAM,CAEvD,KAEL,IAAK,GAAM,CAAC,EAAa,CAAC,EAAa,MAAY,OAAO,QAAQ,EAAmB,CAAE,CACrF,IAAM,EAAW,SAAS,iBAA8B,sBAAsB,EAAY,wBAAwB,CAC5G,EAAa,QAAQ,EAAS,cAAc,OAAO,SAAS,EAAO,OAEzE,IAAK,IAAM,KAAW,EACpB,EAAQ,UAAY,EAAc,KAKlC,GAAiB,EAAiB,IAAsD,CAC5F,IAAK,IAAM,KAAgB,EAErB,EAAa,eACf,EAAa,QAAU,CAAC,IAKxBC,EAA0C,SAAS,iBAA8B,qBAAqB,CAC5G,IAAK,IAAM,KAAiB,EAC1B,EAAO,aAAc,EAAe,EAAuB,CAG7D,IAAMC,EAA8C,SAAS,iBAC3D,oDACD,CAEKC,EAA4C,SAAS,iBAA8B,sBAAsB,CAC/G,IAAK,IAAM,KAAU,EACnB,EAAO,QAAS,MAAc,EAAc,GAAM,EAAc,CAAC,CAGnE,IAAMC,EAA6C,SAAS,iBAA8B,uBAAuB,CACjH,IAAK,IAAM,KAAU,EACnB,EAAO,QAAS,MAAc,EAAc,GAAO,EAAc,CAAC,CAGpE,EAAO,QAAS,aAAc,gBAAmC,CAC/D,IAAM,EAAS,KAAK,eAAe,cAA8B,MAAM,CAGvE,GAFA,EAAc,EAAO,CAEjB,OAAO,gBACT,MAAM,UAAU,UAAU,UAAU,EAAO,UAAU,KAChD,CACL,IAAM,EAAY,OAAO,cAAc,CACvC,GAAI,EAAW,CACb,IAAM,EAAQ,SAAS,aAAa,CACpC,EAAM,mBAAmB,EAAO,CAChC,EAAU,iBAAiB,CAC3B,EAAU,SAAS,EAAM,CACzB,SAAS,YAAY,OAAO,EAIhC,IAAM,EAAa,KAAK,QAAQ,WAC5B,IACF,KAAK,UAAY,IAEnB"} \ No newline at end of file diff --git a/searx/static/themes/simple/js/results.min.js b/searx/static/themes/simple/js/results.min.js index 4d934ce7c..555f9466d 100644 --- a/searx/static/themes/simple/js/results.min.js +++ b/searx/static/themes/simple/js/results.min.js @@ -7,5 +7,5 @@ import{a as e,i as t,n,o as r}from"./searxng.core.min.js"; * @author John Doherty * @license MIT */ -(function(e,t){typeof e.CustomEvent!=`function`&&(e.CustomEvent=function(e,n){n||={bubbles:!1,cancelable:!1,detail:void 0};var r=t.createEvent(`CustomEvent`);return r.initCustomEvent(e,n.bubbles,n.cancelable,n.detail),r},e.CustomEvent.prototype=e.Event.prototype),t.addEventListener(`touchstart`,u,!1),t.addEventListener(`touchmove`,d,!1),t.addEventListener(`touchend`,l,!1);var n=null,r=null,i=null,a=null,o=null,s=null,c=0;function l(e){if(s===e.target){var l=parseInt(f(s,`data-swipe-threshold`,`20`),10),u=f(s,`data-swipe-unit`,`px`),d=parseInt(f(s,`data-swipe-timeout`,`500`),10),p=Date.now()-o,m=``,h=e.changedTouches||e.touches||[];if(u===`vh`&&(l=Math.round(l/100*t.documentElement.clientHeight)),u===`vw`&&(l=Math.round(l/100*t.documentElement.clientWidth)),Math.abs(i)>Math.abs(a)?Math.abs(i)>l&&p0?`swiped-left`:`swiped-right`):Math.abs(a)>l&&p0?`swiped-up`:`swiped-down`),m!==``){var g={dir:m.replace(/swiped-/,``),touchType:(h[0]||{}).touchType||`direct`,fingers:c,xStart:parseInt(n,10),xEnd:parseInt((h[0]||{}).clientX||-1,10),yStart:parseInt(r,10),yEnd:parseInt((h[0]||{}).clientY||-1,10)};s.dispatchEvent(new CustomEvent(`swiped`,{bubbles:!0,cancelable:!0,detail:g})),s.dispatchEvent(new CustomEvent(m,{bubbles:!0,cancelable:!0,detail:g}))}n=null,r=null,o=null}}function u(e){e.target.getAttribute(`data-swipe-ignore`)!==`true`&&(s=e.target,o=Date.now(),n=e.touches[0].clientX,r=e.touches[0].clientY,i=0,a=0,c=e.touches.length)}function d(e){if(!(!n||!r)){var t=e.touches[0].clientX,o=e.touches[0].clientY;i=n-t,a=r-o}}function f(e,n,r){for(;e&&e!==t.documentElement;){var i=e.getAttribute(n);if(i)return i;e=e.parentNode}return r}})(window,document);var i,a=e=>{i&&clearTimeout(i);let t=e.querySelector(`.result-images-source img`);if(!t)return;let n=e.querySelector(`.image_thumbnail`);if(n){if(n.src===`${r.theme_static_path}/img/img_load_error.svg`)return;t.onerror=()=>{t.src=n.src},t.src=n.src}let a=t.getAttribute(`data-src`);a&&(i=setTimeout(()=>{t.src=a,t.removeAttribute(`data-src`)},1e3))},o=document.querySelectorAll(`#urls img.image_thumbnail`);for(let e of o)e.complete&&e.naturalWidth===0&&(e.src=`${r.theme_static_path}/img/img_load_error.svg`),e.onerror=()=>{e.src=`${r.theme_static_path}/img/img_load_error.svg`};document.querySelector(`#search_url button#copy_url`)?.style.setProperty(`display`,`block`),e.selectImage=t=>{document.getElementById(`results`)?.classList.add(`image-detail-open`),window.location.hash=`#image-viewer`,e.scrollPageToSelected?.(),t&&a(t)},e.closeDetail=()=>{document.getElementById(`results`)?.classList.remove(`image-detail-open`),window.location.hash===`#image-viewer`&&window.history.back(),e.scrollPageToSelected?.()},t(`click`,`.btn-collapse`,function(){let e=this.getAttribute(`data-btn-text-collapsed`),t=this.getAttribute(`data-btn-text-not-collapsed`),r=this.getAttribute(`data-target`);if(!(r&&e&&t))return;let i=document.querySelector(r);n(i);let a=this.classList.contains(`collapsed`),o=a?t:e,s=a?e:t;this.innerHTML=this.innerHTML.replace(s,o),this.classList.toggle(`collapsed`),i.classList.toggle(`invisible`)}),t(`click`,`.media-loader`,function(){let e=this.getAttribute(`data-target`);if(!e)return;let t=document.querySelector(`${e} > iframe`);if(n(t),!t.getAttribute(`src`)){let e=t.getAttribute(`data-src`);e&&t.setAttribute(`src`,e)}}),t(`click`,`#copy_url`,async function(){let e=this.parentElement?.querySelector(`pre`);n(e),await navigator.clipboard.writeText(e.innerText);let t=this.dataset.copiedText;t&&(this.innerText=t)}),t(`click`,`.result-detail-close`,t=>{t.preventDefault(),e.closeDetail?.()}),t(`click`,`.result-detail-previous`,t=>{t.preventDefault(),e.selectPrevious?.(!1)}),t(`click`,`.result-detail-next`,t=>{t.preventDefault(),e.selectNext?.(!1)}),window.addEventListener(`hashchange`,()=>{window.location.hash!==`#image-viewer`&&e.closeDetail?.()});var s=document.querySelectorAll(`.swipe-horizontal`);for(let n of s)t(`swiped-left`,n,()=>{e.selectNext?.(!1)}),t(`swiped-right`,n,()=>{e.selectPrevious?.(!1)});window.addEventListener(`scroll`,()=>{let e=document.getElementById(`backToTop`),t=document.getElementById(`results`);if(e&&t){let e=(document.documentElement.scrollTop||document.body.scrollTop)>=100;t.classList.toggle(`scrolling`,e)}},!0); +(function(e,t){typeof e.CustomEvent!=`function`&&(e.CustomEvent=function(e,n){n||={bubbles:!1,cancelable:!1,detail:void 0};var r=t.createEvent(`CustomEvent`);return r.initCustomEvent(e,n.bubbles,n.cancelable,n.detail),r},e.CustomEvent.prototype=e.Event.prototype),t.addEventListener(`touchstart`,u,!1),t.addEventListener(`touchmove`,d,!1),t.addEventListener(`touchend`,l,!1);var n=null,r=null,i=null,a=null,o=null,s=null,c=0;function l(e){if(s===e.target){var l=parseInt(f(s,`data-swipe-threshold`,`20`),10),u=f(s,`data-swipe-unit`,`px`),d=parseInt(f(s,`data-swipe-timeout`,`500`),10),p=Date.now()-o,m=``,h=e.changedTouches||e.touches||[];if(u===`vh`&&(l=Math.round(l/100*t.documentElement.clientHeight)),u===`vw`&&(l=Math.round(l/100*t.documentElement.clientWidth)),Math.abs(i)>Math.abs(a)?Math.abs(i)>l&&p0?`swiped-left`:`swiped-right`):Math.abs(a)>l&&p0?`swiped-up`:`swiped-down`),m!==``){var g={dir:m.replace(/swiped-/,``),touchType:(h[0]||{}).touchType||`direct`,fingers:c,xStart:parseInt(n,10),xEnd:parseInt((h[0]||{}).clientX||-1,10),yStart:parseInt(r,10),yEnd:parseInt((h[0]||{}).clientY||-1,10)};s.dispatchEvent(new CustomEvent(`swiped`,{bubbles:!0,cancelable:!0,detail:g})),s.dispatchEvent(new CustomEvent(m,{bubbles:!0,cancelable:!0,detail:g}))}n=null,r=null,o=null}}function u(e){e.target.getAttribute(`data-swipe-ignore`)!==`true`&&(s=e.target,o=Date.now(),n=e.touches[0].clientX,r=e.touches[0].clientY,i=0,a=0,c=e.touches.length)}function d(e){if(!(!n||!r)){var t=e.touches[0].clientX,o=e.touches[0].clientY;i=n-t,a=r-o}}function f(e,n,r){for(;e&&e!==t.documentElement;){var i=e.getAttribute(n);if(i)return i;e=e.parentNode}return r}})(window,document);var i,a=e=>{i&&clearTimeout(i);let t=e.querySelector(`.result-images-source img`);if(!t)return;let n=e.querySelector(`.image_thumbnail`);if(n){if(n.src===`${r.theme_static_path}/img/img_load_error.svg`)return;t.onerror=()=>{t.src=n.src},t.src=n.src}let a=t.getAttribute(`data-src`);a&&(i=setTimeout(()=>{t.src=a,t.removeAttribute(`data-src`)},1e3))},o=document.querySelectorAll(`#urls img.image_thumbnail`);for(let e of o)e.complete&&e.naturalWidth===0&&(e.src=`${r.theme_static_path}/img/img_load_error.svg`),e.onerror=()=>{e.src=`${r.theme_static_path}/img/img_load_error.svg`};document.querySelector(`#search_url button#copy_url`)?.style.setProperty(`display`,`block`),e.selectImage=t=>{document.getElementById(`results`)?.classList.add(`image-detail-open`),window.location.hash=`#image-viewer`,e.scrollPageToSelected?.(),t&&a(t)},e.closeDetail=()=>{document.getElementById(`results`)?.classList.remove(`image-detail-open`),window.location.hash===`#image-viewer`&&window.history.back(),e.scrollPageToSelected?.()},t(`click`,`.btn-collapse`,function(){let e=this.getAttribute(`data-btn-text-collapsed`),t=this.getAttribute(`data-btn-text-not-collapsed`),r=this.getAttribute(`data-target`);if(!(r&&e&&t))return;let i=document.querySelector(r);n(i);let a=this.classList.contains(`collapsed`),o=a?t:e,s=a?e:t;this.innerHTML=this.innerHTML.replace(s,o),this.classList.toggle(`collapsed`),i.classList.toggle(`invisible`)}),t(`click`,`.media-loader`,function(){let e=this.getAttribute(`data-target`);if(!e)return;let t=document.querySelector(`${e} > iframe`);if(n(t),!t.getAttribute(`src`)){let e=t.getAttribute(`data-src`);e&&t.setAttribute(`src`,e)}}),t(`click`,`#copy_url`,async function(){let e=this.parentElement?.querySelector(`pre`);if(n(e),window.isSecureContext)await navigator.clipboard.writeText(e.innerText);else{let t=window.getSelection();if(t){let n=document.createRange();n.selectNodeContents(e),t.removeAllRanges(),t.addRange(n),document.execCommand(`copy`)}}let t=this.dataset.copiedText;t&&(this.innerText=t)}),t(`click`,`.result-detail-close`,t=>{t.preventDefault(),e.closeDetail?.()}),t(`click`,`.result-detail-previous`,t=>{t.preventDefault(),e.selectPrevious?.(!1)}),t(`click`,`.result-detail-next`,t=>{t.preventDefault(),e.selectNext?.(!1)}),window.addEventListener(`hashchange`,()=>{window.location.hash!==`#image-viewer`&&e.closeDetail?.()});var s=document.querySelectorAll(`.swipe-horizontal`);for(let n of s)t(`swiped-left`,n,()=>{e.selectNext?.(!1)}),t(`swiped-right`,n,()=>{e.selectPrevious?.(!1)});window.addEventListener(`scroll`,()=>{let e=document.getElementById(`backToTop`),t=document.getElementById(`results`);if(e&&t){let e=(document.documentElement.scrollTop||document.body.scrollTop)>=100;t.classList.toggle(`scrolling`,e)}},!0); //# sourceMappingURL=results.min.js.map \ No newline at end of file diff --git a/searx/static/themes/simple/js/results.min.js.map b/searx/static/themes/simple/js/results.min.js.map index fb0cd7f18..6587cad50 100644 --- a/searx/static/themes/simple/js/results.min.js.map +++ b/searx/static/themes/simple/js/results.min.js.map @@ -1 +1 @@ -{"version":3,"file":"results.min.js","names":["window","document","imgTimeoutID: number","imageThumbnails: NodeListOf","copyUrlButton: HTMLButtonElement | null","swipeHorizontal: NodeListOf"],"sources":["../../../../../client/simple/node_modules/swiped-events/src/swiped-events.js","../../../../../client/simple/src/js/main/results.ts"],"sourcesContent":["/*!\n * swiped-events.js - v@version@\n * Pure JavaScript swipe events\n * https://github.com/john-doherty/swiped-events\n * @inspiration https://stackoverflow.com/questions/16348031/disable-scrolling-when-touch-moving-certain-element\n * @author John Doherty \n * @license MIT\n */\n(function (window, document) {\n\n 'use strict';\n\n // patch CustomEvent to allow constructor creation (IE/Chrome)\n if (typeof window.CustomEvent !== 'function') {\n\n window.CustomEvent = function (event, params) {\n\n params = params || { bubbles: false, cancelable: false, detail: undefined };\n\n var evt = document.createEvent('CustomEvent');\n evt.initCustomEvent(event, params.bubbles, params.cancelable, params.detail);\n return evt;\n };\n\n window.CustomEvent.prototype = window.Event.prototype;\n }\n\n document.addEventListener('touchstart', handleTouchStart, false);\n document.addEventListener('touchmove', handleTouchMove, false);\n document.addEventListener('touchend', handleTouchEnd, false);\n\n var xDown = null;\n var yDown = null;\n var xDiff = null;\n var yDiff = null;\n var timeDown = null;\n var startEl = null;\n var touchCount = 0;\n\n /**\n * Fires swiped event if swipe detected on touchend\n * @param {object} e - browser event object\n * @returns {void}\n */\n function handleTouchEnd(e) {\n\n // if the user released on a different target, cancel!\n if (startEl !== e.target) return;\n\n var swipeThreshold = parseInt(getNearestAttribute(startEl, 'data-swipe-threshold', '20'), 10); // default 20 units\n var swipeUnit = getNearestAttribute(startEl, 'data-swipe-unit', 'px'); // default px\n var swipeTimeout = parseInt(getNearestAttribute(startEl, 'data-swipe-timeout', '500'), 10); // default 500ms\n var timeDiff = Date.now() - timeDown;\n var eventType = '';\n var changedTouches = e.changedTouches || e.touches || [];\n\n if (swipeUnit === 'vh') {\n swipeThreshold = Math.round((swipeThreshold / 100) * document.documentElement.clientHeight); // get percentage of viewport height in pixels\n }\n if (swipeUnit === 'vw') {\n swipeThreshold = Math.round((swipeThreshold / 100) * document.documentElement.clientWidth); // get percentage of viewport height in pixels\n }\n\n if (Math.abs(xDiff) > Math.abs(yDiff)) { // most significant\n if (Math.abs(xDiff) > swipeThreshold && timeDiff < swipeTimeout) {\n if (xDiff > 0) {\n eventType = 'swiped-left';\n }\n else {\n eventType = 'swiped-right';\n }\n }\n }\n else if (Math.abs(yDiff) > swipeThreshold && timeDiff < swipeTimeout) {\n if (yDiff > 0) {\n eventType = 'swiped-up';\n }\n else {\n eventType = 'swiped-down';\n }\n }\n\n if (eventType !== '') {\n\n var eventData = {\n dir: eventType.replace(/swiped-/, ''),\n touchType: (changedTouches[0] || {}).touchType || 'direct',\n fingers: touchCount, // Number of fingers used\n xStart: parseInt(xDown, 10),\n xEnd: parseInt((changedTouches[0] || {}).clientX || -1, 10),\n yStart: parseInt(yDown, 10),\n yEnd: parseInt((changedTouches[0] || {}).clientY || -1, 10)\n };\n\n // fire `swiped` event event on the element that started the swipe\n startEl.dispatchEvent(new CustomEvent('swiped', { bubbles: true, cancelable: true, detail: eventData }));\n\n // fire `swiped-dir` event on the element that started the swipe\n startEl.dispatchEvent(new CustomEvent(eventType, { bubbles: true, cancelable: true, detail: eventData }));\n }\n\n // reset values\n xDown = null;\n yDown = null;\n timeDown = null;\n }\n /**\n * Records current location on touchstart event\n * @param {object} e - browser event object\n * @returns {void}\n */\n function handleTouchStart(e) {\n\n // if the element has data-swipe-ignore=\"true\" we stop listening for swipe events\n if (e.target.getAttribute('data-swipe-ignore') === 'true') return;\n\n startEl = e.target;\n\n timeDown = Date.now();\n xDown = e.touches[0].clientX;\n yDown = e.touches[0].clientY;\n xDiff = 0;\n yDiff = 0;\n touchCount = e.touches.length;\n }\n\n /**\n * Records location diff in px on touchmove event\n * @param {object} e - browser event object\n * @returns {void}\n */\n function handleTouchMove(e) {\n\n if (!xDown || !yDown) return;\n\n var xUp = e.touches[0].clientX;\n var yUp = e.touches[0].clientY;\n\n xDiff = xDown - xUp;\n yDiff = yDown - yUp;\n }\n\n /**\n * Gets attribute off HTML element or nearest parent\n * @param {object} el - HTML element to retrieve attribute from\n * @param {string} attributeName - name of the attribute\n * @param {any} defaultValue - default value to return if no match found\n * @returns {any} attribute value or defaultValue\n */\n function getNearestAttribute(el, attributeName, defaultValue) {\n\n // walk up the dom tree looking for attributeName\n while (el && el !== document.documentElement) {\n\n var attributeValue = el.getAttribute(attributeName);\n\n if (attributeValue) {\n return attributeValue;\n }\n\n el = el.parentNode;\n }\n\n return defaultValue;\n }\n\n}(window, document));\n","// SPDX-License-Identifier: AGPL-3.0-or-later\n\nimport \"../../../node_modules/swiped-events/src/swiped-events.js\";\nimport { assertElement, listen, mutable, settings } from \"../core/toolkit.ts\";\n\nlet imgTimeoutID: number;\n\nconst imageLoader = (resultElement: HTMLElement): void => {\n if (imgTimeoutID) clearTimeout(imgTimeoutID);\n\n const imgElement = resultElement.querySelector(\".result-images-source img\");\n if (!imgElement) return;\n\n // use thumbnail until full image loads\n const thumbnail = resultElement.querySelector(\".image_thumbnail\");\n if (thumbnail) {\n if (thumbnail.src === `${settings.theme_static_path}/img/img_load_error.svg`) return;\n\n imgElement.onerror = (): void => {\n imgElement.src = thumbnail.src;\n };\n\n imgElement.src = thumbnail.src;\n }\n\n const imgSource = imgElement.getAttribute(\"data-src\");\n if (!imgSource) return;\n\n // unsafe nodejs specific, cast to https://developer.mozilla.org/en-US/docs/Web/API/Window/setTimeout#return_value\n // https://github.com/searxng/searxng/pull/5073#discussion_r2265767231\n imgTimeoutID = setTimeout(() => {\n imgElement.src = imgSource;\n imgElement.removeAttribute(\"data-src\");\n }, 1000) as unknown as number;\n};\n\nconst imageThumbnails: NodeListOf =\n document.querySelectorAll(\"#urls img.image_thumbnail\");\nfor (const thumbnail of imageThumbnails) {\n if (thumbnail.complete && thumbnail.naturalWidth === 0) {\n thumbnail.src = `${settings.theme_static_path}/img/img_load_error.svg`;\n }\n\n thumbnail.onerror = (): void => {\n thumbnail.src = `${settings.theme_static_path}/img/img_load_error.svg`;\n };\n}\n\nconst copyUrlButton: HTMLButtonElement | null =\n document.querySelector(\"#search_url button#copy_url\");\ncopyUrlButton?.style.setProperty(\"display\", \"block\");\n\nmutable.selectImage = (resultElement: HTMLElement): void => {\n // add a class that can be evaluated in the CSS and indicates that the\n // detail view is open\n const resultsElement = document.getElementById(\"results\");\n resultsElement?.classList.add(\"image-detail-open\");\n\n // add a hash to the browser history so that pressing back doesn't return\n // to the previous page this allows us to dismiss the image details on\n // pressing the back button on mobile devices\n window.location.hash = \"#image-viewer\";\n\n mutable.scrollPageToSelected?.();\n\n // if there is no element given by the caller, stop here\n if (!resultElement) return;\n\n imageLoader(resultElement);\n};\n\nmutable.closeDetail = (): void => {\n const resultsElement = document.getElementById(\"results\");\n resultsElement?.classList.remove(\"image-detail-open\");\n\n // remove #image-viewer hash from url by navigating back\n if (window.location.hash === \"#image-viewer\") {\n window.history.back();\n }\n\n mutable.scrollPageToSelected?.();\n};\n\nlisten(\"click\", \".btn-collapse\", function (this: HTMLElement) {\n const btnLabelCollapsed = this.getAttribute(\"data-btn-text-collapsed\");\n const btnLabelNotCollapsed = this.getAttribute(\"data-btn-text-not-collapsed\");\n const target = this.getAttribute(\"data-target\");\n\n if (!(target && btnLabelCollapsed && btnLabelNotCollapsed)) return;\n\n const targetElement = document.querySelector(target);\n assertElement(targetElement);\n\n const isCollapsed = this.classList.contains(\"collapsed\");\n const newLabel = isCollapsed ? btnLabelNotCollapsed : btnLabelCollapsed;\n const oldLabel = isCollapsed ? btnLabelCollapsed : btnLabelNotCollapsed;\n\n this.innerHTML = this.innerHTML.replace(oldLabel, newLabel);\n this.classList.toggle(\"collapsed\");\n\n targetElement.classList.toggle(\"invisible\");\n});\n\nlisten(\"click\", \".media-loader\", function (this: HTMLElement) {\n const target = this.getAttribute(\"data-target\");\n if (!target) return;\n\n const iframeLoad = document.querySelector(`${target} > iframe`);\n assertElement(iframeLoad);\n\n const srctest = iframeLoad.getAttribute(\"src\");\n if (!srctest) {\n const dataSrc = iframeLoad.getAttribute(\"data-src\");\n if (dataSrc) {\n iframeLoad.setAttribute(\"src\", dataSrc);\n }\n }\n});\n\nlisten(\"click\", \"#copy_url\", async function (this: HTMLElement) {\n const target = this.parentElement?.querySelector(\"pre\");\n assertElement(target);\n\n await navigator.clipboard.writeText(target.innerText);\n const copiedText = this.dataset.copiedText;\n if (copiedText) {\n this.innerText = copiedText;\n }\n});\n\nlisten(\"click\", \".result-detail-close\", (event: Event) => {\n event.preventDefault();\n mutable.closeDetail?.();\n});\n\nlisten(\"click\", \".result-detail-previous\", (event: Event) => {\n event.preventDefault();\n mutable.selectPrevious?.(false);\n});\n\nlisten(\"click\", \".result-detail-next\", (event: Event) => {\n event.preventDefault();\n mutable.selectNext?.(false);\n});\n\n// listen for the back button to be pressed and dismiss the image details when called\nwindow.addEventListener(\"hashchange\", () => {\n if (window.location.hash !== \"#image-viewer\") {\n mutable.closeDetail?.();\n }\n});\n\nconst swipeHorizontal: NodeListOf = document.querySelectorAll(\".swipe-horizontal\");\nfor (const element of swipeHorizontal) {\n listen(\"swiped-left\", element, () => {\n mutable.selectNext?.(false);\n });\n\n listen(\"swiped-right\", element, () => {\n mutable.selectPrevious?.(false);\n });\n}\n\nwindow.addEventListener(\n \"scroll\",\n () => {\n const backToTopElement = document.getElementById(\"backToTop\");\n const resultsElement = document.getElementById(\"results\");\n\n if (backToTopElement && resultsElement) {\n const scrollTop = document.documentElement.scrollTop || document.body.scrollTop;\n const isScrolling = scrollTop >= 100;\n resultsElement.classList.toggle(\"scrolling\", isScrolling);\n }\n },\n true\n);\n"],"x_google_ignoreList":[0],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"results.min.js","names":["window","document","imgTimeoutID: number","imageThumbnails: NodeListOf","copyUrlButton: HTMLButtonElement | null","swipeHorizontal: NodeListOf"],"sources":["../../../../../client/simple/node_modules/swiped-events/src/swiped-events.js","../../../../../client/simple/src/js/main/results.ts"],"sourcesContent":["/*!\n * swiped-events.js - v@version@\n * Pure JavaScript swipe events\n * https://github.com/john-doherty/swiped-events\n * @inspiration https://stackoverflow.com/questions/16348031/disable-scrolling-when-touch-moving-certain-element\n * @author John Doherty \n * @license MIT\n */\n(function (window, document) {\n\n 'use strict';\n\n // patch CustomEvent to allow constructor creation (IE/Chrome)\n if (typeof window.CustomEvent !== 'function') {\n\n window.CustomEvent = function (event, params) {\n\n params = params || { bubbles: false, cancelable: false, detail: undefined };\n\n var evt = document.createEvent('CustomEvent');\n evt.initCustomEvent(event, params.bubbles, params.cancelable, params.detail);\n return evt;\n };\n\n window.CustomEvent.prototype = window.Event.prototype;\n }\n\n document.addEventListener('touchstart', handleTouchStart, false);\n document.addEventListener('touchmove', handleTouchMove, false);\n document.addEventListener('touchend', handleTouchEnd, false);\n\n var xDown = null;\n var yDown = null;\n var xDiff = null;\n var yDiff = null;\n var timeDown = null;\n var startEl = null;\n var touchCount = 0;\n\n /**\n * Fires swiped event if swipe detected on touchend\n * @param {object} e - browser event object\n * @returns {void}\n */\n function handleTouchEnd(e) {\n\n // if the user released on a different target, cancel!\n if (startEl !== e.target) return;\n\n var swipeThreshold = parseInt(getNearestAttribute(startEl, 'data-swipe-threshold', '20'), 10); // default 20 units\n var swipeUnit = getNearestAttribute(startEl, 'data-swipe-unit', 'px'); // default px\n var swipeTimeout = parseInt(getNearestAttribute(startEl, 'data-swipe-timeout', '500'), 10); // default 500ms\n var timeDiff = Date.now() - timeDown;\n var eventType = '';\n var changedTouches = e.changedTouches || e.touches || [];\n\n if (swipeUnit === 'vh') {\n swipeThreshold = Math.round((swipeThreshold / 100) * document.documentElement.clientHeight); // get percentage of viewport height in pixels\n }\n if (swipeUnit === 'vw') {\n swipeThreshold = Math.round((swipeThreshold / 100) * document.documentElement.clientWidth); // get percentage of viewport height in pixels\n }\n\n if (Math.abs(xDiff) > Math.abs(yDiff)) { // most significant\n if (Math.abs(xDiff) > swipeThreshold && timeDiff < swipeTimeout) {\n if (xDiff > 0) {\n eventType = 'swiped-left';\n }\n else {\n eventType = 'swiped-right';\n }\n }\n }\n else if (Math.abs(yDiff) > swipeThreshold && timeDiff < swipeTimeout) {\n if (yDiff > 0) {\n eventType = 'swiped-up';\n }\n else {\n eventType = 'swiped-down';\n }\n }\n\n if (eventType !== '') {\n\n var eventData = {\n dir: eventType.replace(/swiped-/, ''),\n touchType: (changedTouches[0] || {}).touchType || 'direct',\n fingers: touchCount, // Number of fingers used\n xStart: parseInt(xDown, 10),\n xEnd: parseInt((changedTouches[0] || {}).clientX || -1, 10),\n yStart: parseInt(yDown, 10),\n yEnd: parseInt((changedTouches[0] || {}).clientY || -1, 10)\n };\n\n // fire `swiped` event event on the element that started the swipe\n startEl.dispatchEvent(new CustomEvent('swiped', { bubbles: true, cancelable: true, detail: eventData }));\n\n // fire `swiped-dir` event on the element that started the swipe\n startEl.dispatchEvent(new CustomEvent(eventType, { bubbles: true, cancelable: true, detail: eventData }));\n }\n\n // reset values\n xDown = null;\n yDown = null;\n timeDown = null;\n }\n /**\n * Records current location on touchstart event\n * @param {object} e - browser event object\n * @returns {void}\n */\n function handleTouchStart(e) {\n\n // if the element has data-swipe-ignore=\"true\" we stop listening for swipe events\n if (e.target.getAttribute('data-swipe-ignore') === 'true') return;\n\n startEl = e.target;\n\n timeDown = Date.now();\n xDown = e.touches[0].clientX;\n yDown = e.touches[0].clientY;\n xDiff = 0;\n yDiff = 0;\n touchCount = e.touches.length;\n }\n\n /**\n * Records location diff in px on touchmove event\n * @param {object} e - browser event object\n * @returns {void}\n */\n function handleTouchMove(e) {\n\n if (!xDown || !yDown) return;\n\n var xUp = e.touches[0].clientX;\n var yUp = e.touches[0].clientY;\n\n xDiff = xDown - xUp;\n yDiff = yDown - yUp;\n }\n\n /**\n * Gets attribute off HTML element or nearest parent\n * @param {object} el - HTML element to retrieve attribute from\n * @param {string} attributeName - name of the attribute\n * @param {any} defaultValue - default value to return if no match found\n * @returns {any} attribute value or defaultValue\n */\n function getNearestAttribute(el, attributeName, defaultValue) {\n\n // walk up the dom tree looking for attributeName\n while (el && el !== document.documentElement) {\n\n var attributeValue = el.getAttribute(attributeName);\n\n if (attributeValue) {\n return attributeValue;\n }\n\n el = el.parentNode;\n }\n\n return defaultValue;\n }\n\n}(window, document));\n","// SPDX-License-Identifier: AGPL-3.0-or-later\n\nimport \"../../../node_modules/swiped-events/src/swiped-events.js\";\nimport { assertElement, listen, mutable, settings } from \"../core/toolkit.ts\";\n\nlet imgTimeoutID: number;\n\nconst imageLoader = (resultElement: HTMLElement): void => {\n if (imgTimeoutID) clearTimeout(imgTimeoutID);\n\n const imgElement = resultElement.querySelector(\".result-images-source img\");\n if (!imgElement) return;\n\n // use thumbnail until full image loads\n const thumbnail = resultElement.querySelector(\".image_thumbnail\");\n if (thumbnail) {\n if (thumbnail.src === `${settings.theme_static_path}/img/img_load_error.svg`) return;\n\n imgElement.onerror = (): void => {\n imgElement.src = thumbnail.src;\n };\n\n imgElement.src = thumbnail.src;\n }\n\n const imgSource = imgElement.getAttribute(\"data-src\");\n if (!imgSource) return;\n\n // unsafe nodejs specific, cast to https://developer.mozilla.org/en-US/docs/Web/API/Window/setTimeout#return_value\n // https://github.com/searxng/searxng/pull/5073#discussion_r2265767231\n imgTimeoutID = setTimeout(() => {\n imgElement.src = imgSource;\n imgElement.removeAttribute(\"data-src\");\n }, 1000) as unknown as number;\n};\n\nconst imageThumbnails: NodeListOf =\n document.querySelectorAll(\"#urls img.image_thumbnail\");\nfor (const thumbnail of imageThumbnails) {\n if (thumbnail.complete && thumbnail.naturalWidth === 0) {\n thumbnail.src = `${settings.theme_static_path}/img/img_load_error.svg`;\n }\n\n thumbnail.onerror = (): void => {\n thumbnail.src = `${settings.theme_static_path}/img/img_load_error.svg`;\n };\n}\n\nconst copyUrlButton: HTMLButtonElement | null =\n document.querySelector(\"#search_url button#copy_url\");\ncopyUrlButton?.style.setProperty(\"display\", \"block\");\n\nmutable.selectImage = (resultElement: HTMLElement): void => {\n // add a class that can be evaluated in the CSS and indicates that the\n // detail view is open\n const resultsElement = document.getElementById(\"results\");\n resultsElement?.classList.add(\"image-detail-open\");\n\n // add a hash to the browser history so that pressing back doesn't return\n // to the previous page this allows us to dismiss the image details on\n // pressing the back button on mobile devices\n window.location.hash = \"#image-viewer\";\n\n mutable.scrollPageToSelected?.();\n\n // if there is no element given by the caller, stop here\n if (!resultElement) return;\n\n imageLoader(resultElement);\n};\n\nmutable.closeDetail = (): void => {\n const resultsElement = document.getElementById(\"results\");\n resultsElement?.classList.remove(\"image-detail-open\");\n\n // remove #image-viewer hash from url by navigating back\n if (window.location.hash === \"#image-viewer\") {\n window.history.back();\n }\n\n mutable.scrollPageToSelected?.();\n};\n\nlisten(\"click\", \".btn-collapse\", function (this: HTMLElement) {\n const btnLabelCollapsed = this.getAttribute(\"data-btn-text-collapsed\");\n const btnLabelNotCollapsed = this.getAttribute(\"data-btn-text-not-collapsed\");\n const target = this.getAttribute(\"data-target\");\n\n if (!(target && btnLabelCollapsed && btnLabelNotCollapsed)) return;\n\n const targetElement = document.querySelector(target);\n assertElement(targetElement);\n\n const isCollapsed = this.classList.contains(\"collapsed\");\n const newLabel = isCollapsed ? btnLabelNotCollapsed : btnLabelCollapsed;\n const oldLabel = isCollapsed ? btnLabelCollapsed : btnLabelNotCollapsed;\n\n this.innerHTML = this.innerHTML.replace(oldLabel, newLabel);\n this.classList.toggle(\"collapsed\");\n\n targetElement.classList.toggle(\"invisible\");\n});\n\nlisten(\"click\", \".media-loader\", function (this: HTMLElement) {\n const target = this.getAttribute(\"data-target\");\n if (!target) return;\n\n const iframeLoad = document.querySelector(`${target} > iframe`);\n assertElement(iframeLoad);\n\n const srctest = iframeLoad.getAttribute(\"src\");\n if (!srctest) {\n const dataSrc = iframeLoad.getAttribute(\"data-src\");\n if (dataSrc) {\n iframeLoad.setAttribute(\"src\", dataSrc);\n }\n }\n});\n\nlisten(\"click\", \"#copy_url\", async function (this: HTMLElement) {\n const target = this.parentElement?.querySelector(\"pre\");\n assertElement(target);\n\n if (window.isSecureContext) {\n await navigator.clipboard.writeText(target.innerText);\n } else {\n const selection = window.getSelection();\n if (selection) {\n const range = document.createRange();\n range.selectNodeContents(target);\n selection.removeAllRanges();\n selection.addRange(range);\n document.execCommand(\"copy\");\n }\n }\n\n const copiedText = this.dataset.copiedText;\n if (copiedText) {\n this.innerText = copiedText;\n }\n});\n\nlisten(\"click\", \".result-detail-close\", (event: Event) => {\n event.preventDefault();\n mutable.closeDetail?.();\n});\n\nlisten(\"click\", \".result-detail-previous\", (event: Event) => {\n event.preventDefault();\n mutable.selectPrevious?.(false);\n});\n\nlisten(\"click\", \".result-detail-next\", (event: Event) => {\n event.preventDefault();\n mutable.selectNext?.(false);\n});\n\n// listen for the back button to be pressed and dismiss the image details when called\nwindow.addEventListener(\"hashchange\", () => {\n if (window.location.hash !== \"#image-viewer\") {\n mutable.closeDetail?.();\n }\n});\n\nconst swipeHorizontal: NodeListOf = document.querySelectorAll(\".swipe-horizontal\");\nfor (const element of swipeHorizontal) {\n listen(\"swiped-left\", element, () => {\n mutable.selectNext?.(false);\n });\n\n listen(\"swiped-right\", element, () => {\n mutable.selectPrevious?.(false);\n });\n}\n\nwindow.addEventListener(\n \"scroll\",\n () => {\n const backToTopElement = document.getElementById(\"backToTop\");\n const resultsElement = document.getElementById(\"results\");\n\n if (backToTopElement && resultsElement) {\n const scrollTop = document.documentElement.scrollTop || document.body.scrollTop;\n const isScrolling = scrollTop >= 100;\n resultsElement.classList.toggle(\"scrolling\", isScrolling);\n }\n },\n true\n);\n"],"x_google_ignoreList":[0],"mappings":";;;;;;;;;CAQC,SAAU,EAAQ,EAAU,CAKrB,OAAOA,EAAO,aAAgB,aAE9B,EAAO,YAAc,SAAU,EAAO,EAAQ,CAE1C,IAAmB,CAAE,QAAS,GAAO,WAAY,GAAO,OAAQ,IAAA,GAAW,CAE3E,IAAI,EAAMC,EAAS,YAAY,cAAc,CAE7C,OADA,EAAI,gBAAgB,EAAO,EAAO,QAAS,EAAO,WAAY,EAAO,OAAO,CACrE,GAGX,EAAO,YAAY,UAAYD,EAAO,MAAM,WAGhD,EAAS,iBAAiB,aAAc,EAAkB,GAAM,CAChE,EAAS,iBAAiB,YAAa,EAAiB,GAAM,CAC9D,EAAS,iBAAiB,WAAY,EAAgB,GAAM,CAE5D,IAAI,EAAQ,KACR,EAAQ,KACR,EAAQ,KACR,EAAQ,KACR,EAAW,KACX,EAAU,KACV,EAAa,EAOjB,SAAS,EAAe,EAAG,CAGnB,OAAY,EAAE,OAElB,KAAI,EAAiB,SAAS,EAAoB,EAAS,uBAAwB,KAAK,CAAE,GAAG,CACzF,EAAY,EAAoB,EAAS,kBAAmB,KAAK,CACjE,EAAe,SAAS,EAAoB,EAAS,qBAAsB,MAAM,CAAE,GAAG,CACtF,EAAW,KAAK,KAAK,CAAG,EACxB,EAAY,GACZ,EAAiB,EAAE,gBAAkB,EAAE,SAAW,EAAE,CA4BxD,GA1BI,IAAc,OACd,EAAiB,KAAK,MAAO,EAAiB,IAAOC,EAAS,gBAAgB,aAAa,EAE3F,IAAc,OACd,EAAiB,KAAK,MAAO,EAAiB,IAAOA,EAAS,gBAAgB,YAAY,EAG1F,KAAK,IAAI,EAAM,CAAG,KAAK,IAAI,EAAM,CAC7B,KAAK,IAAI,EAAM,CAAG,GAAkB,EAAW,IAC/C,AAII,EAJA,EAAQ,EACI,cAGA,gBAIf,KAAK,IAAI,EAAM,CAAG,GAAkB,EAAW,IACpD,AAII,EAJA,EAAQ,EACI,YAGA,eAIhB,IAAc,GAAI,CAElB,IAAI,EAAY,CACZ,IAAK,EAAU,QAAQ,UAAW,GAAG,CACrC,WAAY,EAAe,IAAM,EAAE,EAAE,WAAa,SAClD,QAAS,EACT,OAAQ,SAAS,EAAO,GAAG,CAC3B,KAAM,UAAU,EAAe,IAAM,EAAE,EAAE,SAAW,GAAI,GAAG,CAC3D,OAAQ,SAAS,EAAO,GAAG,CAC3B,KAAM,UAAU,EAAe,IAAM,EAAE,EAAE,SAAW,GAAI,GAAG,CAC9D,CAGD,EAAQ,cAAc,IAAI,YAAY,SAAU,CAAE,QAAS,GAAM,WAAY,GAAM,OAAQ,EAAW,CAAC,CAAC,CAGxG,EAAQ,cAAc,IAAI,YAAY,EAAW,CAAE,QAAS,GAAM,WAAY,GAAM,OAAQ,EAAW,CAAC,CAAC,CAI7G,EAAQ,KACR,EAAQ,KACR,EAAW,MAOf,SAAS,EAAiB,EAAG,CAGrB,EAAE,OAAO,aAAa,oBAAoB,GAAK,SAEnD,EAAU,EAAE,OAEZ,EAAW,KAAK,KAAK,CACrB,EAAQ,EAAE,QAAQ,GAAG,QACrB,EAAQ,EAAE,QAAQ,GAAG,QACrB,EAAQ,EACR,EAAQ,EACR,EAAa,EAAE,QAAQ,QAQ3B,SAAS,EAAgB,EAAG,CAEpB,MAAC,GAAS,CAAC,GAEf,KAAI,EAAM,EAAE,QAAQ,GAAG,QACnB,EAAM,EAAE,QAAQ,GAAG,QAEvB,EAAQ,EAAQ,EAChB,EAAQ,EAAQ,GAUpB,SAAS,EAAoB,EAAI,EAAe,EAAc,CAG1D,KAAO,GAAM,IAAOA,EAAS,iBAAiB,CAE1C,IAAI,EAAiB,EAAG,aAAa,EAAc,CAEnD,GAAI,EACA,OAAO,EAGX,EAAK,EAAG,WAGZ,OAAO,KAGb,OAAQ,SAAS,CCjKnB,IAAIC,EAEE,EAAe,GAAqC,CACpD,GAAc,aAAa,EAAa,CAE5C,IAAM,EAAa,EAAc,cAAgC,4BAA4B,CAC7F,GAAI,CAAC,EAAY,OAGjB,IAAM,EAAY,EAAc,cAAgC,mBAAmB,CACnF,GAAI,EAAW,CACb,GAAI,EAAU,MAAQ,GAAG,EAAS,kBAAkB,yBAA0B,OAE9E,EAAW,YAAsB,CAC/B,EAAW,IAAM,EAAU,KAG7B,EAAW,IAAM,EAAU,IAG7B,IAAM,EAAY,EAAW,aAAa,WAAW,CAChD,IAIL,EAAe,eAAiB,CAC9B,EAAW,IAAM,EACjB,EAAW,gBAAgB,WAAW,EACrC,IAAK,GAGJC,EACJ,SAAS,iBAAmC,4BAA4B,CAC1E,IAAK,IAAM,KAAa,EAClB,EAAU,UAAY,EAAU,eAAiB,IACnD,EAAU,IAAM,GAAG,EAAS,kBAAkB,0BAGhD,EAAU,YAAsB,CAC9B,EAAU,IAAM,GAAG,EAAS,kBAAkB,0BAKhD,SAAS,cAAiC,8BAA8B,EAC3D,MAAM,YAAY,UAAW,QAAQ,CAEpD,EAAQ,YAAe,GAAqC,CAGnC,SAAS,eAAe,UAAU,EACzC,UAAU,IAAI,oBAAoB,CAKlD,OAAO,SAAS,KAAO,gBAEvB,EAAQ,wBAAwB,CAG3B,GAEL,EAAY,EAAc,EAG5B,EAAQ,gBAA0B,CACT,SAAS,eAAe,UAAU,EACzC,UAAU,OAAO,oBAAoB,CAGjD,OAAO,SAAS,OAAS,iBAC3B,OAAO,QAAQ,MAAM,CAGvB,EAAQ,wBAAwB,EAGlC,EAAO,QAAS,gBAAiB,UAA6B,CAC5D,IAAM,EAAoB,KAAK,aAAa,0BAA0B,CAChE,EAAuB,KAAK,aAAa,8BAA8B,CACvE,EAAS,KAAK,aAAa,cAAc,CAE/C,GAAI,EAAE,GAAU,GAAqB,GAAuB,OAE5D,IAAM,EAAgB,SAAS,cAA2B,EAAO,CACjE,EAAc,EAAc,CAE5B,IAAM,EAAc,KAAK,UAAU,SAAS,YAAY,CAClD,EAAW,EAAc,EAAuB,EAChD,EAAW,EAAc,EAAoB,EAEnD,KAAK,UAAY,KAAK,UAAU,QAAQ,EAAU,EAAS,CAC3D,KAAK,UAAU,OAAO,YAAY,CAElC,EAAc,UAAU,OAAO,YAAY,EAC3C,CAEF,EAAO,QAAS,gBAAiB,UAA6B,CAC5D,IAAM,EAAS,KAAK,aAAa,cAAc,CAC/C,GAAI,CAAC,EAAQ,OAEb,IAAM,EAAa,SAAS,cAAiC,GAAG,EAAO,WAAW,CAIlF,GAHA,EAAc,EAAW,CAGrB,CADY,EAAW,aAAa,MAAM,CAChC,CACZ,IAAM,EAAU,EAAW,aAAa,WAAW,CAC/C,GACF,EAAW,aAAa,MAAO,EAAQ,GAG3C,CAEF,EAAO,QAAS,YAAa,gBAAmC,CAC9D,IAAM,EAAS,KAAK,eAAe,cAA8B,MAAM,CAGvE,GAFA,EAAc,EAAO,CAEjB,OAAO,gBACT,MAAM,UAAU,UAAU,UAAU,EAAO,UAAU,KAChD,CACL,IAAM,EAAY,OAAO,cAAc,CACvC,GAAI,EAAW,CACb,IAAM,EAAQ,SAAS,aAAa,CACpC,EAAM,mBAAmB,EAAO,CAChC,EAAU,iBAAiB,CAC3B,EAAU,SAAS,EAAM,CACzB,SAAS,YAAY,OAAO,EAIhC,IAAM,EAAa,KAAK,QAAQ,WAC5B,IACF,KAAK,UAAY,IAEnB,CAEF,EAAO,QAAS,uBAAyB,GAAiB,CACxD,EAAM,gBAAgB,CACtB,EAAQ,eAAe,EACvB,CAEF,EAAO,QAAS,0BAA4B,GAAiB,CAC3D,EAAM,gBAAgB,CACtB,EAAQ,iBAAiB,GAAM,EAC/B,CAEF,EAAO,QAAS,sBAAwB,GAAiB,CACvD,EAAM,gBAAgB,CACtB,EAAQ,aAAa,GAAM,EAC3B,CAGF,OAAO,iBAAiB,iBAAoB,CACtC,OAAO,SAAS,OAAS,iBAC3B,EAAQ,eAAe,EAEzB,CAEF,IAAME,EAA2C,SAAS,iBAA8B,oBAAoB,CAC5G,IAAK,IAAM,KAAW,EACpB,EAAO,cAAe,MAAe,CACnC,EAAQ,aAAa,GAAM,EAC3B,CAEF,EAAO,eAAgB,MAAe,CACpC,EAAQ,iBAAiB,GAAM,EAC/B,CAGJ,OAAO,iBACL,aACM,CACJ,IAAM,EAAmB,SAAS,eAAe,YAAY,CACvD,EAAiB,SAAS,eAAe,UAAU,CAEzD,GAAI,GAAoB,EAAgB,CAEtC,IAAM,GADY,SAAS,gBAAgB,WAAa,SAAS,KAAK,YACrC,IACjC,EAAe,UAAU,OAAO,YAAa,EAAY,GAG7D,GACD"} \ No newline at end of file