1 CodeMirror.defineMode('tiki', function(config) {
2 function inBlock(style, terminator, returnTokenizer) {
3 return function(stream, state) {
4 while (!stream.eol()) {
5 if (stream.match(terminator)) {
6 state.tokenize = inText;
12 if (returnTokenizer) state.tokenize = returnTokenizer;
18 function inLine(style) {
19 return function(stream, state) {
20 while(!stream.eol()) {
23 state.tokenize = inText;
28 function inText(stream, state) {
29 function chain(parser) {
30 state.tokenize = parser;
31 return parser(stream, state);
34 var sol = stream.sol();
35 var ch = stream.next();
38 switch (ch) { //switch is generally much faster than if, so it is used here
44 while ((c = stream.eat(/[^\s\u00a0=\"\'\/?(}]/))) tagName += c;
45 state.tokenize = inPlugin;
49 if (stream.eat("_")) {
50 return chain(inBlock("strong", "__", inText));
54 if (stream.eat("'")) {
56 return chain(inBlock("em", "''", inText));
60 if (stream.eat("(")) {
61 return chain(inBlock("variable-2", "))", inText));
65 return chain(inBlock("variable-3", "]", inText));
68 if (stream.eat("|")) {
69 return chain(inBlock("comment", "||"));
73 if (stream.eat("=")) {//titleBar
74 return chain(inBlock("header string", "=-", inText));
75 } else if (stream.eat("-")) {//deleted
76 return chain(inBlock("error tw-deleted", "--", inText));
80 if (stream.match("==")) {
81 return chain(inBlock("tw-underline", "===", inText));
85 if (stream.eat(":")) {
86 return chain(inBlock("comment", "::"));
90 return chain(inBlock("tw-box", "^"));
93 if (stream.match("np~")) {
94 return chain(inBlock("meta", "~/np~"));
102 case "!": //header at start of line
103 if (stream.match('!!!!!')) {
104 return chain(inLine("header string"));
105 } else if (stream.match('!!!!')) {
106 return chain(inLine("header string"));
107 } else if (stream.match('!!!')) {
108 return chain(inLine("header string"));
109 } else if (stream.match('!!')) {
110 return chain(inLine("header string"));
112 return chain(inLine("header string"));
115 case "*": //unordered list line item, or <li /> at start of line
116 case "#": //ordered list line item, or <li /> at start of line
117 case "+": //ordered list line item, or <li /> at start of line
118 return chain(inLine("tw-listitem bracket"));
123 //stream.eatWhile(/[&{]/); was eating up plugins, turned off to act less like html and more like tiki
127 var indentUnit = config.indentUnit;
129 // Return variables for tokenizers
130 var pluginName, type;
131 function inPlugin(stream, state) {
132 var ch = stream.next();
133 var peek = stream.peek();
136 state.tokenize = inText;
137 //type = ch == ")" ? "endPlugin" : "selfclosePlugin"; inPlugin
139 } else if (ch == "(" || ch == ")") {
141 } else if (ch == "=") {
146 peek = stream.peek();
149 //here we detect values directly after equal character with no quotes
150 if (!/[\'\"]/.test(peek)) {
151 state.tokenize = inAttributeNoQuote();
156 } else if (/[\'\"]/.test(ch)) {
157 state.tokenize = inAttribute(ch);
158 return state.tokenize(stream, state);
160 stream.eatWhile(/[^\s\u00a0=\"\'\/?]/);
165 function inAttribute(quote) {
166 return function(stream, state) {
167 while (!stream.eol()) {
168 if (stream.next() == quote) {
169 state.tokenize = inPlugin;
177 function inAttributeNoQuote() {
178 return function(stream, state) {
179 while (!stream.eol()) {
180 var ch = stream.next();
181 var peek = stream.peek();
182 if (ch == " " || ch == "," || /[ )}]/.test(peek)) {
183 state.tokenize = inPlugin;
191 var curState, setStyle;
193 for (var i = arguments.length - 1; i >= 0; i--) curState.cc.push(arguments[i]);
197 pass.apply(null, arguments);
201 function pushContext(pluginName, startOfLine) {
202 var noIndent = curState.context && curState.context.noIndent;
204 prev: curState.context,
205 pluginName: pluginName,
206 indent: curState.indented,
207 startOfLine: startOfLine,
212 function popContext() {
213 if (curState.context) curState.context = curState.context.prev;
216 function element(type) {
217 if (type == "openPlugin") {curState.pluginName = pluginName; return cont(attributes, endplugin(curState.startOfLine));}
218 else if (type == "closePlugin") {
220 if (curState.context) {
221 err = curState.context.pluginName != pluginName;
226 if (err) setStyle = "error";
227 return cont(endcloseplugin(err));
229 else if (type == "string") {
230 if (!curState.context || curState.context.name != "!cdata") pushContext("!cdata");
231 if (curState.tokenize == inText) popContext();
237 function endplugin(startOfLine) {
238 return function(type) {
240 type == "selfclosePlugin" ||
244 if (type == "endPlugin") {pushContext(curState.pluginName, startOfLine); return cont();}
249 function endcloseplugin(err) {
250 return function(type) {
251 if (err) setStyle = "error";
252 if (type == "endPlugin") return cont();
257 function attributes(type) {
258 if (type == "keyword") {setStyle = "attribute"; return cont(attributes);}
259 if (type == "equals") return cont(attvalue, attributes);
262 function attvalue(type) {
263 if (type == "keyword") {setStyle = "string"; return cont();}
264 if (type == "string") return cont(attvaluemaybe);
267 function attvaluemaybe(type) {
268 if (type == "string") return cont(attvaluemaybe);
272 startState: function() {
273 return {tokenize: inText, cc: [], indented: 0, startOfLine: true, pluginName: null, context: null};
275 token: function(stream, state) {
277 state.startOfLine = true;
278 state.indented = stream.indentation();
280 if (stream.eatSpace()) return null;
282 setStyle = type = pluginName = null;
283 var style = state.tokenize(stream, state);
284 if ((style || type) && style != "comment") {
287 var comb = state.cc.pop() || element;
288 if (comb(type || style)) break;
291 state.startOfLine = false;
292 return setStyle || style;
294 indent: function(state, textAfter) {
295 var context = state.context;
296 if (context && context.noIndent) return 0;
297 if (context && /^{\//.test(textAfter))
298 context = context.prev;
299 while (context && !context.startOfLine)
300 context = context.prev;
301 if (context) return context.indent + indentUnit;
308 CodeMirror.defineMIME("text/tiki", "tiki");