aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--src/Link.mjs239
-rw-r--r--src/MenuItem.mjs11
-rw-r--r--src/dumbyUtils.mjs292
-rw-r--r--src/dumbymap.mjs13
4 files changed, 288 insertions, 267 deletions
diff --git a/src/Link.mjs b/src/Link.mjs
new file mode 100644
index 0000000..61aa5f8
--- /dev/null
+++ b/src/Link.mjs
@@ -0,0 +1,239 @@
1import LeaderLine from 'leader-line'
2import { insideWindow, insideParent } from './utils'
3
4/** VAR: pattern for coodinates */
5export const coordPattern = /^geo:([-]?[0-9.]+),([-]?[0-9.]+)/
6
7/**
8 * Class: GeoLink - link for maps
9 *
10 * @extends {window.HTMLAnchorElement}
11 */
12export class GeoLink extends window.HTMLAnchorElement {
13 static replaceWith = (link) =>
14 link.replaceWith(new GeoLink(link))
15
16 /**
17 * Creates a new GeoLink instance
18 *
19 * @param {HTMLAnchorElement} link
20 */
21 constructor (link) {
22 super()
23 this.innerHTML = link.innerHTML
24 this.href = link.href
25
26 const url = new URL(link.href)
27 const params = new URLSearchParams(link.search)
28 const xyInParams = params.get('xy')?.split(',')?.map(Number)
29 const [lon, lat] = url.href
30 ?.match(coordPattern)
31 ?.slice(1)
32 ?.reverse()
33 ?.map(Number)
34 const xy = xyInParams ?? [lon, lat]
35
36 if (!xy || isNaN(xy[0]) || isNaN(xy[1])) return false
37
38 // Geo information in link
39 this.dataset.lon = lon
40 this.dataset.lat = lat
41 this.dataset.crs = params.get('crs')
42 this.classList.add('with-leader-line', 'geolink')
43 this.classList.remove('not-geolink')
44 // TODO refactor as data attribute
45 this.targets = params.get('id')?.split(',') ?? null
46 this.title = 'Left-Click to move Camera, Middle-Click to clean anchor'
47 this.lines = []
48
49 // Hover link for LeaderLine
50 this.onmouseover = () => this.getMarkersFromMaps(this)
51 .filter(isAnchorVisible)
52 .forEach(anchor => {
53 const labelText = new URL(this).searchParams.get('text') ?? this.textContent
54 const line = new LeaderLine({
55 start: this,
56 end: anchor,
57 hide: true,
58 middleLabel: labelText,
59 path: 'magnet',
60 })
61 line.show('draw', { duration: 300 })
62
63 this.lines.push(line)
64 })
65
66 this.onmouseout = () => removeLeaderLines(this)
67
68 // Click to move camera
69 this.onclick = (event) => {
70 event.preventDefault()
71 removeLeaderLines(this)
72 this.getMarkersFromMaps().forEach(marker => {
73 const map = marker.closest('.mapclay')
74 map.scrollIntoView({ behavior: 'smooth' })
75 updateMapCameraByMarker([
76 Number(this.dataset.lon),
77 Number(this.dataset.lat),
78 ])(marker)
79 })
80 }
81
82 // Use middle click to remove markers
83 this.onauxclick = (e) => {
84 if (e.which !== 2) return
85 e.preventDefault()
86 removeLeaderLines(this)
87 this.getMarkersFromMaps()
88 .forEach(marker => marker.remove())
89 }
90 }
91
92 /**
93 * getMarkersFromMaps. Get marker elements by GeoLink
94 *
95 * @param {HTMLAnchorElement} link
96 * @return {HTMLElement[]} markers
97 */
98 getMarkersFromMaps () {
99 const params = new URLSearchParams(this.search)
100 const maps = Array.from(
101 this.closest('.Dumby')
102 .querySelectorAll('.mapclay[data-render="fulfilled"]'),
103 )
104 return maps
105 .filter(map => this.targets ? this.targets.includes(map.id) : true)
106 .map(map => {
107 const renderer = map.renderer
108 const lonLat = [Number(this.dataset.lon), Number(this.dataset.lat)]
109
110 const marker = map.querySelector(`.marker[data-xy="${lonLat}"]`) ??
111 renderer.addMarker({
112 xy: lonLat,
113 type: params.get('type') ?? null,
114 })
115 marker.dataset.xy = lonLat
116 marker.title = new URLSearchParams(this.search).get('xy') ?? lonLat
117 const crs = this.dataset.crs
118 if (crs && crs !== 'EPSG:4326') {
119 marker.title += '@' + this.dataset.crs
120 }
121
122 return marker
123 })
124 }
125}
126if (!window.customElements.get('dumby-geolink')) {
127 window.customElements.define('dumby-geolink', GeoLink, { extends: 'a' })
128}
129
130/**
131 * Class: DocLink - link for DOM
132 *
133 * @extends {window.HTMLAnchorElement}
134 */
135export class DocLink extends window.HTMLAnchorElement {
136 static replaceWith = (link) =>
137 link.replaceWith(new DocLink(link))
138
139 /**
140 * Creates a new DocLink instance
141 *
142 * @param {HTMLAnchorElement} link
143 */
144 constructor (link) {
145 super()
146 this.innerHTML = link.innerHTML
147 this.href = link.href
148
149 const label = decodeURIComponent(link.href.split('#')[1])
150 const selector = link.title.split('=>')[1] ?? (label ? '#' + label : null)
151 if (!selector) return false
152
153 this.classList.add('with-leader-line', 'doclink')
154 this.lines = []
155
156 this.onmouseover = () => {
157 const targets = document.querySelectorAll(selector)
158
159 targets.forEach(target => {
160 if (!target?.checkVisibility()) return
161
162 // highlight selected target
163 target.dataset.style = target.style.cssText
164 const rect = target.getBoundingClientRect()
165 const isTiny = rect.width < 100 || rect.height < 100
166 if (isTiny) {
167 target.style.background = 'lightPink'
168 } else {
169 target.style.outline = 'lightPink 6px dashed'
170 }
171
172 // point to selected target
173 const line = new LeaderLine({
174 start: this,
175 end: target,
176 middleLabel: LeaderLine.pathLabel({
177 text: label,
178 fontWeight: 'bold',
179 }),
180 hide: true,
181 path: 'magnet',
182 })
183 this.lines.push(line)
184 line.show('draw', { duration: 300 })
185 })
186 }
187
188 this.onmouseout = () => {
189 removeLeaderLines(this)
190
191 // resume targets from highlight
192 const targets = document.querySelectorAll(selector)
193 targets.forEach(target => {
194 target.style.cssText = target.dataset.style
195 delete target.dataset.style
196 })
197 }
198 }
199}
200if (!window.customElements.get('dumby-doclink')) {
201 window.customElements.define('dumby-doclink', DocLink, { extends: 'a' })
202}
203
204/**
205 * isAnchorVisible. check anchor(marker) is visible for current map camera
206 *
207 * @param {Element} anchor
208 */
209const isAnchorVisible = anchor => {
210 const mapContainer = anchor.closest('.mapclay')
211 return insideWindow(anchor) && insideParent(anchor, mapContainer)
212}
213
214/**
215 * updateMapByMarker. get function for updating map camera by marker
216 *
217 * @param {Number[]} xy
218 * @return {Function} function
219 */
220const updateMapCameraByMarker = lonLat => marker => {
221 const renderer = marker.closest('.mapclay')?.renderer
222 renderer.updateCamera({ center: lonLat }, true)
223}
224
225/**
226 * removeLeaderLines. clean lines start from link
227 *
228 * @param {HTMLAnchorElement} link
229 */
230export const removeLeaderLines = link => {
231 if (!link.lines) return
232 link.lines.forEach(line => {
233 line.hide('draw', { duration: 300 })
234 setTimeout(() => {
235 line.remove()
236 }, 300)
237 })
238 link.lines = []
239}
diff --git a/src/MenuItem.mjs b/src/MenuItem.mjs
index 874c829..912080b 100644
--- a/src/MenuItem.mjs
+++ b/src/MenuItem.mjs
@@ -1,5 +1,5 @@
1import { shiftByWindow } from './utils.mjs' 1import { shiftByWindow } from './utils.mjs'
2import * as utils from './dumbyUtils.mjs' 2import { GeoLink, removeLeaderLines } from './Link.mjs'
3 3
4/** 4/**
5 * @typedef {Object} RefLink 5 * @typedef {Object} RefLink
@@ -431,7 +431,7 @@ export const addRefLink = (cm, refLinks) =>
431/** 431/**
432 * setGeoLinkTypeItem. 432 * setGeoLinkTypeItem.
433 * 433 *
434 * @param {HTMLAnchorElement} link 434 * @param {GeoLink} link
435 * @param {String} text 435 * @param {String} text
436 * @param {String} type 436 * @param {String} type
437 */ 437 */
@@ -439,13 +439,14 @@ export const setGeoLinkTypeItem = ({ link, text, type }) => {
439 const params = new URLSearchParams(link.search) 439 const params = new URLSearchParams(link.search)
440 return new Item({ 440 return new Item({
441 text, 441 text,
442 className: ['keep-menu'],
442 onclick: () => { 443 onclick: () => {
443 params.set('type', type) 444 params.set('type', type)
444 link.search = params 445 link.search = params
445 utils.removeLeaderLines(link) 446 removeLeaderLines(link)
446 utils.getMarkersFromMaps(link) 447 link.getMarkersFromMaps()
447 .forEach(marker => marker.remove()) 448 .forEach(marker => marker.remove())
448 utils.getMarkersFromMaps(link) 449 link.getMarkersFromMaps()
449 }, 450 },
450 }) 451 })
451} 452}
diff --git a/src/dumbyUtils.mjs b/src/dumbyUtils.mjs
index 8fe23eb..6a1eaca 100644
--- a/src/dumbyUtils.mjs
+++ b/src/dumbyUtils.mjs
@@ -1,8 +1,7 @@
1import LeaderLine from 'leader-line' 1import LeaderLine from 'leader-line'
2import { insideWindow, insideParent, replaceTextNodes, full2Half } from './utils' 2import { replaceTextNodes, full2Half } from './utils'
3import proj4 from 'proj4' 3import proj4 from 'proj4'
4 4import { coordPattern, GeoLink } from './Link.mjs'
5export const coordPattern = /^geo:([-]?[0-9.]+),([-]?[0-9.]+)/
6 5
7/** 6/**
8 * focusNextMap. 7 * focusNextMap.
@@ -88,225 +87,6 @@ export function removeBlockFocus () {
88} 87}
89 88
90/** 89/**
91 * getMarkersFromMaps. Get marker elements by GeoLink
92 *
93 * @param {HTMLAnchorElement} link
94 * @return {HTMLElement[]} markers
95 */
96export const getMarkersFromMaps = link => {
97 const params = new URLSearchParams(link.search)
98 const maps = Array.from(
99 link.closest('.Dumby')
100 .querySelectorAll('.mapclay[data-render="fulfilled"]'),
101 )
102 return maps
103 .filter(map => link.targets ? link.targets.includes(map.id) : true)
104 .map(map => {
105 const renderer = map.renderer
106 const lonLat = [Number(link.dataset.lon), Number(link.dataset.lat)]
107
108 const marker = map.querySelector(`.marker[data-xy="${lonLat}"]`) ??
109 renderer.addMarker({
110 xy: lonLat,
111 type: params.get('type') ?? null,
112 })
113 marker.dataset.xy = lonLat
114 marker.title = new URLSearchParams(link.search).get('xy') ?? lonLat
115 const crs = link.dataset.crs
116 if (crs && crs !== 'EPSG:4326') {
117 marker.title += '@' + link.dataset.crs
118 }
119
120 return marker
121 })
122}
123
124/**
125 * addLeaderLine, from link element to target element
126 *
127 * @param {HTMLAnchorElement} link
128 * @param {Element} target
129 */
130const addLeaderLine = (link, target) => {
131 const labelText = new URL(link).searchParams.get('text') ?? link.textContent
132 const line = new LeaderLine({
133 start: link,
134 end: target,
135 hide: true,
136 middleLabel: labelText,
137 path: 'magnet',
138 })
139 line.show('draw', { duration: 300 })
140
141 return line
142}
143
144/**
145 * Create geolinks, which points to map by geo schema and id
146 *
147 * @param {HTMLElement} Elements contains anchor elements for GeoLinks
148 * @returns {Boolean} ture is link is created, false if coordinates are invalid
149 */
150export const createGeoLink = (link) => {
151 const url = new URL(link.href)
152 const params = new URLSearchParams(link.search)
153 const xyInParams = params.get('xy')?.split(',')?.map(Number)
154 const [lon, lat] = url.href
155 ?.match(coordPattern)
156 ?.slice(1)
157 ?.reverse()
158 ?.map(Number)
159 const xy = xyInParams ?? [lon, lat]
160
161 if (!xy || isNaN(xy[0]) || isNaN(xy[1])) return false
162
163 // Geo information in link
164 link.dataset.lon = lon
165 link.dataset.lat = lat
166 link.dataset.crs = params.get('crs')
167 link.classList.add('with-leader-line', 'geolink')
168 link.classList.remove('not-geolink')
169 // TODO refactor as data attribute
170 link.targets = params.get('id')?.split(',') ?? null
171 link.title = 'Left-Click to move Camera, Middle-Click to clean anchor'
172
173 link.lines = []
174
175 // Hover link for LeaderLine
176 link.onmouseover = () => {
177 if (link.dataset.valid === 'false') return
178
179 const anchors = getMarkersFromMaps(link)
180 anchors
181 .filter(isAnchorVisible)
182 .forEach(anchor => {
183 const line = addLeaderLine(link, anchor)
184 link.lines.push(line)
185 })
186 }
187 link.onmouseout = () => removeLeaderLines(link)
188
189 // Click to move camera
190 link.onclick = (event) => {
191 event.preventDefault()
192 if (link.dataset.valid === 'false') return
193
194 removeLeaderLines(link)
195 getMarkersFromMaps(link).forEach(marker => {
196 const map = marker.closest('.mapclay')
197 map.scrollIntoView({ behavior: 'smooth' })
198 updateMapCameraByMarker([
199 Number(link.dataset.lon),
200 Number(link.dataset.lat),
201 ])(marker)
202 })
203 }
204
205 // Use middle click to remove markers
206 link.onauxclick = (e) => {
207 if (e.which !== 2) return
208 e.preventDefault()
209 removeLeaderLines(link)
210 getMarkersFromMaps(link)
211 .forEach(marker => marker.remove())
212 }
213 return true
214}
215
216/**
217 * CreateDocLink.
218 *
219 * @param {HTMLElement} Elements contains anchor elements for doclinks
220 */
221export const createDocLink = link => {
222 const label = decodeURIComponent(link.href.split('#')[1])
223 const selector = link.title.split('=>')[1] ?? (label ? '#' + label : null)
224 if (!selector) return false
225
226 link.classList.add('with-leader-line', 'doclink')
227 link.lines = []
228
229 link.onmouseover = () => {
230 const targets = document.querySelectorAll(selector)
231
232 targets.forEach(target => {
233 if (!target?.checkVisibility()) return
234
235 // highlight selected target
236 target.dataset.style = target.style.cssText
237 const rect = target.getBoundingClientRect()
238 const isTiny = rect.width < 100 || rect.height < 100
239 if (isTiny) {
240 target.style.background = 'lightPink'
241 } else {
242 target.style.outline = 'lightPink 6px dashed'
243 }
244
245 // point to selected target
246 const line = new LeaderLine({
247 start: link,
248 end: target,
249 middleLabel: LeaderLine.pathLabel({
250 text: label,
251 fontWeight: 'bold',
252 }),
253 hide: true,
254 path: 'magnet',
255 })
256 link.lines.push(line)
257 line.show('draw', { duration: 300 })
258 })
259 }
260 link.onmouseout = () => {
261 link.onmouseout = () => removeLeaderLines(link)
262
263 // resume targets from highlight
264 const targets = document.querySelectorAll(selector)
265 targets.forEach(target => {
266 target.style.cssText = target.dataset.style
267 delete target.dataset.style
268 })
269 }
270}
271
272/**
273 * removeLeaderLines. clean lines start from link
274 *
275 * @param {HTMLAnchorElement} link
276 */
277export const removeLeaderLines = link => {
278 if (!link.lines) return
279 link.lines.forEach(line => {
280 line.hide('draw', { duration: 300 })
281 setTimeout(() => {
282 line.remove()
283 }, 300)
284 })
285 link.lines = []
286}
287
288/**
289 * updateMapByMarker. get function for updating map camera by marker
290 *
291 * @param {Number[]} xy
292 * @return {Function} function
293 */
294const updateMapCameraByMarker = lonLat => marker => {
295 const renderer = marker.closest('.mapclay')?.renderer
296 renderer.updateCamera({ center: lonLat }, true)
297}
298
299/**
300 * isAnchorVisible. check anchor(marker) is visible for current map camera
301 *
302 * @param {Element} anchor
303 */
304const isAnchorVisible = anchor => {
305 const mapContainer = anchor.closest('.mapclay')
306 return insideWindow(anchor) && insideParent(anchor, mapContainer)
307}
308
309/**
310 * addMarkerByPoint. 90 * addMarkerByPoint.
311 * 91 *
312 * @param {Number[]} options.point - page XY 92 * @param {Number[]} options.point - page XY
@@ -328,6 +108,30 @@ export const addMarkerByPoint = ({ point, map }) => {
328} 108}
329 109
330/** 110/**
111 * addGeoSchemeByText.
112 *
113 * @param {Node} node
114 */
115export const addGeoSchemeByText = async (node) => {
116 const digit = '[\\d\\uFF10-\\uFF19]'
117 const decimal = '[.\\uFF0E]'
118 const coordPatterns = `(-?${digit}+${decimal}?${digit}*)([,\x2F\uFF0C])(-?${digit}+${decimal}?${digit}*)`
119 const re = new RegExp(coordPatterns, 'g')
120
121 return replaceTextNodes(node, re, match => {
122 const [x, y] = [full2Half(match.at(1)), full2Half(match.at(3))]
123 // Don't process string which can be used as date
124 if (Date.parse(match.at(0) + ' 1990')) return null
125
126 const a = document.createElement('a')
127 a.className = 'not-geolink from-text'
128 a.href = `geo:0,0?xy=${x},${y}`
129 a.textContent = match.at(0)
130 return a
131 })
132}
133
134/**
331 * setGeoSchemeByCRS. 135 * setGeoSchemeByCRS.
332 * @description Add more information into Anchor Element within Geo Scheme by CRS 136 * @description Add more information into Anchor Element within Geo Scheme by CRS
333 * @param {String} crs - EPSG/ESRI Code for CRS 137 * @param {String} crs - EPSG/ESRI Code for CRS
@@ -375,17 +179,17 @@ export const setGeoSchemeByCRS = (crs) => (link) => {
375} 179}
376 180
377/** 181/**
378 * dragForAnchor. 182 * addGeoLinkByDrag.
379 * 183 *
380 * @param {HTMLElement} container 184 * @param {HTMLElement} container
381 * @param {Range} range 185 * @param {Range} range
382 */ 186 */
383export const dragForAnchor = (container, range, endOfLeaderLine) => { 187export const addGeoLinkByDrag = (container, range, endOfLeaderLine) => {
384 // link placeholder when dragging 188 // link placeholder when dragging
385 container.classList.add('dragging-geolink') 189 container.classList.add('dragging-geolink')
386 const geoLink = document.createElement('a') 190 const link = document.createElement('a')
387 geoLink.textContent = range.toString() 191 link.textContent = range.toString()
388 geoLink.classList.add('with-leader-line', 'geolink', 'drag', 'from-text') 192 link.classList.add('with-leader-line', 'geolink', 'drag', 'from-text')
389 193
390 // Replace current content with link 194 // Replace current content with link
391 const originContent = range.cloneContents() 195 const originContent = range.cloneContents()
@@ -394,11 +198,11 @@ export const dragForAnchor = (container, range, endOfLeaderLine) => {
394 range.insertNode(originContent) 198 range.insertNode(originContent)
395 } 199 }
396 range.deleteContents() 200 range.deleteContents()
397 range.insertNode(geoLink) 201 range.insertNode(link)
398 202
399 // Add leader-line 203 // Add leader-line
400 const line = new LeaderLine({ 204 const line = new LeaderLine({
401 start: geoLink, 205 start: link,
402 end: endOfLeaderLine, 206 end: endOfLeaderLine,
403 path: 'magnet', 207 path: 'magnet',
404 }) 208 })
@@ -416,7 +220,7 @@ export const dragForAnchor = (container, range, endOfLeaderLine) => {
416 container.classList.remove('dragging-geolink') 220 container.classList.remove('dragging-geolink')
417 container.onmousemove = null 221 container.onmousemove = null
418 container.onmouseup = null 222 container.onmouseup = null
419 geoLink.classList.remove('drag') 223 link.classList.remove('drag')
420 positionObserver.disconnect() 224 positionObserver.disconnect()
421 line.remove() 225 line.remove()
422 endOfLeaderLine.remove() 226 endOfLeaderLine.remove()
@@ -434,31 +238,7 @@ export const dragForAnchor = (container, range, endOfLeaderLine) => {
434 return 238 return
435 } 239 }
436 240
437 geoLink.href = `geo:${marker.dataset.xy.split(',').reverse()}` 241 link.href = `geo:${marker.dataset.xy.split(',').reverse()}`
438 createGeoLink(geoLink) 242 GeoLink.replaceWith(link)
439 } 243 }
440} 244}
441
442/**
443 * addGeoSchemeByText.
444 *
445 * @param {Node} node
446 */
447export const addGeoSchemeByText = async (node) => {
448 const digit = '[\\d\\uFF10-\\uFF19]'
449 const decimal = '[.\\uFF0E]'
450 const coordPatterns = `(-?${digit}+${decimal}?${digit}*)([,\x2F\uFF0C])(-?${digit}+${decimal}?${digit}*)`
451 const re = new RegExp(coordPatterns, 'g')
452
453 return replaceTextNodes(node, re, match => {
454 const [x, y] = [full2Half(match.at(1)), full2Half(match.at(3))]
455 // Don't process string which can be used as date
456 if (Date.parse(match.at(0) + ' 1990')) return null
457
458 const a = document.createElement('a')
459 a.className = 'not-geolink from-text'
460 a.href = `geo:0,0?xy=${x},${y}`
461 a.textContent = match.at(0)
462 return a
463 })
464}
diff --git a/src/dumbymap.mjs b/src/dumbymap.mjs
index 92fe785..bb3737b 100644
--- a/src/dumbymap.mjs
+++ b/src/dumbymap.mjs
@@ -6,6 +6,7 @@ import MarkdownItInjectLinenumbers from 'markdown-it-inject-linenumbers'
6import * as mapclay from 'mapclay' 6import * as mapclay from 'mapclay'
7import { onRemove, animateRectTransition, throttle, debounce, shiftByWindow } from './utils' 7import { onRemove, animateRectTransition, throttle, debounce, shiftByWindow } from './utils'
8import { Layout, SideBySide, Overlay, Sticky } from './Layout' 8import { Layout, SideBySide, Overlay, Sticky } from './Layout'
9import { GeoLink, DocLink } from './Link.mjs'
9import * as utils from './dumbyUtils' 10import * as utils from './dumbyUtils'
10import * as menuItem from './MenuItem' 11import * as menuItem from './MenuItem'
11import PlainModal from 'plain-modal' 12import PlainModal from 'plain-modal'
@@ -209,7 +210,7 @@ export const generateMaps = (container, {
209 if (node.matches?.('.mapclay') || node.closest?.('.mapclay')) return 210 if (node.matches?.('.mapclay') || node.closest?.('.mapclay')) return
210 211
211 // Add GeoLinks from plain texts 212 // Add GeoLinks from plain texts
212 addGeoLinksByText(node) 213 utils.addGeoSchemeByText(node)
213 214
214 // Render Map 215 // Render Map
215 const mapTarget = node.parentElement?.closest(mapBlockSelector) 216 const mapTarget = node.parentElement?.closest(mapBlockSelector)
@@ -245,9 +246,9 @@ export const generateMaps = (container, {
245 246
246 // Add GeoLinks/DocLinks by pattern 247 // Add GeoLinks/DocLinks by pattern
247 target.querySelectorAll(geoLinkSelector) 248 target.querySelectorAll(geoLinkSelector)
248 .forEach(utils.createGeoLink) 249 .forEach(GeoLink.replaceWith)
249 target.querySelectorAll(docLinkSelector) 250 target.querySelectorAll(docLinkSelector)
250 .forEach(utils.createDocLink) 251 .forEach(DocLink.replaceWith)
251 252
252 // Add GeoLinks from text nodes 253 // Add GeoLinks from text nodes
253 // const addedNodes = Array.from(mutation.addedNodes) 254 // const addedNodes = Array.from(mutation.addedNodes)
@@ -318,7 +319,7 @@ export const generateMaps = (container, {
318 values.at(-1) 319 values.at(-1)
319 .map(utils.setGeoSchemeByCRS(crsString)) 320 .map(utils.setGeoSchemeByCRS(crsString))
320 .filter(link => link) 321 .filter(link => link)
321 .forEach(utils.createGeoLink) 322 .forEach(GeoLink.replaceWith)
322 }) 323 })
323 } 324 }
324 325
@@ -556,7 +557,7 @@ export const generateMaps = (container, {
556 menu.appendChild(new menuItem.Item({ 557 menu.appendChild(new menuItem.Item({
557 text: 'Delete', 558 text: 'Delete',
558 onclick: () => { 559 onclick: () => {
559 utils.getMarkersFromMaps(geoLink) 560 geoLink.getMarkersFromMaps()
560 .forEach(m => m.remove()) 561 .forEach(m => m.remove())
561 geoLink.replaceWith( 562 geoLink.replaceWith(
562 document.createTextNode(geoLink.textContent), 563 document.createTextNode(geoLink.textContent),
@@ -637,7 +638,7 @@ export const generateMaps = (container, {
637 container.appendChild(pointByArrow) 638 container.appendChild(pointByArrow)
638 639
639 const timer = setTimeout(() => { 640 const timer = setTimeout(() => {
640 utils.dragForAnchor(container, range, pointByArrow) 641 utils.addGeoLinkByDrag(container, range, pointByArrow)
641 }, 300) 642 }, 300)
642 643
643 // Update leader-line with mouse move 644 // Update leader-line with mouse move