New API for replacing elements in the final JsonML. Also changes spoiler
tag handling to be more robust with repsect to HTML content.
This commit is contained in:
parent
2d98720cc8
commit
6b9b2d3d6a
|
@ -78,16 +78,23 @@ Discourse.Dialect.inlineBetween({
|
|||
emitter: function(contents) { return ['a', {href: contents, 'data-bbcode': true}, contents]; }
|
||||
});
|
||||
|
||||
|
||||
Discourse.Dialect.inlineBetween({
|
||||
start: '[spoiler]',
|
||||
stop: '[/spoiler]',
|
||||
rawContents: true,
|
||||
emitter: function(contents) {
|
||||
if (/<img/i.test(contents)) {
|
||||
return ['div', { 'class': 'spoiler' }, contents];
|
||||
} else {
|
||||
return ['span', { 'class': 'spoiler' }, contents];
|
||||
}
|
||||
return ["__spoiler", this.processInline(contents)];
|
||||
}
|
||||
});
|
||||
|
||||
Discourse.Dialect.replaceElement('__spoiler', function(node, processInside) {
|
||||
// Surround images with a `<div class='spoiler'>`, any other tags
|
||||
// end up in a `span`
|
||||
if (node.nodeType === 1 && node.nodeName === "IMG") {
|
||||
return ['div', {class: 'spoiler'}, processInside(node)];
|
||||
} else {
|
||||
return ['span', {class: 'spoiler'}, processInside(node)];
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -22,6 +22,50 @@ function initializeDialects() {
|
|||
initialized = true;
|
||||
}
|
||||
|
||||
/**
|
||||
Converts an HTML node Element into JsonML.
|
||||
|
||||
@method nodeToJsonML
|
||||
@param {Element} node to convert
|
||||
@returns {Array} jsonML result
|
||||
**/
|
||||
function nodeToJsonML(node) {
|
||||
if (node.nodeType === 3) {
|
||||
return node.nodeValue;
|
||||
}
|
||||
|
||||
var result = [node.nodeName.toLowerCase()],
|
||||
attributes = {};
|
||||
|
||||
if (node.attributes && node.attributes.length > 0) {
|
||||
for (var i=0; i<node.attributes.length; i++) {
|
||||
var attr = node.attributes[i];
|
||||
attributes[attr.name] = attr.value;
|
||||
}
|
||||
result.push(attributes);
|
||||
}
|
||||
var innerResult = eachNodeList(node.childNodes, nodeToJsonML);
|
||||
return result.concat(innerResult);
|
||||
}
|
||||
|
||||
/**
|
||||
Iterate through a NodeList and apply a function, returning
|
||||
a concatenated array of results.
|
||||
|
||||
@method eachNodeList
|
||||
@param {NodeList} nodeList to iterate through
|
||||
@param {Function} f to call
|
||||
**/
|
||||
function eachNodeList(nodeList, f) {
|
||||
if (!nodeList || nodeList.length === 0) { return []; }
|
||||
|
||||
var result = [];
|
||||
for (var i=0; i<nodeList.length; i++) {
|
||||
result.push(f(nodeList[i], nodeToJsonML));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
Process the text nodes in the JsonML tree, calling any emitters that have
|
||||
been added.
|
||||
|
@ -453,6 +497,32 @@ Discourse.Dialect = {
|
|||
node[node.length-1] = emitter(node[node.length-1]);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
Replaces an element by name in the final jsonML tree by parsing the HTML
|
||||
and using a `replacer` callback.
|
||||
|
||||
@method replaceElement
|
||||
@param {String} nodeName to match
|
||||
@param {Function} replacer function to call on each node
|
||||
**/
|
||||
replaceElement: function(nodeName, replacer) {
|
||||
Discourse.Dialect.on('parseNode', function(event) {
|
||||
var node = event.node;
|
||||
if (node[0] === nodeName) {
|
||||
var doc = document.implementation.createHTMLDocument("");
|
||||
if (doc) {
|
||||
doc.documentElement.innerHTML = node[1];
|
||||
if (doc.body) {
|
||||
var jsonML = eachNodeList(doc.body.childNodes, replacer),
|
||||
parentNode = event.path[event.path.length-1];
|
||||
parentNode.splice.apply(parentNode, [parentNode.indexOf(node), 1].concat(jsonML));
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
};
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
module("Discourse.BBCode");
|
||||
|
||||
var format = function(input, expected, text) {
|
||||
var cooked = Discourse.Markdown.cook(input, {lookupAvatar: false});
|
||||
equal(cooked, "<p>" + expected + "</p>", text);
|
||||
var cooked = Discourse.Markdown.cook(input, {lookupAvatar: false}).replace(/'/g, "\"");
|
||||
equal(cooked, "<p>" + expected.replace(/'/g, "\"") + "</p>", text);
|
||||
};
|
||||
|
||||
test('basic bbcode', function() {
|
||||
|
@ -30,8 +30,15 @@ test('code', function() {
|
|||
});
|
||||
|
||||
test('spoiler', function() {
|
||||
format("[spoiler][/spoiler]", "", "it can spoil nothing");
|
||||
format("[spoiler]it's a sled[/spoiler]", "<span class=\"spoiler\">it's a sled</span>", "supports spoiler tags on text");
|
||||
format("[spoiler]<img src='http://eviltrout.com/eviltrout.png' width='50' height='50'>[/spoiler]", "<div class=\"spoiler\"><img src='http://eviltrout.com/eviltrout.png' width='50' height='50'></div>", "supports spoiler tags on images");
|
||||
format("[spoiler]<img src='http://eviltrout.com/eviltrout.png' width='50' height='50'>[/spoiler]", "<div class=\"spoiler\"><img src='http://eviltrout.com/eviltrout.png' width='50' height='50'/></div>", "supports spoiler tags on images");
|
||||
format("foo bar[spoiler]aaa <img src='#'> bbb[/spoiler]",
|
||||
"foo bar<span class='spoiler'>aaa </span><div class='spoiler'><img src='#'/></div><span class='spoiler'> bbb</span>",
|
||||
"It wraps images and text differently in the same block");
|
||||
format("[spoiler]<a href='#'>hello</a>[/spoiler]",
|
||||
"<span class='spoiler'><a href='#'>hello</a></span>",
|
||||
"It surrounds other HTML");
|
||||
});
|
||||
|
||||
test('lists', function() {
|
||||
|
|
Loading…
Reference in New Issue