marktext/src/muya/lib/selection/dom.js
Ran Luo 230c90c920
container block preview and inline syntax error (#992)
* 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
2019-05-04 23:41:46 +08:00

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
}