diff options
Diffstat (limited to 'src/dumbymap.mjs')
-rw-r--r-- | src/dumbymap.mjs | 68 |
1 files changed, 38 insertions, 30 deletions
diff --git a/src/dumbymap.mjs b/src/dumbymap.mjs index b02e783..180517b 100644 --- a/src/dumbymap.mjs +++ b/src/dumbymap.mjs | |||
@@ -10,15 +10,19 @@ import * as utils from './dumbyUtils' | |||
10 | import * as menuItem from './MenuItem' | 10 | import * as menuItem from './MenuItem' |
11 | import PlainModal from 'plain-modal' | 11 | import PlainModal from 'plain-modal' |
12 | 12 | ||
13 | /** Selector of special HTML Elements */ | ||
13 | const mapBlockSelector = 'pre:has(.language-map)' | 14 | const mapBlockSelector = 'pre:has(.language-map)' |
14 | const docLinkSelector = 'a[href^="#"][title^="=>"]' | 15 | const docLinkSelector = 'a[href^="#"][title^="=>"]' |
15 | const geoLinkSelector = 'a[href^="geo:"]' | 16 | const geoLinkSelector = 'a[href^="geo:"]' |
16 | 17 | ||
18 | /** Default Layouts */ | ||
17 | const layouts = [ | 19 | const layouts = [ |
18 | new Layout({ name: 'normal' }), | 20 | new Layout({ name: 'normal' }), |
19 | new SideBySide({ name: 'side-by-side' }), | 21 | new SideBySide({ name: 'side-by-side' }), |
20 | new Overlay({ name: 'overlay' }), | 22 | new Overlay({ name: 'overlay' }), |
21 | ] | 23 | ] |
24 | |||
25 | /** Cache across every dumbymap generation */ | ||
22 | const mapCache = {} | 26 | const mapCache = {} |
23 | 27 | ||
24 | /** | 28 | /** |
@@ -28,12 +32,12 @@ const mapCache = {} | |||
28 | * @param {String} mdContent -- Texts in Markdown | 32 | * @param {String} mdContent -- Texts in Markdown |
29 | */ | 33 | */ |
30 | export const markdown2HTML = (container, mdContent) => { | 34 | export const markdown2HTML = (container, mdContent) => { |
31 | // Render: Markdown -> HTML {{{ | 35 | /** Prepare Elements for Container */ |
32 | container.replaceChildren() | 36 | container.replaceChildren() |
33 | |||
34 | container.innerHTML = '<div class="SemanticHtml"></div>' | 37 | container.innerHTML = '<div class="SemanticHtml"></div>' |
35 | const htmlHolder = container.querySelector('.SemanticHtml') | 38 | const htmlHolder = container.querySelector('.SemanticHtml') |
36 | 39 | ||
40 | /** Prepare MarkdownIt Instance */ | ||
37 | const md = MarkdownIt({ | 41 | const md = MarkdownIt({ |
38 | html: true, | 42 | html: true, |
39 | breaks: true, | 43 | breaks: true, |
@@ -48,11 +52,11 @@ export const markdown2HTML = (container, mdContent) => { | |||
48 | .use(MarkdownItFrontMatter) | 52 | .use(MarkdownItFrontMatter) |
49 | .use(MarkdownItInjectLinenumbers) | 53 | .use(MarkdownItInjectLinenumbers) |
50 | 54 | ||
51 | // Create links with geo scheme | 55 | /** Set up linkify for GeoLinks */ |
52 | const coordinateRegex = /^(\D*)(-?\d+\.?\d*)\s*([,\x2F\uFF0C])\s*(-?\d+\.?\d*)/ | 56 | const coordinateRegex = /^(\D*)(-?\d+\.?\d*)\s*([,\x2F\uFF0C])\s*(-?\d+\.?\d*)/ |
53 | const coordinateValue = { | 57 | const coordinateValue = { |
54 | validate: coordinateRegex, | 58 | validate: coordinateRegex, |
55 | normalize: function (match) { | 59 | normalize: function(match) { |
56 | const [, , x, sep, y] = match.text.match(coordinateRegex) | 60 | const [, , x, sep, y] = match.text.match(coordinateRegex) |
57 | match.url = `geo:${y},${x}?xy=${x},${y}` | 61 | match.url = `geo:${y},${x}?xy=${x},${y}` |
58 | match.text = `${x}${sep} ${y}` | 62 | match.text = `${x}${sep} ${y}` |
@@ -65,6 +69,7 @@ export const markdown2HTML = (container, mdContent) => { | |||
65 | md.linkify.add(prefix, coordinateValue), | 69 | md.linkify.add(prefix, coordinateValue), |
66 | ) | 70 | ) |
67 | 71 | ||
72 | /** Custom rule for Blocks in DumbyMap */ | ||
68 | // FIXME A better way to generate blocks | 73 | // FIXME A better way to generate blocks |
69 | md.renderer.rules.dumby_block_open = () => '<div>' | 74 | md.renderer.rules.dumby_block_open = () => '<div>' |
70 | md.renderer.rules.dumby_block_close = () => '</div>' | 75 | md.renderer.rules.dumby_block_close = () => '</div>' |
@@ -87,8 +92,9 @@ export const markdown2HTML = (container, mdContent) => { | |||
87 | state.tokens.push(new state.Token('dumby_block_close', '', -1)) | 92 | state.tokens.push(new state.Token('dumby_block_close', '', -1)) |
88 | }) | 93 | }) |
89 | 94 | ||
95 | /** Render HTML */ | ||
90 | htmlHolder.innerHTML = md.render(mdContent) | 96 | htmlHolder.innerHTML = md.render(mdContent) |
91 | 97 | /** Post HTML rendered */ | |
92 | // TODO Do this in markdown-it | 98 | // TODO Do this in markdown-it |
93 | const blocks = htmlHolder.querySelectorAll(':scope > div:not(:has(nav))') | 99 | const blocks = htmlHolder.querySelectorAll(':scope > div:not(:has(nav))') |
94 | blocks.forEach(b => { | 100 | blocks.forEach(b => { |
@@ -97,7 +103,6 @@ export const markdown2HTML = (container, mdContent) => { | |||
97 | }) | 103 | }) |
98 | 104 | ||
99 | return container | 105 | return container |
100 | // }}} | ||
101 | } | 106 | } |
102 | 107 | ||
103 | /** | 108 | /** |
@@ -108,6 +113,8 @@ export const markdown2HTML = (container, mdContent) => { | |||
108 | * @return {Object} dumbymap -- Include and Elements and Methods about managing contents | 113 | * @return {Object} dumbymap -- Include and Elements and Methods about managing contents |
109 | */ | 114 | */ |
110 | export const generateMaps = (container, { delay, renderCallback } = {}) => { | 115 | export const generateMaps = (container, { delay, renderCallback } = {}) => { |
116 | |||
117 | /** Prepare Contaner/HTML Holder/Showcase */ | ||
111 | container.classList.add('Dumby') | 118 | container.classList.add('Dumby') |
112 | container.removeAttribute('data-layout') | 119 | container.removeAttribute('data-layout') |
113 | container.setAttribute('data-layout', layouts[0].name) | 120 | container.setAttribute('data-layout', layouts[0].name) |
@@ -117,10 +124,13 @@ export const generateMaps = (container, { delay, renderCallback } = {}) => { | |||
117 | container.appendChild(showcase) | 124 | container.appendChild(showcase) |
118 | showcase.classList.add('Showcase') | 125 | showcase.classList.add('Showcase') |
119 | const renderPromises = [] | 126 | const renderPromises = [] |
127 | |||
128 | /** Prepare Modal */ | ||
120 | const modalContent = document.createElement('div') | 129 | const modalContent = document.createElement('div') |
121 | container.appendChild(modalContent) | 130 | container.appendChild(modalContent) |
122 | const modal = new PlainModal(modalContent) | 131 | const modal = new PlainModal(modalContent) |
123 | 132 | ||
133 | /** Define dumbymap Object */ | ||
124 | const dumbymap = { | 134 | const dumbymap = { |
125 | layouts, | 135 | layouts, |
126 | container, | 136 | container, |
@@ -152,21 +162,16 @@ export const generateMaps = (container, { delay, renderCallback } = {}) => { | |||
152 | dumbymap.utils[util] = func.bind(dumbymap) | 162 | dumbymap.utils[util] = func.bind(dumbymap) |
153 | }) | 163 | }) |
154 | 164 | ||
155 | // LeaderLine {{{ | 165 | /** Create GeoLinks and DocLinks */ |
156 | 166 | container.querySelectorAll(docLinkSelector) | |
157 | Array.from(container.querySelectorAll(docLinkSelector)).filter( | 167 | .forEach(utils.createDocLink) |
158 | utils.createDocLink, | ||
159 | ) | ||
160 | |||
161 | // Add GeoLinks | ||
162 | container.querySelectorAll(geoLinkSelector) | 168 | container.querySelectorAll(geoLinkSelector) |
163 | .forEach(utils.createGeoLink) | 169 | .forEach(utils.createGeoLink) |
164 | 170 | ||
165 | // }}} | 171 | /** |
166 | // CSS observer {{{ | 172 | * mapFocusObserver. observe for map focus |
167 | // Focus Map {{{ | 173 | * @return {MutationObserver} observer |
168 | // Set focusArea | 174 | */ |
169 | |||
170 | const mapFocusObserver = () => | 175 | const mapFocusObserver = () => |
171 | new window.MutationObserver(mutations => { | 176 | new window.MutationObserver(mutations => { |
172 | const mutation = mutations.at(-1) | 177 | const mutation = mutations.at(-1) |
@@ -238,11 +243,8 @@ export const generateMaps = (container, { delay, renderCallback } = {}) => { | |||
238 | }).finished.finally(afterAnimation) | 243 | }).finished.finally(afterAnimation) |
239 | } | 244 | } |
240 | }) | 245 | }) |
241 | // }}} | ||
242 | // Layout {{{ | ||
243 | // press key to switch layout | ||
244 | 246 | ||
245 | // observe layout change | 247 | /** Observer for layout changes */ |
246 | const layoutObserver = new window.MutationObserver(mutations => { | 248 | const layoutObserver = new window.MutationObserver(mutations => { |
247 | const mutation = mutations.at(-1) | 249 | const mutation = mutations.at(-1) |
248 | const oldLayout = mutation.oldValue | 250 | const oldLayout = mutation.oldValue |
@@ -281,10 +283,12 @@ export const generateMaps = (container, { delay, renderCallback } = {}) => { | |||
281 | }) | 283 | }) |
282 | 284 | ||
283 | onRemove(htmlHolder, () => layoutObserver.disconnect()) | 285 | onRemove(htmlHolder, () => layoutObserver.disconnect()) |
284 | // }}} | ||
285 | // }}} | ||
286 | // Render Maps {{{ | ||
287 | 286 | ||
287 | /** | ||
288 | * afterMapRendered. callback of each map rendered | ||
289 | * | ||
290 | * @param {Object} renderer | ||
291 | */ | ||
288 | const afterMapRendered = renderer => { | 292 | const afterMapRendered = renderer => { |
289 | const mapElement = renderer.target | 293 | const mapElement = renderer.target |
290 | // FIXME | 294 | // FIXME |
@@ -371,7 +375,11 @@ export const generateMaps = (container, { delay, renderCallback } = {}) => { | |||
371 | }, | 375 | }, |
372 | stepCallback: updateAttributeByStep, | 376 | stepCallback: updateAttributeByStep, |
373 | }) | 377 | }) |
378 | |||
379 | /** Get render method by converter */ | ||
374 | const render = renderWith(configConverter) | 380 | const render = renderWith(configConverter) |
381 | |||
382 | /** Render each taget element for maps */ | ||
375 | let order = 0 | 383 | let order = 0 |
376 | elementsWithMapConfig.forEach(target => { | 384 | elementsWithMapConfig.forEach(target => { |
377 | // Get text in code block starts with markdown text '```map' | 385 | // Get text in code block starts with markdown text '```map' |
@@ -428,8 +436,8 @@ export const generateMaps = (container, { delay, renderCallback } = {}) => { | |||
428 | clearTimeout(timer) | 436 | clearTimeout(timer) |
429 | }) | 437 | }) |
430 | }) | 438 | }) |
431 | // }}} | 439 | |
432 | // Menu {{{ | 440 | /** Prepare Context Menu */ |
433 | const menu = document.createElement('div') | 441 | const menu = document.createElement('div') |
434 | menu.className = 'menu' | 442 | menu.className = 'menu' |
435 | menu.style.display = 'none' | 443 | menu.style.display = 'none' |
@@ -441,7 +449,7 @@ export const generateMaps = (container, { delay, renderCallback } = {}) => { | |||
441 | } | 449 | } |
442 | container.appendChild(menu) | 450 | container.appendChild(menu) |
443 | 451 | ||
444 | // Menu Items | 452 | /** Menu Items for Context Menu */ |
445 | container.oncontextmenu = e => { | 453 | container.oncontextmenu = e => { |
446 | menu.replaceChildren() | 454 | menu.replaceChildren() |
447 | menu.style.display = 'block' | 455 | menu.style.display = 'block' |
@@ -479,7 +487,7 @@ export const generateMaps = (container, { delay, renderCallback } = {}) => { | |||
479 | return menu | 487 | return menu |
480 | } | 488 | } |
481 | 489 | ||
482 | // Remove menu when click outside | 490 | /** Event Handler when clicking outside of Context Manu */ |
483 | const actionOutsideMenu = e => { | 491 | const actionOutsideMenu = e => { |
484 | if (menu.style.display === 'none') return | 492 | if (menu.style.display === 'none') return |
485 | const keepMenu = e.target.closest('.keep-menu') || e.target.classList.contains('.keep-menu') | 493 | const keepMenu = e.target.closest('.keep-menu') || e.target.classList.contains('.keep-menu') |
@@ -499,6 +507,6 @@ export const generateMaps = (container, { delay, renderCallback } = {}) => { | |||
499 | onRemove(htmlHolder, () => | 507 | onRemove(htmlHolder, () => |
500 | document.removeEventListener('click', actionOutsideMenu), | 508 | document.removeEventListener('click', actionOutsideMenu), |
501 | ) | 509 | ) |
502 | // }}} | 510 | |
503 | return Object.seal(dumbymap) | 511 | return Object.seal(dumbymap) |
504 | } | 512 | } |