Yang UI - new features and fixes
[dlux.git] / modules / yangui-resources / src / main / resources / yangui / assets / js / codemirror / mode / jade / jade.js
1 // CodeMirror, copyright (c) by Marijn Haverbeke and others
2 // Distributed under an MIT license: http://codemirror.net/LICENSE
3
4 (function(mod) {
5   if (typeof exports == "object" && typeof module == "object") // CommonJS
6     mod(require("../../lib/codemirror"), require("../javascript/javascript"), require("../css/css"), require("../htmlmixed/htmlmixed"));
7   else if (typeof define == "function" && define.amd) // AMD
8     define(["../../lib/codemirror", "../javascript/javascript", "../css/css", "../htmlmixed/htmlmixed"], mod);
9   else // Plain browser env
10     mod(CodeMirror);
11 })(function(CodeMirror) {
12 "use strict";
13
14 CodeMirror.defineMode('jade', function (config) {
15   // token types
16   var KEYWORD = 'keyword';
17   var DOCTYPE = 'meta';
18   var ID = 'builtin';
19   var CLASS = 'qualifier';
20
21   var ATTRS_NEST = {
22     '{': '}',
23     '(': ')',
24     '[': ']'
25   };
26
27   var jsMode = CodeMirror.getMode(config, 'javascript');
28
29   function State() {
30     this.javaScriptLine = false;
31     this.javaScriptLineExcludesColon = false;
32
33     this.javaScriptArguments = false;
34     this.javaScriptArgumentsDepth = 0;
35
36     this.isInterpolating = false;
37     this.interpolationNesting = 0;
38
39     this.jsState = jsMode.startState();
40
41     this.restOfLine = '';
42
43     this.isIncludeFiltered = false;
44     this.isEach = false;
45
46     this.lastTag = '';
47     this.scriptType = '';
48
49     // Attributes Mode
50     this.isAttrs = false;
51     this.attrsNest = [];
52     this.inAttributeName = true;
53     this.attributeIsType = false;
54     this.attrValue = '';
55
56     // Indented Mode
57     this.indentOf = Infinity;
58     this.indentToken = '';
59
60     this.innerMode = null;
61     this.innerState = null;
62
63     this.innerModeForLine = false;
64   }
65   /**
66    * Safely copy a state
67    *
68    * @return {State}
69    */
70   State.prototype.copy = function () {
71     var res = new State();
72     res.javaScriptLine = this.javaScriptLine;
73     res.javaScriptLineExcludesColon = this.javaScriptLineExcludesColon;
74     res.javaScriptArguments = this.javaScriptArguments;
75     res.javaScriptArgumentsDepth = this.javaScriptArgumentsDepth;
76     res.isInterpolating = this.isInterpolating;
77     res.interpolationNesting = this.intpolationNesting;
78
79     res.jsState = CodeMirror.copyState(jsMode, this.jsState);
80
81     res.innerMode = this.innerMode;
82     if (this.innerMode && this.innerState) {
83       res.innerState = CodeMirror.copyState(this.innerMode, this.innerState);
84     }
85
86     res.restOfLine = this.restOfLine;
87
88     res.isIncludeFiltered = this.isIncludeFiltered;
89     res.isEach = this.isEach;
90     res.lastTag = this.lastTag;
91     res.scriptType = this.scriptType;
92     res.isAttrs = this.isAttrs;
93     res.attrsNest = this.attrsNest.slice();
94     res.inAttributeName = this.inAttributeName;
95     res.attributeIsType = this.attributeIsType;
96     res.attrValue = this.attrValue;
97     res.indentOf = this.indentOf;
98     res.indentToken = this.indentToken;
99
100     res.innerModeForLine = this.innerModeForLine;
101
102     return res;
103   };
104
105   function javaScript(stream, state) {
106     if (stream.sol()) {
107       // if javaScriptLine was set at end of line, ignore it
108       state.javaScriptLine = false;
109       state.javaScriptLineExcludesColon = false;
110     }
111     if (state.javaScriptLine) {
112       if (state.javaScriptLineExcludesColon && stream.peek() === ':') {
113         state.javaScriptLine = false;
114         state.javaScriptLineExcludesColon = false;
115         return;
116       }
117       var tok = jsMode.token(stream, state.jsState);
118       if (stream.eol()) state.javaScriptLine = false;
119       return tok || true;
120     }
121   }
122   function javaScriptArguments(stream, state) {
123     if (state.javaScriptArguments) {
124       if (state.javaScriptArgumentsDepth === 0 && stream.peek() !== '(') {
125         state.javaScriptArguments = false;
126         return;
127       }
128       if (stream.peek() === '(') {
129         state.javaScriptArgumentsDepth++;
130       } else if (stream.peek() === ')') {
131         state.javaScriptArgumentsDepth--;
132       }
133       if (state.javaScriptArgumentsDepth === 0) {
134         state.javaScriptArguments = false;
135         return;
136       }
137
138       var tok = jsMode.token(stream, state.jsState);
139       return tok || true;
140     }
141   }
142
143   function yieldStatement(stream) {
144     if (stream.match(/^yield\b/)) {
145         return 'keyword';
146     }
147   }
148
149   function doctype(stream) {
150     if (stream.match(/^(?:doctype) *([^\n]+)?/)) {
151         return DOCTYPE;
152     }
153   }
154
155   function interpolation(stream, state) {
156     if (stream.match('#{')) {
157       state.isInterpolating = true;
158       state.interpolationNesting = 0;
159       return 'punctuation';
160     }
161   }
162
163   function interpolationContinued(stream, state) {
164     if (state.isInterpolating) {
165       if (stream.peek() === '}') {
166         state.interpolationNesting--;
167         if (state.interpolationNesting < 0) {
168           stream.next();
169           state.isInterpolating = false;
170           return 'puncutation';
171         }
172       } else if (stream.peek() === '{') {
173         state.interpolationNesting++;
174       }
175       return jsMode.token(stream, state.jsState) || true;
176     }
177   }
178
179   function caseStatement(stream, state) {
180     if (stream.match(/^case\b/)) {
181       state.javaScriptLine = true;
182       return KEYWORD;
183     }
184   }
185
186   function when(stream, state) {
187     if (stream.match(/^when\b/)) {
188       state.javaScriptLine = true;
189       state.javaScriptLineExcludesColon = true;
190       return KEYWORD;
191     }
192   }
193
194   function defaultStatement(stream) {
195     if (stream.match(/^default\b/)) {
196       return KEYWORD;
197     }
198   }
199
200   function extendsStatement(stream, state) {
201     if (stream.match(/^extends?\b/)) {
202       state.restOfLine = 'string';
203       return KEYWORD;
204     }
205   }
206
207   function append(stream, state) {
208     if (stream.match(/^append\b/)) {
209       state.restOfLine = 'variable';
210       return KEYWORD;
211     }
212   }
213   function prepend(stream, state) {
214     if (stream.match(/^prepend\b/)) {
215       state.restOfLine = 'variable';
216       return KEYWORD;
217     }
218   }
219   function block(stream, state) {
220     if (stream.match(/^block\b *(?:(prepend|append)\b)?/)) {
221       state.restOfLine = 'variable';
222       return KEYWORD;
223     }
224   }
225
226   function include(stream, state) {
227     if (stream.match(/^include\b/)) {
228       state.restOfLine = 'string';
229       return KEYWORD;
230     }
231   }
232
233   function includeFiltered(stream, state) {
234     if (stream.match(/^include:([a-zA-Z0-9\-]+)/, false) && stream.match('include')) {
235       state.isIncludeFiltered = true;
236       return KEYWORD;
237     }
238   }
239
240   function includeFilteredContinued(stream, state) {
241     if (state.isIncludeFiltered) {
242       var tok = filter(stream, state);
243       state.isIncludeFiltered = false;
244       state.restOfLine = 'string';
245       return tok;
246     }
247   }
248
249   function mixin(stream, state) {
250     if (stream.match(/^mixin\b/)) {
251       state.javaScriptLine = true;
252       return KEYWORD;
253     }
254   }
255
256   function call(stream, state) {
257     if (stream.match(/^\+([-\w]+)/)) {
258       if (!stream.match(/^\( *[-\w]+ *=/, false)) {
259         state.javaScriptArguments = true;
260         state.javaScriptArgumentsDepth = 0;
261       }
262       return 'variable';
263     }
264     if (stream.match(/^\+#{/, false)) {
265       stream.next();
266       state.mixinCallAfter = true;
267       return interpolation(stream, state);
268     }
269   }
270   function callArguments(stream, state) {
271     if (state.mixinCallAfter) {
272       state.mixinCallAfter = false;
273       if (!stream.match(/^\( *[-\w]+ *=/, false)) {
274         state.javaScriptArguments = true;
275         state.javaScriptArgumentsDepth = 0;
276       }
277       return true;
278     }
279   }
280
281   function conditional(stream, state) {
282     if (stream.match(/^(if|unless|else if|else)\b/)) {
283       state.javaScriptLine = true;
284       return KEYWORD;
285     }
286   }
287
288   function each(stream, state) {
289     if (stream.match(/^(- *)?(each|for)\b/)) {
290       state.isEach = true;
291       return KEYWORD;
292     }
293   }
294   function eachContinued(stream, state) {
295     if (state.isEach) {
296       if (stream.match(/^ in\b/)) {
297         state.javaScriptLine = true;
298         state.isEach = false;
299         return KEYWORD;
300       } else if (stream.sol() || stream.eol()) {
301         state.isEach = false;
302       } else if (stream.next()) {
303         while (!stream.match(/^ in\b/, false) && stream.next());
304         return 'variable';
305       }
306     }
307   }
308
309   function whileStatement(stream, state) {
310     if (stream.match(/^while\b/)) {
311       state.javaScriptLine = true;
312       return KEYWORD;
313     }
314   }
315
316   function tag(stream, state) {
317     var captures;
318     if (captures = stream.match(/^(\w(?:[-:\w]*\w)?)\/?/)) {
319       state.lastTag = captures[1].toLowerCase();
320       if (state.lastTag === 'script') {
321         state.scriptType = 'application/javascript';
322       }
323       return 'tag';
324     }
325   }
326
327   function filter(stream, state) {
328     if (stream.match(/^:([\w\-]+)/)) {
329       var innerMode;
330       if (config && config.innerModes) {
331         innerMode = config.innerModes(stream.current().substring(1));
332       }
333       if (!innerMode) {
334         innerMode = stream.current().substring(1);
335       }
336       if (typeof innerMode === 'string') {
337         innerMode = CodeMirror.getMode(config, innerMode);
338       }
339       setInnerMode(stream, state, innerMode);
340       return 'atom';
341     }
342   }
343
344   function code(stream, state) {
345     if (stream.match(/^(!?=|-)/)) {
346       state.javaScriptLine = true;
347       return 'punctuation';
348     }
349   }
350
351   function id(stream) {
352     if (stream.match(/^#([\w-]+)/)) {
353       return ID;
354     }
355   }
356
357   function className(stream) {
358     if (stream.match(/^\.([\w-]+)/)) {
359       return CLASS;
360     }
361   }
362
363   function attrs(stream, state) {
364     if (stream.peek() == '(') {
365       stream.next();
366       state.isAttrs = true;
367       state.attrsNest = [];
368       state.inAttributeName = true;
369       state.attrValue = '';
370       state.attributeIsType = false;
371       return 'punctuation';
372     }
373   }
374
375   function attrsContinued(stream, state) {
376     if (state.isAttrs) {
377       if (ATTRS_NEST[stream.peek()]) {
378         state.attrsNest.push(ATTRS_NEST[stream.peek()]);
379       }
380       if (state.attrsNest[state.attrsNest.length - 1] === stream.peek()) {
381         state.attrsNest.pop();
382       } else  if (stream.eat(')')) {
383         state.isAttrs = false;
384         return 'punctuation';
385       }
386       if (state.inAttributeName && stream.match(/^[^=,\)!]+/)) {
387         if (stream.peek() === '=' || stream.peek() === '!') {
388           state.inAttributeName = false;
389           state.jsState = jsMode.startState();
390           if (state.lastTag === 'script' && stream.current().trim().toLowerCase() === 'type') {
391             state.attributeIsType = true;
392           } else {
393             state.attributeIsType = false;
394           }
395         }
396         return 'attribute';
397       }
398
399       var tok = jsMode.token(stream, state.jsState);
400       if (state.attributeIsType && tok === 'string') {
401         state.scriptType = stream.current().toString();
402       }
403       if (state.attrsNest.length === 0 && (tok === 'string' || tok === 'variable' || tok === 'keyword')) {
404         try {
405           Function('', 'var x ' + state.attrValue.replace(/,\s*$/, '').replace(/^!/, ''));
406           state.inAttributeName = true;
407           state.attrValue = '';
408           stream.backUp(stream.current().length);
409           return attrsContinued(stream, state);
410         } catch (ex) {
411           //not the end of an attribute
412         }
413       }
414       state.attrValue += stream.current();
415       return tok || true;
416     }
417   }
418
419   function attributesBlock(stream, state) {
420     if (stream.match(/^&attributes\b/)) {
421       state.javaScriptArguments = true;
422       state.javaScriptArgumentsDepth = 0;
423       return 'keyword';
424     }
425   }
426
427   function indent(stream) {
428     if (stream.sol() && stream.eatSpace()) {
429       return 'indent';
430     }
431   }
432
433   function comment(stream, state) {
434     if (stream.match(/^ *\/\/(-)?([^\n]*)/)) {
435       state.indentOf = stream.indentation();
436       state.indentToken = 'comment';
437       return 'comment';
438     }
439   }
440
441   function colon(stream) {
442     if (stream.match(/^: */)) {
443       return 'colon';
444     }
445   }
446
447   function text(stream, state) {
448     if (stream.match(/^(?:\| ?| )([^\n]+)/)) {
449       return 'string';
450     }
451     if (stream.match(/^(<[^\n]*)/, false)) {
452       // html string
453       setInnerMode(stream, state, 'htmlmixed');
454       state.innerModeForLine = true;
455       return innerMode(stream, state, true);
456     }
457   }
458
459   function dot(stream, state) {
460     if (stream.eat('.')) {
461       var innerMode = null;
462       if (state.lastTag === 'script' && state.scriptType.toLowerCase().indexOf('javascript') != -1) {
463         innerMode = state.scriptType.toLowerCase().replace(/"|'/g, '');
464       } else if (state.lastTag === 'style') {
465         innerMode = 'css';
466       }
467       setInnerMode(stream, state, innerMode);
468       return 'dot';
469     }
470   }
471
472   function fail(stream) {
473     stream.next();
474     return null;
475   }
476
477
478   function setInnerMode(stream, state, mode) {
479     mode = CodeMirror.mimeModes[mode] || mode;
480     mode = config.innerModes ? config.innerModes(mode) || mode : mode;
481     mode = CodeMirror.mimeModes[mode] || mode;
482     mode = CodeMirror.getMode(config, mode);
483     state.indentOf = stream.indentation();
484
485     if (mode && mode.name !== 'null') {
486       state.innerMode = mode;
487     } else {
488       state.indentToken = 'string';
489     }
490   }
491   function innerMode(stream, state, force) {
492     if (stream.indentation() > state.indentOf || (state.innerModeForLine && !stream.sol()) || force) {
493       if (state.innerMode) {
494         if (!state.innerState) {
495           state.innerState = state.innerMode.startState ? state.innerMode.startState(stream.indentation()) : {};
496         }
497         return stream.hideFirstChars(state.indentOf + 2, function () {
498           return state.innerMode.token(stream, state.innerState) || true;
499         });
500       } else {
501         stream.skipToEnd();
502         return state.indentToken;
503       }
504     } else if (stream.sol()) {
505       state.indentOf = Infinity;
506       state.indentToken = null;
507       state.innerMode = null;
508       state.innerState = null;
509     }
510   }
511   function restOfLine(stream, state) {
512     if (stream.sol()) {
513       // if restOfLine was set at end of line, ignore it
514       state.restOfLine = '';
515     }
516     if (state.restOfLine) {
517       stream.skipToEnd();
518       var tok = state.restOfLine;
519       state.restOfLine = '';
520       return tok;
521     }
522   }
523
524
525   function startState() {
526     return new State();
527   }
528   function copyState(state) {
529     return state.copy();
530   }
531   /**
532    * Get the next token in the stream
533    *
534    * @param {Stream} stream
535    * @param {State} state
536    */
537   function nextToken(stream, state) {
538     var tok = innerMode(stream, state)
539       || restOfLine(stream, state)
540       || interpolationContinued(stream, state)
541       || includeFilteredContinued(stream, state)
542       || eachContinued(stream, state)
543       || attrsContinued(stream, state)
544       || javaScript(stream, state)
545       || javaScriptArguments(stream, state)
546       || callArguments(stream, state)
547
548       || yieldStatement(stream, state)
549       || doctype(stream, state)
550       || interpolation(stream, state)
551       || caseStatement(stream, state)
552       || when(stream, state)
553       || defaultStatement(stream, state)
554       || extendsStatement(stream, state)
555       || append(stream, state)
556       || prepend(stream, state)
557       || block(stream, state)
558       || include(stream, state)
559       || includeFiltered(stream, state)
560       || mixin(stream, state)
561       || call(stream, state)
562       || conditional(stream, state)
563       || each(stream, state)
564       || whileStatement(stream, state)
565       || tag(stream, state)
566       || filter(stream, state)
567       || code(stream, state)
568       || id(stream, state)
569       || className(stream, state)
570       || attrs(stream, state)
571       || attributesBlock(stream, state)
572       || indent(stream, state)
573       || text(stream, state)
574       || comment(stream, state)
575       || colon(stream, state)
576       || dot(stream, state)
577       || fail(stream, state);
578
579     return tok === true ? null : tok;
580   }
581   return {
582     startState: startState,
583     copyState: copyState,
584     token: nextToken
585   };
586 });
587
588 CodeMirror.defineMIME('text/x-jade', 'jade');
589
590 });