2012-12-13 21:34:31 +00:00
|
|
|
/*\
|
2012-12-20 16:41:06 +00:00
|
|
|
title: $:/core/modules/parsers/wikiparser/rules/list.js
|
2012-12-13 21:34:31 +00:00
|
|
|
type: application/javascript
|
2012-12-20 16:02:03 +00:00
|
|
|
module-type: wikirule
|
2012-12-13 21:34:31 +00:00
|
|
|
|
|
|
|
Wiki text block rule for lists. For example:
|
|
|
|
|
2012-12-22 23:09:44 +00:00
|
|
|
```
|
2012-12-13 21:34:31 +00:00
|
|
|
* This is an unordered list
|
|
|
|
* It has two items
|
|
|
|
|
|
|
|
# This is a numbered list
|
|
|
|
## With a subitem
|
|
|
|
# And a third item
|
|
|
|
|
|
|
|
; This is a term that is being defined
|
|
|
|
: This is the definition of that term
|
2012-12-22 23:09:44 +00:00
|
|
|
```
|
2012-12-13 21:34:31 +00:00
|
|
|
|
|
|
|
Note that lists can be nested arbitrarily:
|
|
|
|
|
2012-12-22 23:09:44 +00:00
|
|
|
```
|
2012-12-13 21:34:31 +00:00
|
|
|
#** One
|
|
|
|
#* Two
|
|
|
|
#** Three
|
|
|
|
#**** Four
|
|
|
|
#**# Five
|
|
|
|
#**## Six
|
|
|
|
## Seven
|
|
|
|
### Eight
|
|
|
|
## Nine
|
2012-12-22 23:09:44 +00:00
|
|
|
```
|
2012-12-13 21:34:31 +00:00
|
|
|
|
|
|
|
A CSS class can be applied to a list item as follows:
|
|
|
|
|
2012-12-22 23:09:44 +00:00
|
|
|
```
|
2012-12-13 21:34:31 +00:00
|
|
|
* List item one
|
|
|
|
*.active List item two has the class `active`
|
|
|
|
* List item three
|
2012-12-22 23:09:44 +00:00
|
|
|
```
|
2012-12-13 21:34:31 +00:00
|
|
|
|
|
|
|
\*/
|
|
|
|
(function(){
|
|
|
|
|
|
|
|
/*jslint node: true, browser: true */
|
|
|
|
/*global $tw: false */
|
|
|
|
"use strict";
|
|
|
|
|
2012-12-14 13:31:47 +00:00
|
|
|
exports.name = "list";
|
2012-12-20 16:02:03 +00:00
|
|
|
exports.types = {block: true};
|
2012-12-13 21:34:31 +00:00
|
|
|
|
2012-12-14 15:44:19 +00:00
|
|
|
exports.init = function(parser) {
|
|
|
|
this.parser = parser;
|
2012-12-14 13:31:47 +00:00
|
|
|
// Regexp to match
|
2013-12-04 23:31:55 +00:00
|
|
|
this.matchRegExp = /([\*#;:>]+)/mg;
|
2012-12-13 21:34:31 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
var listTypes = {
|
|
|
|
"*": {listTag: "ul", itemTag: "li"},
|
|
|
|
"#": {listTag: "ol", itemTag: "li"},
|
|
|
|
";": {listTag: "dl", itemTag: "dt"},
|
2013-12-05 16:20:22 +00:00
|
|
|
":": {listTag: "dl", itemTag: "dd"},
|
2020-04-07 19:39:13 +00:00
|
|
|
">": {listTag: "blockquote", itemTag: "div"}
|
2012-12-13 21:34:31 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
Parse the most recent match
|
|
|
|
*/
|
2012-12-14 13:31:47 +00:00
|
|
|
exports.parse = function() {
|
2012-12-13 21:34:31 +00:00
|
|
|
// Array of parse tree nodes for the previous row of the list
|
|
|
|
var listStack = [];
|
|
|
|
// Cycle through the items in the list
|
|
|
|
while(true) {
|
|
|
|
// Match the list marker
|
2013-12-04 23:31:55 +00:00
|
|
|
var reMatch = /([\*#;:>]+)/mg;
|
2012-12-13 21:34:31 +00:00
|
|
|
reMatch.lastIndex = this.parser.pos;
|
|
|
|
var match = reMatch.exec(this.parser.source);
|
|
|
|
if(!match || match.index !== this.parser.pos) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
// Check whether the list type of the top level matches
|
|
|
|
var listInfo = listTypes[match[0].charAt(0)];
|
|
|
|
if(listStack.length > 0 && listStack[0].tag !== listInfo.listTag) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
// Move past the list marker
|
|
|
|
this.parser.pos = match.index + match[0].length;
|
|
|
|
// Walk through the list markers for the current row
|
|
|
|
for(var t=0; t<match[0].length; t++) {
|
|
|
|
listInfo = listTypes[match[0].charAt(t)];
|
|
|
|
// Remove any stacked up element if we can't re-use it because the list type doesn't match
|
|
|
|
if(listStack.length > t && listStack[t].tag !== listInfo.listTag) {
|
|
|
|
listStack.splice(t,listStack.length - t);
|
|
|
|
}
|
|
|
|
// Construct the list element or reuse the previous one at this level
|
|
|
|
if(listStack.length <= t) {
|
|
|
|
var listElement = {type: "element", tag: listInfo.listTag, children: [
|
|
|
|
{type: "element", tag: listInfo.itemTag, children: []}
|
|
|
|
]};
|
|
|
|
// Link this list element into the last child item of the parent list item
|
|
|
|
if(t) {
|
|
|
|
var prevListItem = listStack[t-1].children[listStack[t-1].children.length-1];
|
|
|
|
prevListItem.children.push(listElement);
|
|
|
|
}
|
|
|
|
// Save this element in the stack
|
|
|
|
listStack[t] = listElement;
|
|
|
|
} else if(t === (match[0].length - 1)) {
|
|
|
|
listStack[t].children.push({type: "element", tag: listInfo.itemTag, children: []});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if(listStack.length > match[0].length) {
|
|
|
|
listStack.splice(match[0].length,listStack.length - match[0].length);
|
|
|
|
}
|
|
|
|
// Process the body of the list item into the last list item
|
|
|
|
var lastListChildren = listStack[listStack.length-1].children,
|
|
|
|
lastListItem = lastListChildren[lastListChildren.length-1],
|
2012-12-15 17:35:16 +00:00
|
|
|
classes = this.parser.parseClasses();
|
|
|
|
this.parser.skipWhitespace({treatNewlinesAsNonWhitespace: true});
|
2012-12-20 12:18:38 +00:00
|
|
|
var tree = this.parser.parseInlineRun(/(\r?\n)/mg);
|
2012-12-15 17:35:16 +00:00
|
|
|
lastListItem.children.push.apply(lastListItem.children,tree);
|
|
|
|
if(classes.length > 0) {
|
|
|
|
$tw.utils.addClassToParseTreeNode(lastListItem,classes.join(" "));
|
2012-12-13 21:34:31 +00:00
|
|
|
}
|
|
|
|
// Consume any whitespace following the list item
|
|
|
|
this.parser.skipWhitespace();
|
2013-07-05 21:37:55 +00:00
|
|
|
}
|
2012-12-13 21:34:31 +00:00
|
|
|
// Return the root element of the list
|
|
|
|
return [listStack[0]];
|
|
|
|
};
|
|
|
|
|
|
|
|
})();
|