diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/MenuItem.mjs | 87 | ||||
| -rw-r--r-- | src/dumbyUtils.mjs | 69 | ||||
| -rw-r--r-- | src/dumbymap.mjs | 77 |
3 files changed, 108 insertions, 125 deletions
diff --git a/src/MenuItem.mjs b/src/MenuItem.mjs index b4d4650..1ab7d5f 100644 --- a/src/MenuItem.mjs +++ b/src/MenuItem.mjs | |||
| @@ -1,6 +1,3 @@ | |||
| 1 | import { createGeoLink } from './dumbymap'; | ||
| 2 | import { scrollToBlock } from './dumbyUtils'; | ||
| 3 | |||
| 4 | class Item extends HTMLDivElement { | 1 | class Item extends HTMLDivElement { |
| 5 | constructor({ text, innerHTML, onclick }) { | 2 | constructor({ text, innerHTML, onclick }) { |
| 6 | super(); | 3 | super(); |
| @@ -35,10 +32,10 @@ class Folder extends HTMLDivElement { | |||
| 35 | } | 32 | } |
| 36 | window.customElements.define('menu-folder', Folder, { extends: 'div' }); | 33 | window.customElements.define('menu-folder', Folder, { extends: 'div' }); |
| 37 | 34 | ||
| 38 | export const pickMapItem = dumbymap => | 35 | export const pickMapItem = ({ utils }) => |
| 39 | new Folder({ | 36 | new Folder({ |
| 40 | innerHTML: '<span>Maps<span><span class="info">(Tab)</span>', | 37 | innerHTML: '<span>Maps<span><span class="info">(Tab)</span>', |
| 41 | items: dumbymap.utils.renderedMaps().map( | 38 | items: utils.renderedMaps().map( |
| 42 | map => | 39 | map => |
| 43 | new Item({ | 40 | new Item({ |
| 44 | text: map.id, | 41 | text: map.id, |
| @@ -50,10 +47,10 @@ export const pickMapItem = dumbymap => | |||
| 50 | ), | 47 | ), |
| 51 | }); | 48 | }); |
| 52 | 49 | ||
| 53 | export const pickBlockItem = dumbymap => | 50 | export const pickBlockItem = ({ blocks, utils }) => |
| 54 | new Folder({ | 51 | new Folder({ |
| 55 | innerHTML: '<span>Blocks<span><span class="info">(n/p)</span>', | 52 | innerHTML: '<span>Blocks<span><span class="info">(n/p)</span>', |
| 56 | items: dumbymap.blocks.map( | 53 | items: blocks.map( |
| 57 | (block, index) => | 54 | (block, index) => |
| 58 | new Item({ | 55 | new Item({ |
| 59 | text: | 56 | text: |
| @@ -64,67 +61,54 @@ export const pickBlockItem = dumbymap => | |||
| 64 | .concat(' ...'), | 61 | .concat(' ...'), |
| 65 | onclick: () => { | 62 | onclick: () => { |
| 66 | block.classList.add('focus'); | 63 | block.classList.add('focus'); |
| 67 | scrollToBlock(block); | 64 | utils.scrollToBlock(block); |
| 68 | }, | 65 | }, |
| 69 | }), | 66 | }), |
| 70 | ), | 67 | ), |
| 71 | }); | 68 | }); |
| 72 | 69 | ||
| 73 | export const pickLayoutItem = dumbymap => | 70 | export const pickLayoutItem = ({ container, layouts }) => |
| 74 | new Folder({ | 71 | new Folder({ |
| 75 | innerHTML: '<span>Layouts<span><span class="info">(x)</span>', | 72 | innerHTML: '<span>Layouts<span><span class="info">(x)</span>', |
| 76 | items: [ | 73 | items: [ |
| 77 | new Item({ | 74 | new Item({ |
| 78 | text: 'EDIT', | 75 | text: 'EDIT', |
| 79 | onclick: () => | 76 | onclick: () => |
| 80 | dumbymap.container | 77 | container.closest('[data-mode]').setAttribute('data-mode', 'editing'), |
| 81 | .closest('[data-mode]') | ||
| 82 | .setAttribute('data-mode', 'editing'), | ||
| 83 | }), | 78 | }), |
| 84 | ...dumbymap.layouts.map( | 79 | ...layouts.map( |
| 85 | layout => | 80 | layout => |
| 86 | new Item({ | 81 | new Item({ |
| 87 | text: layout.name, | 82 | text: layout.name, |
| 88 | onclick: () => | 83 | onclick: () => container.setAttribute('data-layout', layout.name), |
| 89 | dumbymap.container.setAttribute('data-layout', layout.name), | ||
| 90 | }), | 84 | }), |
| 91 | ), | 85 | ), |
| 92 | ], | 86 | ], |
| 93 | }); | 87 | }); |
| 94 | 88 | ||
| 95 | export class GeoLink { | 89 | export const addGeoLink = ({ utils }, range) => |
| 96 | constructor({ range }) { | 90 | new Item({ |
| 97 | this.range = range; | 91 | text: 'Add GeoLink', |
| 98 | } | 92 | onclick: () => { |
| 99 | 93 | const content = range.toString(); | |
| 100 | createElement = () => { | 94 | // FIXME Apply geolink only on matching sub-range |
| 101 | const element = document.createElement('div'); | 95 | const match = content.match(/(^\D*[\d.]+)\D+([\d.]+)\D*$/); |
| 102 | element.className = 'menu-item'; | 96 | if (!match) return false; |
| 103 | element.innerText = 'Add GeoLink'; | 97 | |
| 104 | element.onclick = this.addGeoLinkbyRange; | 98 | const [x, y] = match.slice(1); |
| 105 | 99 | const anchor = document.createElement('a'); | |
| 106 | return element; | 100 | anchor.textContent = content; |
| 107 | }; | 101 | // FIXME apply WGS84 |
| 108 | 102 | anchor.href = `geo:${y},${x}?xy=${x},${y}`; | |
| 109 | addGeoLinkbyRange = () => { | 103 | |
| 110 | const range = this.range; | 104 | // FIXME |
| 111 | const content = range.toString(); | 105 | if (utils.createGeoLink(anchor)) { |
| 112 | // FIXME Apply geolink only on matching sub-range | 106 | range.deleteContents(); |
| 113 | const match = content.match(/(^\D*[\d.]+)\D+([\d.]+)\D*$/); | 107 | range.insertNode(anchor); |
| 114 | if (!match) return false; | 108 | } |
| 115 | 109 | }, | |
| 116 | const [x, y] = match.slice(1); | 110 | }); |
| 117 | const anchor = document.createElement('a'); | ||
| 118 | anchor.textContent = content; | ||
| 119 | // FIXME apply WGS84 | ||
| 120 | anchor.href = `geo:${y},${x}?xy=${x},${y}`; | ||
| 121 | 111 | ||
| 122 | if (createGeoLink(anchor)) { | ||
| 123 | range.deleteContents(); | ||
| 124 | range.insertNode(anchor); | ||
| 125 | } | ||
| 126 | }; | ||
| 127 | } | ||
| 128 | export class Suggestion { | 112 | export class Suggestion { |
| 129 | constructor({ text, replace }) { | 113 | constructor({ text, replace }) { |
| 130 | this.text = text; | 114 | this.text = text; |
| @@ -161,11 +145,10 @@ export class Suggestion { | |||
| 161 | } | 145 | } |
| 162 | } | 146 | } |
| 163 | 147 | ||
| 164 | export const renderResults = (dumbymap, map) => | 148 | export const renderResults = ({ modal, modalContent }, map) => |
| 165 | new Item({ | 149 | new Item({ |
| 166 | text: 'Render Results', | 150 | text: 'Render Results', |
| 167 | onclick: e => { | 151 | onclick: () => { |
| 168 | const modal = dumbymap.modal; | ||
| 169 | modal.open(); | 152 | modal.open(); |
| 170 | modal.overlayBlur = 3; | 153 | modal.overlayBlur = 3; |
| 171 | modal.closeByEscKey = false; | 154 | modal.closeByEscKey = false; |
| @@ -175,7 +158,7 @@ export const renderResults = (dumbymap, map) => | |||
| 175 | map.renderer.results.forEach(result => | 158 | map.renderer.results.forEach(result => |
| 176 | printObject( | 159 | printObject( |
| 177 | result, | 160 | result, |
| 178 | dumbymap.modalContent, | 161 | modalContent, |
| 179 | `${result.func.name} (${result.state})`, | 162 | `${result.func.name} (${result.state})`, |
| 180 | ), | 163 | ), |
| 181 | ); | 164 | ); |
| @@ -195,7 +178,7 @@ function printObject(obj, parentElement, name) { | |||
| 195 | if (typeof value === 'object') { | 178 | if (typeof value === 'object') { |
| 196 | printObject(value, detailsEle, key); | 179 | printObject(value, detailsEle, key); |
| 197 | } else { | 180 | } else { |
| 198 | let valueString = | 181 | const valueString = |
| 199 | typeof value === 'function' | 182 | typeof value === 'function' |
| 200 | ? `<pre>${value}</pre>` | 183 | ? `<pre>${value}</pre>` |
| 201 | : value ?? typeof value; | 184 | : value ?? typeof value; |
diff --git a/src/dumbyUtils.mjs b/src/dumbyUtils.mjs index e7edee3..71dc290 100644 --- a/src/dumbyUtils.mjs +++ b/src/dumbyUtils.mjs | |||
| @@ -1,3 +1,5 @@ | |||
| 1 | import LeaderLine from 'leader-line'; | ||
| 2 | |||
| 1 | export function focusNextMap(reverse = false) { | 3 | export function focusNextMap(reverse = false) { |
| 2 | const renderedList = this.utils.renderedMaps(); | 4 | const renderedList = this.utils.renderedMaps(); |
| 3 | const index = renderedList.findIndex(e => e.classList.contains('focus')); | 5 | const index = renderedList.findIndex(e => e.classList.contains('focus')); |
| @@ -56,3 +58,70 @@ export function switchToNextLayout(reverse = false) { | |||
| 56 | export function removeBlockFocus() { | 58 | export function removeBlockFocus() { |
| 57 | this.blocks.forEach(b => b.classList.remove('focus')); | 59 | this.blocks.forEach(b => b.classList.remove('focus')); |
| 58 | } | 60 | } |
| 61 | |||
| 62 | /** | ||
| 63 | * Create geolinks, which points to map by geo schema and id | ||
| 64 | * | ||
| 65 | * @param {HTMLElement} Elements contains anchor elements for doclinks | ||
| 66 | * @returns {Boolean} ture is link is created, false if coordinates are invalid | ||
| 67 | */ | ||
| 68 | export const createGeoLink = (link, callback = null) => { | ||
| 69 | const url = new URL(link.href); | ||
| 70 | const xyInParams = url.searchParams.get('xy'); | ||
| 71 | const xy = xyInParams | ||
| 72 | ? xyInParams.split(',')?.map(Number) | ||
| 73 | : url?.href | ||
| 74 | ?.match(/^geo:([0-9.,]+)/) | ||
| 75 | ?.at(1) | ||
| 76 | ?.split(',') | ||
| 77 | ?.reverse() | ||
| 78 | ?.map(Number); | ||
| 79 | |||
| 80 | if (!xy || isNaN(xy[0]) || isNaN(xy[1])) return false; | ||
| 81 | |||
| 82 | // Geo information in link | ||
| 83 | link.url = url; | ||
| 84 | link.xy = xy; | ||
| 85 | link.classList.add('with-leader-line', 'geolink'); | ||
| 86 | link.targets = link.url.searchParams.get('id')?.split(',') ?? null; | ||
| 87 | |||
| 88 | // LeaderLine | ||
| 89 | link.lines = []; | ||
| 90 | callback?.call(this, link); | ||
| 91 | |||
| 92 | return true; | ||
| 93 | }; | ||
| 94 | |||
| 95 | /** | ||
| 96 | * CreateDocLink. | ||
| 97 | * | ||
| 98 | * @param {HTMLElement} Elements contains anchor elements for doclinks | ||
| 99 | */ | ||
| 100 | export const createDocLink = link => { | ||
| 101 | link.classList.add('with-leader-line', 'doclink'); | ||
| 102 | link.lines = []; | ||
| 103 | |||
| 104 | link.onmouseover = () => { | ||
| 105 | const label = decodeURIComponent(link.href.split('#')[1]); | ||
| 106 | const selector = link.title.split('=>')[1] ?? '#' + label; | ||
| 107 | const target = document.querySelector(selector); | ||
| 108 | if (!target?.checkVisibility()) return; | ||
| 109 | |||
| 110 | const line = new LeaderLine({ | ||
| 111 | start: link, | ||
| 112 | end: target, | ||
| 113 | middleLabel: LeaderLine.pathLabel({ | ||
| 114 | text: label, | ||
| 115 | fontWeight: 'bold', | ||
| 116 | }), | ||
| 117 | hide: true, | ||
| 118 | path: 'magnet', | ||
| 119 | }); | ||
| 120 | link.lines.push(line); | ||
| 121 | line.show('draw', { duration: 300 }); | ||
| 122 | }; | ||
| 123 | link.onmouseout = () => { | ||
| 124 | link.lines.forEach(line => line.remove()); | ||
| 125 | link.lines.length = 0; | ||
| 126 | }; | ||
| 127 | }; | ||
diff --git a/src/dumbymap.mjs b/src/dumbymap.mjs index c7bdc92..72936ec 100644 --- a/src/dumbymap.mjs +++ b/src/dumbymap.mjs | |||
| @@ -21,76 +21,6 @@ const layouts = [ | |||
| 21 | ]; | 21 | ]; |
| 22 | const mapCache = {}; | 22 | const mapCache = {}; |
| 23 | 23 | ||
| 24 | // FUNCTION: Get DocLinks from special anchor element {{{ | ||
| 25 | /** | ||
| 26 | * CreateDocLink. | ||
| 27 | * | ||
| 28 | * @param {HTMLElement} Elements contains anchor elements for doclinks | ||
| 29 | */ | ||
| 30 | export const createDocLink = link => { | ||
| 31 | link.classList.add('with-leader-line', 'doclink'); | ||
| 32 | link.lines = []; | ||
| 33 | |||
| 34 | link.onmouseover = () => { | ||
| 35 | const label = decodeURIComponent(link.href.split('#')[1]); | ||
| 36 | const selector = link.title.split('=>')[1] ?? '#' + label; | ||
| 37 | const target = document.querySelector(selector); | ||
| 38 | if (!target?.checkVisibility()) return; | ||
| 39 | |||
| 40 | const line = new LeaderLine({ | ||
| 41 | start: link, | ||
| 42 | end: target, | ||
| 43 | middleLabel: LeaderLine.pathLabel({ | ||
| 44 | text: label, | ||
| 45 | fontWeight: 'bold', | ||
| 46 | }), | ||
| 47 | hide: true, | ||
| 48 | path: 'magnet', | ||
| 49 | }); | ||
| 50 | link.lines.push(line); | ||
| 51 | line.show('draw', { duration: 300 }); | ||
| 52 | }; | ||
| 53 | link.onmouseout = () => { | ||
| 54 | link.lines.forEach(line => line.remove()); | ||
| 55 | link.lines.length = 0; | ||
| 56 | }; | ||
| 57 | }; | ||
| 58 | // }}} | ||
| 59 | // FUNCTION: Get GeoLinks from special anchor element {{{ | ||
| 60 | /** | ||
| 61 | * Create geolinks, which points to map by geo schema and id | ||
| 62 | * | ||
| 63 | * @param {HTMLElement} Elements contains anchor elements for doclinks | ||
| 64 | * @returns {Boolean} ture is link is created, false if coordinates are invalid | ||
| 65 | */ | ||
| 66 | export const createGeoLink = (link, callback = null) => { | ||
| 67 | const url = new URL(link.href); | ||
| 68 | const xyInParams = url.searchParams.get('xy'); | ||
| 69 | const xy = xyInParams | ||
| 70 | ? xyInParams.split(',')?.map(Number) | ||
| 71 | : url?.href | ||
| 72 | ?.match(/^geo:([0-9.,]+)/) | ||
| 73 | ?.at(1) | ||
| 74 | ?.split(',') | ||
| 75 | ?.reverse() | ||
| 76 | ?.map(Number); | ||
| 77 | |||
| 78 | if (!xy || isNaN(xy[0]) || isNaN(xy[1])) return false; | ||
| 79 | |||
| 80 | // Geo information in link | ||
| 81 | link.url = url; | ||
| 82 | link.xy = xy; | ||
| 83 | link.classList.add('with-leader-line', 'geolink'); | ||
| 84 | link.targets = link.url.searchParams.get('id')?.split(',') ?? null; | ||
| 85 | |||
| 86 | // LeaderLine | ||
| 87 | link.lines = []; | ||
| 88 | callback?.call(this, link); | ||
| 89 | |||
| 90 | return true; | ||
| 91 | }; | ||
| 92 | // }}} | ||
| 93 | |||
| 94 | export const markdown2HTML = (container, mdContent) => { | 24 | export const markdown2HTML = (container, mdContent) => { |
| 95 | // Render: Markdown -> HTML {{{ | 25 | // Render: Markdown -> HTML {{{ |
| 96 | container.replaceChildren(); | 26 | container.replaceChildren(); |
| @@ -184,7 +114,9 @@ export const generateMaps = (container, { delay, mapCallback }) => { | |||
| 184 | 114 | ||
| 185 | // LeaderLine {{{ | 115 | // LeaderLine {{{ |
| 186 | 116 | ||
| 187 | Array.from(container.querySelectorAll(docLinkSelector)).filter(createDocLink); | 117 | Array.from(container.querySelectorAll(docLinkSelector)).filter( |
| 118 | utils.createDocLink, | ||
| 119 | ); | ||
| 188 | 120 | ||
| 189 | // Get anchors with "geo:" scheme | 121 | // Get anchors with "geo:" scheme |
| 190 | htmlHolder.anchors = []; | 122 | htmlHolder.anchors = []; |
| @@ -202,7 +134,7 @@ export const generateMaps = (container, { delay, mapCallback }) => { | |||
| 202 | }; | 134 | }; |
| 203 | const geoLinks = Array.from( | 135 | const geoLinks = Array.from( |
| 204 | container.querySelectorAll(geoLinkSelector), | 136 | container.querySelectorAll(geoLinkSelector), |
| 205 | ).filter(l => createGeoLink(l, geoLinkCallback)); | 137 | ).filter(l => utils.createGeoLink(l, geoLinkCallback)); |
| 206 | 138 | ||
| 207 | const isAnchorPointedBy = link => anchor => { | 139 | const isAnchorPointedBy = link => anchor => { |
| 208 | const mapContainer = anchor.closest('.mapclay'); | 140 | const mapContainer = anchor.closest('.mapclay'); |
| @@ -527,7 +459,6 @@ export const generateMaps = (container, { delay, mapCallback }) => { | |||
| 527 | }); | 459 | }); |
| 528 | }); | 460 | }); |
| 529 | // }}} | 461 | // }}} |
| 530 | |||
| 531 | // Menu {{{ | 462 | // Menu {{{ |
| 532 | const menu = document.createElement('div'); | 463 | const menu = document.createElement('div'); |
| 533 | menu.className = 'menu'; | 464 | menu.className = 'menu'; |