From 1c7e77b8546ac32b8176ab54dd06ede6ba7deb55 Mon Sep 17 00:00:00 2001 From: Hsieh Chin Fan Date: Wed, 2 Oct 2024 10:12:46 +0800 Subject: style: switch to standardjs --- src/editor.mjs | 548 ++++++++++++++++++++++++++++----------------------------- 1 file changed, 274 insertions(+), 274 deletions(-) (limited to 'src/editor.mjs') diff --git a/src/editor.mjs b/src/editor.mjs index 6f529ed..4c50e79 100644 --- a/src/editor.mjs +++ b/src/editor.mjs @@ -1,44 +1,44 @@ -/*global EasyMDE*/ -/*eslint no-undef: "error"*/ -import { markdown2HTML, generateMaps } from './dumbymap'; -import { defaultAliases, parseConfigsFromYaml } from 'mapclay'; -import * as menuItem from './MenuItem'; +/* global EasyMDE */ +/* eslint no-undef: "error" */ +import { markdown2HTML, generateMaps } from './dumbymap' +import { defaultAliases, parseConfigsFromYaml } from 'mapclay' +import * as menuItem from './MenuItem' // Set up Containers {{{ -const HtmlContainer = document.querySelector('.DumbyMap'); -const textArea = document.querySelector('.editor textarea'); -let dumbymap; +const HtmlContainer = document.querySelector('.DumbyMap') +const textArea = document.querySelector('.editor textarea') +let dumbymap -new MutationObserver(mutations => { - const mutation = mutations.at(-1); - const mode = mutation.target.getAttribute('data-mode'); - const layout = HtmlContainer.getAttribute('data-layout'); +new window.MutationObserver(mutations => { + const mutation = mutations.at(-1) + const mode = mutation.target.getAttribute('data-mode') + const layout = HtmlContainer.getAttribute('data-layout') if (mode === 'editing' && layout !== 'normal') { - HtmlContainer.setAttribute('data-layout', 'normal'); + HtmlContainer.setAttribute('data-layout', 'normal') } }).observe(document.querySelector('[data-mode]'), { attributes: true, attributeFilter: ['data-mode'], - attributeOldValue: true, -}); + attributeOldValue: true +}) const toggleEditing = () => { - const mode = document.body.getAttribute('data-mode'); - document.body.setAttribute('data-mode', mode === 'editing' ? '' : 'editing'); -}; + const mode = document.body.getAttribute('data-mode') + document.body.setAttribute('data-mode', mode === 'editing' ? '' : 'editing') +} // }}} // Set up EasyMDE {{{ // Content values for editor const defaultContent = - '## Links\n\n- [Go to marker](geo:24,121?id=foo,leaflet&text=normal "Link Test")\n\n```map\nid: foo\nuse: Maplibre\n```\n'; + '## Links\n\n- [Go to marker](geo:24,121?id=foo,leaflet&text=normal "Link Test")\n\n```map\nid: foo\nuse: Maplibre\n```\n' const editor = new EasyMDE({ element: textArea, initialValue: defaultContent, autosave: { enabled: true, - uniqueId: 'dumbymap', + uniqueId: 'dumbymap' }, indentWithTabs: false, lineNumbers: true, @@ -51,105 +51,105 @@ const editor = new EasyMDE({ map: 'Ctrl-Alt-M', debug: 'Ctrl-Alt-D', toggleUnorderedList: null, - toggleOrderedList: null, + toggleOrderedList: null }, toolbar: [ { name: 'map', title: 'Toggle Map Generation', text: '🌏', - action: () => toggleEditing(), + action: () => toggleEditing() }, { name: 'debug', title: 'Save content as URL', text: '🤔', action: () => { - const state = { content: editor.value() }; - window.location.hash = encodeURIComponent(JSON.stringify(state)); - navigator.clipboard.writeText(window.location.href); - alert('URL copied to clipboard'); - }, + const state = { content: editor.value() } + window.location.hash = encodeURIComponent(JSON.stringify(state)) + navigator.clipboard.writeText(window.location.href) + window.alert('URL copied to clipboard') + } }, { name: 'undo', title: 'Undo last editing', text: '\u27F2', - action: EasyMDE.undo, + action: EasyMDE.undo }, { name: 'redo', text: '\u27F3', title: 'Redo editing', - action: EasyMDE.redo, + action: EasyMDE.redo }, '|', { name: 'heading-1', text: 'H1', title: 'Big Heading', - action: EasyMDE['heading-1'], + action: EasyMDE['heading-1'] }, { name: 'heading-2', text: 'H2', title: 'Medium Heading', - action: EasyMDE['heading-2'], + action: EasyMDE['heading-2'] }, '|', { name: 'link', text: '\u{1F517}', title: 'Create Link', - action: EasyMDE.drawLink, + action: EasyMDE.drawLink }, { name: 'image', text: '\u{1F5BC}', title: 'Create Image', - action: EasyMDE.drawImage, + action: EasyMDE.drawImage }, '|', { name: 'Bold', text: '\u{1D401}', title: 'Bold', - action: EasyMDE.toggleBold, + action: EasyMDE.toggleBold }, { name: 'Italic', text: '\u{1D43C}', title: 'Italic', - action: EasyMDE.toggleItalic, - }, - ], -}); + action: EasyMDE.toggleItalic + } + ] +}) -const cm = editor.codemirror; +const cm = editor.codemirror const getStateFromHash = hash => { - const hashValue = hash.substring(1); - const stateString = decodeURIComponent(hashValue); + const hashValue = hash.substring(1) + const stateString = decodeURIComponent(hashValue) try { - return JSON.parse(stateString) ?? {}; + return JSON.parse(stateString) ?? {} } catch (_) { - return {}; + return {} } -}; +} const getContentFromHash = hash => { - const state = getStateFromHash(hash); - return state.content; -}; + const state = getStateFromHash(hash) + return state.content +} -const initialState = getStateFromHash(window.location.hash); -window.location.hash = ''; -const contentFromHash = initialState.content; +const initialState = getStateFromHash(window.location.hash) +window.location.hash = '' +const contentFromHash = initialState.content // Seems like autosave would overwrite initialValue, set content from hash here if (contentFromHash) { - editor.cleanup(); - editor.value(contentFromHash); + editor.cleanup() + editor.value(contentFromHash) } // }}} // Set up logic about editor content {{{ @@ -159,30 +159,30 @@ const afterMapRendered = _ => { // const lonLat = mapHolder.renderer.unproject([event.x, event.y]) // // TODO... // } -}; -markdown2HTML(HtmlContainer, editor.value()); -dumbymap = generateMaps(HtmlContainer, afterMapRendered); +} +markdown2HTML(HtmlContainer, editor.value()) +dumbymap = generateMaps(HtmlContainer, afterMapRendered) // Quick hack to style lines inside code block const addClassToCodeLines = () => { - const lines = cm.getLineHandle(0).parent.lines; - let insideCodeBlock = false; + const lines = cm.getLineHandle(0).parent.lines + let insideCodeBlock = false lines.forEach((line, index) => { if (line.text.match(/^[\u0060]{3}/)) { - insideCodeBlock = !insideCodeBlock; + insideCodeBlock = !insideCodeBlock } else if (insideCodeBlock) { - cm.addLineClass(index, 'text', 'inside-code-block'); + cm.addLineClass(index, 'text', 'inside-code-block') } else { - cm.removeLineClass(index, 'text', 'inside-code-block'); + cm.removeLineClass(index, 'text', 'inside-code-block') } - }); -}; -addClassToCodeLines(); + }) +} +addClassToCodeLines() const completeForCodeBlock = change => { - const line = change.to.line; + const line = change.to.line if (change.origin === '+input') { - const text = change.text[0]; + const text = change.text[0] // Completion for YAML doc separator if ( @@ -190,21 +190,21 @@ const completeForCodeBlock = change => { change.to.ch === 0 && insideCodeblockForMap(cm.getCursor()) ) { - cm.setSelection({ line: line, ch: 0 }, { line: line, ch: 1 }); - cm.replaceSelection(text.repeat(3) + '\n'); + cm.setSelection({ line, ch: 0 }, { line, ch: 1 }) + cm.replaceSelection(text.repeat(3) + '\n') } // Completion for Code fence if (text === '`' && change.to.ch === 0) { - cm.setSelection({ line: line, ch: 0 }, { line: line, ch: 1 }); - cm.replaceSelection(text.repeat(3)); + cm.setSelection({ line, ch: 0 }, { line, ch: 1 }) + cm.replaceSelection(text.repeat(3)) const numberOfFences = cm .getValue() .split('\n') - .filter(line => line.match(/[\u0060]{3}/)).length; + .filter(line => line.match(/[\u0060]{3}/)).length if (numberOfFences % 2 === 1) { - cm.replaceSelection('map\n\n```'); - cm.setCursor({ line: line + 1 }); + cm.replaceSelection('map\n\n```') + cm.setCursor({ line: line + 1 }) } } } @@ -212,63 +212,64 @@ const completeForCodeBlock = change => { // For YAML doc separator,
and code fence // Auto delete to start of line if (change.origin === '+delete') { - const match = change.removed[0].match(/^[-\u0060]$/)?.at(0); + const match = change.removed[0].match(/^[-\u0060]$/)?.at(0) if (match && cm.getLine(line) === match.repeat(2) && match) { - cm.setSelection({ line: line, ch: 0 }, { line: line, ch: 2 }); - cm.replaceSelection(''); + cm.setSelection({ line, ch: 0 }, { line, ch: 2 }) + cm.replaceSelection('') } } -}; - -const debounceForMap = (() => { - let timer = null; +} - return function (...args) { - dumbymap = generateMaps.apply(this, args); - // clearTimeout(timer); - // timer = setTimeout(() => { - // dumbymap = generateMaps.apply(this, args) - // }, 10); - }; -})(); +/* Disable debounce temporarily */ +// const debounceForMap = (() => { +// const timer = null +// +// return function (...args) { +// dumbymap = generateMaps.apply(this, args) +// clearTimeout(timer); +// timer = setTimeout(() => { +// dumbymap = generateMaps.apply(this, args) +// }, 10); +// } +// })() const updateDumbyMap = () => { - markdown2HTML(HtmlContainer, editor.value()); + markdown2HTML(HtmlContainer, editor.value()) // TODO Test if generate maps intantly is OK with map cache // debounceForMap(HtmlContainer, afterMapRendered) - dumbymap = generateMaps(HtmlContainer, afterMapRendered); -}; + dumbymap = generateMaps(HtmlContainer, afterMapRendered) +} -updateDumbyMap(); +updateDumbyMap() // Re-render HTML by editor content cm.on('change', (_, change) => { - updateDumbyMap(); - addClassToCodeLines(); - completeForCodeBlock(change); -}); + updateDumbyMap() + addClassToCodeLines() + completeForCodeBlock(change) +}) // Set class for focus cm.on('focus', () => { - cm.getWrapperElement().classList.add('focus'); - HtmlContainer.classList.remove('focus'); -}); + cm.getWrapperElement().classList.add('focus') + HtmlContainer.classList.remove('focus') +}) cm.on('beforeChange', (_, change) => { - const line = change.to.line; + const line = change.to.line // Don't allow more content after YAML doc separator if (change.origin.match(/^(\+input|paste)$/)) { if (cm.getLine(line) === '---' && change.text[0] !== '') { - change.cancel(); + change.cancel() } } -}); +}) // Reload editor content by hash value window.onhashchange = () => { - const content = getContentFromHash(window.location.hash); - if (content) editor.value(content); -}; + const content = getContentFromHash(window.location.hash) + if (content) editor.value(content) +} // FIXME DEBUGONLY // generateMaps(HtmlContainer) @@ -279,159 +280,159 @@ window.onhashchange = () => { // }}} // Completion in Code Blok {{{ // Elements about suggestions {{{ -const menu = document.createElement('div'); -menu.className = 'menu editor-menu'; -menu.style.display = 'none'; -menu.onclick = () => (menu.style.display = 'none'); -new MutationObserver(() => { +const menu = document.createElement('div') +menu.className = 'menu editor-menu' +menu.style.display = 'none' +menu.onclick = () => (menu.style.display = 'none') +new window.MutationObserver(() => { if (menu.style.display === 'none') { - menu.replaceChildren(); + menu.replaceChildren() } }).observe(menu, { attributes: true, - attributeFilter: ['style'], -}); -document.body.append(menu); + attributeFilter: ['style'] +}) +document.body.append(menu) -const rendererOptions = {}; +const rendererOptions = {} // }}} // Aliases for map options {{{ -const aliasesForMapOptions = {}; -const defaultApply = './dist/default.yml'; +const aliasesForMapOptions = {} +const defaultApply = './dist/default.yml' fetch(defaultApply) .then(res => res.text()) .then(rawText => { - const config = parseConfigsFromYaml(rawText)?.at(0); - Object.assign(aliasesForMapOptions, config.aliases ?? {}); + const config = parseConfigsFromYaml(rawText)?.at(0) + Object.assign(aliasesForMapOptions, config.aliases ?? {}) }) - .catch(err => console.warn(`Fail to get aliases from ${defaultApply}`, err)); + .catch(err => console.warn(`Fail to get aliases from ${defaultApply}`, err)) // }}} // FUNCTION: Check if current token is inside code block {{{ const insideCodeblockForMap = anchor => { - const token = cm.getTokenAt(anchor); + const token = cm.getTokenAt(anchor) const insideCodeBlock = token.state.overlay.codeBlock && - !cm.getLine(anchor.line).match(/^[\u0060]{3}/); - if (!insideCodeBlock) return false; + !cm.getLine(anchor.line).match(/^[\u0060]{3}/) + if (!insideCodeBlock) return false - let line = anchor.line - 1; + let line = anchor.line - 1 while (line >= 0) { - const content = cm.getLine(line); + const content = cm.getLine(line) if (content === '```map') { - return true; + return true } else if (content === '```') { - return false; + return false } - line = line - 1; + line = line - 1 } - return false; -}; + return false +} // }}} // FUNCTION: Get Renderer by cursor position in code block {{{ const getLineWithRenderer = anchor => { - const currentLine = anchor.line; - if (!cm.getLine) return null; + const currentLine = anchor.line + if (!cm.getLine) return null - const match = line => cm.getLine(line).match(/^use: /); + const match = line => cm.getLine(line).match(/^use: /) - if (match(currentLine)) return currentLine; + if (match(currentLine)) return currentLine // Look backward/forward for pattern of used renderer: /use: .+/ - let pl = currentLine - 1; + let pl = currentLine - 1 while (pl > 0 && insideCodeblockForMap(anchor)) { - const text = cm.getLine(pl); + const text = cm.getLine(pl) if (match(pl)) { - return pl; + return pl } else if (text.match(/^---|^[\u0060]{3}/)) { - break; + break } - pl = pl - 1; + pl = pl - 1 } - let nl = currentLine + 1; + let nl = currentLine + 1 while (insideCodeblockForMap(anchor)) { - const text = cm.getLine(nl); + const text = cm.getLine(nl) if (match(nl)) { - return nl; + return nl } else if (text.match(/^---|^[\u0060]{3}/)) { - return null; + return null } - nl = nl + 1; + nl = nl + 1 } - return null; -}; + return null +} // }}} // FUNCTION: Return suggestions for valid options {{{ const getSuggestionsForOptions = (optionTyped, validOptions) => { - let suggestOptions = []; + let suggestOptions = [] const matchedOptions = validOptions.filter(o => - o.valueOf().toLowerCase().includes(optionTyped.toLowerCase()), - ); + o.valueOf().toLowerCase().includes(optionTyped.toLowerCase()) + ) if (matchedOptions.length > 0) { - suggestOptions = matchedOptions; + suggestOptions = matchedOptions } else { - suggestOptions = validOptions; + suggestOptions = validOptions } return suggestOptions.map( o => new menuItem.Suggestion({ text: `${o.valueOf()}`, - replace: `${o.valueOf()}: `, - }), - ); -}; + replace: `${o.valueOf()}: ` + }) + ) +} // }}} // FUNCTION: Return suggestion for example of option value {{{ const getSuggestionFromMapOption = option => { - if (!option.example) return null; + if (!option.example) return null const text = option.example_desc ? `${option.example_desc}${option.example}` - : `${option.example}`; + : `${option.example}` return new menuItem.Suggestion({ - text: text, - replace: `${option.valueOf()}: ${option.example ?? ''}`, - }); -}; + text, + replace: `${option.valueOf()}: ${option.example ?? ''}` + }) +} // }}} // FUNCTION: Return suggestions from aliases {{{ const getSuggestionsFromAliases = option => Object.entries(aliasesForMapOptions[option.valueOf()] ?? {})?.map(record => { - const [alias, value] = record; - const valueString = JSON.stringify(value).replaceAll('"', ''); + const [alias, value] = record + const valueString = JSON.stringify(value).replaceAll('"', '') return new menuItem.Suggestion({ text: `${alias}${valueString}`, - replace: `${option.valueOf()}: ${valueString}`, - }); - }) ?? []; + replace: `${option.valueOf()}: ${valueString}` + }) + }) ?? [] // }}} // FUCNTION: Handler for map codeblock {{{ const handleTypingInCodeBlock = anchor => { - const text = cm.getLine(anchor.line); + const text = cm.getLine(anchor.line) if (text.match(/^\s\+$/) && text.length % 2 !== 0) { // TODO Completion for even number of spaces } else if (text.match(/^-/)) { // TODO Completion for YAML doc separator } else { - const suggestions = getSuggestions(anchor); - addSuggestions(anchor, suggestions); + const suggestions = getSuggestions(anchor) + addSuggestions(anchor, suggestions) } -}; +} // }}} // FUNCTION: get suggestions by current input {{{ const getSuggestions = anchor => { - const text = cm.getLine(anchor.line); + const text = cm.getLine(anchor.line) // Clear marks on text cm.findMarks({ ...anchor, ch: 0 }, { ...anchor, ch: text.length }).forEach( - m => m.clear(), - ); + m => m.clear() + ) // Mark user input invalid by case const markInputIsInvalid = () => @@ -440,231 +441,230 @@ const getSuggestions = anchor => { .markText( { ...anchor, ch: 0 }, { ...anchor, ch: text.length }, - { className: 'invalid-input' }, - ); + { className: 'invalid-input' } + ) // Check if "use: " is set - const lineWithRenderer = getLineWithRenderer(anchor); + const lineWithRenderer = getLineWithRenderer(anchor) const renderer = lineWithRenderer ? cm.getLine(lineWithRenderer).split(' ')[1] - : null; + : null if (renderer && anchor.line !== lineWithRenderer) { // Do not check properties - if (text.startsWith(' ')) return []; + if (text.startsWith(' ')) return [] // If no valid options for current used renderer, go get it! - const validOptions = rendererOptions[renderer]; + const validOptions = rendererOptions[renderer] if (!validOptions) { // Get list of valid options for current renderer - const rendererUrl = defaultAliases.use[renderer]?.value; + const rendererUrl = defaultAliases.use[renderer]?.value import(rendererUrl) .then(rendererModule => { - rendererOptions[renderer] = rendererModule.default.validOptions; - const currentAnchor = cm.getCursor(); + rendererOptions[renderer] = rendererModule.default.validOptions + const currentAnchor = cm.getCursor() if (insideCodeblockForMap(currentAnchor)) { - handleTypingInCodeBlock(currentAnchor); + handleTypingInCodeBlock(currentAnchor) } }) .catch(_ => { - markInputIsInvalid(lineWithRenderer); + markInputIsInvalid(lineWithRenderer) console.warn( - `Fail to get valid options from Renderer typed: ${renderer}`, - ); - }); - return []; + `Fail to get valid options from Renderer typed: ${renderer}` + ) + }) + return [] } // If input is "key:value" (no space left after colon), then it is invalid - const isKeyFinished = text.includes(':'); - const isValidKeyValue = text.match(/^[^:]+:\s+/); + const isKeyFinished = text.includes(':') + const isValidKeyValue = text.match(/^[^:]+:\s+/) if (isKeyFinished && !isValidKeyValue) { - markInputIsInvalid(); - return []; + markInputIsInvalid() + return [] } // If user is typing option - const keyTyped = text.split(':')[0].trim(); + const keyTyped = text.split(':')[0].trim() if (!isKeyFinished) { - markInputIsInvalid(); - return getSuggestionsForOptions(keyTyped, validOptions); + markInputIsInvalid() + return getSuggestionsForOptions(keyTyped, validOptions) } // If user is typing value - const matchedOption = validOptions.find(o => o.name === keyTyped); + const matchedOption = validOptions.find(o => o.name === keyTyped) if (isKeyFinished && !matchedOption) { - markInputIsInvalid(); + markInputIsInvalid() } if (isKeyFinished && matchedOption) { - const valueTyped = text.substring(text.indexOf(':') + 1).trim(); - const isValidValue = matchedOption.isValid(valueTyped); + const valueTyped = text.substring(text.indexOf(':') + 1).trim() + const isValidValue = matchedOption.isValid(valueTyped) if (!valueTyped) { return [ getSuggestionFromMapOption(matchedOption), - ...getSuggestionsFromAliases(matchedOption), - ].filter(s => s instanceof menuItem.Suggestion); + ...getSuggestionsFromAliases(matchedOption) + ].filter(s => s instanceof menuItem.Suggestion) } if (valueTyped && !isValidValue) { - markInputIsInvalid(); - return []; + markInputIsInvalid() + return [] } } } else { // Suggestion for "use" const rendererSuggestions = Object.entries(defaultAliases.use) .filter(([renderer]) => { - const suggestion = `use: ${renderer}`; - const suggestionPattern = suggestion.replace(' ', '').toLowerCase(); - const textPattern = text.replace(' ', '').toLowerCase(); - return suggestion !== text && suggestionPattern.includes(textPattern); + const suggestion = `use: ${renderer}` + const suggestionPattern = suggestion.replace(' ', '').toLowerCase() + const textPattern = text.replace(' ', '').toLowerCase() + return suggestion !== text && suggestionPattern.includes(textPattern) }) .map( ([renderer, info]) => new menuItem.Suggestion({ text: `use: ${renderer}`, - replace: `use: ${renderer}`, - }), - ); - return rendererSuggestions.length > 0 ? rendererSuggestions : []; + replace: `use: ${renderer}` + }) + ) + return rendererSuggestions.length > 0 ? rendererSuggestions : [] } - return []; -}; + return [] +} // }}} // {{{ FUNCTION: Show element about suggestions const addSuggestions = (anchor, suggestions) => { if (suggestions.length === 0) { - menu.style.display = 'none'; - return; + menu.style.display = 'none' + return } else { - menu.style.display = 'block'; + menu.style.display = 'block' } - menu.innerHTML = ''; + menu.innerHTML = '' suggestions .map(s => s.createElement(cm)) - .forEach(option => menu.appendChild(option)); - - const widgetAnchor = document.createElement('div'); - cm.addWidget(anchor, widgetAnchor, true); - const rect = widgetAnchor.getBoundingClientRect(); - menu.style.left = `calc(${rect.left}px + 2rem)`; - menu.style.top = `calc(${rect.bottom}px + 1rem)`; - menu.style.maxWidth = `calc(${window.innerWidth}px - ${rect.x}px - 3rem)`; - menu.style.display = 'block'; -}; + .forEach(option => menu.appendChild(option)) + + const widgetAnchor = document.createElement('div') + cm.addWidget(anchor, widgetAnchor, true) + const rect = widgetAnchor.getBoundingClientRect() + menu.style.left = `calc(${rect.left}px + 2rem)` + menu.style.top = `calc(${rect.bottom}px + 1rem)` + menu.style.maxWidth = `calc(${window.innerWidth}px - ${rect.x}px - 3rem)` + menu.style.display = 'block' +} // }}} // EVENT: Suggests for current selection {{{ // FIXME Dont show suggestion when selecting multiple chars cm.on('cursorActivity', _ => { - menu.style.display = 'none'; - const anchor = cm.getCursor(); + menu.style.display = 'none' + const anchor = cm.getCursor() if (insideCodeblockForMap(anchor)) { - handleTypingInCodeBlock(anchor); + handleTypingInCodeBlock(anchor) } -}); +}) cm.on('blur', () => { if (menu.checkVisibility()) { cm.focus() } else { - cm.getWrapperElement().classList.remove('focus'); - HtmlContainer.classList.add('focus'); + cm.getWrapperElement().classList.remove('focus') + HtmlContainer.classList.add('focus') } -}); +}) // }}} // EVENT: keydown for suggestions {{{ -const keyForSuggestions = ['Tab', 'Enter', 'Escape']; +const keyForSuggestions = ['Tab', 'Enter', 'Escape'] cm.on('keydown', (_, e) => { if ( !cm.hasFocus || !keyForSuggestions.includes(e.key) || menu.style.display === 'none' - ) - return; + ) { return } // Directly add a newline when no suggestion is selected - const currentSuggestion = menu.querySelector('.container__suggestion.focus'); - if (!currentSuggestion && e.key === 'Enter') return; + const currentSuggestion = menu.querySelector('.container__suggestion.focus') + if (!currentSuggestion && e.key === 'Enter') return // Override default behavior - e.preventDefault(); + e.preventDefault() // Suggestion when pressing Tab or Shift + Tab const nextSuggestion = currentSuggestion?.nextSibling ?? - menu.querySelector('.container__suggestion:first-child'); + menu.querySelector('.container__suggestion:first-child') const previousSuggestion = currentSuggestion?.previousSibling ?? - menu.querySelector('.container__suggestion:last-child'); - const focusSuggestion = e.shiftKey ? previousSuggestion : nextSuggestion; + menu.querySelector('.container__suggestion:last-child') + const focusSuggestion = e.shiftKey ? previousSuggestion : nextSuggestion // Current editor selection state switch (e.key) { case 'Tab': - Array.from(menu.children).forEach(s => s.classList.remove('focus')); - focusSuggestion.classList.add('focus'); - focusSuggestion.scrollIntoView({ behavior: 'smooth', block: 'nearest' }); - break; + Array.from(menu.children).forEach(s => s.classList.remove('focus')) + focusSuggestion.classList.add('focus') + focusSuggestion.scrollIntoView({ behavior: 'smooth', block: 'nearest' }) + break case 'Enter': - currentSuggestion.onclick(); - break; + currentSuggestion.onclick() + break case 'Escape': - if (!menu.checkVisibility()) break; + if (!menu.checkVisibility()) break // HACK delay menu display change for blur event, mark cm focus should keep - setTimeout(() => (menu.style.display = 'none'), 50); - break; + setTimeout(() => (menu.style.display = 'none'), 50) + break } -}); +}) document.onkeydown = e => { if (e.altKey && e.ctrlKey && e.key === 'm') { - toggleEditing(); - e.preventDefault(); - return null; + toggleEditing() + e.preventDefault() + return null } if (!cm.hasFocus()) { if (e.key === 'F1') { - e.preventDefault(); - cm.focus(); + e.preventDefault() + cm.focus() } if (e.key === 'Tab') { - e.preventDefault(); - dumbymap.utils.focusNextMap(e.shiftKey); + e.preventDefault() + dumbymap.utils.focusNextMap(e.shiftKey) } if (e.key === 'x' || e.key === 'X') { - e.preventDefault(); - dumbymap.utils.switchToNextLayout(e.shiftKey); + e.preventDefault() + dumbymap.utils.switchToNextLayout(e.shiftKey) } if (e.key === 'n') { - e.preventDefault(); - dumbymap.utils.focusNextBlock(); + e.preventDefault() + dumbymap.utils.focusNextBlock() } if (e.key === 'p') { - e.preventDefault(); - dumbymap.utils.focusNextBlock(true); + e.preventDefault() + dumbymap.utils.focusNextBlock(true) } if (e.key === 'Escape') { - e.preventDefault(); - dumbymap.utils.removeBlockFocus(); + e.preventDefault() + dumbymap.utils.removeBlockFocus() } } -}; +} // }}} // }}} // Layout Switch {{{ -new MutationObserver(mutaions => { - const mutation = mutaions.at(-1); - const layout = HtmlContainer.getAttribute('data-layout'); +new window.MutationObserver(mutaions => { + const mutation = mutaions.at(-1) + const layout = HtmlContainer.getAttribute('data-layout') if (layout !== 'normal' || mutation.oldValue === 'normal') { - document.body.setAttribute('data-mode', ''); + document.body.setAttribute('data-mode', '') } }).observe(HtmlContainer, { attributes: true, attributeFilter: ['data-layout'], - attributeOldValue: true, -}); + attributeOldValue: true +}) // }}} // vim: sw=2 ts=2 foldmethod=marker foldmarker={{{,}}} -- cgit v1.2.3-70-g09d2