mirror of
https://github.com/marktext/marktext.git
synced 2025-05-03 21:39:51 +08:00

* opti: container block preview * remove unused codes * rewrite createBlock method * remove ag-line classname * just push codes * hand enter + shift in paragraph * update import markdown and export markdown * update part updateCtrl * update indent code block * auto indent when press shift + enter * update thematic break * update inline syntax update reg * update list and task list * update atx heading and setext heading * update paragraph * update block quote * adjust cursor in heading * update codes * paragraph turn into feature check * check copy paste * update turn into * fix: delete last # error * fix: turn setext heading to atx heading error * fix: delete thematic break error * paste and copy * workarond turndown to support soft line break * fix: unable create table * modify export markdown * modify test markdown * fix: cursor error when update blockquote * readd cursor check when dispatch changes * fix: inline math create a lot extra char * add code cache clear after each render * fallback to prismjs2
140 lines
3.7 KiB
JavaScript
140 lines
3.7 KiB
JavaScript
import {
|
|
LOWERCASE_TAGS, CLASS_OR_ID, blockContainerElementNames, emptyElementNames
|
|
} from '../config'
|
|
const CHOP_TEXT_REG = /(\*{1,3})([^*]+)(\1)/g
|
|
|
|
export const getTextContent = (node, blackList) => {
|
|
if (node.nodeType === 3) {
|
|
return node.textContent
|
|
} else if (!blackList) {
|
|
return node.textContent
|
|
}
|
|
|
|
let text = ''
|
|
if (blackList.some(className => node.classList && node.classList.contains(className))) {
|
|
return text
|
|
}
|
|
if (node.nodeType === 3) {
|
|
text += node.textContent
|
|
} else {
|
|
const childNodes = node.childNodes
|
|
for (const n of childNodes) {
|
|
text += getTextContent(n, blackList)
|
|
}
|
|
}
|
|
return text
|
|
}
|
|
|
|
export const findNearestParagraph = node => {
|
|
if (!node) {
|
|
return null
|
|
}
|
|
do {
|
|
if (isAganippeParagraph(node)) return node
|
|
node = node.parentNode
|
|
} while (node)
|
|
return null
|
|
}
|
|
|
|
export const findOutMostParagraph = node => {
|
|
do {
|
|
let parentNode = node.parentNode
|
|
if (isAganippeEditorElement(parentNode) && isAganippeParagraph(node)) return node
|
|
node = parentNode
|
|
} while (node)
|
|
}
|
|
|
|
export const isAganippeParagraph = element => {
|
|
return element && element.classList && element.classList.contains(CLASS_OR_ID['AG_PARAGRAPH'])
|
|
}
|
|
|
|
export const isBlockContainer = element => {
|
|
return element && element.nodeType !== 3 &&
|
|
blockContainerElementNames.indexOf(element.nodeName.toLowerCase()) !== -1
|
|
}
|
|
|
|
export const isAganippeEditorElement = element => {
|
|
return element && element.id === CLASS_OR_ID['AG_EDITOR_ID']
|
|
}
|
|
|
|
export const traverseUp = (current, testElementFunction) => {
|
|
if (!current) {
|
|
return false
|
|
}
|
|
|
|
do {
|
|
if (current.nodeType === 1) {
|
|
if (testElementFunction(current)) {
|
|
return current
|
|
}
|
|
// do not traverse upwards past the nearest containing editor
|
|
if (isAganippeEditorElement(current)) {
|
|
return false
|
|
}
|
|
}
|
|
|
|
current = current.parentNode
|
|
} while (current)
|
|
|
|
return false
|
|
}
|
|
|
|
export const getFirstSelectableLeafNode = element => {
|
|
while (element && element.firstChild) {
|
|
element = element.firstChild
|
|
}
|
|
|
|
// We don't want to set the selection to an element that can't have children, this messes up Gecko.
|
|
element = traverseUp(element, el => {
|
|
return emptyElementNames.indexOf(el.nodeName.toLowerCase()) === -1
|
|
})
|
|
// Selecting at the beginning of a table doesn't work in PhantomJS.
|
|
if (element.nodeName.toLowerCase() === LOWERCASE_TAGS.table) {
|
|
const firstCell = element.querySelector('th, td')
|
|
if (firstCell) {
|
|
element = firstCell
|
|
}
|
|
}
|
|
return element
|
|
}
|
|
|
|
export const getClosestBlockContainer = node => {
|
|
return traverseUp(node, node => {
|
|
return isBlockContainer(node) || isAganippeEditorElement(node)
|
|
})
|
|
}
|
|
|
|
export const getCursorPositionWithinMarkedText = (markedText, cursorOffset) => {
|
|
const chunks = []
|
|
let match
|
|
let result = { type: 'OUT' }
|
|
|
|
do {
|
|
match = CHOP_TEXT_REG.exec(markedText)
|
|
if (match) {
|
|
chunks.push({
|
|
index: match.index + match[1].length,
|
|
leftSymbol: match[1],
|
|
rightSymbol: match[3],
|
|
lastIndex: CHOP_TEXT_REG.lastIndex - match[3].length
|
|
})
|
|
}
|
|
} while (match)
|
|
|
|
chunks.forEach(chunk => {
|
|
const { index, leftSymbol, rightSymbol, lastIndex } = chunk
|
|
if (cursorOffset > index && cursorOffset < lastIndex) {
|
|
result = { type: 'IN', info: leftSymbol } // rightSymbol is also ok
|
|
} else if (cursorOffset === index) {
|
|
result = { type: 'LEFT', info: leftSymbol.length }
|
|
} else if (cursorOffset === lastIndex) {
|
|
result = { type: 'RIGHT', info: rightSymbol.length }
|
|
}
|
|
})
|
|
return result
|
|
}
|
|
|
|
export const compareParagraphsOrder = (paragraph1, paragraph2) => {
|
|
return paragraph1.compareDocumentPosition(paragraph2) & Node.DOCUMENT_POSITION_FOLLOWING
|
|
}
|