diff options
-rw-r--r-- | eslint.config.js | 49 | ||||
-rw-r--r-- | package.json | 3 | ||||
-rw-r--r-- | rollup.config.js | 27 | ||||
-rw-r--r-- | src/Layout.mjs | 8 | ||||
-rw-r--r-- | src/MenuItem.mjs | 46 | ||||
-rw-r--r-- | src/dumbyUtils.mjs | 36 | ||||
-rw-r--r-- | src/dumbymap.mjs | 44 | ||||
-rw-r--r-- | src/editor.mjs | 140 | ||||
-rw-r--r-- | src/utils.mjs | 8 |
9 files changed, 154 insertions, 207 deletions
diff --git a/eslint.config.js b/eslint.config.js deleted file mode 100644 index 3bca729..0000000 --- a/eslint.config.js +++ /dev/null | |||
@@ -1,49 +0,0 @@ | |||
1 | import globals from 'globals' | ||
2 | import js from '@eslint/js' | ||
3 | import importPlugin from 'eslint-plugin-import' | ||
4 | import promisePlugin from 'eslint-plugin-promise' | ||
5 | import nodePlugin from 'eslint-plugin-node' | ||
6 | import jsdoc from 'eslint-plugin-jsdoc' | ||
7 | |||
8 | export default [ | ||
9 | js.configs.recommended, | ||
10 | { | ||
11 | languageOptions: { | ||
12 | ecmaVersion: 2022, | ||
13 | sourceType: 'module', | ||
14 | globals: { | ||
15 | ...globals.browser, | ||
16 | ...globals.node | ||
17 | } | ||
18 | }, | ||
19 | plugins: { | ||
20 | import: importPlugin, | ||
21 | promise: promisePlugin, | ||
22 | node: nodePlugin | ||
23 | }, | ||
24 | rules: { | ||
25 | 'no-unused-vars': ['warn', { | ||
26 | varsIgnorePattern: '^_', | ||
27 | argsIgnorePattern: '^_', | ||
28 | caughtErrorsIgnorePattern: '^ignore|_' | ||
29 | }], | ||
30 | 'import/no-unresolved': 'error', | ||
31 | 'no-console': ['error', { allow: ['info', 'warn', 'error'] }], | ||
32 | eqeqeq: ['error', 'always'], | ||
33 | // 'curly': ['warn', 'multi'], | ||
34 | 'prefer-const': 'error', | ||
35 | 'no-var': 'error', | ||
36 | 'array-callback-return': 'error', | ||
37 | 'no-unexpected-multiline': 'warn' | ||
38 | } | ||
39 | }, | ||
40 | { | ||
41 | files: ['src/*js'], | ||
42 | plugins: { | ||
43 | jsdoc | ||
44 | }, | ||
45 | rules: { | ||
46 | 'jsdoc/require-description': 'warn' | ||
47 | } | ||
48 | } | ||
49 | ] | ||
diff --git a/package.json b/package.json index 2c39691..0017e6c 100644 --- a/package.json +++ b/package.json | |||
@@ -25,7 +25,7 @@ | |||
25 | "build-resources": "cp node_modules/easymde/dist/easymde.min.js dist; cp node_modules/easymde/dist/easymde.min.css dist/css", | 25 | "build-resources": "cp node_modules/easymde/dist/easymde.min.js dist; cp node_modules/easymde/dist/easymde.min.css dist/css", |
26 | "server": "live-server --port=8080 --ignore='**/src/**js' --wait=2000 --no-browser --cors", | 26 | "server": "live-server --port=8080 --ignore='**/src/**js' --wait=2000 --no-browser --cors", |
27 | "dev": "npm run server", | 27 | "dev": "npm run server", |
28 | "lint": "npx standard", | 28 | "lint": "standard --fix", |
29 | "style": "scripts/stylelint.sh", | 29 | "style": "scripts/stylelint.sh", |
30 | "prepack": "npm run lint && npm run style && npm run build", | 30 | "prepack": "npm run lint && npm run style && npm run build", |
31 | "postpack": "rm -rf dist/css dist/renderers; ln -sf `pwd`/src/css dist; cp node_modules/easymde/dist/easymde.min.css src/css; ln -sf `pwd`/mapclay/dist/renderers dist" | 31 | "postpack": "rm -rf dist/css dist/renderers; ln -sf `pwd`/src/css dist; cp node_modules/easymde/dist/easymde.min.css src/css; ln -sf `pwd`/mapclay/dist/renderers dist" |
@@ -38,7 +38,6 @@ | |||
38 | "globals": "^15.10.0", | 38 | "globals": "^15.10.0", |
39 | "rollup": "^4.24.0", | 39 | "rollup": "^4.24.0", |
40 | "rollup-plugin-bundle-stats": "^4.15.1", | 40 | "rollup-plugin-bundle-stats": "^4.15.1", |
41 | "standard": "^17.1.2", | ||
42 | "stylelint": "^16.9.0", | 41 | "stylelint": "^16.9.0", |
43 | "stylelint-config-standard": "^36.0.1", | 42 | "stylelint-config-standard": "^36.0.1", |
44 | "stylelint-order": "^6.0.4" | 43 | "stylelint-order": "^6.0.4" |
diff --git a/rollup.config.js b/rollup.config.js index 145224b..f5a22eb 100644 --- a/rollup.config.js +++ b/rollup.config.js | |||
@@ -13,12 +13,12 @@ const general = { | |||
13 | dir: './dist', | 13 | dir: './dist', |
14 | format: 'esm', | 14 | format: 'esm', |
15 | entryFileNames: '[name].mjs', | 15 | entryFileNames: '[name].mjs', |
16 | sourcemap: 'true' | 16 | sourcemap: 'true', |
17 | } | 17 | }, |
18 | ], | 18 | ], |
19 | watch: { | 19 | watch: { |
20 | clearScreen: false, | 20 | clearScreen: false, |
21 | include: ['src/**', 'mapclay/dist/mapclay.mjs'] | 21 | include: ['src/**', 'mapclay/dist/mapclay.mjs'], |
22 | }, | 22 | }, |
23 | context: 'window', | 23 | context: 'window', |
24 | plugins: [ | 24 | plugins: [ |
@@ -26,13 +26,12 @@ const general = { | |||
26 | name: 'watch-mapclay', | 26 | name: 'watch-mapclay', |
27 | buildStart () { | 27 | buildStart () { |
28 | const mapclayPath = join(process.cwd(), 'mapclay', 'dist', 'mapclay.mjs') | 28 | const mapclayPath = join(process.cwd(), 'mapclay', 'dist', 'mapclay.mjs') |
29 | console.log('Watching:', mapclayPath) | ||
30 | if (existsSync(mapclayPath)) { | 29 | if (existsSync(mapclayPath)) { |
31 | this.addWatchFile(mapclayPath) | 30 | this.addWatchFile(mapclayPath) |
32 | } else { | 31 | } else { |
33 | console.log('mapclay.mjs not found at:', mapclayPath) | 32 | console.warn('mapclay.mjs not found at:', mapclayPath) |
34 | } | 33 | } |
35 | } | 34 | }, |
36 | }, | 35 | }, |
37 | { | 36 | { |
38 | name: 'leader-line', | 37 | name: 'leader-line', |
@@ -41,7 +40,7 @@ const general = { | |||
41 | return `${code}\nexport default LeaderLine;` | 40 | return `${code}\nexport default LeaderLine;` |
42 | } | 41 | } |
43 | return null | 42 | return null |
44 | } | 43 | }, |
45 | }, | 44 | }, |
46 | { | 45 | { |
47 | name: 'mapclay', | 46 | name: 'mapclay', |
@@ -50,24 +49,24 @@ const general = { | |||
50 | return './mapclay/dist/mapclay.mjs' | 49 | return './mapclay/dist/mapclay.mjs' |
51 | } | 50 | } |
52 | return null | 51 | return null |
53 | } | 52 | }, |
54 | }, | 53 | }, |
55 | node(), | 54 | node(), |
56 | commonjs(), | 55 | commonjs(), |
57 | production && terser({ | 56 | production && terser({ |
58 | keep_fnames: true | 57 | keep_fnames: true, |
59 | }), | 58 | }), |
60 | production && bundleStats() | 59 | production && bundleStats(), |
61 | ] | 60 | ], |
62 | } | 61 | } |
63 | 62 | ||
64 | export default [ | 63 | export default [ |
65 | { | 64 | { |
66 | input: 'src/editor.mjs' | 65 | input: 'src/editor.mjs', |
67 | }, | 66 | }, |
68 | { | 67 | { |
69 | input: 'src/dumbymap.mjs' | 68 | input: 'src/dumbymap.mjs', |
70 | } | 69 | }, |
71 | ] | 70 | ] |
72 | .map(config => ({ ...general, ...config })) | 71 | .map(config => ({ ...general, ...config })) |
73 | .filter((config) => production || config.input.match(/editor/)) | 72 | .filter((config) => production || config.input.match(/editor/)) |
diff --git a/src/Layout.mjs b/src/Layout.mjs index 6bb6282..c5276b4 100644 --- a/src/Layout.mjs +++ b/src/Layout.mjs | |||
@@ -55,7 +55,7 @@ export class SideBySide extends Layout { | |||
55 | 55 | ||
56 | const draggable = new PlainDraggable(bar, { | 56 | const draggable = new PlainDraggable(bar, { |
57 | handle, | 57 | handle, |
58 | containment: { left: '25%', top: 0, right: '75%', height: 0 } | 58 | containment: { left: '25%', top: 0, right: '75%', height: 0 }, |
59 | }) | 59 | }) |
60 | draggable.draggableCursor = 'grab' | 60 | draggable.draggableCursor = 'grab' |
61 | 61 | ||
@@ -110,7 +110,7 @@ export class Overlay extends Layout { | |||
110 | addDraggable = element => { | 110 | addDraggable = element => { |
111 | // Make sure current element always on top | 111 | // Make sure current element always on top |
112 | const siblings = Array.from( | 112 | const siblings = Array.from( |
113 | element.parentElement?.querySelectorAll(':scope > *') ?? [] | 113 | element.parentElement?.querySelectorAll(':scope > *') ?? [], |
114 | ) | 114 | ) |
115 | let popTimer = null | 115 | let popTimer = null |
116 | element.onmouseover = () => { | 116 | element.onmouseover = () => { |
@@ -135,7 +135,7 @@ export class Overlay extends Layout { | |||
135 | top, | 135 | top, |
136 | left, | 136 | left, |
137 | handle: draggablePart, | 137 | handle: draggablePart, |
138 | snap: { x: { step: 20 }, y: { step: 20 } } | 138 | snap: { x: { step: 20 }, y: { step: 20 } }, |
139 | }) | 139 | }) |
140 | 140 | ||
141 | // FIXME use pure CSS to hide utils | 141 | // FIXME use pure CSS to hide utils |
@@ -215,7 +215,7 @@ export class Overlay extends Layout { | |||
215 | animateRectTransition( | 215 | animateRectTransition( |
216 | wrapper, | 216 | wrapper, |
217 | { left: originLeft, top: originTop }, | 217 | { left: originLeft, top: originTop }, |
218 | { resume: true, duration: 300 } | 218 | { resume: true, duration: 300 }, |
219 | ).finished.finally(() => this.addDraggable(wrapper)) | 219 | ).finished.finally(() => this.addDraggable(wrapper)) |
220 | 220 | ||
221 | // Trivial case: | 221 | // Trivial case: |
diff --git a/src/MenuItem.mjs b/src/MenuItem.mjs index d580cb3..0fea539 100644 --- a/src/MenuItem.mjs +++ b/src/MenuItem.mjs | |||
@@ -81,9 +81,9 @@ export const pickMapItem = ({ utils }) => | |||
81 | onclick: () => { | 81 | onclick: () => { |
82 | map.classList.add('focus') | 82 | map.classList.add('focus') |
83 | map.scrollIntoView({ behavior: 'smooth' }) | 83 | map.scrollIntoView({ behavior: 'smooth' }) |
84 | } | 84 | }, |
85 | }) | 85 | }), |
86 | ) | 86 | ), |
87 | }) | 87 | }) |
88 | 88 | ||
89 | /** | 89 | /** |
@@ -118,10 +118,10 @@ export const pickBlockItem = ({ blocks, utils }) => | |||
118 | // UX: remove menu after user select/deselect blocks | 118 | // UX: remove menu after user select/deselect blocks |
119 | const submenu = e.target.closest('.sub-menu') | 119 | const submenu = e.target.closest('.sub-menu') |
120 | submenu.onmouseleave = () => { submenu.closest('.menu').style.display = 'none' } | 120 | submenu.onmouseleave = () => { submenu.closest('.menu').style.display = 'none' } |
121 | } | 121 | }, |
122 | }) | 122 | }) |
123 | } | 123 | }, |
124 | ) | 124 | ), |
125 | }) | 125 | }) |
126 | 126 | ||
127 | /** | 127 | /** |
@@ -138,14 +138,14 @@ export const pickLayoutItem = ({ container, layouts }) => | |||
138 | layout => | 138 | layout => |
139 | new Item({ | 139 | new Item({ |
140 | text: layout.name, | 140 | text: layout.name, |
141 | onclick: () => container.setAttribute('data-layout', layout.name) | 141 | onclick: () => container.setAttribute('data-layout', layout.name), |
142 | }) | 142 | }), |
143 | ), | 143 | ), |
144 | new Item({ | 144 | new Item({ |
145 | innerHTML: '<a href="https://github.com/outdoorsafetylab/dumbymap#layouts" class="external" style="display: block; padding: 0.5rem;">More...</a>', | 145 | innerHTML: '<a href="https://github.com/outdoorsafetylab/dumbymap#layouts" class="external" style="display: block; padding: 0.5rem;">More...</a>', |
146 | style: 'padding: 0;' | 146 | style: 'padding: 0;', |
147 | }) | 147 | }), |
148 | ] | 148 | ], |
149 | }) | 149 | }) |
150 | 150 | ||
151 | /** | 151 | /** |
@@ -174,7 +174,7 @@ export const addGeoLink = ({ utils }, range) => | |||
174 | range.deleteContents() | 174 | range.deleteContents() |
175 | range.insertNode(anchor) | 175 | range.insertNode(anchor) |
176 | } | 176 | } |
177 | } | 177 | }, |
178 | }) | 178 | }) |
179 | 179 | ||
180 | /** | 180 | /** |
@@ -195,7 +195,7 @@ export class Suggestion extends Item { | |||
195 | 195 | ||
196 | this.onmouseover = () => { | 196 | this.onmouseover = () => { |
197 | Array.from(this.parentElement?.children)?.forEach(s => | 197 | Array.from(this.parentElement?.children)?.forEach(s => |
198 | s.classList.remove('focus') | 198 | s.classList.remove('focus'), |
199 | ) | 199 | ) |
200 | this.classList.add('focus') | 200 | this.classList.add('focus') |
201 | } | 201 | } |
@@ -244,12 +244,12 @@ export const renderResults = ({ modal, modalContent }, map) => | |||
244 | success: 'green', | 244 | success: 'green', |
245 | fail: 'red', | 245 | fail: 'red', |
246 | skip: 'black', | 246 | skip: 'black', |
247 | stop: 'chocolate' | 247 | stop: 'chocolate', |
248 | }[result.state] ?? 'black' | 248 | }[result.state] ?? 'black' |
249 | printObject( | 249 | printObject( |
250 | result, | 250 | result, |
251 | modalContent, | 251 | modalContent, |
252 | `${result.func.name} <span style='float: right;'><span style='display: inline-block; width: 100px; color: ${color};'>${result.state}</span></span>` | 252 | `${result.func.name} <span style='float: right;'><span style='display: inline-block; width: 100px; color: ${color};'>${result.state}</span></span>`, |
253 | ) | 253 | ) |
254 | } | 254 | } |
255 | 255 | ||
@@ -258,7 +258,7 @@ export const renderResults = ({ modal, modalContent }, map) => | |||
258 | prepareHeading.textContent = 'Prepare Steps' | 258 | prepareHeading.textContent = 'Prepare Steps' |
259 | modalContent.appendChild(prepareHeading) | 259 | modalContent.appendChild(prepareHeading) |
260 | const prepareSteps = map.renderer.results.filter( | 260 | const prepareSteps = map.renderer.results.filter( |
261 | r => r.type === 'prepare' | 261 | r => r.type === 'prepare', |
262 | ) | 262 | ) |
263 | prepareSteps.forEach(printDetails) | 263 | prepareSteps.forEach(printDetails) |
264 | 264 | ||
@@ -268,7 +268,7 @@ export const renderResults = ({ modal, modalContent }, map) => | |||
268 | modalContent.appendChild(renderHeading) | 268 | modalContent.appendChild(renderHeading) |
269 | const renderSteps = map.renderer.results.filter(r => r.type === 'render') | 269 | const renderSteps = map.renderer.results.filter(r => r.type === 'render') |
270 | renderSteps.forEach(printDetails) | 270 | renderSteps.forEach(printDetails) |
271 | } | 271 | }, |
272 | }) | 272 | }) |
273 | 273 | ||
274 | /** | 274 | /** |
@@ -326,7 +326,7 @@ function printObject (obj, parentElement, name = null) { | |||
326 | export const toggleBlockFocus = block => | 326 | export const toggleBlockFocus = block => |
327 | new Item({ | 327 | new Item({ |
328 | text: 'Toggle Focus', | 328 | text: 'Toggle Focus', |
329 | onclick: () => block.classList.toggle('focus') | 329 | onclick: () => block.classList.toggle('focus'), |
330 | }) | 330 | }) |
331 | 331 | ||
332 | /** | 332 | /** |
@@ -337,7 +337,7 @@ export const toggleBlockFocus = block => | |||
337 | export const toggleMapFocus = map => | 337 | export const toggleMapFocus = map => |
338 | new Item({ | 338 | new Item({ |
339 | text: 'Toggle Focus', | 339 | text: 'Toggle Focus', |
340 | onclick: () => map.classList.toggle('focus') | 340 | onclick: () => map.classList.toggle('focus'), |
341 | }) | 341 | }) |
342 | 342 | ||
343 | /** | 343 | /** |
@@ -354,7 +354,7 @@ export const getCoordinatesByPixels = (map, xy) => | |||
354 | const xyString = `[${x.toFixed(7)}, ${y.toFixed(7)}]` | 354 | const xyString = `[${x.toFixed(7)}, ${y.toFixed(7)}]` |
355 | navigator.clipboard.writeText(xyString) | 355 | navigator.clipboard.writeText(xyString) |
356 | window.alert(`${xyString} copied to clipboard`) | 356 | window.alert(`${xyString} copied to clipboard`) |
357 | } | 357 | }, |
358 | }) | 358 | }) |
359 | 359 | ||
360 | /** | 360 | /** |
@@ -365,7 +365,7 @@ export const getCoordinatesByPixels = (map, xy) => | |||
365 | export const restoreCamera = map => | 365 | export const restoreCamera = map => |
366 | new Item({ | 366 | new Item({ |
367 | text: 'Restore Camera', | 367 | text: 'Restore Camera', |
368 | onclick: () => map.renderer.restoreCamera() | 368 | onclick: () => map.renderer.restoreCamera(), |
369 | }) | 369 | }) |
370 | 370 | ||
371 | /** | 371 | /** |
@@ -387,6 +387,6 @@ export const addRefLink = (cm, refLinks) => | |||
387 | } else { | 387 | } else { |
388 | cm.replaceSelection(`[${selection}][${refLink.ref}]`) | 388 | cm.replaceSelection(`[${selection}][${refLink.ref}]`) |
389 | } | 389 | } |
390 | } | 390 | }, |
391 | })) | 391 | })), |
392 | }) | 392 | }) |
diff --git a/src/dumbyUtils.mjs b/src/dumbyUtils.mjs index 852e4c7..2efc3b1 100644 --- a/src/dumbyUtils.mjs +++ b/src/dumbyUtils.mjs | |||
@@ -6,7 +6,7 @@ import { insideWindow, insideParent } from './utils' | |||
6 | * | 6 | * |
7 | * @param {Boolean} reverse -- focus previous map | 7 | * @param {Boolean} reverse -- focus previous map |
8 | */ | 8 | */ |
9 | export function focusNextMap(reverse = false) { | 9 | export function focusNextMap (reverse = false) { |
10 | const renderedList = this.utils.renderedMaps() | 10 | const renderedList = this.utils.renderedMaps() |
11 | const index = renderedList.findIndex(e => e.classList.contains('focus')) | 11 | const index = renderedList.findIndex(e => e.classList.contains('focus')) |
12 | const nextIndex = (index + (reverse ? -1 : 1)) % renderedList.length | 12 | const nextIndex = (index + (reverse ? -1 : 1)) % renderedList.length |
@@ -21,13 +21,13 @@ export function focusNextMap(reverse = false) { | |||
21 | * | 21 | * |
22 | * @param {Boolean} reverse -- focus previous block | 22 | * @param {Boolean} reverse -- focus previous block |
23 | */ | 23 | */ |
24 | export function focusNextBlock(reverse = false) { | 24 | export function focusNextBlock (reverse = false) { |
25 | const blocks = this.blocks.filter(b => | 25 | const blocks = this.blocks.filter(b => |
26 | b.checkVisibility({ | 26 | b.checkVisibility({ |
27 | contentVisibilityAuto: true, | 27 | contentVisibilityAuto: true, |
28 | opacityProperty: true, | 28 | opacityProperty: true, |
29 | visibilityProperty: true | 29 | visibilityProperty: true, |
30 | }) | 30 | }), |
31 | ) | 31 | ) |
32 | const index = blocks.findIndex(e => e.classList.contains('focus')) | 32 | const index = blocks.findIndex(e => e.classList.contains('focus')) |
33 | const nextIndex = (index + (reverse ? -1 : 1)) % blocks.length | 33 | const nextIndex = (index + (reverse ? -1 : 1)) % blocks.length |
@@ -56,7 +56,7 @@ export const scrollToBlock = block => { | |||
56 | /** | 56 | /** |
57 | * focusDelay. Delay of throttle, value changes by cases | 57 | * focusDelay. Delay of throttle, value changes by cases |
58 | */ | 58 | */ |
59 | export function focusDelay() { | 59 | export function focusDelay () { |
60 | return window.window.getComputedStyle(this.showcase).display === 'none' ? 50 : 300 | 60 | return window.window.getComputedStyle(this.showcase).display === 'none' ? 50 : 300 |
61 | } | 61 | } |
62 | 62 | ||
@@ -65,7 +65,7 @@ export function focusDelay() { | |||
65 | * | 65 | * |
66 | * @param {Boolean} reverse -- Switch to previous one | 66 | * @param {Boolean} reverse -- Switch to previous one |
67 | */ | 67 | */ |
68 | export function switchToNextLayout(reverse = false) { | 68 | export function switchToNextLayout (reverse = false) { |
69 | const layouts = this.layouts | 69 | const layouts = this.layouts |
70 | const currentLayoutName = this.container.getAttribute('data-layout') | 70 | const currentLayoutName = this.container.getAttribute('data-layout') |
71 | const currentIndex = layouts.map(l => l.name).indexOf(currentLayoutName) | 71 | const currentIndex = layouts.map(l => l.name).indexOf(currentLayoutName) |
@@ -81,7 +81,7 @@ export function switchToNextLayout(reverse = false) { | |||
81 | /** | 81 | /** |
82 | * removeBlockFocus. | 82 | * removeBlockFocus. |
83 | */ | 83 | */ |
84 | export function removeBlockFocus() { | 84 | export function removeBlockFocus () { |
85 | this.blocks.forEach(b => b.classList.remove('focus')) | 85 | this.blocks.forEach(b => b.classList.remove('focus')) |
86 | } | 86 | } |
87 | 87 | ||
@@ -94,7 +94,7 @@ export function removeBlockFocus() { | |||
94 | const getMarkersFromMaps = link => { | 94 | const getMarkersFromMaps = link => { |
95 | const maps = Array.from( | 95 | const maps = Array.from( |
96 | link.closest('.Dumby') | 96 | link.closest('.Dumby') |
97 | .querySelectorAll('.mapclay[data-render="fulfilled"]') | 97 | .querySelectorAll('.mapclay[data-render="fulfilled"]'), |
98 | ) | 98 | ) |
99 | return maps | 99 | return maps |
100 | .filter(map => link.targets ? link.targets.includes(map.id) : true) | 100 | .filter(map => link.targets ? link.targets.includes(map.id) : true) |
@@ -105,7 +105,7 @@ const getMarkersFromMaps = link => { | |||
105 | return map.querySelector(`.marker[title="${markerTitle}"]`) ?? | 105 | return map.querySelector(`.marker[title="${markerTitle}"]`) ?? |
106 | renderer.addMarker({ | 106 | renderer.addMarker({ |
107 | xy: link.xy, | 107 | xy: link.xy, |
108 | title: markerTitle | 108 | title: markerTitle, |
109 | }) | 109 | }) |
110 | }) | 110 | }) |
111 | } | 111 | } |
@@ -122,7 +122,7 @@ const addLeaderLine = (link, target) => { | |||
122 | end: target, | 122 | end: target, |
123 | hide: true, | 123 | hide: true, |
124 | middleLabel: link.url.searchParams.get('text'), | 124 | middleLabel: link.url.searchParams.get('text'), |
125 | path: 'magnet' | 125 | path: 'magnet', |
126 | }) | 126 | }) |
127 | line.show('draw', { duration: 300 }) | 127 | line.show('draw', { duration: 300 }) |
128 | 128 | ||
@@ -196,10 +196,10 @@ export const createDocLink = link => { | |||
196 | end: target, | 196 | end: target, |
197 | middleLabel: LeaderLine.pathLabel({ | 197 | middleLabel: LeaderLine.pathLabel({ |
198 | text: label, | 198 | text: label, |
199 | fontWeight: 'bold' | 199 | fontWeight: 'bold', |
200 | }), | 200 | }), |
201 | hide: true, | 201 | hide: true, |
202 | path: 'magnet' | 202 | path: 'magnet', |
203 | }) | 203 | }) |
204 | link.lines.push(line) | 204 | link.lines.push(line) |
205 | line.show('draw', { duration: 300 }) | 205 | line.show('draw', { duration: 300 }) |
@@ -229,7 +229,6 @@ const removeLeaderLines = link => { | |||
229 | * @return {Function} function | 229 | * @return {Function} function |
230 | */ | 230 | */ |
231 | const updateMapCameraByMarker = xy => marker => { | 231 | const updateMapCameraByMarker = xy => marker => { |
232 | console.log('update') | ||
233 | const renderer = marker.closest('.mapclay')?.renderer | 232 | const renderer = marker.closest('.mapclay')?.renderer |
234 | renderer.updateCamera({ center: xy }, true) | 233 | renderer.updateCamera({ center: xy }, true) |
235 | } | 234 | } |
@@ -244,10 +243,17 @@ const isAnchorVisible = anchor => { | |||
244 | return insideWindow(anchor) && insideParent(anchor, mapContainer) | 243 | return insideWindow(anchor) && insideParent(anchor, mapContainer) |
245 | } | 244 | } |
246 | 245 | ||
246 | /** | ||
247 | * addAnchorByPoint. | ||
248 | * | ||
249 | * @param {point} options.point -- object has {x, y} for window coordinates | ||
250 | * @param {HTMLElement} options.map | ||
251 | * @param {Function} options.validateAnchorName -- validate anchor name is OK to use | ||
252 | */ | ||
247 | export const addAnchorByPoint = ({ | 253 | export const addAnchorByPoint = ({ |
248 | point, | 254 | point, |
249 | map, | 255 | map, |
250 | validateAnchorName = () => true | 256 | validateAnchorName = () => true, |
251 | }) => { | 257 | }) => { |
252 | const rect = map.getBoundingClientRect() | 258 | const rect = map.getBoundingClientRect() |
253 | const [x, y] = map.renderer | 259 | const [x, y] = map.renderer |
@@ -268,7 +274,7 @@ export const addAnchorByPoint = ({ | |||
268 | map.renderer.addMarker({ | 274 | map.renderer.addMarker({ |
269 | xy: [x, y], | 275 | xy: [x, y], |
270 | title: `${map.id}@${x},${y}`, | 276 | title: `${map.id}@${x},${y}`, |
271 | type: 'circle' | 277 | type: 'circle', |
272 | }) | 278 | }) |
273 | 279 | ||
274 | return { ref: anchorName, link } | 280 | return { ref: anchorName, link } |
diff --git a/src/dumbymap.mjs b/src/dumbymap.mjs index 1da5bb6..b02e783 100644 --- a/src/dumbymap.mjs +++ b/src/dumbymap.mjs | |||
@@ -17,7 +17,7 @@ const geoLinkSelector = 'a[href^="geo:"]' | |||
17 | const layouts = [ | 17 | const layouts = [ |
18 | new Layout({ name: 'normal' }), | 18 | new Layout({ name: 'normal' }), |
19 | new SideBySide({ name: 'side-by-side' }), | 19 | new SideBySide({ name: 'side-by-side' }), |
20 | new Overlay({ name: 'overlay' }) | 20 | new Overlay({ name: 'overlay' }), |
21 | ] | 21 | ] |
22 | const mapCache = {} | 22 | const mapCache = {} |
23 | 23 | ||
@@ -37,12 +37,12 @@ export const markdown2HTML = (container, mdContent) => { | |||
37 | const md = MarkdownIt({ | 37 | const md = MarkdownIt({ |
38 | html: true, | 38 | html: true, |
39 | breaks: true, | 39 | breaks: true, |
40 | linkify: true | 40 | linkify: true, |
41 | }) | 41 | }) |
42 | .use(MarkdownItAnchor, { | 42 | .use(MarkdownItAnchor, { |
43 | permalink: MarkdownItAnchor.permalink.linkInsideHeader({ | 43 | permalink: MarkdownItAnchor.permalink.linkInsideHeader({ |
44 | placement: 'before' | 44 | placement: 'before', |
45 | }) | 45 | }), |
46 | }) | 46 | }) |
47 | .use(MarkdownItFootnote) | 47 | .use(MarkdownItFootnote) |
48 | .use(MarkdownItFrontMatter) | 48 | .use(MarkdownItFrontMatter) |
@@ -58,11 +58,11 @@ export const markdown2HTML = (container, mdContent) => { | |||
58 | match.text = `${x}${sep} ${y}` | 58 | match.text = `${x}${sep} ${y}` |
59 | match.index += match.text.indexOf(x) + 1 | 59 | match.index += match.text.indexOf(x) + 1 |
60 | return match | 60 | return match |
61 | } | 61 | }, |
62 | } | 62 | } |
63 | const patterns = ['[', '(', '📍', '\uFF08', '@', 'geo:', 'twd'] | 63 | const patterns = ['[', '(', '📍', '\uFF08', '@', 'geo:', 'twd'] |
64 | patterns.forEach(prefix => | 64 | patterns.forEach(prefix => |
65 | md.linkify.add(prefix, coordinateValue) | 65 | md.linkify.add(prefix, coordinateValue), |
66 | ) | 66 | ) |
67 | 67 | ||
68 | // FIXME A better way to generate blocks | 68 | // FIXME A better way to generate blocks |
@@ -133,7 +133,7 @@ export const generateMaps = (container, { delay, renderCallback } = {}) => { | |||
133 | ...utils, | 133 | ...utils, |
134 | renderedMaps: () => | 134 | renderedMaps: () => |
135 | Array.from( | 135 | Array.from( |
136 | container.querySelectorAll('.mapclay[data-render=fulfilled]') | 136 | container.querySelectorAll('.mapclay[data-render=fulfilled]'), |
137 | ).sort((a, b) => a.style.order > b.style.order), | 137 | ).sort((a, b) => a.style.order > b.style.order), |
138 | setContextMenu: (menuCallback) => { | 138 | setContextMenu: (menuCallback) => { |
139 | const originalCallback = container.oncontextmenu | 139 | const originalCallback = container.oncontextmenu |
@@ -145,8 +145,8 @@ export const generateMaps = (container, { delay, renderCallback } = {}) => { | |||
145 | } | 145 | } |
146 | }, | 146 | }, |
147 | focusNextMap: throttle(utils.focusNextMap, utils.focusDelay), | 147 | focusNextMap: throttle(utils.focusNextMap, utils.focusDelay), |
148 | switchToNextLayout: throttle(utils.switchToNextLayout, 300) | 148 | switchToNextLayout: throttle(utils.switchToNextLayout, 300), |
149 | } | 149 | }, |
150 | } | 150 | } |
151 | Object.entries(dumbymap.utils).forEach(([util, func]) => { | 151 | Object.entries(dumbymap.utils).forEach(([util, func]) => { |
152 | dumbymap.utils[util] = func.bind(dumbymap) | 152 | dumbymap.utils[util] = func.bind(dumbymap) |
@@ -155,7 +155,7 @@ export const generateMaps = (container, { delay, renderCallback } = {}) => { | |||
155 | // LeaderLine {{{ | 155 | // LeaderLine {{{ |
156 | 156 | ||
157 | Array.from(container.querySelectorAll(docLinkSelector)).filter( | 157 | Array.from(container.querySelectorAll(docLinkSelector)).filter( |
158 | utils.createDocLink | 158 | utils.createDocLink, |
159 | ) | 159 | ) |
160 | 160 | ||
161 | // Add GeoLinks | 161 | // Add GeoLinks |
@@ -177,7 +177,7 @@ export const generateMaps = (container, { delay, renderCallback } = {}) => { | |||
177 | showcase.checkVisibility({ | 177 | showcase.checkVisibility({ |
178 | contentVisibilityAuto: true, | 178 | contentVisibilityAuto: true, |
179 | opacityProperty: true, | 179 | opacityProperty: true, |
180 | visibilityProperty: true | 180 | visibilityProperty: true, |
181 | }) | 181 | }) |
182 | 182 | ||
183 | if (focus) { | 183 | if (focus) { |
@@ -217,12 +217,12 @@ export const generateMaps = (container, { delay, renderCallback } = {}) => { | |||
217 | // Resume rect from Semantic HTML to Showcase, with animation | 217 | // Resume rect from Semantic HTML to Showcase, with animation |
218 | animateRectTransition(target, placeholder.getBoundingClientRect(), { | 218 | animateRectTransition(target, placeholder.getBoundingClientRect(), { |
219 | duration: 300, | 219 | duration: 300, |
220 | resume: true | 220 | resume: true, |
221 | }) | 221 | }) |
222 | } else if (showcase.contains(target)) { | 222 | } else if (showcase.contains(target)) { |
223 | // Check placeholder is inside Semantic HTML | 223 | // Check placeholder is inside Semantic HTML |
224 | const placeholder = htmlHolder.querySelector( | 224 | const placeholder = htmlHolder.querySelector( |
225 | `[data-placeholder="${target.id}"]` | 225 | `[data-placeholder="${target.id}"]`, |
226 | ) | 226 | ) |
227 | if (!placeholder) { throw Error(`Cannot find placeholder for map "${target.id}"`) } | 227 | if (!placeholder) { throw Error(`Cannot find placeholder for map "${target.id}"`) } |
228 | 228 | ||
@@ -234,7 +234,7 @@ export const generateMaps = (container, { delay, renderCallback } = {}) => { | |||
234 | 234 | ||
235 | // animation from Showcase to placeholder | 235 | // animation from Showcase to placeholder |
236 | animateRectTransition(target, placeholder.getBoundingClientRect(), { | 236 | animateRectTransition(target, placeholder.getBoundingClientRect(), { |
237 | duration: 300 | 237 | duration: 300, |
238 | }).finished.finally(afterAnimation) | 238 | }).finished.finally(afterAnimation) |
239 | } | 239 | } |
240 | }) | 240 | }) |
@@ -277,7 +277,7 @@ export const generateMaps = (container, { delay, renderCallback } = {}) => { | |||
277 | attributes: true, | 277 | attributes: true, |
278 | attributeFilter: ['data-layout'], | 278 | attributeFilter: ['data-layout'], |
279 | attributeOldValue: true, | 279 | attributeOldValue: true, |
280 | characterDataOldValue: true | 280 | characterDataOldValue: true, |
281 | }) | 281 | }) |
282 | 282 | ||
283 | onRemove(htmlHolder, () => layoutObserver.disconnect()) | 283 | onRemove(htmlHolder, () => layoutObserver.disconnect()) |
@@ -303,7 +303,7 @@ export const generateMaps = (container, { delay, renderCallback } = {}) => { | |||
303 | observer.observe(mapElement, { | 303 | observer.observe(mapElement, { |
304 | attributes: true, | 304 | attributes: true, |
305 | attributeFilter: ['class'], | 305 | attributeFilter: ['class'], |
306 | attributeOldValue: true | 306 | attributeOldValue: true, |
307 | }) | 307 | }) |
308 | onRemove(dumbymap.htmlHolder, () => { | 308 | onRemove(dumbymap.htmlHolder, () => { |
309 | observer.disconnect() | 309 | observer.disconnect() |
@@ -329,7 +329,7 @@ export const generateMaps = (container, { delay, renderCallback } = {}) => { | |||
329 | 329 | ||
330 | // Render each code block with "language-map" class | 330 | // Render each code block with "language-map" class |
331 | const elementsWithMapConfig = Array.from( | 331 | const elementsWithMapConfig = Array.from( |
332 | container.querySelectorAll(mapBlockSelector) ?? [] | 332 | container.querySelectorAll(mapBlockSelector) ?? [], |
333 | ) | 333 | ) |
334 | /** | 334 | /** |
335 | * updateAttributeByStep. | 335 | * updateAttributeByStep. |
@@ -338,7 +338,7 @@ export const generateMaps = (container, { delay, renderCallback } = {}) => { | |||
338 | */ | 338 | */ |
339 | const updateAttributeByStep = ({ results, target, steps }) => { | 339 | const updateAttributeByStep = ({ results, target, steps }) => { |
340 | let passNum = results.filter( | 340 | let passNum = results.filter( |
341 | r => r.type === 'render' && r.state.match(/success|skip/) | 341 | r => r.type === 'render' && r.state.match(/success|skip/), |
342 | ).length | 342 | ).length |
343 | const total = steps.length | 343 | const total = steps.length |
344 | passNum += `/${total}` | 344 | passNum += `/${total}` |
@@ -367,9 +367,9 @@ export const generateMaps = (container, { delay, renderCallback } = {}) => { | |||
367 | ...config, | 367 | ...config, |
368 | aliases: { | 368 | aliases: { |
369 | ...defaultAliases, | 369 | ...defaultAliases, |
370 | ...(config.aliases ?? {}) | 370 | ...(config.aliases ?? {}), |
371 | }, | 371 | }, |
372 | stepCallback: updateAttributeByStep | 372 | stepCallback: updateAttributeByStep, |
373 | }) | 373 | }) |
374 | const render = renderWith(configConverter) | 374 | const render = renderWith(configConverter) |
375 | let order = 0 | 375 | let order = 0 |
@@ -422,7 +422,7 @@ export const generateMaps = (container, { delay, renderCallback } = {}) => { | |||
422 | } | 422 | } |
423 | }) | 423 | }) |
424 | }, | 424 | }, |
425 | delay ?? 1000 | 425 | delay ?? 1000, |
426 | ) | 426 | ) |
427 | onRemove(htmlHolder, () => { | 427 | onRemove(htmlHolder, () => { |
428 | clearTimeout(timer) | 428 | clearTimeout(timer) |
@@ -497,7 +497,7 @@ export const generateMaps = (container, { delay, renderCallback } = {}) => { | |||
497 | } | 497 | } |
498 | document.addEventListener('click', actionOutsideMenu) | 498 | document.addEventListener('click', actionOutsideMenu) |
499 | onRemove(htmlHolder, () => | 499 | onRemove(htmlHolder, () => |
500 | document.removeEventListener('click', actionOutsideMenu) | 500 | document.removeEventListener('click', actionOutsideMenu), |
501 | ) | 501 | ) |
502 | // }}} | 502 | // }}} |
503 | return Object.seal(dumbymap) | 503 | return Object.seal(dumbymap) |
diff --git a/src/editor.mjs b/src/editor.mjs index 3714327..55c6267 100644 --- a/src/editor.mjs +++ b/src/editor.mjs | |||
@@ -3,12 +3,12 @@ | |||
3 | import { markdown2HTML, generateMaps } from './dumbymap' | 3 | import { markdown2HTML, generateMaps } from './dumbymap' |
4 | import { defaultAliases, parseConfigsFromYaml } from 'mapclay' | 4 | import { defaultAliases, parseConfigsFromYaml } from 'mapclay' |
5 | import * as menuItem from './MenuItem' | 5 | import * as menuItem from './MenuItem' |
6 | import { shiftByWindow } from './utils.mjs' | ||
7 | import { addAnchorByPoint } from './dumbyUtils.mjs' | 6 | import { addAnchorByPoint } from './dumbyUtils.mjs' |
7 | import { shiftByWindow } from './utils.mjs' | ||
8 | import LeaderLine from 'leader-line' | 8 | import LeaderLine from 'leader-line' |
9 | 9 | ||
10 | // Set up Containers {{{ | 10 | // Set up Containers {{{ |
11 | 11 | /** Variables about dumbymap and editor **/ | |
12 | const url = new URL(window.location) | 12 | const url = new URL(window.location) |
13 | const context = document.querySelector('[data-mode]') | 13 | const context = document.querySelector('[data-mode]') |
14 | const dumbyContainer = document.querySelector('.DumbyMap') | 14 | const dumbyContainer = document.querySelector('.DumbyMap') |
@@ -27,7 +27,6 @@ const appendRefLink = ({ cm, ref, link }) => { | |||
27 | 27 | ||
28 | refLinks.push({ ref, link }) | 28 | refLinks.push({ ref, link }) |
29 | } | 29 | } |
30 | |||
31 | /** | 30 | /** |
32 | * Watch for changes of editing mode | 31 | * Watch for changes of editing mode |
33 | * | 32 | * |
@@ -43,11 +42,10 @@ new window.MutationObserver(() => { | |||
43 | }).observe(context, { | 42 | }).observe(context, { |
44 | attributes: true, | 43 | attributes: true, |
45 | attributeFilter: ['data-mode'], | 44 | attributeFilter: ['data-mode'], |
46 | attributeOldValue: true | 45 | attributeOldValue: true, |
47 | }) | 46 | }) |
48 | |||
49 | /** | 47 | /** |
50 | * toggle editing mode | 48 | * toggleEditing: toggle editing mode |
51 | */ | 49 | */ |
52 | const toggleEditing = () => { | 50 | const toggleEditing = () => { |
53 | const mode = context.getAttribute('data-mode') | 51 | const mode = context.getAttribute('data-mode') |
@@ -55,9 +53,7 @@ const toggleEditing = () => { | |||
55 | } | 53 | } |
56 | // }}} | 54 | // }}} |
57 | // Set up EasyMDE {{{ | 55 | // Set up EasyMDE {{{ |
58 | 56 | /** Contents for tutorial **/ | |
59 | // Content values for editor | ||
60 | |||
61 | const defaultContent = | 57 | const defaultContent = |
62 | `<br> | 58 | `<br> |
63 | 59 | ||
@@ -105,12 +101,13 @@ If you want know more, take a look at subjects below: | |||
105 | [Editor]: #This%20is%20editor! "=>.editor" | 101 | [Editor]: #This%20is%20editor! "=>.editor" |
106 | [subject]: placeholder` | 102 | [subject]: placeholder` |
107 | 103 | ||
104 | /** Editor from EasyMDE **/ | ||
108 | const editor = new EasyMDE({ | 105 | const editor = new EasyMDE({ |
109 | element: textArea, | 106 | element: textArea, |
110 | initialValue: defaultContent, | 107 | initialValue: defaultContent, |
111 | autosave: { | 108 | autosave: { |
112 | enabled: true, | 109 | enabled: true, |
113 | uniqueId: 'dumbymap' | 110 | uniqueId: 'dumbymap', |
114 | }, | 111 | }, |
115 | indentWithTabs: false, | 112 | indentWithTabs: false, |
116 | lineNumbers: true, | 113 | lineNumbers: true, |
@@ -123,21 +120,21 @@ const editor = new EasyMDE({ | |||
123 | map: 'Ctrl-Alt-M', | 120 | map: 'Ctrl-Alt-M', |
124 | debug: 'Ctrl-Alt-D', | 121 | debug: 'Ctrl-Alt-D', |
125 | toggleUnorderedList: null, | 122 | toggleUnorderedList: null, |
126 | toggleOrderedList: null | 123 | toggleOrderedList: null, |
127 | }, | 124 | }, |
128 | toolbar: [ | 125 | toolbar: [ |
129 | { | 126 | { |
130 | name: 'roll', | 127 | name: 'roll', |
131 | title: 'Roll a Dice', | 128 | title: 'Roll a Dice', |
132 | text: '\u{2684}', | 129 | text: '\u{2684}', |
133 | action: () => addMapRandomlyByPreset() | 130 | action: () => addMapRandomlyByPreset(), |
134 | }, | 131 | }, |
135 | { | 132 | { |
136 | name: 'export', | 133 | name: 'export', |
137 | title: 'Export current page', | 134 | title: 'Export current page', |
138 | text: '\u{1F4BE}', | 135 | text: '\u{1F4BE}', |
139 | action: () => { | 136 | action: () => { |
140 | } | 137 | }, |
141 | }, | 138 | }, |
142 | { | 139 | { |
143 | name: 'hash', | 140 | name: 'hash', |
@@ -150,59 +147,59 @@ const editor = new EasyMDE({ | |||
150 | window.location.search = '' | 147 | window.location.search = '' |
151 | navigator.clipboard.writeText(window.location.href) | 148 | navigator.clipboard.writeText(window.location.href) |
152 | window.alert('URL updated in address bar, you can save current page as bookmark') | 149 | window.alert('URL updated in address bar, you can save current page as bookmark') |
153 | } | 150 | }, |
154 | }, | 151 | }, |
155 | '|', | 152 | '|', |
156 | { | 153 | { |
157 | name: 'undo', | 154 | name: 'undo', |
158 | title: 'Undo last editing', | 155 | title: 'Undo last editing', |
159 | text: '\u27F2', | 156 | text: '\u27F2', |
160 | action: EasyMDE.undo | 157 | action: EasyMDE.undo, |
161 | }, | 158 | }, |
162 | { | 159 | { |
163 | name: 'redo', | 160 | name: 'redo', |
164 | text: '\u27F3', | 161 | text: '\u27F3', |
165 | title: 'Redo editing', | 162 | title: 'Redo editing', |
166 | action: EasyMDE.redo | 163 | action: EasyMDE.redo, |
167 | }, | 164 | }, |
168 | '|', | 165 | '|', |
169 | { | 166 | { |
170 | name: 'heading-1', | 167 | name: 'heading-1', |
171 | text: 'H1', | 168 | text: 'H1', |
172 | title: 'Big Heading', | 169 | title: 'Big Heading', |
173 | action: EasyMDE['heading-1'] | 170 | action: EasyMDE['heading-1'], |
174 | }, | 171 | }, |
175 | { | 172 | { |
176 | name: 'heading-2', | 173 | name: 'heading-2', |
177 | text: 'H2', | 174 | text: 'H2', |
178 | title: 'Medium Heading', | 175 | title: 'Medium Heading', |
179 | action: EasyMDE['heading-2'] | 176 | action: EasyMDE['heading-2'], |
180 | }, | 177 | }, |
181 | '|', | 178 | '|', |
182 | { | 179 | { |
183 | name: 'link', | 180 | name: 'link', |
184 | text: '\u{1F517}', | 181 | text: '\u{1F517}', |
185 | title: 'Create Link', | 182 | title: 'Create Link', |
186 | action: EasyMDE.drawLink | 183 | action: EasyMDE.drawLink, |
187 | }, | 184 | }, |
188 | { | 185 | { |
189 | name: 'image', | 186 | name: 'image', |
190 | text: '\u{1F5BC}', | 187 | text: '\u{1F5BC}', |
191 | title: 'Create Image', | 188 | title: 'Create Image', |
192 | action: EasyMDE.drawImage | 189 | action: EasyMDE.drawImage, |
193 | }, | 190 | }, |
194 | '|', | 191 | '|', |
195 | { | 192 | { |
196 | name: 'Bold', | 193 | name: 'Bold', |
197 | text: '\u{1D401}', | 194 | text: '\u{1D401}', |
198 | title: 'Bold', | 195 | title: 'Bold', |
199 | action: EasyMDE.toggleBold | 196 | action: EasyMDE.toggleBold, |
200 | }, | 197 | }, |
201 | { | 198 | { |
202 | name: 'Italic', | 199 | name: 'Italic', |
203 | text: '\u{1D43C}', | 200 | text: '\u{1D43C}', |
204 | title: 'Italic', | 201 | title: 'Italic', |
205 | action: EasyMDE.toggleItalic | 202 | action: EasyMDE.toggleItalic, |
206 | }, | 203 | }, |
207 | '|', | 204 | '|', |
208 | { | 205 | { |
@@ -213,13 +210,14 @@ const editor = new EasyMDE({ | |||
213 | editor.value(defaultContent) | 210 | editor.value(defaultContent) |
214 | refLinks = getRefLinks() | 211 | refLinks = getRefLinks() |
215 | updateDumbyMap() | 212 | updateDumbyMap() |
216 | } | 213 | }, |
217 | } | 214 | }, |
218 | ] | 215 | ], |
219 | }) | 216 | }) |
220 | 217 | /** CodeMirror Instance **/ | |
221 | const cm = editor.codemirror | 218 | const cm = editor.codemirror |
222 | 219 | ||
220 | /** Ref Links **/ | ||
223 | const getRefLinks = () => editor.value() | 221 | const getRefLinks = () => editor.value() |
224 | .split('\n') | 222 | .split('\n') |
225 | .map(line => { | 223 | .map(line => { |
@@ -244,7 +242,6 @@ const getStateFromHash = hash => { | |||
244 | return {} | 242 | return {} |
245 | } | 243 | } |
246 | } | 244 | } |
247 | |||
248 | /** | 245 | /** |
249 | * get editor content from hash string | 246 | * get editor content from hash string |
250 | * | 247 | * |
@@ -254,7 +251,9 @@ const getContentFromHash = hash => { | |||
254 | const state = getStateFromHash(hash) | 251 | const state = getStateFromHash(hash) |
255 | return state.content | 252 | return state.content |
256 | } | 253 | } |
254 | /** Hash and Query Parameters in URL **/ | ||
257 | const contentFromHash = getContentFromHash(window.location.hash) | 255 | const contentFromHash = getContentFromHash(window.location.hash) |
256 | window.location.hash = '' | ||
258 | 257 | ||
259 | if (url.searchParams.get('content') === 'tutorial') { | 258 | if (url.searchParams.get('content') === 'tutorial') { |
260 | editor.value(defaultContent) | 259 | editor.value(defaultContent) |
@@ -263,12 +262,10 @@ if (url.searchParams.get('content') === 'tutorial') { | |||
263 | editor.cleanup() | 262 | editor.cleanup() |
264 | editor.value(contentFromHash) | 263 | editor.value(contentFromHash) |
265 | } | 264 | } |
266 | |||
267 | window.location.hash = '' | ||
268 | |||
269 | // }}} | 265 | // }}} |
270 | // Set up logic about editor content {{{ | 266 | // Set up logic about editor content {{{ |
271 | 267 | ||
268 | /** Sync scroll from HTML to CodeMirror **/ | ||
272 | const htmlOnScroll = (ele) => () => { | 269 | const htmlOnScroll = (ele) => () => { |
273 | if (textArea.dataset.scrollLine) return | 270 | if (textArea.dataset.scrollLine) return |
274 | 271 | ||
@@ -288,12 +285,11 @@ const htmlOnScroll = (ele) => () => { | |||
288 | } | 285 | } |
289 | } | 286 | } |
290 | 287 | ||
291 | // Sync CodeMirror LineNumber with HTML Contents | ||
292 | new window.MutationObserver(() => { | 288 | new window.MutationObserver(() => { |
293 | clearTimeout(dumbyContainer.timer) | 289 | clearTimeout(dumbyContainer.timer) |
294 | dumbyContainer.timer = setTimeout( | 290 | dumbyContainer.timer = setTimeout( |
295 | () => delete dumbyContainer.dataset.scrollLine, | 291 | () => delete dumbyContainer.dataset.scrollLine, |
296 | 50 | 292 | 50, |
297 | ) | 293 | ) |
298 | 294 | ||
299 | const line = dumbyContainer.dataset.scrollLine | 295 | const line = dumbyContainer.dataset.scrollLine |
@@ -306,7 +302,7 @@ new window.MutationObserver(() => { | |||
306 | } | 302 | } |
307 | }).observe(dumbyContainer, { | 303 | }).observe(dumbyContainer, { |
308 | attributes: true, | 304 | attributes: true, |
309 | attributeFilter: ['data-scroll-line'] | 305 | attributeFilter: ['data-scroll-line'], |
310 | }) | 306 | }) |
311 | 307 | ||
312 | const setScrollLine = () => { | 308 | const setScrollLine = () => { |
@@ -318,12 +314,12 @@ const setScrollLine = () => { | |||
318 | } | 314 | } |
319 | cm.on('scroll', setScrollLine) | 315 | cm.on('scroll', setScrollLine) |
320 | 316 | ||
321 | // Sync HTML Contents with CodeMirror LineNumber | 317 | /** Sync scroll from CodeMirror to HTML **/ |
322 | new window.MutationObserver(() => { | 318 | new window.MutationObserver(() => { |
323 | clearTimeout(textArea.timer) | 319 | clearTimeout(textArea.timer) |
324 | textArea.timer = setTimeout( | 320 | textArea.timer = setTimeout( |
325 | () => delete textArea.dataset.scrollLine, | 321 | () => delete textArea.dataset.scrollLine, |
326 | 1000 | 322 | 1000, |
327 | ) | 323 | ) |
328 | 324 | ||
329 | const line = textArea.dataset.scrollLine | 325 | const line = textArea.dataset.scrollLine |
@@ -345,11 +341,9 @@ new window.MutationObserver(() => { | |||
345 | dumbymap.htmlHolder.scrollBy(0, top - coords.top + 30) | 341 | dumbymap.htmlHolder.scrollBy(0, top - coords.top + 30) |
346 | }).observe(textArea, { | 342 | }).observe(textArea, { |
347 | attributes: true, | 343 | attributes: true, |
348 | attributeFilter: ['data-scroll-line'] | 344 | attributeFilter: ['data-scroll-line'], |
349 | }) | 345 | }) |
350 | 346 | ||
351 | markdown2HTML(dumbyContainer, editor.value()) | ||
352 | |||
353 | /** | 347 | /** |
354 | * addClassToCodeLines. Quick hack to style lines inside code block | 348 | * addClassToCodeLines. Quick hack to style lines inside code block |
355 | */ | 349 | */ |
@@ -444,7 +438,7 @@ const menuForEditor = (event, menu) => { | |||
444 | if (context.dataset.mode !== 'editing') { | 438 | if (context.dataset.mode !== 'editing') { |
445 | const switchToEditingMode = new menuItem.Item({ | 439 | const switchToEditingMode = new menuItem.Item({ |
446 | innerHTML: '<strong>EDIT</strong>', | 440 | innerHTML: '<strong>EDIT</strong>', |
447 | onclick: () => (context.dataset.mode = 'editing') | 441 | onclick: () => (context.dataset.mode = 'editing'), |
448 | }) | 442 | }) |
449 | menu.appendChild(switchToEditingMode) | 443 | menu.appendChild(switchToEditingMode) |
450 | } | 444 | } |
@@ -456,7 +450,7 @@ const menuForEditor = (event, menu) => { | |||
456 | onclick: (event) => { | 450 | onclick: (event) => { |
457 | const { ref, link } = addAnchorByPoint({ point: event, map, validateAnchorName }) | 451 | const { ref, link } = addAnchorByPoint({ point: event, map, validateAnchorName }) |
458 | appendRefLink({ cm, ref, link }) | 452 | appendRefLink({ cm, ref, link }) |
459 | } | 453 | }, |
460 | }) | 454 | }) |
461 | menu.insertBefore(item, menu.firstChild) | 455 | menu.insertBefore(item, menu.firstChild) |
462 | } | 456 | } |
@@ -468,7 +462,7 @@ const menuForEditor = (event, menu) => { | |||
468 | const updateDumbyMap = () => { | 462 | const updateDumbyMap = () => { |
469 | markdown2HTML(dumbyContainer, editor.value()) | 463 | markdown2HTML(dumbyContainer, editor.value()) |
470 | // debounceForMap(dumbyContainer, afterMapRendered) | 464 | // debounceForMap(dumbyContainer, afterMapRendered) |
471 | dumbymap = generateMaps(dumbyContainer, { renderCallback }) | 465 | dumbymap = generateMaps(dumbyContainer, {}) |
472 | // Set onscroll callback | 466 | // Set onscroll callback |
473 | const htmlHolder = dumbymap.htmlHolder | 467 | const htmlHolder = dumbymap.htmlHolder |
474 | htmlHolder.onscroll = htmlOnScroll(htmlHolder) | 468 | htmlHolder.onscroll = htmlOnScroll(htmlHolder) |
@@ -521,7 +515,7 @@ new window.MutationObserver(() => { | |||
521 | } | 515 | } |
522 | }).observe(menu, { | 516 | }).observe(menu, { |
523 | attributes: true, | 517 | attributes: true, |
524 | attributeFilter: ['style'] | 518 | attributeFilter: ['style'], |
525 | }) | 519 | }) |
526 | document.body.append(menu) | 520 | document.body.append(menu) |
527 | 521 | ||
@@ -613,7 +607,7 @@ const getSuggestionsForOptions = (optionTyped, validOptions) => { | |||
613 | let suggestOptions = [] | 607 | let suggestOptions = [] |
614 | 608 | ||
615 | const matchedOptions = validOptions.filter(o => | 609 | const matchedOptions = validOptions.filter(o => |
616 | o.valueOf().toLowerCase().includes(optionTyped.toLowerCase()) | 610 | o.valueOf().toLowerCase().includes(optionTyped.toLowerCase()), |
617 | ) | 611 | ) |
618 | 612 | ||
619 | if (matchedOptions.length > 0) { | 613 | if (matchedOptions.length > 0) { |
@@ -627,8 +621,8 @@ const getSuggestionsForOptions = (optionTyped, validOptions) => { | |||
627 | new menuItem.Suggestion({ | 621 | new menuItem.Suggestion({ |
628 | text: `<span>${o.valueOf()}</span><span class='info' title="${o.desc ?? ''}">ⓘ</span>`, | 622 | text: `<span>${o.valueOf()}</span><span class='info' title="${o.desc ?? ''}">ⓘ</span>`, |
629 | replace: `${o.valueOf()}: `, | 623 | replace: `${o.valueOf()}: `, |
630 | cm | 624 | cm, |
631 | }) | 625 | }), |
632 | ) | 626 | ) |
633 | } | 627 | } |
634 | // }}} | 628 | // }}} |
@@ -647,7 +641,7 @@ const getSuggestionFromMapOption = option => { | |||
647 | return new menuItem.Suggestion({ | 641 | return new menuItem.Suggestion({ |
648 | text, | 642 | text, |
649 | replace: `${option.valueOf()}: ${option.example ?? ''}`, | 643 | replace: `${option.valueOf()}: ${option.example ?? ''}`, |
650 | cm | 644 | cm, |
651 | }) | 645 | }) |
652 | } | 646 | } |
653 | // }}} | 647 | // }}} |
@@ -663,7 +657,7 @@ const getSuggestionsFromAliases = option => | |||
663 | return new menuItem.Suggestion({ | 657 | return new menuItem.Suggestion({ |
664 | text: `<span>${alias}</span><span class="truncate" style="color: gray">${valueString}</span>`, | 658 | text: `<span>${alias}</span><span class="truncate" style="color: gray">${valueString}</span>`, |
665 | replace: `${option.valueOf()}: ${valueString}`, | 659 | replace: `${option.valueOf()}: ${valueString}`, |
666 | cm | 660 | cm, |
667 | }) | 661 | }) |
668 | }) ?? [] | 662 | }) ?? [] |
669 | // }}} | 663 | // }}} |
@@ -694,7 +688,7 @@ const getSuggestions = anchor => { | |||
694 | 688 | ||
695 | // Clear marks on text | 689 | // Clear marks on text |
696 | cm.findMarks({ ...anchor, ch: 0 }, { ...anchor, ch: text.length }).forEach( | 690 | cm.findMarks({ ...anchor, ch: 0 }, { ...anchor, ch: text.length }).forEach( |
697 | m => m.clear() | 691 | m => m.clear(), |
698 | ) | 692 | ) |
699 | 693 | ||
700 | // Mark user input invalid by case | 694 | // Mark user input invalid by case |
@@ -704,7 +698,7 @@ const getSuggestions = anchor => { | |||
704 | .markText( | 698 | .markText( |
705 | { ...anchor, ch: 0 }, | 699 | { ...anchor, ch: 0 }, |
706 | { ...anchor, ch: text.length }, | 700 | { ...anchor, ch: text.length }, |
707 | { className: 'invalid-input' } | 701 | { className: 'invalid-input' }, |
708 | ) | 702 | ) |
709 | 703 | ||
710 | // Check if "use: <renderer>" is set | 704 | // Check if "use: <renderer>" is set |
@@ -732,7 +726,7 @@ const getSuggestions = anchor => { | |||
732 | .catch(_ => { | 726 | .catch(_ => { |
733 | markInputIsInvalid(lineWithRenderer) | 727 | markInputIsInvalid(lineWithRenderer) |
734 | console.warn( | 728 | console.warn( |
735 | `Fail to get valid options from Renderer typed: ${renderer}` | 729 | `Fail to get valid options from Renderer typed: ${renderer}`, |
736 | ) | 730 | ) |
737 | }) | 731 | }) |
738 | return [] | 732 | return [] |
@@ -765,7 +759,7 @@ const getSuggestions = anchor => { | |||
765 | if (!valueTyped) { | 759 | if (!valueTyped) { |
766 | return [ | 760 | return [ |
767 | getSuggestionFromMapOption(matchedOption), | 761 | getSuggestionFromMapOption(matchedOption), |
768 | ...getSuggestionsFromAliases(matchedOption) | 762 | ...getSuggestionsFromAliases(matchedOption), |
769 | ].filter(s => s instanceof menuItem.Suggestion) | 763 | ].filter(s => s instanceof menuItem.Suggestion) |
770 | } | 764 | } |
771 | if (valueTyped && !isValidValue) { | 765 | if (valueTyped && !isValidValue) { |
@@ -787,19 +781,19 @@ const getSuggestions = anchor => { | |||
787 | new menuItem.Suggestion({ | 781 | new menuItem.Suggestion({ |
788 | text: `<span>use: ${renderer}</span><span class='info' title="${info.desc}">ⓘ</span>`, | 782 | text: `<span>use: ${renderer}</span><span class='info' title="${info.desc}">ⓘ</span>`, |
789 | replace: `use: ${renderer}`, | 783 | replace: `use: ${renderer}`, |
790 | cm | 784 | cm, |
791 | }) | 785 | }), |
792 | ) | 786 | ) |
793 | return rendererSuggestions.length === 0 | 787 | return rendererSuggestions.length === 0 |
794 | ? [] | 788 | ? [] |
795 | : [ | 789 | : [ |
796 | ...rendererSuggestions, | 790 | ...rendererSuggestions, |
797 | new menuItem.Item({ | 791 | new menuItem.Item({ |
798 | innerHTML: '<a href="https://github.com/outdoorsafetylab/mapclay#renderer" class="external" style="display: block;">More...</a>', | 792 | innerHTML: '<a href="https://github.com/outdoorsafetylab/mapclay#renderer" class="external" style="display: block;">More...</a>', |
799 | className: ['suggestion'], | 793 | className: ['suggestion'], |
800 | onclick: () => window.open('https://github.com/outdoorsafetylab/mapclay#renderer', '_blank') | 794 | onclick: () => window.open('https://github.com/outdoorsafetylab/mapclay#renderer', '_blank'), |
801 | }) | 795 | }), |
802 | ] | 796 | ] |
803 | } | 797 | } |
804 | return [] | 798 | return [] |
805 | } | 799 | } |
@@ -943,7 +937,7 @@ new window.MutationObserver(mutaions => { | |||
943 | }).observe(dumbyContainer, { | 937 | }).observe(dumbyContainer, { |
944 | attributes: true, | 938 | attributes: true, |
945 | attributeFilter: ['data-layout'], | 939 | attributeFilter: ['data-layout'], |
946 | attributeOldValue: true | 940 | attributeOldValue: true, |
947 | }) | 941 | }) |
948 | // }}} | 942 | // }}} |
949 | 943 | ||
@@ -954,7 +948,7 @@ const addMapRandomlyByPreset = () => { | |||
954 | const yamlText = [ | 948 | const yamlText = [ |
955 | 'apply: dist/default.yml', | 949 | 'apply: dist/default.yml', |
956 | 'width: 85%', | 950 | 'width: 85%', |
957 | 'height: 200px' | 951 | 'height: 200px', |
958 | ] | 952 | ] |
959 | const order = [ | 953 | const order = [ |
960 | 'id', | 954 | 'id', |
@@ -964,12 +958,12 @@ const addMapRandomlyByPreset = () => { | |||
964 | 'height', | 958 | 'height', |
965 | 'center', | 959 | 'center', |
966 | 'XYZ', | 960 | 'XYZ', |
967 | 'zoom' | 961 | 'zoom', |
968 | ] | 962 | ] |
969 | const aliasesEntries = Object.entries(aliasesForMapOptions) | 963 | const aliasesEntries = Object.entries(aliasesForMapOptions) |
970 | .filter(([key, _]) => | 964 | .filter(([key, _]) => |
971 | order.includes(key) && | 965 | order.includes(key) && |
972 | !yamlText.find(text => text.startsWith(key)) | 966 | !yamlText.find(text => text.startsWith(key)), |
973 | ) | 967 | ) |
974 | if (aliasesEntries.length === 0) return | 968 | if (aliasesEntries.length === 0) return |
975 | 969 | ||
@@ -997,12 +991,12 @@ const addMapRandomlyByPreset = () => { | |||
997 | }) | 991 | }) |
998 | 992 | ||
999 | yamlText.sort((a, b) => | 993 | yamlText.sort((a, b) => |
1000 | order.indexOf(a.split(':')[0]) > order.indexOf(b.split(':')[0]) | 994 | order.indexOf(a.split(':')[0]) > order.indexOf(b.split(':')[0]), |
1001 | ) | 995 | ) |
1002 | const anchor = cm.getCursor() | 996 | const anchor = cm.getCursor() |
1003 | cm.replaceRange( | 997 | cm.replaceRange( |
1004 | '\n```map\n' + yamlText.join('\n') + '\n```\n', | 998 | '\n```map\n' + yamlText.join('\n') + '\n```\n', |
1005 | anchor | 999 | anchor, |
1006 | ) | 1000 | ) |
1007 | } | 1001 | } |
1008 | 1002 | ||
@@ -1068,6 +1062,7 @@ document.addEventListener('selectionchange', () => { | |||
1068 | } | 1062 | } |
1069 | }) | 1063 | }) |
1070 | 1064 | ||
1065 | /** Drag/Drop on map for new reference style link */ | ||
1071 | dumbyContainer.onmousedown = (e) => { | 1066 | dumbyContainer.onmousedown = (e) => { |
1072 | // Check should start drag event for GeoLink | 1067 | // Check should start drag event for GeoLink |
1073 | const selection = document.getSelection() | 1068 | const selection = document.getSelection() |
@@ -1087,14 +1082,13 @@ dumbyContainer.onmousedown = (e) => { | |||
1087 | lineEnd.style.cssText = `position: absolute; left: ${e.clientX}px; top: ${e.clientY}px;` | 1082 | lineEnd.style.cssText = `position: absolute; left: ${e.clientX}px; top: ${e.clientY}px;` |
1088 | document.body.appendChild(lineEnd) | 1083 | document.body.appendChild(lineEnd) |
1089 | 1084 | ||
1090 | menu.style.display = 'block' | ||
1091 | const line = new LeaderLine({ | 1085 | const line = new LeaderLine({ |
1092 | start: geoLink, | 1086 | start: geoLink, |
1093 | end: lineEnd, | 1087 | end: lineEnd, |
1094 | path: 'magnet' | 1088 | path: 'magnet', |
1095 | }) | 1089 | }) |
1096 | 1090 | ||
1097 | function onMouseMove(event) { | 1091 | function onMouseMove (event) { |
1098 | lineEnd.style.left = event.clientX + 'px' | 1092 | lineEnd.style.left = event.clientX + 'px' |
1099 | lineEnd.style.top = event.clientY + 'px' | 1093 | lineEnd.style.top = event.clientY + 'px' |
1100 | line.position() | 1094 | line.position() |
@@ -1102,7 +1096,7 @@ dumbyContainer.onmousedown = (e) => { | |||
1102 | } | 1096 | } |
1103 | 1097 | ||
1104 | dumbyContainer.onmousemove = onMouseMove | 1098 | dumbyContainer.onmousemove = onMouseMove |
1105 | dumbyContainer.onmouseup = function(e) { | 1099 | dumbyContainer.onmouseup = function (e) { |
1106 | dumbyContainer.onmousemove = null | 1100 | dumbyContainer.onmousemove = null |
1107 | dumbyContainer.onmouseup = null | 1101 | dumbyContainer.onmouseup = null |
1108 | line?.remove() | 1102 | line?.remove() |
@@ -1122,16 +1116,14 @@ dumbyContainer.onmousedown = (e) => { | |||
1122 | return | 1116 | return |
1123 | } | 1117 | } |
1124 | 1118 | ||
1125 | const {ref, link} = refLink | 1119 | const { ref, link } = refLink |
1126 | appendRefLink({ cm, ref, link }) | 1120 | appendRefLink({ cm, ref, link }) |
1127 | if (selection === ref) { | 1121 | if (selection === ref) { |
1128 | cm.replaceSelection(`[${selection}]`) | 1122 | cm.replaceSelection(`[${selection}]`) |
1129 | } else { | 1123 | } else { |
1130 | cm.replaceSelection(`[${selection}][${ref}]`) | 1124 | cm.replaceSelection(`[${selection}][${ref}]`) |
1131 | } | 1125 | } |
1132 | }; | 1126 | } |
1133 | } | 1127 | } |
1134 | 1128 | ||
1135 | dumbyContainer.ondragstart = () => false | 1129 | dumbyContainer.ondragstart = () => false |
1136 | |||
1137 | // vim: sw=2 ts=2 foldmethod=marker foldmarker={{{,}}} | ||
diff --git a/src/utils.mjs b/src/utils.mjs index dba023a..d408b3d 100644 --- a/src/utils.mjs +++ b/src/utils.mjs | |||
@@ -39,7 +39,7 @@ export const animateRectTransition = (element, rect, options = {}) => { | |||
39 | width: w2, | 39 | width: w2, |
40 | height: h2, | 40 | height: h2, |
41 | left: x2, | 41 | left: x2, |
42 | top: y2 | 42 | top: y2, |
43 | } = element.getBoundingClientRect() | 43 | } = element.getBoundingClientRect() |
44 | 44 | ||
45 | const rw = (w1 ?? w2) / w2 | 45 | const rw = (w1 ?? w2) / w2 |
@@ -55,13 +55,13 @@ export const animateRectTransition = (element, rect, options = {}) => { | |||
55 | const transform2 = `translate(${dx}px, ${dy}px) scale(${rw}, ${rh})` | 55 | const transform2 = `translate(${dx}px, ${dy}px) scale(${rw}, ${rh})` |
56 | const keyframes = [ | 56 | const keyframes = [ |
57 | { transform: transform1, opacity: 1 }, | 57 | { transform: transform1, opacity: 1 }, |
58 | { transform: transform2, opacity: 0.3 } | 58 | { transform: transform2, opacity: 0.3 }, |
59 | ] | 59 | ] |
60 | if (options.resume === true) keyframes.reverse() | 60 | if (options.resume === true) keyframes.reverse() |
61 | 61 | ||
62 | return element.animate(keyframes, { | 62 | return element.animate(keyframes, { |
63 | duration: options.duration ?? 500, | 63 | duration: options.duration ?? 500, |
64 | easing: 'ease-in-out' | 64 | easing: 'ease-in-out', |
65 | }) | 65 | }) |
66 | } | 66 | } |
67 | 67 | ||
@@ -81,7 +81,7 @@ export function throttle (func, delay) { | |||
81 | 81 | ||
82 | timerFlag = setTimeout( | 82 | timerFlag = setTimeout( |
83 | () => (timerFlag = null), | 83 | () => (timerFlag = null), |
84 | typeof delay === 'function' ? delay.call(context) : delay | 84 | typeof delay === 'function' ? delay.call(context) : delay, |
85 | ) | 85 | ) |
86 | 86 | ||
87 | return func.call(context, ...args) | 87 | return func.call(context, ...args) |