let isWhiteSpace; function trailingSpaceOnly(src, start, max) { let i; for(i=start;i= 'a' && letter <= 'z') || (letter >= 'A' && letter <= 'Z'))) { break; } } tag = src.slice(start+1, i); if (!tag) { return; } if (closingTag) { if (src[i] === ']') { if (multiline && !trailingSpaceOnly(src, i+1, max)) { return; } tag = tag.toLowerCase(); return {tag, length: tag.length+3, closing: true}; } return; } for (;i= endLine) { // unclosed bbcode block should not be autoclosed by end of document. return; } start = state.bMarks[line] + state.tShift[line]; max = state.eMarks[line]; if (start < max && state.sCount[line] < state.blkIndent) { // non-empty line with negative indent should stop the list: // - ``` // test break; } // bbcode close [ === 91 if (91 !== state.src.charCodeAt(start)) { continue; } if (state.sCount[line] - state.blkIndent >= 4) { // closing bbcode less than 4 spaces continue; } closeTag = parseBBCodeTag(state.src, start, max, true); if (closeTag && closeTag.closing && closeTag.tag === openTag.tag) { if (nesting === 0) { closeTag.line = line; closeTag.block = true; break; } nesting--; } if (closeTag && !closeTag.closing && closeTag.tag === openTag.tag) { nesting++; } closeTag = null; } return closeTag; } function findInlineCloseTag(state, openTag, start, max) { let closeTag; let possibleTag = false; for(let j=max-1;j>start;j--){ if (!possibleTag) { if (state.src.charCodeAt(j) === 93 /* ] */) { possibleTag = true; continue; } if(!isWhiteSpace(state.src.charCodeAt(j))) { break; } } else { if (state.src.charCodeAt(j) === 91 /* [ */) { closeTag = parseBBCodeTag(state.src, j, max); if (!closeTag || closeTag.tag !== openTag.tag || !closeTag.closing) { closeTag = null; } else { closeTag.start = j; break; } } } } return closeTag; } function applyBBCode(state, startLine, endLine, silent, md) { var nextLine, oldParent, oldLineMax, rule, start = state.bMarks[startLine] + state.tShift[startLine], initial = start, max = state.eMarks[startLine]; // [ === 91 if (91 !== state.src.charCodeAt(start)) { return false; } let info = parseBBCodeTag(state.src, start, max); if (!info || info.closing) { return false; } let ruleInfo = md.block.bbcode.ruler.getRuleForTag(info.tag); if (!ruleInfo) { return false; } rule = ruleInfo.rule; // Since start is found, we can report success here in validation mode if (silent) { return true; } // Search for the end of the block nextLine = startLine; // We might have a single inline bbcode let closeTag = findInlineCloseTag(state, info, start + info.length, max); if (!closeTag) { if (!trailingSpaceOnly(state.src, start + info.length, max)) { return false; } closeTag = findBlockCloseTag(state, info, nextLine+1, endLine); } if (!closeTag) { return false; } nextLine = closeTag.line || startLine; oldParent = state.parentType; oldLineMax = state.lineMax; // this will prevent lazy continuations from ever going past our end marker // which can happen if we are parsing a bbcode block state.lineMax = nextLine; if (rule.replace) { let content; if (startLine === nextLine) { content = state.src.slice(start + info.length, closeTag.start); } else { content = state.src.slice(state.bMarks[startLine+1], state.eMarks[nextLine-1]); } if (!rule.replace.call(this, state, info, content)) { return false; } } else { if (rule.before) { rule.before.call(this, state, info, state.src.slice(initial, initial + info.length + 1)); } let wrapTag; if (rule.wrap) { let token; if (typeof rule.wrap === 'function') { token = new state.Token('wrap_bbcode', 'div', 1); token.level = state.level+1; if (!rule.wrap(token, info)) { return false; } state.tokens.push(token); state.level = token.level; wrapTag = token.tag; } else { let split = rule.wrap.split('.'); wrapTag = split[0]; let className = split.slice(1).join(' '); token = state.push('wrap_bbcode', wrapTag, 1); if (className) { token.attrs = [['class', className]]; } } } let lastToken = state.tokens[state.tokens.length-1]; lastToken.map = [ startLine, nextLine ]; if (closeTag.block) { state.md.block.tokenize(state, startLine + 1, nextLine); } else { let token = state.push('paragraph_open', 'p', 1); token.map = [startLine, startLine]; token = state.push('inline', '', 0); token.children = []; token.map = [startLine, startLine]; token.content = state.src.slice(start + info.length, closeTag.start); state.push('paragraph_close', 'p', -1); } if (rule.wrap) { state.push('wrap_bbcode', wrapTag, -1); } if (rule.after) { rule.after.call(this, state, lastToken, state.src.slice(start-2, start + closeTag.length - 1)); } } state.parentType = oldParent; state.lineMax = oldLineMax; state.line = nextLine+1; return true; } export function setup(helper) { helper.registerPlugin(md => { const ruler = md.block.bbcode.ruler; ruler.push('code', { tag: 'code', replace: function(state, tagInfo, content) { let token; token = state.push('fence', 'code', 0); token.content = content; return true; } }); isWhiteSpace = md.utils.isWhiteSpace; md.block.ruler.after('fence', 'bbcode', (state, startLine, endLine, silent)=> { return applyBBCode(state, startLine, endLine, silent, md); }, { alt: ['paragraph', 'reference', 'blockquote', 'list'] }); }); }