aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--package.json1
-rw-r--r--src/Layout.mjs26
-rw-r--r--src/MenuItem.mjs36
-rw-r--r--src/dumbymap.mjs18
-rw-r--r--src/editor.mjs63
5 files changed, 104 insertions, 40 deletions
diff --git a/package.json b/package.json
index 78a217c..7487e9c 100644
--- a/package.json
+++ b/package.json
@@ -28,6 +28,7 @@
28 "dev": "npm run server", 28 "dev": "npm run server",
29 "lint": "standard --fix", 29 "lint": "standard --fix",
30 "style": "scripts/stylelint.sh", 30 "style": "scripts/stylelint.sh",
31 "docs": "jsdoc -c scripts/jsdoc.conf; xdg-open http://localhost:8080/docs/",
31 "prepack": "npm run lint && npm run style && npm run build", 32 "prepack": "npm run lint && npm run style && npm run build",
32 "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" 33 "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"
33 }, 34 },
diff --git a/src/Layout.mjs b/src/Layout.mjs
index 5236475..7b14926 100644
--- a/src/Layout.mjs
+++ b/src/Layout.mjs
@@ -2,13 +2,17 @@ import PlainDraggable from 'plain-draggable'
2import { onRemove, animateRectTransition } from './utils' 2import { onRemove, animateRectTransition } from './utils'
3 3
4/** 4/**
5 * Layout. Basic class for layout 5 * Basic class for layout
6 */ 6 */
7export class Layout { 7export class Layout {
8 /** 8 /**
9 * constructor. 9 * Creates a new Layout instance
10 * 10 *
11 * @param {} options 11 * @param {Object} options - The options for the layout
12 * @param {string} options.name - The name of the layout
13 * @param {Function} [options.enterHandler] - Handler called when entering the layout
14 * @param {Function} [options.leaveHandler] - Handler called when leaving the layout
15 * @throws {Error} If the layout name is not provided
12 */ 16 */
13 constructor (options = {}) { 17 constructor (options = {}) {
14 if (!options.name) throw Error('Layout name is not given') 18 if (!options.name) throw Error('Layout name is not given')
@@ -18,7 +22,9 @@ export class Layout {
18 } 22 }
19 23
20 /** 24 /**
21 * valueOf. 25 * Returns the name of the layout
26 *
27 * @returns {string} The name of the layout
22 */ 28 */
23 valueOf = () => this.name 29 valueOf = () => this.name
24} 30}
@@ -35,9 +41,12 @@ export class SideBySide extends Layout {
35 name = 'side-by-side' 41 name = 'side-by-side'
36 42
37 /** 43 /**
38 * enterHandler. 44 * Handler called when entering the Side-By-Side layout
39 * 45 *
40 * @param {} 46 * @param {Object} options - The options object
47 * @param {HTMLElement} options.container - The main container element
48 * @param {HTMLElement} options.htmlHolder - The HTML content holder
49 * @param {HTMLElement} options.showcase - The showcase element
41 */ 50 */
42 enterHandler = ({ container, htmlHolder, showcase }) => { 51 enterHandler = ({ container, htmlHolder, showcase }) => {
43 const bar = document.createElement('div') 52 const bar = document.createElement('div')
@@ -71,9 +80,10 @@ export class SideBySide extends Layout {
71 } 80 }
72 81
73 /** 82 /**
74 * leaveHandler. 83 * Handler called when leaving the Side-By-Side layout
75 * 84 *
76 * @param {} 85 * @param {Object} options - The options object
86 * @param {HTMLElement} options.container - The main container element
77 */ 87 */
78 leaveHandler = ({ container }) => { 88 leaveHandler = ({ container }) => {
79 container.querySelector('.bar')?.remove() 89 container.querySelector('.bar')?.remove()
diff --git a/src/MenuItem.mjs b/src/MenuItem.mjs
index 0fea539..8b54539 100644
--- a/src/MenuItem.mjs
+++ b/src/MenuItem.mjs
@@ -1,15 +1,28 @@
1import { shiftByWindow } from './utils.mjs' 1import { shiftByWindow } from './utils.mjs'
2 2
3/** 3/**
4 * Item. Basic Element for menu item 4 * @typedef {Object} RefLink
5 * @property {string} ref -- name of link
6 * @property {string} link -- content of link
7 * @property {string|null} title -- title of link
8 */
9
10/**
11 * Basic Element for menu item
5 * 12 *
6 * @extends {window.HTMLDivElement} 13 * @extends {window.HTMLDivElement}
7 */ 14 */
8export class Item extends window.HTMLDivElement { 15export class Item extends window.HTMLDivElement {
9 /** 16 /**
10 * constructor. 17 * Creates a new Item instance
11 * 18 *
12 * @param {Object} 19 * @param {Object} options - The options for the item
20 * @param {string} [options.text] - The text content of the item
21 * @param {string} [options.innerHTML] - The HTML content of the item
22 * @param {string} [options.title] - The title attribute for the item
23 * @param {Function} [options.onclick] - The click event handler
24 * @param {string} [options.style] - The CSS style string
25 * @param {string[]} [options.className] - Additional CSS classes
13 */ 26 */
14 constructor ({ text, innerHTML, title, onclick, style, className }) { 27 constructor ({ text, innerHTML, title, onclick, style, className }) {
15 super() 28 super()
@@ -30,15 +43,18 @@ export class Item extends window.HTMLDivElement {
30window.customElements.define('menu-item', Item, { extends: 'div' }) 43window.customElements.define('menu-item', Item, { extends: 'div' })
31 44
32/** 45/**
33 * Folder. Basic Element for menu item, it generate submenu on hover 46 * Basic Element for menu item that generates a submenu on hover
34 * 47 *
35 * @extends {window.HTMLDivElement} 48 * @extends {window.HTMLDivElement}
36 */ 49 */
37export class Folder extends window.HTMLDivElement { 50export class Folder extends window.HTMLDivElement {
38 /** 51 /**
39 * constructor. 52 * Creates a new Folder instance
40 * 53 *
41 * @param {} 54 * @param {Object} options - The options for the folder
55 * @param {string} [options.text] - The text content of the folder
56 * @param {string} [options.innerHTML] - The HTML content of the folder
57 * @param {Item[]} options.items - The submenu items
42 */ 58 */
43 constructor ({ text, innerHTML, items }) { 59 constructor ({ text, innerHTML, items }) {
44 super() 60 super()
@@ -67,9 +83,11 @@ export class Folder extends window.HTMLDivElement {
67window.customElements.define('menu-folder', Folder, { extends: 'div' }) 83window.customElements.define('menu-folder', Folder, { extends: 'div' })
68 84
69/** 85/**
70 * pickMapItem. 86 * Creates a menu item for picking a map
71 * 87 *
72 * @param {Function[]} options.utils 88 * @param {Object} options - The options object
89 * @param {Object} options.utils - Utility functions
90 * @returns {Folder} A Folder instance for picking a map
73 */ 91 */
74export const pickMapItem = ({ utils }) => 92export const pickMapItem = ({ utils }) =>
75 new Folder({ 93 new Folder({
@@ -372,7 +390,7 @@ export const restoreCamera = map =>
372 * addRefLink. replace selected text into markdown link by reference style links 390 * addRefLink. replace selected text into markdown link by reference style links
373 * 391 *
374 * @param {CodeMirror} cm 392 * @param {CodeMirror} cm
375 * @param {Object[]} refLinks -- object for { ref, link } 393 * @param {RefLink[]} refLinks
376 */ 394 */
377export const addRefLink = (cm, refLinks) => 395export const addRefLink = (cm, refLinks) =>
378 new Folder({ 396 new Folder({
diff --git a/src/dumbymap.mjs b/src/dumbymap.mjs
index ac8392c..b01c73e 100644
--- a/src/dumbymap.mjs
+++ b/src/dumbymap.mjs
@@ -26,10 +26,11 @@ const defaultLayouts = [
26const mapCache = {} 26const mapCache = {}
27 27
28/** 28/**
29 * markdown2HTML. 29 * Converts Markdown content to HTML and prepares it for DumbyMap rendering
30 * 30 *
31 * @param {HTMLElement} container -- Target Element to include generated HTML contents 31 * @param {HTMLElement} container - Target Element to include generated HTML contents
32 * @param {String} mdContent -- Texts in Markdown 32 * @param {string} mdContent - Texts in Markdown format
33 * @returns {Object} An object representing the DumbyMap instance
33 */ 34 */
34export const markdown2HTML = (container, mdContent) => { 35export const markdown2HTML = (container, mdContent) => {
35 /** Prepare Elements for Container */ 36 /** Prepare Elements for Container */
@@ -106,11 +107,12 @@ export const markdown2HTML = (container, mdContent) => {
106} 107}
107 108
108/** 109/**
109 * generateMaps. 110 * Generates maps based on the provided configuration
110 * 111 *
111 * @param {HTMLElement} container -- Target Element contains HTML contents 112 * @param {HTMLElement} container - The container element for the maps
112 * @param {Number} options.delay -- delay of map generation, milliseconds 113 * @param {Object} options - Configuration options
113 * @return {Object} dumbymap -- Include and Elements and Methods about managing contents 114 * @param {number} [options.delay=1000] - Delay before rendering maps (in milliseconds)
115 * @param {Function} options.mapCallback - Callback function to be called after map rendering
114 */ 116 */
115export const generateMaps = (container, { layouts = [], delay, renderCallback } = {}) => { 117export const generateMaps = (container, { layouts = [], delay, renderCallback } = {}) => {
116 /** Prepare Contaner/HTML Holder/Showcase */ 118 /** Prepare Contaner/HTML Holder/Showcase */
@@ -509,4 +511,4 @@ export const generateMaps = (container, { layouts = [], delay, renderCallback }
509 ) 511 )
510 512
511 return Object.seal(dumbymap) 513 return Object.seal(dumbymap)
512} 514} \ No newline at end of file
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)