From f598dcbf6283f2c2b81a63d315548c6bc4e943fb Mon Sep 17 00:00:00 2001 From: Hsieh Chin Fan Date: Sun, 29 Sep 2024 15:06:59 +0800 Subject: style: prettier --- src/Layout.mjs | 88 ++++++++++++------------ src/MenuItem.mjs | 34 +++++----- src/css/dumbymap.css | 4 +- src/css/style.css | 2 +- src/dumbyUtils.mjs | 28 ++++---- src/dumbymap.mjs | 174 ++++++++++++++++++++++++----------------------- src/editor.mjs | 188 +++++++++++++++++++++++++-------------------------- src/utils.mjs | 8 +-- 8 files changed, 264 insertions(+), 262 deletions(-) (limited to 'src') diff --git a/src/Layout.mjs b/src/Layout.mjs index 9cf2ab9..3a835c9 100644 --- a/src/Layout.mjs +++ b/src/Layout.mjs @@ -1,9 +1,9 @@ -import PlainDraggable from "plain-draggable"; -import { onRemove, animateRectTransition } from "./utils"; +import PlainDraggable from 'plain-draggable'; +import { onRemove, animateRectTransition } from './utils'; export class Layout { constructor(options = {}) { - if (!options.name) throw Error("Layout name is not given"); + if (!options.name) throw Error('Layout name is not given'); this.name = options.name; this.enterHandler = options.enterHandler; this.leaveHandler = options.leaveHandler; @@ -12,63 +12,63 @@ export class Layout { } export class SideBySide extends Layout { - name = "side-by-side"; + name = 'side-by-side'; enterHandler = ({ container, htmlHolder, showcase }) => { - const bar = document.createElement("div"); - bar.className = "bar"; + const bar = document.createElement('div'); + bar.className = 'bar'; bar.innerHTML = '
'; - const handle = bar.querySelector(".bar-handle"); + const handle = bar.querySelector('.bar-handle'); container.appendChild(bar); // Resize views by value const resizeByLeft = left => { - htmlHolder.style.width = left + "px"; + htmlHolder.style.width = left + 'px'; showcase.style.width = - parseFloat(getComputedStyle(container).width) - left + "px"; + parseFloat(getComputedStyle(container).width) - left + 'px'; }; const draggable = new PlainDraggable(bar, { handle: handle, - containment: { left: "25%", top: 0, right: "75%", height: 0 }, + containment: { left: '25%', top: 0, right: '75%', height: 0 }, }); - draggable.draggableCursor = "grab"; + draggable.draggableCursor = 'grab'; draggable.onDrag = pos => { - handle.style.transform = "unset"; + handle.style.transform = 'unset'; resizeByLeft(pos.left); }; draggable.onDragEnd = _ => { - handle.removeAttribute("style"); + handle.removeAttribute('style'); }; onRemove(bar, () => draggable.remove()); }; leaveHandler = ({ container }) => { - container.querySelector(".bar")?.remove(); + container.querySelector('.bar')?.remove(); }; } export class Overlay extends Layout { - name = "overlay"; + name = 'overlay'; saveLeftTopAsData = element => { const { left, top } = element.getBoundingClientRect(); - element.setAttribute("data-left", left); - element.setAttribute("data-top", top); + element.setAttribute('data-left', left); + element.setAttribute('data-top', top); }; addDraggable = element => { // Make sure current element always on top const siblings = Array.from( - element.parentElement?.querySelectorAll(":scope > *") ?? [], + element.parentElement?.querySelectorAll(':scope > *') ?? [], ); let popTimer = null; element.onmouseover = () => { popTimer = setTimeout(() => { - siblings.forEach(e => e.style.removeProperty("z-index")); - element.style.zIndex = "9001"; + siblings.forEach(e => e.style.removeProperty('z-index')); + element.style.zIndex = '9001'; }, 200); }; element.onmouseout = () => { @@ -76,9 +76,9 @@ export class Overlay extends Layout { }; // Add draggable part - const draggablePart = document.createElement("div"); + const draggablePart = document.createElement('div'); element.appendChild(draggablePart); - draggablePart.className = "draggable-part"; + draggablePart.className = 'draggable-part'; draggablePart.innerHTML = '
\u2630
'; // Add draggable instance @@ -91,16 +91,16 @@ export class Overlay extends Layout { }); // FIXME use pure CSS to hide utils - const utils = element.querySelector(".utils"); + const utils = element.querySelector('.utils'); draggable.onDragStart = () => { - utils.style.display = "none"; - element.classList.add("drag"); + utils.style.display = 'none'; + element.classList.add('drag'); }; draggable.onDragEnd = () => { - utils.style = ""; - element.classList.remove("drag"); - element.style.zIndex = "9000"; + utils.style = ''; + element.classList.remove('drag'); + element.style.zIndex = '9000'; }; // Reposition draggable instance when resized @@ -125,12 +125,12 @@ export class Overlay extends Layout { // Create draggable blocks and set each position by previous one let [left, top] = [20, 20]; blocks.forEach(block => { - const originLeft = Number(block.getAttribute("data-left")); - const originTop = Number(block.getAttribute("data-top")); + const originLeft = Number(block.getAttribute('data-left')); + const originTop = Number(block.getAttribute('data-top')); // Create draggable block - const wrapper = document.createElement("div"); - wrapper.classList.add("draggable-block"); + const wrapper = document.createElement('div'); + wrapper.classList.add('draggable-block'); wrapper.innerHTML = `
\u274C
@@ -138,18 +138,18 @@ export class Overlay extends Layout {
\u2796
`; - wrapper.title = "Middle-click to hide block"; + wrapper.title = 'Middle-click to hide block'; wrapper.onmouseup = e => { // Hide block with middle click if (e.button === 1) { - wrapper.classList.add("hide"); + wrapper.classList.add('hide'); } }; // Set DOMRect for wrapper wrapper.appendChild(block); - wrapper.style.left = left + "px"; - wrapper.style.top = top + "px"; + wrapper.style.left = left + 'px'; + wrapper.style.top = top + 'px'; htmlHolder.appendChild(wrapper); const { width } = wrapper.getBoundingClientRect(); left += width + 30; @@ -168,7 +168,7 @@ export class Overlay extends Layout { // Trivial case: // This hack make sure utils remains at the same place even when wrapper resized // Prevent DOMRect changes when user clicking plus/minus button many times - const utils = wrapper.querySelector(".utils"); + const utils = wrapper.querySelector('.utils'); utils.onmouseover = () => { const { left, top } = utils.getBoundingClientRect(); utils.style.cssText = `visibility: visible; z-index: 9000; position: fixed; transition: unset; left: ${left}px; top: ${top}px;`; @@ -176,20 +176,20 @@ export class Overlay extends Layout { }; utils.onmouseout = () => { wrapper.appendChild(utils); - utils.removeAttribute("style"); + utils.removeAttribute('style'); }; // Close button - wrapper.querySelector("#close").onclick = () => { - wrapper.classList.add("hide"); - utils.removeAttribute("style"); + wrapper.querySelector('#close').onclick = () => { + wrapper.classList.add('hide'); + utils.removeAttribute('style'); }; // Plus/Minus font-size of content - wrapper.querySelector("#plus-font-size").onclick = () => { + wrapper.querySelector('#plus-font-size').onclick = () => { const fontSize = parseFloat(getComputedStyle(block).fontSize) / 16; block.style.fontSize = `${fontSize + 0.2}rem`; }; - wrapper.querySelector("#minus-font-size").onclick = () => { + wrapper.querySelector('#minus-font-size').onclick = () => { const fontSize = parseFloat(getComputedStyle(block).fontSize) / 16; block.style.fontSize = `${fontSize - 0.2}rem`; }; @@ -198,7 +198,7 @@ export class Overlay extends Layout { leaveHandler = ({ htmlHolder, blocks }) => { const resumeFromDraggable = block => { - const draggableContainer = block.closest(".draggable-block"); + const draggableContainer = block.closest('.draggable-block'); if (!draggableContainer) return; htmlHolder.appendChild(block); draggableContainer.remove(); diff --git a/src/MenuItem.mjs b/src/MenuItem.mjs index 2f9aff4..734c313 100644 --- a/src/MenuItem.mjs +++ b/src/MenuItem.mjs @@ -1,8 +1,8 @@ -import { createGeoLink } from "./dumbymap"; +import { createGeoLink } from './dumbymap'; export function nextMap() { - const element = document.createElement("div"); - element.className = "menu-item"; + const element = document.createElement('div'); + element.className = 'menu-item'; element.innerHTML = 'Next Map (Tab)'; element.onclick = () => this.utils.focusNextMap(); @@ -10,8 +10,8 @@ export function nextMap() { } export function nextBlock() { - const element = document.createElement("div"); - element.className = "menu-item"; + const element = document.createElement('div'); + element.className = 'menu-item'; element.innerHTML = 'Next Block (n)'; element.onclick = () => this.utils.focusNextBlock(); @@ -19,8 +19,8 @@ export function nextBlock() { } export function nextLayout() { - const element = document.createElement("div"); - element.className = "menu-item"; + const element = document.createElement('div'); + element.className = 'menu-item'; element.innerHTML = 'Next Layout (x)'; element.onclick = () => this.utils.switchToNextLayout(); @@ -33,9 +33,9 @@ export class GeoLink { } createElement = () => { - const element = document.createElement("div"); - element.className = "menu-item"; - element.innerText = "Add GeoLink"; + const element = document.createElement('div'); + element.className = 'menu-item'; + element.innerText = 'Add GeoLink'; element.onclick = this.addGeoLinkbyRange; return element; @@ -49,7 +49,7 @@ export class GeoLink { if (!match) return false; const [x, y] = match.slice(1); - const anchor = document.createElement("a"); + const anchor = document.createElement('a'); anchor.textContent = content; // FIXME apply WGS84 anchor.href = `geo:${y},${x}?xy=${x},${y}`; @@ -67,21 +67,21 @@ export class Suggestion { } createElement(codemirror) { - const option = document.createElement("div"); - if (this.text.startsWith("<")) { + const option = document.createElement('div'); + if (this.text.startsWith('<')) { option.innerHTML = this.text; } else { option.innerText = this.text; } - option.classList.add("container__suggestion"); + option.classList.add('container__suggestion'); option.onmouseover = () => { Array.from(option.parentElement?.children ?? []).forEach(s => - s.classList.remove("focus"), + s.classList.remove('focus'), ); - option.classList.add("focus"); + option.classList.add('focus'); }; option.onmouseout = () => { - option.classList.remove("focus"); + option.classList.remove('focus'); }; option.onclick = () => { const anchor = codemirror.getCursor(); diff --git a/src/css/dumbymap.css b/src/css/dumbymap.css index c6782fc..f04b1fb 100644 --- a/src/css/dumbymap.css +++ b/src/css/dumbymap.css @@ -101,12 +101,12 @@ root { transform: translate(-50%, -50%); } - &[data-render='fulfilled'][data-report$="\20"]::after { + &[data-render='fulfilled'][data-report$='\20']::after { content: '\2714 ' attr(data-report); animation: 1.5s forwards fade-out cubic-bezier(0.44, 0.18, 0.86, -0.21); } - &[data-render='unfulfilled'][data-report$="\20"]::after { + &[data-render='unfulfilled'][data-report$='\20']::after { content: '\2716 ' attr(data-report); animation: 2.5s forwards fade-out cubic-bezier(0.44, 0.18, 0.86, -0.21); } diff --git a/src/css/style.css b/src/css/style.css index d98e871..ecc7dd8 100644 --- a/src/css/style.css +++ b/src/css/style.css @@ -84,7 +84,7 @@ a { color: #007bff; text-decoration: none; - &[href^=http]::after { + &[href^='http']::after { content: ''; display: inline-block; width: 11px; diff --git a/src/dumbyUtils.mjs b/src/dumbyUtils.mjs index d30145c..cfac00b 100644 --- a/src/dumbyUtils.mjs +++ b/src/dumbyUtils.mjs @@ -1,16 +1,16 @@ export function focusNextMap(reverse = false) { const renderedList = Array.from( - this.htmlHolder.querySelectorAll("[data-render=fulfilled]"), + this.htmlHolder.querySelectorAll('[data-render=fulfilled]'), ); const mapNum = renderedList.length; if (mapNum === 0) return; // Get current focused map element - const currentFocus = this.container.querySelector(".mapclay.focus"); + const currentFocus = this.container.querySelector('.mapclay.focus'); // Remove class name of focus for ALL candidates // This may trigger animation - renderedList.forEach(ele => ele.classList.remove("focus")); + renderedList.forEach(ele => ele.classList.remove('focus')); // Get next existing map element const padding = reverse ? -1 : 1; @@ -19,18 +19,18 @@ export function focusNextMap(reverse = false) { : 0; nextIndex = (nextIndex + mapNum) % mapNum; const nextFocus = renderedList[nextIndex]; - nextFocus.classList.add("focus"); + nextFocus.classList.add('focus'); return nextFocus; } export function focusDelay() { - return window.getComputedStyle(this.showcase).display === "none" ? 50 : 300; + return window.getComputedStyle(this.showcase).display === 'none' ? 50 : 300; } export function switchToNextLayout(reverse = false) { const layouts = this.layouts; - const currentLayoutName = this.container.getAttribute("data-layout"); + const currentLayoutName = this.container.getAttribute('data-layout'); const currentIndex = layouts.map(l => l.name).indexOf(currentLayoutName); const padding = reverse ? -1 : 1; const nextIndex = @@ -38,7 +38,7 @@ export function switchToNextLayout(reverse = false) { ? 0 : (currentIndex + padding + layouts.length) % layouts.length; const nextLayout = layouts[nextIndex]; - this.container.setAttribute("data-layout", nextLayout.name); + this.container.setAttribute('data-layout', nextLayout.name); } export function focusNextBlock(reverse = false) { @@ -49,7 +49,7 @@ export function focusNextBlock(reverse = false) { visibilityProperty: true, }), ); - const currentBlock = blocks.find(b => b.classList.contains("focus")); + const currentBlock = blocks.find(b => b.classList.contains('focus')); const currentIndex = blocks.indexOf(currentBlock); const padding = reverse ? -1 : 1; const nextIndex = @@ -57,16 +57,16 @@ export function focusNextBlock(reverse = false) { ? 0 : (currentIndex + padding + blocks.length) % blocks.length; const nextBlock = blocks[nextIndex]; - blocks.forEach(b => b.classList.remove("focus")); - nextBlock?.classList?.add("focus"); + blocks.forEach(b => b.classList.remove('focus')); + nextBlock?.classList?.add('focus'); const scrollBlock = nextBlock.getBoundingClientRect().height > nextBlock.parentElement.getBoundingClientRect().height * 0.8 - ? "nearest" - : "center"; - nextBlock.scrollIntoView({ behavior: "smooth", block: scrollBlock }); + ? 'nearest' + : 'center'; + nextBlock.scrollIntoView({ behavior: 'smooth', block: scrollBlock }); } export function removeBlockFocus() { - this.blocks.forEach(b => b.classList.remove("focus")); + this.blocks.forEach(b => b.classList.remove('focus')); } diff --git a/src/dumbymap.mjs b/src/dumbymap.mjs index 241c6b9..dc22021 100644 --- a/src/dumbymap.mjs +++ b/src/dumbymap.mjs @@ -1,21 +1,21 @@ -import MarkdownIt from "markdown-it"; -import MarkdownItAnchor from "markdown-it-anchor"; -import MarkdownItFootnote from "markdown-it-footnote"; -import MarkdownItFrontMatter from "markdown-it-front-matter"; -import MarkdownItTocDoneRight from "markdown-it-toc-done-right"; -import LeaderLine from "leader-line"; -import { renderWith, defaultAliases, parseConfigsFromYaml } from "mapclay"; -import { onRemove, animateRectTransition, throttle } from "./utils"; -import { Layout, SideBySide, Overlay } from "./Layout"; -import * as utils from "./dumbyUtils"; +import MarkdownIt from 'markdown-it'; +import MarkdownItAnchor from 'markdown-it-anchor'; +import MarkdownItFootnote from 'markdown-it-footnote'; +import MarkdownItFrontMatter from 'markdown-it-front-matter'; +import MarkdownItTocDoneRight from 'markdown-it-toc-done-right'; +import LeaderLine from 'leader-line'; +import { renderWith, defaultAliases, parseConfigsFromYaml } from 'mapclay'; +import { onRemove, animateRectTransition, throttle } from './utils'; +import { Layout, SideBySide, Overlay } from './Layout'; +import * as utils from './dumbyUtils'; const docLinkSelector = 'a[href^="#"][title^="=>"]'; const geoLinkSelector = 'a[href^="geo:"]'; const layouts = [ - new Layout({ name: "normal" }), - new SideBySide({ name: "side-by-side" }), - new Overlay({ name: "overlay" }), + new Layout({ name: 'normal' }), + new SideBySide({ name: 'side-by-side' }), + new Overlay({ name: 'overlay' }), ]; const mapCache = {}; @@ -26,12 +26,12 @@ const mapCache = {}; * @param {HTMLElement} Elements contains anchor elements for doclinks */ export const createDocLink = link => { - link.classList.add("with-leader-line", "doclink"); + link.classList.add('with-leader-line', 'doclink'); link.lines = []; link.onmouseover = () => { - const label = decodeURIComponent(link.href.split("#")[1]); - const selector = link.title.split("=>")[1] ?? "#" + label; + const label = decodeURIComponent(link.href.split('#')[1]); + const selector = link.title.split('=>')[1] ?? '#' + label; const target = document.querySelector(selector); if (!target?.checkVisibility()) return; @@ -40,13 +40,13 @@ export const createDocLink = link => { end: target, middleLabel: LeaderLine.pathLabel({ text: label, - fontWeight: "bold", + fontWeight: 'bold', }), hide: true, - path: "magnet", + path: 'magnet', }); link.lines.push(line); - line.show("draw", { duration: 300 }); + line.show('draw', { duration: 300 }); }; link.onmouseout = () => { link.lines.forEach(line => line.remove()); @@ -63,13 +63,13 @@ export const createDocLink = link => { */ export const createGeoLink = (link, callback = null) => { const url = new URL(link.href); - const xyInParams = url.searchParams.get("xy"); + const xyInParams = url.searchParams.get('xy'); const xy = xyInParams - ? xyInParams.split(",")?.map(Number) + ? xyInParams.split(',')?.map(Number) : url?.href ?.match(/^geo:([0-9.,]+)/) ?.at(1) - ?.split(",") + ?.split(',') ?.reverse() ?.map(Number); @@ -78,8 +78,8 @@ export const createGeoLink = (link, callback = null) => { // Geo information in link link.url = url; link.xy = xy; - link.classList.add("with-leader-line", "geolink"); - link.targets = link.url.searchParams.get("id")?.split(",") ?? null; + link.classList.add('with-leader-line', 'geolink'); + link.targets = link.url.searchParams.get('id')?.split(',') ?? null; // LeaderLine link.lines = []; @@ -94,7 +94,7 @@ export const markdown2HTML = (container, mdContent) => { Array.from(container.children).map(e => e.remove()); container.innerHTML = '
'; - const htmlHolder = container.querySelector(".SemanticHtml"); + const htmlHolder = container.querySelector('.SemanticHtml'); const md = MarkdownIt({ html: true, @@ -102,7 +102,7 @@ export const markdown2HTML = (container, mdContent) => { }) .use(MarkdownItAnchor, { permalink: MarkdownItAnchor.permalink.linkInsideHeader({ - placement: "before", + placement: 'before', }), }) .use(MarkdownItFootnote) @@ -110,49 +110,49 @@ export const markdown2HTML = (container, mdContent) => { .use(MarkdownItTocDoneRight); // FIXME A better way to generate blocks - md.renderer.rules.dumby_block_open = () => "
"; - md.renderer.rules.dumby_block_close = () => "
"; + md.renderer.rules.dumby_block_open = () => '
'; + md.renderer.rules.dumby_block_close = () => '
'; - md.core.ruler.before("block", "dumby_block", state => { - state.tokens.push(new state.Token("dumby_block_open", "", 1)); + md.core.ruler.before('block', 'dumby_block', state => { + state.tokens.push(new state.Token('dumby_block_open', '', 1)); }); // Add close tag for block with more than 2 empty lines - md.block.ruler.before("table", "dumby_block", (state, startLine) => { + md.block.ruler.before('table', 'dumby_block', (state, startLine) => { if ( - state.src[state.bMarks[startLine - 1]] === "\n" && - state.src[state.bMarks[startLine - 2]] === "\n" && - state.tokens.at(-1).type !== "list_item_open" // Quick hack for not adding tag after "::marker" for
  • + state.src[state.bMarks[startLine - 1]] === '\n' && + state.src[state.bMarks[startLine - 2]] === '\n' && + state.tokens.at(-1).type !== 'list_item_open' // Quick hack for not adding tag after "::marker" for
  • ) { - state.push("dumby_block_close", "", -1); - state.push("dumby_block_open", "", 1); + state.push('dumby_block_close', '', -1); + state.push('dumby_block_open', '', 1); } }); - md.core.ruler.after("block", "dumby_block", state => { - state.tokens.push(new state.Token("dumby_block_close", "", -1)); + md.core.ruler.after('block', 'dumby_block', state => { + state.tokens.push(new state.Token('dumby_block_close', '', -1)); }); - const contentWithToc = "${toc}\n\n\n" + mdContent; + const contentWithToc = '${toc}\n\n\n' + mdContent; htmlHolder.innerHTML = md.render(contentWithToc); // TODO Do this in markdown-it - const blocks = htmlHolder.querySelectorAll(":scope > div:not(:has(nav))"); + const blocks = htmlHolder.querySelectorAll(':scope > div:not(:has(nav))'); blocks.forEach(b => { - b.classList.add("dumby-block"); - b.setAttribute("data-total", blocks.length); + b.classList.add('dumby-block'); + b.setAttribute('data-total', blocks.length); }); return container; //}}} }; export const generateMaps = (container, { delay, mapCallback }) => { - container.classList.add("Dumby"); - const htmlHolder = container.querySelector(".SemanticHtml") ?? container; - const blocks = Array.from(htmlHolder.querySelectorAll(".dumby-block")); - const showcase = document.createElement("div"); + container.classList.add('Dumby'); + const htmlHolder = container.querySelector('.SemanticHtml') ?? container; + const blocks = Array.from(htmlHolder.querySelectorAll('.dumby-block')); + const showcase = document.createElement('div'); container.appendChild(showcase); - showcase.classList.add("Showcase"); + showcase.classList.add('Showcase'); const renderMaps = []; const dumbymap = { @@ -196,13 +196,13 @@ export const generateMaps = (container, { delay, mapCallback }) => { ).filter(l => createGeoLink(l, geoLinkCallback)); const isAnchorPointedBy = link => anchor => { - const mapContainer = anchor.closest(".mapclay"); + const mapContainer = anchor.closest('.mapclay'); const isTarget = !link.targets || link.targets.includes(mapContainer.id); return anchor.title === link.url.pathname && isTarget; }; const isAnchorVisible = anchor => { - const mapContainer = anchor.closest(".mapclay"); + const mapContainer = anchor.closest('.mapclay'); return insideWindow(anchor) && insideParent(anchor, mapContainer); }; @@ -211,10 +211,10 @@ export const generateMaps = (container, { delay, mapCallback }) => { start: link, end: anchor, hide: true, - middleLabel: link.url.searchParams.get("text"), - path: "magnet", + middleLabel: link.url.searchParams.get('text'), + path: 'magnet', }); - line.show("draw", { duration: 300 }); + line.show('draw', { duration: 300 }); return line; }; @@ -232,7 +232,7 @@ export const generateMaps = (container, { delay, mapCallback }) => { }; const updateMapByMarker = xy => marker => { - const renderer = marker.closest(".mapclay")?.renderer; + const renderer = marker.closest('.mapclay')?.renderer; renderer.updateCamera({ center: xy }, true); }; @@ -273,7 +273,7 @@ export const generateMaps = (container, { delay, mapCallback }) => { const target = mutation.target; const focus = target .getAttribute(mutation.attributeName) - .includes("focus"); + .includes('focus'); const shouldBeInShowcase = focus && showcase.checkVisibility({ @@ -287,8 +287,8 @@ export const generateMaps = (container, { delay, mapCallback }) => { // Placeholder for map in Showcase, it should has the same DOMRect const placeholder = target.cloneNode(true); - placeholder.removeAttribute("id"); - placeholder.classList.remove("mapclay", "focus"); + placeholder.removeAttribute('id'); + placeholder.classList.remove('mapclay', 'focus'); target.parentElement.replaceChild(placeholder, target); // FIXME Maybe use @start-style for CSS @@ -297,10 +297,10 @@ export const generateMaps = (container, { delay, mapCallback }) => { // To make sure the original height of placeholder is applied, DOM changes seems needed // then set data-attribute for CSS selector to change height to 0 placeholder.getBoundingClientRect(); - placeholder.setAttribute("data-placeholder", target.id); + placeholder.setAttribute('data-placeholder', target.id); // To fit showcase, remove all inline style - target.removeAttribute("style"); + target.removeAttribute('style'); showcase.appendChild(target); // Resume rect from Semantic HTML to Showcase, with animation @@ -333,7 +333,7 @@ export const generateMaps = (container, { delay, mapCallback }) => { // Layout {{{ // press key to switch layout const defaultLayout = layouts[0]; - container.setAttribute("data-layout", defaultLayout.name); + container.setAttribute('data-layout', defaultLayout.name); // observe layout change const layoutObserver = new MutationObserver(mutations => { @@ -351,7 +351,7 @@ export const generateMaps = (container, { delay, mapCallback }) => { Object.values(dumbymap) .flat() .filter(ele => ele instanceof HTMLElement) - .forEach(ele => ele.removeAttribute("style")); + .forEach(ele => ele.removeAttribute('style')); if (newLayout) { layouts @@ -362,13 +362,13 @@ export const generateMaps = (container, { delay, mapCallback }) => { // Since layout change may show/hide showcase, the current focused map should do something // Reset attribute triggers MutationObserver which is observing it const focusMap = - container.querySelector(".mapclay.focus") ?? - container.querySelector(".mapclay"); - focusMap?.classList?.add("focus"); + container.querySelector('.mapclay.focus') ?? + container.querySelector('.mapclay'); + focusMap?.classList?.add('focus'); }); layoutObserver.observe(container, { attributes: true, - attributeFilter: ["data-layout"], + attributeFilter: ['data-layout'], attributeOldValue: true, characterDataOldValue: true, }); @@ -380,8 +380,10 @@ export const generateMaps = (container, { delay, mapCallback }) => { const afterMapRendered = renderer => { const mapElement = renderer.target; - mapElement.setAttribute("tabindex", "-1"); - if (mapElement.getAttribute("data-render") === "fulfilled") { + //FIXME + mapElement.renderer = renderer; + mapElement.setAttribute('tabindex', '-1'); + if (mapElement.getAttribute('data-render') === 'fulfilled') { mapCache[mapElement.id] = renderer; } @@ -394,14 +396,14 @@ export const generateMaps = (container, { delay, mapCallback }) => { // Add markers with Geolinks renderer.addMarkers(markers); mapElement - .querySelectorAll(".marker") + .querySelectorAll('.marker') .forEach(marker => htmlHolder.anchors.push(marker)); // Work with Mutation Observer const observer = mapFocusObserver(); mapFocusObserver().observe(mapElement, { attributes: true, - attributeFilter: ["class"], + attributeFilter: ['class'], attributeOldValue: true, }); onRemove(mapElement, () => observer.disconnect()); @@ -412,10 +414,10 @@ export const generateMaps = (container, { delay, mapCallback }) => { const assignMapId = config => { let mapId = config.id; if (!mapId) { - mapId = config.use?.split("/")?.at(-1); + mapId = config.use?.split('/')?.at(-1); let counter = 1; while (!mapId || mapIdList.includes(mapId)) { - mapId = `${config.use ?? "unnamed"}-${counter}`; + mapId = `${config.use ?? 'unnamed'}-${counter}`; counter++; } config.id = mapId; @@ -426,21 +428,21 @@ export const generateMaps = (container, { delay, mapCallback }) => { // Render each code block with "language-map" class const elementsWithMapConfig = Array.from( - container.querySelectorAll('pre:has(.language-map)') ?? [] - ) + container.querySelectorAll('pre:has(.language-map)') ?? [], + ); /** * updateAttributeByStep. * * @param {Object} -- renderer which is running steps */ const updateAttributeByStep = ({ results, target, steps }) => { - let passNum = results - .filter(r => r.type === 'step' && r.state.match(/success|skip/)) - .length + let passNum = results.filter( + r => r.type === 'step' && r.state.match(/success|skip/), + ).length; const total = steps.length; - passNum += `/${total}`; - if (results.filter(r=>r.type === 'step').length === total) { - passNum += '\u0020' + passNum += `/${total}`; + if (results.filter(r => r.type === 'step').length === total) { + passNum += '\u0020'; } // FIXME HACK use MutationObserver for animation @@ -449,7 +451,7 @@ export const generateMaps = (container, { delay, mapCallback }) => { await new Promise(resolve => setTimeout(resolve, 150)); target.setAttribute('data-report', passNum); }); - } + }; /** * config converter for mapclay.renderWith() * @@ -465,21 +467,21 @@ export const generateMaps = (container, { delay, mapCallback }) => { ...(config.aliases ?? {}), }, stepCallback: updateAttributeByStep, - }) - const render = renderWith(configConverter) + }); + const render = renderWith(configConverter); elementsWithMapConfig.forEach(target => { // Get text in code block starts with markdown text '```map' const configText = target - .querySelector(".language-map") + .querySelector('.language-map') .textContent // BE CAREFUL!!! 0xa0 char is "non-breaking spaces" in HTML text content // replace it by normal space - .replace(/\u00A0/g, "\u0020"); + .replace(/\u00A0/g, '\u0020'); let configList = []; try { configList = parseConfigsFromYaml(configText).map(assignMapId); } catch (_) { - console.warn("Fail to parse yaml config for element", target); + console.warn('Fail to parse yaml config for element', target); return; } @@ -494,9 +496,9 @@ export const generateMaps = (container, { delay, mapCallback }) => { }); // trivial: if map cache is applied, do not show yaml text - if (target.querySelector(".mapclay")) { + if (target.querySelector('.mapclay')) { target - .querySelectorAll(":scope > :not([data-render=fulfilled])") + .querySelectorAll(':scope > :not([data-render=fulfilled])') .forEach(e => e.remove()); } diff --git a/src/editor.mjs b/src/editor.mjs index 73177b6..f80ff07 100644 --- a/src/editor.mjs +++ b/src/editor.mjs @@ -1,22 +1,22 @@ /*global EasyMDE*/ /*eslint no-undef: "error"*/ -import { markdown2HTML, generateMaps } from "./dumbymap"; -import { defaultAliases, parseConfigsFromYaml } from "mapclay"; -import * as menuItem from "./MenuItem"; +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"); +const HtmlContainer = document.querySelector('.DumbyMap'); +const textArea = document.querySelector('.editor textarea'); let dumbymap; const toggleEditing = () => { - if (document.body.getAttribute("data-mode") === "editing") { - document.body.removeAttribute("data-mode"); + if (document.body.getAttribute('data-mode') === 'editing') { + document.body.removeAttribute('data-mode'); } else { - document.body.setAttribute("data-mode", "editing"); + document.body.setAttribute('data-mode', 'editing'); } - HtmlContainer.setAttribute("data-layout", "normal"); + HtmlContainer.setAttribute('data-layout', 'normal'); }; // }}} // Set up EasyMDE {{{ @@ -30,37 +30,37 @@ const editor = new EasyMDE({ initialValue: defaultContent, autosave: { enabled: true, - uniqueId: "dumbymap", + uniqueId: 'dumbymap', }, indentWithTabs: false, lineNumbers: true, promptURLs: true, uploadImage: true, spellChecker: false, - toolbarButtonClassPrefix: "mde", + toolbarButtonClassPrefix: 'mde', status: false, shortcuts: { - map: "Ctrl-Alt-M", - debug: "Ctrl-Alt-D", + map: 'Ctrl-Alt-M', + debug: 'Ctrl-Alt-D', toggleUnorderedList: null, toggleOrderedList: null, }, toolbar: [ { - name: "map", - title: "Toggle Map Generation", - text: "🌏", + name: 'map', + title: 'Toggle Map Generation', + text: '🌏', action: () => toggleEditing(), }, { - name: "debug", - title: "Save content as URL", - text: "🤔", + 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"); + alert('URL copied to clipboard'); }, }, "undo", @@ -103,7 +103,7 @@ const getContentFromHash = hash => { }; const initialState = getStateFromHash(window.location.hash); -window.location.hash = ""; +window.location.hash = ''; const contentFromHash = initialState.content; // Seems like autosave would overwrite initialValue, set content from hash here @@ -131,9 +131,9 @@ const addClassToCodeLines = () => { if (line.text.match(/^[\u0060]{3}/)) { 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'); } }); }; @@ -141,29 +141,29 @@ addClassToCodeLines(); const completeForCodeBlock = change => { const line = change.to.line; - if (change.origin === "+input") { + if (change.origin === '+input') { const text = change.text[0]; // Completion for YAML doc separator if ( - text === "-" && + text === '-' && change.to.ch === 0 && insideCodeblockForMap(cm.getCursor()) ) { cm.setSelection({ line: line, ch: 0 }, { line: line, ch: 1 }); - cm.replaceSelection(text.repeat(3) + "\n"); + cm.replaceSelection(text.repeat(3) + '\n'); } // Completion for Code fence - if (text === "`" && change.to.ch === 0) { + if (text === '`' && change.to.ch === 0) { cm.setSelection({ line: line, ch: 0 }, { line: line, ch: 1 }); cm.replaceSelection(text.repeat(3)); const numberOfFences = cm .getValue() - .split("\n") + .split('\n') .filter(line => line.match(/[\u0060]{3}/)).length; if (numberOfFences % 2 === 1) { - cm.replaceSelection("map\n\n```"); + cm.replaceSelection('map\n\n```'); cm.setCursor({ line: line + 1 }); } } @@ -171,11 +171,11 @@ const completeForCodeBlock = change => { // For YAML doc separator,
    and code fence // Auto delete to start of line - if (change.origin === "+delete") { + if (change.origin === '+delete') { 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.replaceSelection(''); } } }; @@ -202,23 +202,23 @@ const updateDumbyMap = () => { updateDumbyMap(); // Re-render HTML by editor content -cm.on("change", (_, change) => { +cm.on('change', (_, change) => { updateDumbyMap(); addClassToCodeLines(); completeForCodeBlock(change); }); // Set class for focus -cm.on("focus", () => { - cm.getWrapperElement().classList.add("focus"); - HtmlContainer.classList.remove("focus"); +cm.on('focus', () => { + cm.getWrapperElement().classList.add('focus'); + HtmlContainer.classList.remove('focus'); }); -cm.on("beforeChange", (_, change) => { +cm.on('beforeChange', (_, change) => { 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] !== "") { + if (cm.getLine(line) === '---' && change.text[0] !== '') { change.cancel(); } } @@ -239,9 +239,9 @@ window.onhashchange = () => { // }}} // Completion in Code Blok {{{ // Elements about suggestions {{{ -const menu = document.createElement("div"); -menu.id = "menu"; -menu.onclick = () => (menu.style.display = "none"); +const menu = document.createElement('div'); +menu.id = 'menu'; +menu.onclick = () => (menu.style.display = 'none'); document.body.append(menu); const rendererOptions = {}; @@ -249,7 +249,7 @@ const rendererOptions = {}; // }}} // Aliases for map options {{{ const aliasesForMapOptions = {}; -const defaultApply = "./dist/default.yml"; +const defaultApply = './dist/default.yml'; fetch(defaultApply) .then(res => res.text()) .then(rawText => { @@ -269,9 +269,9 @@ const insideCodeblockForMap = anchor => { let line = anchor.line - 1; while (line >= 0) { const content = cm.getLine(line); - if (content === "```map") { + if (content === '```map') { return true; - } else if (content === "```") { + } else if (content === '```') { return false; } line = line - 1; @@ -331,7 +331,7 @@ const getSuggestionsForOptions = (optionTyped, validOptions) => { return suggestOptions.map( o => new menuItem.Suggestion({ - text: `${o.valueOf()}`, + text: `${o.valueOf()}`, replace: `${o.valueOf()}: `, }), ); @@ -347,7 +347,7 @@ const getSuggestionFromMapOption = option => { return new menuItem.Suggestion({ text: text, - replace: `${option.valueOf()}: ${option.example ?? ""}`, + replace: `${option.valueOf()}: ${option.example ?? ''}`, }); }; // }}} @@ -355,7 +355,7 @@ const getSuggestionFromMapOption = option => { const getSuggestionsFromAliases = option => Object.entries(aliasesForMapOptions[option.valueOf()] ?? {})?.map(record => { const [alias, value] = record; - const valueString = JSON.stringify(value).replaceAll('"', ""); + const valueString = JSON.stringify(value).replaceAll('"', ''); return new menuItem.Suggestion({ text: `${alias}${valueString}`, replace: `${option.valueOf()}: ${valueString}`, @@ -391,17 +391,17 @@ 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 renderer = lineWithRenderer - ? cm.getLine(lineWithRenderer).split(" ")[1] + ? cm.getLine(lineWithRenderer).split(' ')[1] : 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]; @@ -426,7 +426,7 @@ const getSuggestions = anchor => { } // If input is "key:value" (no space left after colon), then it is invalid - const isKeyFinished = text.includes(":"); + const isKeyFinished = text.includes(':'); const isValidKeyValue = text.match(/^[^:]+:\s+/); if (isKeyFinished && !isValidKeyValue) { markInputIsInvalid(); @@ -434,7 +434,7 @@ const getSuggestions = anchor => { } // If user is typing option - const keyTyped = text.split(":")[0].trim(); + const keyTyped = text.split(':')[0].trim(); if (!isKeyFinished) { markInputIsInvalid(); return getSuggestionsForOptions(keyTyped, validOptions); @@ -447,7 +447,7 @@ const getSuggestions = anchor => { } if (isKeyFinished && matchedOption) { - const valueTyped = text.substring(text.indexOf(":") + 1).trim(); + const valueTyped = text.substring(text.indexOf(':') + 1).trim(); const isValidValue = matchedOption.isValid(valueTyped); if (!valueTyped) { return [ @@ -465,8 +465,8 @@ const getSuggestions = anchor => { const rendererSuggestions = Object.entries(defaultAliases.use) .filter(([renderer]) => { const suggestion = `use: ${renderer}`; - const suggestionPattern = suggestion.replace(" ", "").toLowerCase(); - const textPattern = text.replace(" ", "").toLowerCase(); + const suggestionPattern = suggestion.replace(' ', '').toLowerCase(); + const textPattern = text.replace(' ', '').toLowerCase(); return suggestion !== text && suggestionPattern.includes(textPattern); }) .map( @@ -484,55 +484,55 @@ const getSuggestions = anchor => { // {{{ FUNCTION: Show element about suggestions const addSuggestions = (anchor, suggestions) => { if (suggestions.length === 0) { - menu.style.display = "none"; + 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"); + 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"; + menu.style.display = 'block'; }; // }}} // EVENT: Suggests for current selection {{{ // FIXME Dont show suggestion when selecting multiple chars -cm.on("cursorActivity", _ => { - menu.style.display = "none"; +cm.on('cursorActivity', _ => { + menu.style.display = 'none'; const anchor = cm.getCursor(); if (insideCodeblockForMap(anchor)) { handleTypingInCodeBlock(anchor); } }); -cm.on("blur", () => { - menu.style.display = "none"; - cm.getWrapperElement().classList.remove("focus"); - HtmlContainer.classList.add("focus"); +cm.on('blur', () => { + menu.style.display = 'none'; + cm.getWrapperElement().classList.remove('focus'); + HtmlContainer.classList.add('focus'); }); // }}} // EVENT: keydown for suggestions {{{ -const keyForSuggestions = ["Tab", "Enter", "Escape"]; -cm.on("keydown", (_, e) => { +const keyForSuggestions = ['Tab', 'Enter', 'Escape']; +cm.on('keydown', (_, e) => { if ( !cm.hasFocus || !keyForSuggestions.includes(e.key) || - menu.style.display === "none" + menu.style.display === 'none' ) 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(); @@ -540,25 +540,25 @@ cm.on("keydown", (_, e) => { // 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"); + menu.querySelector('.container__suggestion:last-child'); const focusSuggestion = e.shiftKey ? previousSuggestion : nextSuggestion; // Current editor selection state const anchor = cm.getCursor(); 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" }); + case 'Tab': + Array.from(menu.children).forEach(s => s.classList.remove('focus')); + focusSuggestion.classList.add('focus'); + focusSuggestion.scrollIntoView({ behavior: 'smooth', block: 'nearest' }); break; - case "Enter": + case 'Enter': currentSuggestion.onclick(); break; - case "Escape": - menu.style.display = "none"; + case 'Escape': + menu.style.display = 'none'; // Focus editor again setTimeout(() => cm.focus() && cm.setCursor(anchor), 100); break; @@ -566,34 +566,34 @@ cm.on("keydown", (_, e) => { }); document.onkeydown = e => { - if (e.altKey && e.ctrlKey && e.key === "m") { + if (e.altKey && e.ctrlKey && e.key === 'm') { toggleEditing(); e.preventDefault(); return null; } if (!cm.hasFocus()) { - if (e.key === "F1") { + if (e.key === 'F1') { e.preventDefault(); cm.focus(); } - if (e.key === "Tab") { + if (e.key === 'Tab') { e.preventDefault(); dumbymap.utils.focusNextMap(e.shiftKey); } - if (e.key === "x" || e.key === "X") { + if (e.key === 'x' || e.key === 'X') { e.preventDefault(); dumbymap.utils.switchToNextLayout(e.shiftKey); } - if (e.key === "n") { + if (e.key === 'n') { e.preventDefault(); dumbymap.utils.focusNextBlock(); } - if (e.key === "p") { + if (e.key === 'p') { e.preventDefault(); dumbymap.utils.focusNextBlock(true); } - if (e.key === "Escape") { + if (e.key === 'Escape') { e.preventDefault(); dumbymap.utils.removeBlockFocus(); } @@ -604,15 +604,15 @@ document.onkeydown = e => { // }}} // Layout Switch {{{ const layoutObserver = new MutationObserver(() => { - const layout = HtmlContainer.getAttribute("data-layout"); - if (layout !== "normal") { - document.body.removeAttribute("data-mode"); + const layout = HtmlContainer.getAttribute('data-layout'); + if (layout !== 'normal') { + document.body.removeAttribute('data-mode'); } }); layoutObserver.observe(HtmlContainer, { attributes: true, - attributeFilter: ["data-layout"], + attributeFilter: ['data-layout'], attributeOldValue: true, }); // }}} @@ -624,7 +624,7 @@ document.oncontextmenu = e => { const range = selection.getRangeAt(0); if (selection) { e.preventDefault(); - menu.innerHTML = ""; + menu.innerHTML = ''; menu.style.cssText = `display: block; left: ${e.clientX + 10}px; top: ${e.clientY + 5}px;`; const addGeoLink = new menuItem.GeoLink({ range }); menu.appendChild(addGeoLink.createElement()); @@ -635,7 +635,7 @@ document.oncontextmenu = e => { }; const actionOutsideMenu = e => { - if (menu.style.display === "none" || cm.hasFocus()) return; + if (menu.style.display === 'none' || cm.hasFocus()) return; const rect = menu.getBoundingClientRect(); if ( e.clientX < rect.left || @@ -643,11 +643,11 @@ const actionOutsideMenu = e => { e.clientY < rect.top || e.clientY > rect.top + rect.height ) { - menu.style.display = "none"; + menu.style.display = 'none'; } }; -document.addEventListener("click", actionOutsideMenu); +document.addEventListener('click', actionOutsideMenu); // }}} diff --git a/src/utils.mjs b/src/utils.mjs index ad9131e..3824672 100644 --- a/src/utils.mjs +++ b/src/utils.mjs @@ -6,7 +6,7 @@ */ export const onRemove = (element, callback) => { const parent = element.parentNode; - if (!parent) throw new Error("The node must already be attached"); + if (!parent) throw new Error('The node must already be attached'); const obs = new MutationObserver(mutations => { for (const mutation of mutations) { @@ -33,7 +33,7 @@ export const onRemove = (element, callback) => { */ export const animateRectTransition = (element, rect, options = {}) => { if (!element.parentElement) - throw new Error("The node must already be attached"); + throw new Error('The node must already be attached'); const { width: w1, height: h1, left: x1, top: y1 } = rect; const { @@ -62,7 +62,7 @@ export const animateRectTransition = (element, rect, options = {}) => { return element.animate(keyframes, { duration: options.duration ?? 500, - easing: "ease-in-out", + easing: 'ease-in-out', }); }; @@ -82,7 +82,7 @@ export function throttle(func, delay) { timerFlag = setTimeout( () => (timerFlag = null), - typeof delay === "function" ? delay.call(context) : delay, + typeof delay === 'function' ? delay.call(context) : delay, ); return func.call(context, ...args); -- cgit v1.2.3-70-g09d2