aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/editor.mjs
diff options
context:
space:
mode:
Diffstat (limited to 'src/editor.mjs')
-rw-r--r--src/editor.mjs63
1 files changed, 48 insertions, 15 deletions
diff --git a/src/editor.mjs b/src/editor.mjs
index 8cb7045..5c65af4 100644
--- a/src/editor.mjs
+++ b/src/editor.mjs
@@ -1,5 +1,4 @@
1/* global EasyMDE */ 1/* global EasyMDE */
2/* eslint no-undef: "error" */
3import { markdown2HTML, generateMaps } from './dumbymap' 2import { markdown2HTML, generateMaps } from './dumbymap'
4import { defaultAliases, parseConfigsFromYaml } from 'mapclay' 3import { defaultAliases, parseConfigsFromYaml } from 'mapclay'
5import * as menuItem from './MenuItem' 4import * as menuItem from './MenuItem'
@@ -7,18 +6,41 @@ import { addAnchorByPoint } from './dumbyUtils.mjs'
7import { shiftByWindow } from './utils.mjs' 6import { shiftByWindow } from './utils.mjs'
8import LeaderLine from 'leader-line' 7import LeaderLine from 'leader-line'
9 8
9/**
10 * @typedef {Object} RefLink
11 * @property {string} ref -- name of link
12 * @property {string} link -- content of link
13 * @property {string|null} title -- title of link
14 */
15
10// Set up Containers {{{ 16// Set up Containers {{{
11/** Variables about dumbymap and editor **/ 17/** Variables about dumbymap and editor **/
12const url = new URL(window.location) 18const url = new URL(window.location)
13const context = document.querySelector('[data-mode]') 19const context = document.querySelector('[data-mode]')
14const dumbyContainer = document.querySelector('.DumbyMap') 20const dumbyContainer = document.querySelector('.DumbyMap')
21dumbyContainer.dataset.scrollLine = ''
15const textArea = document.querySelector('.editor textarea') 22const textArea = document.querySelector('.editor textarea')
16let dumbymap 23let dumbymap
17 24
18const refLinkPattern = /\[([^\x5B\x5D]+)\]:\s+(.+)/ 25/** Variables about Reference Style Links in Markdown */
26const refLinkPattern = /\[([^\x5B\x5D]+)\]:\s+(\S+)(\s["'](\S+)["'])?/
19let refLinks = [] 27let refLinks = []
28
29/**
30 * Validates if the given anchor name is unique
31 *
32 * @param {string} anchorName - The anchor name to validate
33 * @returns {boolean} True if the anchor name is unique, false otherwise
34 */
20const validateAnchorName = anchorName => 35const validateAnchorName = anchorName =>
21 !refLinks.find(obj => obj.ref === anchorName) 36 !refLinks.find(obj => obj.ref === anchorName)
37
38/**
39 * Appends a reference link to the CodeMirror instance
40 *
41 * @param {CodeMirror} cm - The CodeMirror instance
42 * @param {RefLink} refLink - The reference link to append
43 */
22const appendRefLink = (cm, refLink) => { 44const appendRefLink = (cm, refLink) => {
23 const { ref, link, title } = refLink 45 const { ref, link, title } = refLink
24 let refLinkString = `\n[${ref}]: ${link} "${title ?? ''}"` 46 let refLinkString = `\n[${ref}]: ${link} "${title ?? ''}"`
@@ -46,7 +68,7 @@ new window.MutationObserver(() => {
46 attributeOldValue: true, 68 attributeOldValue: true,
47}) 69})
48/** 70/**
49 * toggleEditing: toggle editing mode 71 * Toggles the editing mode
50 */ 72 */
51const toggleEditing = () => { 73const toggleEditing = () => {
52 const mode = context.dataset.mode 74 const mode = context.dataset.mode
@@ -236,12 +258,15 @@ const editor = new EasyMDE({
236/** CodeMirror Instance **/ 258/** CodeMirror Instance **/
237const cm = editor.codemirror 259const cm = editor.codemirror
238 260
239/** Ref Links **/ 261/**
262 * getRefLinks from contents of editor
263 * @return {RefLink[]} refLinks
264 */
240const getRefLinks = () => editor.value() 265const getRefLinks = () => editor.value()
241 .split('\n') 266 .split('\n')
242 .map(line => { 267 .map(line => {
243 const [, ref, link] = line.match(refLinkPattern) ?? [] 268 const [, ref, link,, title] = line.match(refLinkPattern) ?? []
244 return { ref, link } 269 return { ref, link, title }
245 }) 270 })
246 .filter(({ ref, link }) => ref && link) 271 .filter(({ ref, link }) => ref && link)
247 272
@@ -284,8 +309,12 @@ if (url.searchParams.get('content') === 'tutorial') {
284// }}} 309// }}}
285// Set up logic about editor content {{{ 310// Set up logic about editor content {{{
286 311
287/** Sync scroll from HTML to CodeMirror **/ 312/**
288const htmlOnScroll = (ele) => () => { 313 * updateScrollLine. Update data attribute by scroll on given element
314 *
315 * @param {HTMLElement} ele
316 */
317const updateScrollLine = (ele) => () => {
289 if (textArea.dataset.scrollLine) return 318 if (textArea.dataset.scrollLine) return
290 319
291 const threshold = ele.scrollTop + window.innerHeight / 2 + 30 320 const threshold = ele.scrollTop + window.innerHeight / 2 + 30
@@ -300,14 +329,14 @@ const htmlOnScroll = (ele) => () => {
300 const offset = (line.offsetTop + block.offsetTop - ele.scrollTop) 329 const offset = (line.offsetTop + block.offsetTop - ele.scrollTop)
301 330
302 if (linenumber) { 331 if (linenumber) {
303 dumbyContainer.dataset.scrollLine = linenumber + '/' + offset 332 ele.closest('[data-scroll-line]').dataset.scrollLine = linenumber + '/' + offset
304 } 333 }
305} 334}
306 335
307new window.MutationObserver(() => { 336new window.MutationObserver(() => {
308 clearTimeout(dumbyContainer.timer) 337 clearTimeout(dumbyContainer.timer)
309 dumbyContainer.timer = setTimeout( 338 dumbyContainer.timer = setTimeout(
310 () => delete dumbyContainer.dataset.scrollLine, 339 () => { dumbyContainer.dataset.scrollLine = '' },
311 50, 340 50,
312 ) 341 )
313 342
@@ -324,7 +353,11 @@ new window.MutationObserver(() => {
324 attributeFilter: ['data-scroll-line'], 353 attributeFilter: ['data-scroll-line'],
325}) 354})
326 355
327const setScrollLine = () => { 356/**
357 * updateScrollLineByCodeMirror.
358 * @param {CodeMirror} cm
359 */
360const updateCMScrollLine = (cm) => {
328 if (dumbyContainer.dataset.scrollLine) return 361 if (dumbyContainer.dataset.scrollLine) return
329 362
330 const lineNumber = cm.getCursor()?.line ?? 363 const lineNumber = cm.getCursor()?.line ??
@@ -332,14 +365,14 @@ const setScrollLine = () => {
332 textArea.dataset.scrollLine = lineNumber 365 textArea.dataset.scrollLine = lineNumber
333} 366}
334cm.on('scroll', () => { 367cm.on('scroll', () => {
335 if (cm.hasFocus()) setScrollLine() 368 if (cm.hasFocus()) updateCMScrollLine(cm)
336}) 369})
337 370
338/** Sync scroll from CodeMirror to HTML **/ 371/** Sync scroll from CodeMirror to HTML **/
339new window.MutationObserver(() => { 372new window.MutationObserver(() => {
340 clearTimeout(textArea.timer) 373 clearTimeout(textArea.timer)
341 textArea.timer = setTimeout( 374 textArea.timer = setTimeout(
342 () => delete textArea.dataset.scrollLine, 375 () => { textArea.dataset.scrollLine = '' },
343 1000, 376 1000,
344 ) 377 )
345 378
@@ -486,7 +519,7 @@ const updateDumbyMap = (callback = null) => {
486 dumbymap = generateMaps(dumbyContainer) 519 dumbymap = generateMaps(dumbyContainer)
487 // Set onscroll callback 520 // Set onscroll callback
488 const htmlHolder = dumbymap.htmlHolder 521 const htmlHolder = dumbymap.htmlHolder
489 htmlHolder.onscroll = htmlOnScroll(htmlHolder) 522 htmlHolder.onscroll = updateScrollLine(htmlHolder)
490 // Set oncontextmenu callback 523 // Set oncontextmenu callback
491 dumbymap.utils.setContextMenu(menuForEditor) 524 dumbymap.utils.setContextMenu(menuForEditor)
492 525
@@ -497,7 +530,7 @@ updateDumbyMap()
497// Re-render HTML by editor content 530// Re-render HTML by editor content
498cm.on('change', (_, change) => { 531cm.on('change', (_, change) => {
499 updateDumbyMap(() => { 532 updateDumbyMap(() => {
500 setScrollLine() 533 updateCMScrollLine(cm)
501 }) 534 })
502 addClassToCodeLines() 535 addClassToCodeLines()
503 completeForCodeBlock(change) 536 completeForCodeBlock(change)