forked from FINAKON/HelpProject
1. Initial Commit - a boiler plate code and POC to realize the concept of context sensitive help 2. Frontend code written in ReactJS 3. Backend code written in Java, Spring Boot Framework 4. Frontend Start: pre-requisites : node, npm npm run dev ==> to start the frontend vite server 5. Backend Start: pre-requisites : java, mvn mvn spring-boot:run ==> to start the backend server 6. Visit http://localhost:5173/ for basic demo of help, press F1 in textboxes 7. Visit http://localhost:5173/editor and enter "admin123" to add/modify texts. Happy Coding !!! Thank you, Bhargava.
253 lines
7.5 KiB
JavaScript
253 lines
7.5 KiB
JavaScript
/**
|
|
* @typedef {import('hast').Element} Element
|
|
* @typedef {import('hast').ElementContent} ElementContent
|
|
*
|
|
* @typedef {import('./state.js').State} State
|
|
*/
|
|
|
|
/**
|
|
* @callback FootnoteBackContentTemplate
|
|
* Generate content for the backreference dynamically.
|
|
*
|
|
* For the following markdown:
|
|
*
|
|
* ```markdown
|
|
* Alpha[^micromark], bravo[^micromark], and charlie[^remark].
|
|
*
|
|
* [^remark]: things about remark
|
|
* [^micromark]: things about micromark
|
|
* ```
|
|
*
|
|
* This function will be called with:
|
|
*
|
|
* * `0` and `0` for the backreference from `things about micromark` to
|
|
* `alpha`, as it is the first used definition, and the first call to it
|
|
* * `0` and `1` for the backreference from `things about micromark` to
|
|
* `bravo`, as it is the first used definition, and the second call to it
|
|
* * `1` and `0` for the backreference from `things about remark` to
|
|
* `charlie`, as it is the second used definition
|
|
* @param {number} referenceIndex
|
|
* Index of the definition in the order that they are first referenced,
|
|
* 0-indexed.
|
|
* @param {number} rereferenceIndex
|
|
* Index of calls to the same definition, 0-indexed.
|
|
* @returns {Array<ElementContent> | ElementContent | string}
|
|
* Content for the backreference when linking back from definitions to their
|
|
* reference.
|
|
*
|
|
* @callback FootnoteBackLabelTemplate
|
|
* Generate a back label dynamically.
|
|
*
|
|
* For the following markdown:
|
|
*
|
|
* ```markdown
|
|
* Alpha[^micromark], bravo[^micromark], and charlie[^remark].
|
|
*
|
|
* [^remark]: things about remark
|
|
* [^micromark]: things about micromark
|
|
* ```
|
|
*
|
|
* This function will be called with:
|
|
*
|
|
* * `0` and `0` for the backreference from `things about micromark` to
|
|
* `alpha`, as it is the first used definition, and the first call to it
|
|
* * `0` and `1` for the backreference from `things about micromark` to
|
|
* `bravo`, as it is the first used definition, and the second call to it
|
|
* * `1` and `0` for the backreference from `things about remark` to
|
|
* `charlie`, as it is the second used definition
|
|
* @param {number} referenceIndex
|
|
* Index of the definition in the order that they are first referenced,
|
|
* 0-indexed.
|
|
* @param {number} rereferenceIndex
|
|
* Index of calls to the same definition, 0-indexed.
|
|
* @returns {string}
|
|
* Back label to use when linking back from definitions to their reference.
|
|
*/
|
|
|
|
import structuredClone from '@ungap/structured-clone'
|
|
import {normalizeUri} from 'micromark-util-sanitize-uri'
|
|
|
|
/**
|
|
* Generate the default content that GitHub uses on backreferences.
|
|
*
|
|
* @param {number} _
|
|
* Index of the definition in the order that they are first referenced,
|
|
* 0-indexed.
|
|
* @param {number} rereferenceIndex
|
|
* Index of calls to the same definition, 0-indexed.
|
|
* @returns {Array<ElementContent>}
|
|
* Content.
|
|
*/
|
|
export function defaultFootnoteBackContent(_, rereferenceIndex) {
|
|
/** @type {Array<ElementContent>} */
|
|
const result = [{type: 'text', value: '↩'}]
|
|
|
|
if (rereferenceIndex > 1) {
|
|
result.push({
|
|
type: 'element',
|
|
tagName: 'sup',
|
|
properties: {},
|
|
children: [{type: 'text', value: String(rereferenceIndex)}]
|
|
})
|
|
}
|
|
|
|
return result
|
|
}
|
|
|
|
/**
|
|
* Generate the default label that GitHub uses on backreferences.
|
|
*
|
|
* @param {number} referenceIndex
|
|
* Index of the definition in the order that they are first referenced,
|
|
* 0-indexed.
|
|
* @param {number} rereferenceIndex
|
|
* Index of calls to the same definition, 0-indexed.
|
|
* @returns {string}
|
|
* Label.
|
|
*/
|
|
export function defaultFootnoteBackLabel(referenceIndex, rereferenceIndex) {
|
|
return (
|
|
'Back to reference ' +
|
|
(referenceIndex + 1) +
|
|
(rereferenceIndex > 1 ? '-' + rereferenceIndex : '')
|
|
)
|
|
}
|
|
|
|
/**
|
|
* Generate a hast footer for called footnote definitions.
|
|
*
|
|
* @param {State} state
|
|
* Info passed around.
|
|
* @returns {Element | undefined}
|
|
* `section` element or `undefined`.
|
|
*/
|
|
// eslint-disable-next-line complexity
|
|
export function footer(state) {
|
|
const clobberPrefix =
|
|
typeof state.options.clobberPrefix === 'string'
|
|
? state.options.clobberPrefix
|
|
: 'user-content-'
|
|
const footnoteBackContent =
|
|
state.options.footnoteBackContent || defaultFootnoteBackContent
|
|
const footnoteBackLabel =
|
|
state.options.footnoteBackLabel || defaultFootnoteBackLabel
|
|
const footnoteLabel = state.options.footnoteLabel || 'Footnotes'
|
|
const footnoteLabelTagName = state.options.footnoteLabelTagName || 'h2'
|
|
const footnoteLabelProperties = state.options.footnoteLabelProperties || {
|
|
className: ['sr-only']
|
|
}
|
|
/** @type {Array<ElementContent>} */
|
|
const listItems = []
|
|
let referenceIndex = -1
|
|
|
|
while (++referenceIndex < state.footnoteOrder.length) {
|
|
const definition = state.footnoteById.get(
|
|
state.footnoteOrder[referenceIndex]
|
|
)
|
|
|
|
if (!definition) {
|
|
continue
|
|
}
|
|
|
|
const content = state.all(definition)
|
|
const id = String(definition.identifier).toUpperCase()
|
|
const safeId = normalizeUri(id.toLowerCase())
|
|
let rereferenceIndex = 0
|
|
/** @type {Array<ElementContent>} */
|
|
const backReferences = []
|
|
const counts = state.footnoteCounts.get(id)
|
|
|
|
// eslint-disable-next-line no-unmodified-loop-condition
|
|
while (counts !== undefined && ++rereferenceIndex <= counts) {
|
|
if (backReferences.length > 0) {
|
|
backReferences.push({type: 'text', value: ' '})
|
|
}
|
|
|
|
let children =
|
|
typeof footnoteBackContent === 'string'
|
|
? footnoteBackContent
|
|
: footnoteBackContent(referenceIndex, rereferenceIndex)
|
|
|
|
if (typeof children === 'string') {
|
|
children = {type: 'text', value: children}
|
|
}
|
|
|
|
backReferences.push({
|
|
type: 'element',
|
|
tagName: 'a',
|
|
properties: {
|
|
href:
|
|
'#' +
|
|
clobberPrefix +
|
|
'fnref-' +
|
|
safeId +
|
|
(rereferenceIndex > 1 ? '-' + rereferenceIndex : ''),
|
|
dataFootnoteBackref: '',
|
|
ariaLabel:
|
|
typeof footnoteBackLabel === 'string'
|
|
? footnoteBackLabel
|
|
: footnoteBackLabel(referenceIndex, rereferenceIndex),
|
|
className: ['data-footnote-backref']
|
|
},
|
|
children: Array.isArray(children) ? children : [children]
|
|
})
|
|
}
|
|
|
|
const tail = content[content.length - 1]
|
|
|
|
if (tail && tail.type === 'element' && tail.tagName === 'p') {
|
|
const tailTail = tail.children[tail.children.length - 1]
|
|
if (tailTail && tailTail.type === 'text') {
|
|
tailTail.value += ' '
|
|
} else {
|
|
tail.children.push({type: 'text', value: ' '})
|
|
}
|
|
|
|
tail.children.push(...backReferences)
|
|
} else {
|
|
content.push(...backReferences)
|
|
}
|
|
|
|
/** @type {Element} */
|
|
const listItem = {
|
|
type: 'element',
|
|
tagName: 'li',
|
|
properties: {id: clobberPrefix + 'fn-' + safeId},
|
|
children: state.wrap(content, true)
|
|
}
|
|
|
|
state.patch(definition, listItem)
|
|
|
|
listItems.push(listItem)
|
|
}
|
|
|
|
if (listItems.length === 0) {
|
|
return
|
|
}
|
|
|
|
return {
|
|
type: 'element',
|
|
tagName: 'section',
|
|
properties: {dataFootnotes: true, className: ['footnotes']},
|
|
children: [
|
|
{
|
|
type: 'element',
|
|
tagName: footnoteLabelTagName,
|
|
properties: {
|
|
...structuredClone(footnoteLabelProperties),
|
|
id: 'footnote-label'
|
|
},
|
|
children: [{type: 'text', value: footnoteLabel}]
|
|
},
|
|
{type: 'text', value: '\n'},
|
|
{
|
|
type: 'element',
|
|
tagName: 'ol',
|
|
properties: {},
|
|
children: state.wrap(listItems, true)
|
|
},
|
|
{type: 'text', value: '\n'}
|
|
]
|
|
}
|
|
}
|