aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/editor.mjs
diff options
context:
space:
mode:
Diffstat (limited to 'src/editor.mjs')
-rw-r--r--src/editor.mjs140
1 files changed, 66 insertions, 74 deletions
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 @@
3import { markdown2HTML, generateMaps } from './dumbymap' 3import { markdown2HTML, generateMaps } from './dumbymap'
4import { defaultAliases, parseConfigsFromYaml } from 'mapclay' 4import { defaultAliases, parseConfigsFromYaml } from 'mapclay'
5import * as menuItem from './MenuItem' 5import * as menuItem from './MenuItem'
6import { shiftByWindow } from './utils.mjs'
7import { addAnchorByPoint } from './dumbyUtils.mjs' 6import { addAnchorByPoint } from './dumbyUtils.mjs'
7import { shiftByWindow } from './utils.mjs'
8import LeaderLine from 'leader-line' 8import LeaderLine from 'leader-line'
9 9
10// Set up Containers {{{ 10// Set up Containers {{{
11 11/** Variables about dumbymap and editor **/
12const url = new URL(window.location) 12const url = new URL(window.location)
13const context = document.querySelector('[data-mode]') 13const context = document.querySelector('[data-mode]')
14const dumbyContainer = document.querySelector('.DumbyMap') 14const 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 */
52const toggleEditing = () => { 50const 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
61const defaultContent = 57const 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 **/
108const editor = new EasyMDE({ 105const 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 **/
221const cm = editor.codemirror 218const cm = editor.codemirror
222 219
220/** Ref Links **/
223const getRefLinks = () => editor.value() 221const 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 **/
257const contentFromHash = getContentFromHash(window.location.hash) 255const contentFromHash = getContentFromHash(window.location.hash)
256window.location.hash = ''
258 257
259if (url.searchParams.get('content') === 'tutorial') { 258if (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
267window.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 **/
272const htmlOnScroll = (ele) => () => { 269const 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
292new window.MutationObserver(() => { 288new 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
312const setScrollLine = () => { 308const setScrollLine = () => {
@@ -318,12 +314,12 @@ const setScrollLine = () => {
318} 314}
319cm.on('scroll', setScrollLine) 315cm.on('scroll', setScrollLine)
320 316
321// Sync HTML Contents with CodeMirror LineNumber 317/** Sync scroll from CodeMirror to HTML **/
322new window.MutationObserver(() => { 318new 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
351markdown2HTML(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) => {
468const updateDumbyMap = () => { 462const 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})
526document.body.append(menu) 520document.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 */
1071dumbyContainer.onmousedown = (e) => { 1066dumbyContainer.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
1135dumbyContainer.ondragstart = () => false 1129dumbyContainer.ondragstart = () => false
1136
1137// vim: sw=2 ts=2 foldmethod=marker foldmarker={{{,}}}