Files
lms/frontend/src/utils/markdownParser.js
2024-12-23 11:33:09 +05:30

157 lines
3.4 KiB
JavaScript

export class Markdown {
constructor({ data, api, readOnly, config }) {
this.api = api
this.data = data || {}
this.config = config || {}
this.text = data.text || ''
this.readOnly = readOnly
}
static get isReadOnlySupported() {
return true
}
static get conversionConfig() {
return {
export: 'text',
import: 'text',
}
}
onPaste(event) {
const data = {
text: event.detail.data.innerHTML,
}
this.data = data
window.requestAnimationFrame(() => {
if (!this.wrapper) {
return
}
this.wrapper.innerHTML = this.data.text || ''
})
}
static get pasteConfig() {
return {
tags: ['P'],
}
}
render() {
this.wrapper = document.createElement('div')
this.wrapper.classList.add('cdx-block')
this.wrapper.classList.add('ce-paragraph')
this.wrapper.innerHTML = this.text
if (!this.readOnly) {
this.wrapper.contentEditable = true
this.wrapper.innerHTML = this.text
this.wrapper.addEventListener('keydown', (event) => {
const value = event.target.textContent
if (event.keyCode === 32 && value.startsWith('#')) {
this.convertToHeader(event, value)
} else if (event.keyCode === 13) {
this.parseContent(event)
}
})
}
return this.wrapper
}
convertToHeader(event, value) {
event.preventDefault()
if (['#', '##', '###', '####', '#####', '######'].includes(value)) {
let level = value.length
event.target.textContent = ''
this.convertBlock('header', {
level: level,
})
}
}
parseContent(event) {
event.preventDefault()
const previousLine = this.wrapper.textContent
if (previousLine && this.hasImage(previousLine)) {
this.wrapper.textContent = ''
this.convertBlock('image')
} else if (previousLine && this.hasLink(previousLine)) {
const { text, url } = this.extractLink(previousLine)
const anchorTag = `<a href="${url}" target="_blank">${text}</a>`
this.convertBlock('paragraph', {
text: previousLine.replace(/\[.+?\]\(.+?\)/, anchorTag),
})
} else if (previousLine && previousLine.startsWith('- ')) {
this.convertBlock('list', {
style: 'unordered',
items: [
{
content: previousLine.replace('- ', ''),
},
],
})
} else if (previousLine && previousLine.startsWith('1. ')) {
this.convertBlock('list', {
style: 'ordered',
items: [
{
content: previousLine.replace('1. ', ''),
},
],
})
} else if (previousLine && this.canBeEmbed(previousLine)) {
this.wrapper.textContent = ''
this.convertBlock('embed', {
source: previousLine,
})
}
}
async convertBlock(type, data, index = null) {
const currentIndex = this.api.blocks.getCurrentBlockIndex()
const currentBlock = this.api.blocks.getBlockByIndex(currentIndex)
await this.api.blocks.convert(currentBlock.id, type, data)
this.api.caret.focus(true)
}
save(blockContent) {
return {
text: blockContent.innerHTML,
}
}
hasImage(line) {
return /!\[.+?\]\(.+?\)/.test(line)
}
extractImage(line) {
const match = line.match(/!\[(.+?)\]\((.+?)\)/)
if (match) {
return { alt: match[1], url: match[2] }
}
return { alt: '', url: '' }
}
hasLink(line) {
return /\[.+?\]\(.+?\)/.test(line)
}
extractLink(line) {
const match = line.match(/\[(.+?)\]\((.+?)\)/)
if (match) {
return { text: match[1], url: match[2] }
}
return { text: '', url: '' }
}
canBeEmbed(line) {
return /^https?:\/\/.+/.test(line)
}
}
export default Markdown