discourse/app/assets/javascripts/pretty-text/engines/discourse-markdown/bbcode-inline.js.es6

245 lines
5.8 KiB
JavaScript

import { parseBBCodeTag } from 'pretty-text/engines/discourse-markdown/bbcode-block';
function tokanizeBBCode(state, silent, ruler) {
let pos = state.pos;
// 91 = [
if (silent || state.src.charCodeAt(pos) !== 91) {
return false;
}
const tagInfo = parseBBCodeTag(state.src, pos, state.posMax);
if (!tagInfo) {
return false;
}
let rule, i;
let ruleInfo = ruler.getRuleForTag(tagInfo.tag);
if (!ruleInfo) { return false; }
rule = ruleInfo.rule;
if (rule.replace) {
// special handling for replace
// we pass raw contents to callback so we simply need to greedy match to end tag
if (tagInfo.closing) {
return false;
}
let closeTag = '[/' + tagInfo.tag + ']';
let found = false;
for(i=state.pos+tagInfo.length; i<=state.posMax-closeTag.length; i++) {
if (state.src.charCodeAt(pos) === 91 && state.src.slice(i, i + closeTag.length).toLowerCase() === closeTag) {
found = true;
break;
}
}
if (!found) {
return false;
}
let content = state.src.slice(state.pos+tagInfo.length, i);
if (rule.replace(state, tagInfo, content)) {
state.pos = i + closeTag.length;
return true;
} else {
return false;
}
} else {
tagInfo.rule = rule;
let token = state.push('text', '' , 0);
token.content = state.src.slice(pos, pos+tagInfo.length);
token.meta = 'bbcode';
state.delimiters.push({
bbInfo: tagInfo,
marker: 'bb' + tagInfo.tag,
open: !tagInfo.closing,
close: !!tagInfo.closing,
token: state.tokens.length - 1,
level: state.level,
end: -1,
jump: 0
});
state.pos = pos + tagInfo.length;
return true;
}
}
function processBBCode(state, silent) {
let i,
startDelim,
endDelim,
token,
tagInfo,
delimiters = state.delimiters,
max = delimiters.length;
if (silent) {
return;
}
for (i=0; i<max-1; i++) {
startDelim = delimiters[i];
tagInfo = startDelim.bbInfo;
if (!tagInfo) {
continue;
}
if (startDelim.end === -1) {
continue;
}
endDelim = delimiters[startDelim.end];
token = state.tokens[startDelim.token];
let tag, className;
if (typeof tagInfo.rule.wrap === 'function') {
let content = "";
for (let j = startDelim.token+1; j < endDelim.token; j++) {
let inner = state.tokens[j];
if (inner.type === 'text' && inner.meta !== 'bbcode') {
content += inner.content;
}
}
tagInfo.rule.wrap(token, state.tokens[endDelim.token], tagInfo, content);
continue;
} else {
let split = tagInfo.rule.wrap.split('.');
tag = split[0];
className = split.slice(1).join(' ');
}
token.type = 'bbcode_' + tagInfo.tag + '_open';
token.tag = tag;
if (className) {
token.attrs = [['class', className]];
}
token.nesting = 1;
token.markup = token.content;
token.content = '';
token = state.tokens[endDelim.token];
token.type = 'bbcode_' + tagInfo.tag + '_close';
token.tag = tag;
token.nesting = -1;
token.markup = token.content;
token.content = '';
}
return false;
}
export function setup(helper) {
helper.whiteList(['span.bbcode-b', 'span.bbcode-i', 'span.bbcode-u', 'span.bbcode-s']);
helper.registerOptions(opts => {
opts.features['bbcode-inline'] = true;
});
helper.registerPlugin(md => {
const ruler = md.inline.bbcode.ruler;
md.inline.ruler.push('bbcode-inline', (state,silent) => tokanizeBBCode(state,silent,ruler));
md.inline.ruler2.before('text_collapse', 'bbcode-inline', processBBCode);
ruler.push('code', {
tag: 'code',
replace: function(state, tagInfo, content) {
let token;
token = state.push('code_inline', 'code', 0);
token.content = content;
return true;
}
});
const simpleUrlRegex = /^http[s]?:\/\//;
ruler.push('url', {
tag: 'url',
wrap: function(startToken, endToken, tagInfo, content) {
const url = (tagInfo.attrs['_default'] || content).trim();
if (simpleUrlRegex.test(url)) {
startToken.type = 'link_open';
startToken.tag = 'a';
startToken.attrs = [['href', url], ['data-bbcode', 'true']];
startToken.content = '';
startToken.nesting = 1;
endToken.type = 'link_close';
endToken.tag = 'a';
endToken.content = '';
endToken.nesting = -1;
} else {
// just strip the bbcode tag
endToken.content = '';
startToken.content = '';
// edge case, we don't want this detected as a onebox if auto linked
// this ensures it is not stripped
startToken.type = 'html_inline';
}
return false;
}
});
ruler.push('email', {
tag: 'email',
replace: function(state, tagInfo, content) {
let token;
let email = tagInfo.attrs['_default'] || content;
token = state.push('link_open', 'a', 1);
token.attrs = [['href', 'mailto:' + email], ['data-bbcode', 'true']];
token = state.push('text', '', 0);
token.content = content;
token = state.push('link_close', 'a', -1);
return true;
}
});
ruler.push('image', {
tag: 'img',
replace: function(state, tagInfo, content) {
let token = state.push('image', 'img', 0);
token.attrs = [['src', content],['alt','']];
token.children = [];
return true;
}
});
ruler.push('bold', {
tag: 'b',
wrap: 'span.bbcode-b',
});
ruler.push('italic', {
tag: 'i',
wrap: 'span.bbcode-i'
});
ruler.push('underline', {
tag: 'u',
wrap: 'span.bbcode-u'
});
ruler.push('strike', {
tag: 's',
wrap: 'span.bbcode-s'
});
});
}