Add basic list serialization to HtmlSerializer

* Added 'definitionterm' and 'definitiondata' styles to support definition
  lists, and special-case handling in the serializer to wrap both in dls.
This commit is contained in:
Gabriel Wicke 2011-11-04 10:02:59 +00:00
parent 63398b5749
commit 85def70a8a
2 changed files with 122 additions and 87 deletions

View file

@ -91,8 +91,81 @@ es.HtmlSerializer.prototype.paragraph = function( node, raw ) {
};
es.HtmlSerializer.prototype.list = function( node ) {
// TODO: Convert list from flat to structured format and output it as HTML
return '<!-- TODO: Support list and listItem nodes -->';
var out = [], // List of list nodes
bstack = [], // Bullet stack, previous element's listStyles
bnext = [], // Next element's listStyles
closeTags = []; // Stack of close tags for currently active lists
var commonPrefixLength = function (x, y) {
var minLength = Math.min(x.length, y.length);
for(var i = 0; i < minLength; i++) {
if (x[i] !== y[i]
// Both definitiondata and definitionterm are
// inside dls, so consider them equivalent here.
&& [x[i], y[i]].sort()
!== ['definitiondata', 'definitionterm'] )
{
break;
}
}
return i;
}
var popTags = function ( n ) {
for (var i = 0; i < n; i++ ) {
out.push(closeTags.pop());
}
}
var openLists = function ( bs, bn, attribs ) {
var prefix = commonPrefixLength (bs, bn);
// pop close tags from stack
popTags(closeTags.length - prefix);
for(var i = prefix; i < bn.length; i++) {
var c = bn[i];
switch (c) {
case 'bullet':
out.push(es.Html.makeOpeningTag('ul', attribs));
closeTags.push(es.Html.makeClosingTag('ul'));
break;
case 'number':
out.push(es.Html.makeOpeningTag('ol', attribs));
closeTags.push(es.Html.makeClosingTag('ol'));
break;
case 'definitionterm':
case 'definitiondata':
out.push(es.Html.makeOpeningTag('dl', attribs));
closeTags.push(es.Html.makeClosingTag('dl'));
break;
default:
throw("Unknown node prefix " + c);
}
};
}
var childrenLength = node.children.length;
for (var i = 0; i < childrenLength; i++) {
var e = node.children[i];
bnext = e.attributes.styles;
delete e.attributes['styles'];
openLists( bstack, bnext, e.attributes );
var tag;
switch(bnext[bnext.length - 1]) {
case 'definitionterm':
tag = 'dt'; break;
case 'definitiondata':
tag = 'dd'; break;
default:
tag = 'li'; break;
}
out.push( es.Html.makeTag(tag, e.attributes,
this.content(e.content)
)
);
bstack = bnext;
};
popTags(closeTags.length);
return out.join("\n");
};
es.HtmlSerializer.prototype.table = function( node ) {

View file

@ -81,6 +81,25 @@
return dumped_text;
}
// Convert list prefixes to a list of WikiDom list styles
var bulletsToTypes = function (bullets) {
var bTypes = [];
var blen = bullets.length;
for (var i = 0; i < bullets.length; i++) {
switch (bullets[i]) {
case '*':
bTypes.push('bullet'); break;
case '#':
bTypes.push('number'); break;
case ';':
bTypes.push('definitionterm'); break;
case ':':
bTypes.push('definitiondata'); break;
}
}
return bTypes;
}
}
start
@ -93,7 +112,7 @@ start
else
es.push(ei);
});
dp(print_r(es));
dp(es);
return {
type: 'page',
content: es
@ -547,86 +566,32 @@ ext_param_val
lists = es:(dtdd / li)+
{
var out = [], // List of list nodes
bstack = "", // Bullet stack, previous element's listStyle
bnext = "", // Next element's listStyle
nodes = []; // Stack of currently active, nested list nodes
var commonPrefixLength = function (x, y) {
var minLength = Math.min(x.length, y.length);
for(var i = 0; i < minLength; i++) {
if (x[i] != y[i])
break;
}
return i;
}
var pushN = function ( n ) {
if (nodes.length > 0) {
nodes[nodes.length - 1].content.push(n);
// flatten es
var esLen = es.length;
var flatEs = [];
for (var i = 0; i < esLen; i++) {
if (es[i].constructor === Array) {
flatEs.concat(es[i]);
} else {
out.push(n);
nodes.push(n);
flatEs.push(es[i]);
}
}
var openLists = function ( bs, bn ) {
var prefix = commonPrefixLength (bs, bn);
nodes = nodes.slice(0, prefix);
$.each(bn.slice(prefix, bn.length), function (i, c) {
switch (c) {
case '*':
pushN({type: 'ul', content: []});
break;
case '#':
pushN({type: 'ol', content: []});
break;
case ';':
case ':':
pushN({type: 'dl', content: []});
break;
default:
throw("Unknown node prefix " + c);
}
});
return {
type: 'list',
children: es
}
$.each(es, function(i, e) {
if (e.type == 'dtdd') {
bnext = e.content[0].listStyle;
lnode = openLists( bstack, bnext );
nodes[nodes.length - 1].content =
nodes[nodes.length - 1].content.concat(e.content);
} else {
bnext = e.listStyle;
openLists( bstack, bnext, nodes );
nodes[nodes.length - 1].content.push(e);
}
bstack = bnext;
});
//dp("out: " + print_r(out, 5));
return out;
}
li = bullets:list_char+
c:(inlineline / anyline)
newline
{
var type;
switch (bullets[bullets.length - 1]) {
case '#':
case '*':
type = 'li'; break;
case ';': type = 'dt'; break;
case ':': type = 'dd'; break;
}
return {
type: type,
listStyle: bullets,
content: c
type: 'listItem',
attributes: {
styles: bulletsToTypes(bullets)
},
content: c[0]
};
}
@ -640,21 +605,18 @@ dtdd = bullets:list_char+
if (bullets[bullets.length - 1] != ';') {
return null;
} else {
return {
type: 'dtdd',
content: [
{
type: 'dt',
listStyle: bullets,
content: c
}, {
type: 'dd',
listStyle: bullets.slice(0, bullets.length - 1) + ':',
content: d
}
]
}
return [
{
type: 'listItem',
attributes: {styles: bulletsToTypes(bullets)},
content: c[0]
}, {
type: 'listItem',
attributes: {styles: bulletsToTypes(
bullets.slice(0, bullets.length - 1) + ':')},
content: d[0]
}
]
}
}