scripts/CodeMirror/mode/tiki/tiki.js (view raw)
1// CodeMirror, copyright (c) by Marijn Haverbeke and others
2// Distributed under an MIT license: https://codemirror.net/LICENSE
3
4(function(mod) {
5 if (typeof exports == "object" && typeof module == "object") // CommonJS
6 mod(require("../../lib/codemirror"));
7 else if (typeof define == "function" && define.amd) // AMD
8 define(["../../lib/codemirror"], mod);
9 else // Plain browser env
10 mod(CodeMirror);
11})(function(CodeMirror) {
12"use strict";
13
14CodeMirror.defineMode('tiki', function(config) {
15 function inBlock(style, terminator, returnTokenizer) {
16 return function(stream, state) {
17 while (!stream.eol()) {
18 if (stream.match(terminator)) {
19 state.tokenize = inText;
20 break;
21 }
22 stream.next();
23 }
24
25 if (returnTokenizer) state.tokenize = returnTokenizer;
26
27 return style;
28 };
29 }
30
31 function inLine(style) {
32 return function(stream, state) {
33 while(!stream.eol()) {
34 stream.next();
35 }
36 state.tokenize = inText;
37 return style;
38 };
39 }
40
41 function inText(stream, state) {
42 function chain(parser) {
43 state.tokenize = parser;
44 return parser(stream, state);
45 }
46
47 var sol = stream.sol();
48 var ch = stream.next();
49
50 //non start of line
51 switch (ch) { //switch is generally much faster than if, so it is used here
52 case "{": //plugin
53 stream.eat("/");
54 stream.eatSpace();
55 stream.eatWhile(/[^\s\u00a0=\"\'\/?(}]/);
56 state.tokenize = inPlugin;
57 return "tag";
58 case "_": //bold
59 if (stream.eat("_"))
60 return chain(inBlock("strong", "__", inText));
61 break;
62 case "'": //italics
63 if (stream.eat("'"))
64 return chain(inBlock("em", "''", inText));
65 break;
66 case "(":// Wiki Link
67 if (stream.eat("("))
68 return chain(inBlock("variable-2", "))", inText));
69 break;
70 case "[":// Weblink
71 return chain(inBlock("variable-3", "]", inText));
72 break;
73 case "|": //table
74 if (stream.eat("|"))
75 return chain(inBlock("comment", "||"));
76 break;
77 case "-":
78 if (stream.eat("=")) {//titleBar
79 return chain(inBlock("header string", "=-", inText));
80 } else if (stream.eat("-")) {//deleted
81 return chain(inBlock("error tw-deleted", "--", inText));
82 }
83 break;
84 case "=": //underline
85 if (stream.match("=="))
86 return chain(inBlock("tw-underline", "===", inText));
87 break;
88 case ":":
89 if (stream.eat(":"))
90 return chain(inBlock("comment", "::"));
91 break;
92 case "^": //box
93 return chain(inBlock("tw-box", "^"));
94 break;
95 case "~": //np
96 if (stream.match("np~"))
97 return chain(inBlock("meta", "~/np~"));
98 break;
99 }
100
101 //start of line types
102 if (sol) {
103 switch (ch) {
104 case "!": //header at start of line
105 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"));
111 } else if (stream.match('!!')) {
112 return chain(inLine("header string"));
113 } else {
114 return chain(inLine("header string"));
115 }
116 break;
117 case "*": //unordered list line item, or <li /> at start of line
118 case "#": //ordered list line item, or <li /> at start of line
119 case "+": //ordered list line item, or <li /> at start of line
120 return chain(inLine("tw-listitem bracket"));
121 break;
122 }
123 }
124
125 //stream.eatWhile(/[&{]/); was eating up plugins, turned off to act less like html and more like tiki
126 return null;
127 }
128
129 var indentUnit = config.indentUnit;
130
131 // Return variables for tokenizers
132 var pluginName, type;
133 function inPlugin(stream, state) {
134 var ch = stream.next();
135 var peek = stream.peek();
136
137 if (ch == "}") {
138 state.tokenize = inText;
139 //type = ch == ")" ? "endPlugin" : "selfclosePlugin"; inPlugin
140 return "tag";
141 } else if (ch == "(" || ch == ")") {
142 return "bracket";
143 } else if (ch == "=") {
144 type = "equals";
145
146 if (peek == ">") {
147 stream.next();
148 peek = stream.peek();
149 }
150
151 //here we detect values directly after equal character with no quotes
152 if (!/[\'\"]/.test(peek)) {
153 state.tokenize = inAttributeNoQuote();
154 }
155 //end detect values
156
157 return "operator";
158 } else if (/[\'\"]/.test(ch)) {
159 state.tokenize = inAttribute(ch);
160 return state.tokenize(stream, state);
161 } else {
162 stream.eatWhile(/[^\s\u00a0=\"\'\/?]/);
163 return "keyword";
164 }
165 }
166
167 function inAttribute(quote) {
168 return function(stream, state) {
169 while (!stream.eol()) {
170 if (stream.next() == quote) {
171 state.tokenize = inPlugin;
172 break;
173 }
174 }
175 return "string";
176 };
177 }
178
179 function inAttributeNoQuote() {
180 return function(stream, state) {
181 while (!stream.eol()) {
182 var ch = stream.next();
183 var peek = stream.peek();
184 if (ch == " " || ch == "," || /[ )}]/.test(peek)) {
185 state.tokenize = inPlugin;
186 break;
187 }
188 }
189 return "string";
190};
191 }
192
193var curState, setStyle;
194function pass() {
195 for (var i = arguments.length - 1; i >= 0; i--) curState.cc.push(arguments[i]);
196}
197
198function cont() {
199 pass.apply(null, arguments);
200 return true;
201}
202
203function pushContext(pluginName, startOfLine) {
204 var noIndent = curState.context && curState.context.noIndent;
205 curState.context = {
206 prev: curState.context,
207 pluginName: pluginName,
208 indent: curState.indented,
209 startOfLine: startOfLine,
210 noIndent: noIndent
211 };
212}
213
214function popContext() {
215 if (curState.context) curState.context = curState.context.prev;
216}
217
218function element(type) {
219 if (type == "openPlugin") {curState.pluginName = pluginName; return cont(attributes, endplugin(curState.startOfLine));}
220 else if (type == "closePlugin") {
221 var err = false;
222 if (curState.context) {
223 err = curState.context.pluginName != pluginName;
224 popContext();
225 } else {
226 err = true;
227 }
228 if (err) setStyle = "error";
229 return cont(endcloseplugin(err));
230 }
231 else if (type == "string") {
232 if (!curState.context || curState.context.name != "!cdata") pushContext("!cdata");
233 if (curState.tokenize == inText) popContext();
234 return cont();
235 }
236 else return cont();
237}
238
239function endplugin(startOfLine) {
240 return function(type) {
241 if (
242 type == "selfclosePlugin" ||
243 type == "endPlugin"
244 )
245 return cont();
246 if (type == "endPlugin") {pushContext(curState.pluginName, startOfLine); return cont();}
247 return cont();
248 };
249}
250
251function endcloseplugin(err) {
252 return function(type) {
253 if (err) setStyle = "error";
254 if (type == "endPlugin") return cont();
255 return pass();
256 };
257}
258
259function attributes(type) {
260 if (type == "keyword") {setStyle = "attribute"; return cont(attributes);}
261 if (type == "equals") return cont(attvalue, attributes);
262 return pass();
263}
264function attvalue(type) {
265 if (type == "keyword") {setStyle = "string"; return cont();}
266 if (type == "string") return cont(attvaluemaybe);
267 return pass();
268}
269function attvaluemaybe(type) {
270 if (type == "string") return cont(attvaluemaybe);
271 else return pass();
272}
273return {
274 startState: function() {
275 return {tokenize: inText, cc: [], indented: 0, startOfLine: true, pluginName: null, context: null};
276 },
277 token: function(stream, state) {
278 if (stream.sol()) {
279 state.startOfLine = true;
280 state.indented = stream.indentation();
281 }
282 if (stream.eatSpace()) return null;
283
284 setStyle = type = pluginName = null;
285 var style = state.tokenize(stream, state);
286 if ((style || type) && style != "comment") {
287 curState = state;
288 while (true) {
289 var comb = state.cc.pop() || element;
290 if (comb(type || style)) break;
291 }
292 }
293 state.startOfLine = false;
294 return setStyle || style;
295 },
296 indent: function(state, textAfter) {
297 var context = state.context;
298 if (context && context.noIndent) return 0;
299 if (context && /^{\//.test(textAfter))
300 context = context.prev;
301 while (context && !context.startOfLine)
302 context = context.prev;
303 if (context) return context.indent + indentUnit;
304 else return 0;
305 },
306 electricChars: "/"
307};
308});
309
310CodeMirror.defineMIME("text/tiki", "tiki");
311
312});