mirror of
https://github.com/marktext/marktext.git
synced 2025-05-03 05:09:56 +08:00

* feat: add front menu * update changelog * feat: add short cut of paragraph edit * fix location bug of submenu * update checkbox style in editor * update selected background color * update KEYBINDINGS.md * Bullet to ordered list issue: * emit change event after control paragraph * fix: marked parse error * disable table, front-matter and horizontal line in paragraph turn into, and fixed paragraph turn into html and math * fix unwanted space before text paragraph when heading turn into text paragraph * fix error when turn heading to paragraph * put front menu on the left on front icon * update readme * if the selection span in two lnes, disable paragraph turn into heading
291 lines
7.2 KiB
JavaScript
291 lines
7.2 KiB
JavaScript
import ContentState from './contentState'
|
|
import EventCenter from './eventHandler/event'
|
|
import Clipboard from './eventHandler/clipboard'
|
|
import Keyboard from './eventHandler/keyboard'
|
|
import ClickEvent from './eventHandler/clickEvent'
|
|
import { CLASS_OR_ID, MUYA_DEFAULT_OPTION } from './config'
|
|
import { wordCount } from './utils'
|
|
import ExportMarkdown from './utils/exportMarkdown'
|
|
import ExportHtml from './utils/exportHtml'
|
|
import ToolTip from './ui/tooltip'
|
|
import './assets/styles/index.css'
|
|
|
|
class Muya {
|
|
static plugins = []
|
|
static use (plugin) {
|
|
this.plugins.push(plugin)
|
|
}
|
|
constructor (container, options) {
|
|
this.options = Object.assign({}, MUYA_DEFAULT_OPTION, options)
|
|
const { focusMode, markdown } = this.options
|
|
this.focusMode = focusMode
|
|
this.markdown = markdown
|
|
this.container = getContainer(container, this.options)
|
|
this.eventCenter = new EventCenter()
|
|
this.tooltip = new ToolTip(this)
|
|
// UI plugins
|
|
if (Muya.plugins.length) {
|
|
for (const Plugin of Muya.plugins) {
|
|
this[Plugin.pluginName] = new Plugin(this)
|
|
}
|
|
}
|
|
|
|
this.contentState = new ContentState(this, this.options)
|
|
this.clipboard = new Clipboard(this)
|
|
this.clickEvent = new ClickEvent(this)
|
|
this.keyboard = new Keyboard(this)
|
|
this.init()
|
|
}
|
|
|
|
init () {
|
|
const { container, contentState, eventCenter } = this
|
|
contentState.stateRender.setContainer(container.children[0])
|
|
eventCenter.subscribe('stateChange', this.dispatchChange)
|
|
contentState.listenForPathChange()
|
|
const { focusMode, markdown } = this
|
|
this.setMarkdown(markdown)
|
|
this.setFocusMode(focusMode)
|
|
}
|
|
|
|
dispatchChange = () => {
|
|
const { eventCenter } = this
|
|
const markdown = this.markdown = this.getMarkdown()
|
|
const wordCount = this.getWordCount(markdown)
|
|
const cursor = this.getCursor()
|
|
const history = this.getHistory()
|
|
const toc = this.getTOC()
|
|
eventCenter.dispatch('change', { markdown, wordCount, cursor, history, toc })
|
|
}
|
|
|
|
getMarkdown () {
|
|
const blocks = this.contentState.getBlocks()
|
|
return new ExportMarkdown(blocks).generate()
|
|
}
|
|
|
|
getHistory () {
|
|
return this.contentState.getHistory()
|
|
}
|
|
|
|
getTOC () {
|
|
return this.contentState.getTOC()
|
|
}
|
|
|
|
setHistory (history) {
|
|
return this.contentState.setHistory(history)
|
|
}
|
|
|
|
clearHistory () {
|
|
return this.contentState.history.clearHistory()
|
|
}
|
|
|
|
exportStyledHTML (title = '', printOptimization = false) {
|
|
const { markdown } = this
|
|
return new ExportHtml(markdown).generate(title, printOptimization)
|
|
}
|
|
|
|
exportHtml () {
|
|
const { markdown } = this
|
|
return new ExportHtml(markdown).renderHtml()
|
|
}
|
|
|
|
getWordCount (markdown) {
|
|
return wordCount(markdown)
|
|
}
|
|
|
|
getCursor () {
|
|
return this.contentState.getCodeMirrorCursor()
|
|
}
|
|
|
|
setMarkdown (markdown, cursor, isRenderCursor = true) {
|
|
let newMarkdown = markdown
|
|
if (cursor) {
|
|
newMarkdown = this.contentState.addCursorToMarkdown(markdown, cursor)
|
|
}
|
|
this.contentState.importMarkdown(newMarkdown)
|
|
this.contentState.importCursor(cursor)
|
|
this.contentState.render(isRenderCursor)
|
|
setTimeout(() => {
|
|
this.dispatchChange()
|
|
}, 0)
|
|
}
|
|
|
|
createTable (tableChecker) {
|
|
return this.contentState.createTable(tableChecker)
|
|
}
|
|
|
|
getSelection () {
|
|
return this.contentState.selectionChange()
|
|
}
|
|
|
|
setFocusMode (bool) {
|
|
const { container, focusMode } = this
|
|
if (bool && !focusMode) {
|
|
container.classList.add(CLASS_OR_ID['AG_FOCUS_MODE'])
|
|
} else {
|
|
container.classList.remove(CLASS_OR_ID['AG_FOCUS_MODE'])
|
|
}
|
|
this.focusMode = bool
|
|
}
|
|
|
|
setFont ({ fontSize, lineHeight }) {
|
|
if (fontSize) this.contentState.fontSize = parseInt(fontSize, 10)
|
|
if (lineHeight) this.contentState.lineHeight = lineHeight
|
|
}
|
|
|
|
setListItemPreference (preferLooseListItem) {
|
|
this.contentState.preferLooseListItem = preferLooseListItem
|
|
}
|
|
|
|
setTabSize (tabSize) {
|
|
if (!tabSize || typeof tabSize !== 'number') {
|
|
tabSize = 4
|
|
} else if (tabSize < 1) {
|
|
tabSize = 1
|
|
}
|
|
|
|
this.contentState.tabSize = tabSize
|
|
}
|
|
|
|
updateParagraph (type) {
|
|
this.contentState.updateParagraph(type)
|
|
}
|
|
|
|
duplicate () {
|
|
this.contentState.duplicate()
|
|
}
|
|
|
|
deleteParagraph () {
|
|
this.contentState.deleteParagraph()
|
|
}
|
|
|
|
insertParagraph (location/* before or after */, text = '', outMost = false) {
|
|
this.contentState.insertParagraph(location, text, outMost)
|
|
}
|
|
|
|
editTable (data) {
|
|
this.contentState.editTable(data)
|
|
}
|
|
|
|
hasFocus () {
|
|
return document.activeElement === this.container
|
|
}
|
|
|
|
focus () {
|
|
this.contentState.setCursor()
|
|
this.container.focus()
|
|
}
|
|
|
|
blur () {
|
|
this.container.blur()
|
|
}
|
|
|
|
showAutoImagePath (files) {
|
|
const list = files.map(f => {
|
|
const iconClass = f.type === 'directory' ? 'icon-folder' : 'icon-image'
|
|
return Object.assign(f, { iconClass, text: f.file + (f.type === 'directory' ? '/' : '') })
|
|
})
|
|
this.contentState.showAutoImagePath(list)
|
|
}
|
|
|
|
format (type) {
|
|
this.contentState.format(type)
|
|
}
|
|
|
|
insertImage (url) {
|
|
this.contentState.insertImage(url)
|
|
}
|
|
|
|
search (value, opt) {
|
|
const { selectHighlight } = opt
|
|
this.contentState.search(value, opt)
|
|
this.contentState.render(!!selectHighlight)
|
|
return this.contentState.searchMatches
|
|
}
|
|
|
|
replace (value, opt) {
|
|
this.contentState.replace(value, opt)
|
|
this.contentState.render(false)
|
|
return this.contentState.searchMatches
|
|
}
|
|
|
|
find (action/* pre or next */) {
|
|
this.contentState.find(action)
|
|
this.contentState.render(false)
|
|
return this.contentState.searchMatches
|
|
}
|
|
|
|
on (event, listener) {
|
|
this.eventCenter.subscribe(event, listener)
|
|
}
|
|
|
|
off (event, listener) {
|
|
this.eventCenter.unsubscribe(event, listener)
|
|
}
|
|
|
|
once (event, listener) {
|
|
this.eventCenter.subscribeOnce(event, listener)
|
|
}
|
|
|
|
undo () {
|
|
this.contentState.history.undo()
|
|
}
|
|
|
|
redo () {
|
|
this.contentState.history.redo()
|
|
}
|
|
|
|
copyAsMarkdown () {
|
|
this.clipboard.copyAsMarkdown()
|
|
}
|
|
|
|
copyAsHtml () {
|
|
this.clipboard.copyAsHtml()
|
|
}
|
|
|
|
pasteAsPlainText () {
|
|
this.clipboard.pasteAsPlainText()
|
|
}
|
|
|
|
copy (name) {
|
|
this.clipboard.copy(name)
|
|
}
|
|
|
|
destroy () {
|
|
this.contentState.clear()
|
|
this.quickInsert.destroy()
|
|
this.codePicker.destroy()
|
|
this.tablePicker.destroy()
|
|
this.emojiPicker.destroy()
|
|
this.imagePathPicker.destroy()
|
|
this.eventCenter.detachAllDomEvents()
|
|
}
|
|
}
|
|
|
|
/**
|
|
* [ensureContainerDiv ensure container element is div]
|
|
*/
|
|
function getContainer (originContainer, options) {
|
|
const { hideQuickInsertHint } = options
|
|
const container = document.createElement('div')
|
|
const rootDom = document.createElement('div')
|
|
const attrs = originContainer.attributes
|
|
// copy attrs from origin container to new div element
|
|
Array.from(attrs).forEach(attr => {
|
|
container.setAttribute(attr.name, attr.value)
|
|
})
|
|
|
|
if (!hideQuickInsertHint) {
|
|
container.classList.add('ag-show-quick-insert-hint')
|
|
}
|
|
|
|
container.setAttribute('contenteditable', true)
|
|
container.setAttribute('autocorrect', false)
|
|
container.setAttribute('autocomplete', 'off')
|
|
container.setAttribute('spellcheck', false)
|
|
container.appendChild(rootDom)
|
|
originContainer.replaceWith(container)
|
|
return container
|
|
}
|
|
|
|
export default Muya
|