diff --git a/src/markd/index.wc b/src/markd/index.wc index ffa58e3..506f340 100644 --- a/src/markd/index.wc +++ b/src/markd/index.wc @@ -1,5 +1,5 @@ @@ -243,7 +253,7 @@ code.inline { import $ from '../utils' import '../code/index' -// import parser from './core' +import core from './core' import parser from './parser' export default class Markd { @@ -257,9 +267,16 @@ export default class Markd { } mounted() { - // this.__BOX__.innerHTML = parser.safe(this.textContent) + // this.__BOX__.innerHTML = core.safe(this.textContent) this.__BOX__.innerHTML = parser(this.textContent) this.textContent = '' + + $.bind(this.__BOX__, 'click', ev => { + if (ev.target.className === 'md-head-link') { + var ot = ev.target.offsetTop + document.documentElement.scrollTop = ot + } + }) } } diff --git a/src/markd/parser.js b/src/markd/parser.js index f026972..f3be4b7 100644 --- a/src/markd/parser.js +++ b/src/markd/parser.js @@ -6,6 +6,8 @@ 'use strict' const HR_LIST = ['=', '-', '_', '*'] +const LIST_REG = /^(([\+\-\*])|(\d+\.))\s/ +const TODO_REG = /^\[(x|\s)\]\s/ const log = console.log const Helper = { @@ -16,12 +18,42 @@ const Helper = { return str.startsWith(s.repeat(3)) } return false + }, + // 是否列表, -1不是, 1为有序列表, 0为无序列表 + isList(str) { + var v = str.trim() + if (LIST_REG.test(v)) { + var n = +v[0] + if (n === n) { + return 1 + } else { + return 0 + } + } + return -1 + }, + // 是否任务列表 + isTodo(str) { + var v = str.trim() + if (TODO_REG.test(v)) { + return v[1] === 'x' ? 1 : 0 + } + return -1 + }, + ltrim(str) { + if (str.trimStart) { + return str.trimStart() + } + return str.replace(/^\s+/, '') } } -const Tool = { +class Tool { + constructor(list) { + this.list = list + } // 初始化字符串, 处理多余换行等 - init(str) { + static init(str) { // 去掉\r, 将\t转为空格(2个) str = str.replace(/\r/g, '').replace(/\t/g, ' ') var list = [] @@ -52,10 +84,9 @@ const Tool = { } } } - log(list) - this.list = list - return this - }, + + return new this(list) + } parse() { var html = '' @@ -65,6 +96,10 @@ const Tool = { var blockquoteLevel = 0 var isParagraph = false + var isList = false + var orderListLevel = -1 + var unorderListLevel = -1 + // for (let it of this.list) { // 空行 @@ -78,10 +113,9 @@ const Tool = { // 引用结束 if (isBlockquote) { isBlockquote = false - html += '' if (emptyLineLength > 0) { + emptyLineLength = 0 while (blockquoteLevel > 0) { - emptyLineLength = 0 blockquoteLevel-- html += '' } @@ -89,13 +123,25 @@ const Tool = { continue } + if (isList) { + while (orderListLevel > -1 || unorderListLevel > -1) { + if (orderListLevel > unorderListLevel) { + html += '' + orderListLevel-- + } else { + html += '' + unorderListLevel-- + } + } + isList = false + continue + } + // if (isParagraph) { isParagraph = false html += '

' - } /* else { - html += '
' - } */ + } } } else { // wc-code标签直接拼接 @@ -109,35 +155,45 @@ const Tool = { continue } + // 无属性标签 + if (Helper.isHr(it)) { + html += '
' + continue + } + + // 优先处理一些常规样式 it = it .replace(/`(.*?)`/g, '$1') .replace(/(\-\-|\*\*)(.*?)\1/g, '$2') - .replace(/(\-|\*)(.*?)\1/g, '$2') + .replace(/(\-|\_|\*)(.*?)\1/g, '$2') + .replace(/~~(.*?)~~/g, '$1') .replace(/\!\[([^]*?)\]\(([^)]*?)\)/g, '$1') .replace(/\[([^]*?)\]\(([^)]*?)\)/g, '$1') - // + // 引用 if (it.startsWith('>')) { - html += it.replace(/^(>+) /, m => { - let len = m.trim().length - let tmp = '' - if (isBlockquote) { - // 若之前已经有一个未闭合的引用, 需要减去已有缩进级别, 避免产生新的引用标签 - len = len - blockquoteLevel - } else { - blockquoteLevel = len - } - log('bq: ', blockquoteLevel, it) - while (len > 0) { - len-- - tmp += '
' - } - return tmp - }) - if (isBlockquote) { html += '
' } + html += it.replace(/^(>+) /, (p, m) => { + let len = m.length + let tmp = '' + let loop = len + // 若之前已经有一个未闭合的引用, 需要减去已有缩进级别, 避免产生新的引用标签 + if (isBlockquote) { + loop = len - blockquoteLevel + } else { + } + + while (loop > 0) { + loop-- + tmp += '
' + } + + blockquoteLevel = len + return tmp + }) + isParagraph = false isBlockquote = true continue @@ -148,17 +204,79 @@ const Tool = { continue } - // + // 标题只能是单行 if (it.startsWith('#')) { isParagraph = false - let end = '' - html += it.replace(/^#{1,6} /, m => { - let level = m.trim().length - end = `` - return `` - }) - html += end + html += it.replace(/^(#{1,6}) (.*)/, (p, m1, m2) => { + m2 = m2.trim() + let level = m1.trim().length + let hash = m2.replace(/\s/g, '').replace(/<\/?[^>]*?>/g, '') + + if (level === 1) { + return `

${m2}

` + } else { + return `
${m2}
` + } + }) + + continue + } + + // 列表 + let listChecked = Helper.isList(it) + if (~listChecked) { + // 左侧空格长度 + let tmp = Helper.ltrim(it) + let ltrim = it.length - tmp.length + let word = tmp.replace(LIST_REG, '').trim() + let level = Math.floor(ltrim / 2) + let tag = listChecked > 0 ? 'ol' : 'ul' + + if (!isList) { + html += `<${tag}>` + if (listChecked === 1) { + orderListLevel = level + } else { + unorderListLevel = level + } + html += `
  • ${word}
  • ` + } else { + if (listChecked === 1) { + if (level > orderListLevel) { + html = html.replace(/<\/li>$/, '') + html += `<${tag}>
  • ${word}
  • ` + } else if (level === orderListLevel) { + html += `
  • ${word}
  • ` + } else { + html += `
  • ${word}
  • ` + } + orderListLevel = level + } else { + if (level > unorderListLevel) { + html = html.replace(/<\/li>$/, '') + html += `<${tag}>
  • ${word}
  • ` + } else if (level === unorderListLevel) { + html += `
  • ${word}
  • ` + } else { + html += `
  • ${word}
  • ` + } + unorderListLevel = level + } + } + + isList = true + continue + } + + // 任务 + let todoChecked = Helper.isTodo(it) + if (~todoChecked) { + let word = it.replace(TODO_REG, '').trim() + let stat = todoChecked === 1 ? 'checked' : '' + let txt = todoChecked === 1 ? `${word}` : word + + html += `
    ${txt}
    ` continue }