Subversion-Projekte lars-tiefland.ci

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
776 lars 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("../htmlmixed/htmlmixed"),
7
        require("../../addon/mode/overlay"));
8
  else if (typeof define == "function" && define.amd) // AMD
9
    define(["../../lib/codemirror", "../htmlmixed/htmlmixed",
10
            "../../addon/mode/overlay"], mod);
11
  else // Plain browser env
12
    mod(CodeMirror);
13
})(function(CodeMirror) {
14
  "use strict";
15
 
16
  CodeMirror.defineMode("django:inner", function() {
17
    var keywords = ["block", "endblock", "for", "endfor", "true", "false",
18
                    "loop", "none", "self", "super", "if", "endif", "as",
19
                    "else", "import", "with", "endwith", "without", "context", "ifequal", "endifequal",
20
                    "ifnotequal", "endifnotequal", "extends", "include", "load", "comment",
21
                    "endcomment", "empty", "url", "static", "trans", "blocktrans", "now", "regroup",
22
                    "lorem", "ifchanged", "endifchanged", "firstof", "debug", "cycle", "csrf_token",
23
                    "autoescape", "endautoescape", "spaceless", "ssi", "templatetag",
24
                    "verbatim", "endverbatim", "widthratio"],
25
        filters = ["add", "addslashes", "capfirst", "center", "cut", "date",
26
                   "default", "default_if_none", "dictsort",
27
                   "dictsortreversed", "divisibleby", "escape", "escapejs",
28
                   "filesizeformat", "first", "floatformat", "force_escape",
29
                   "get_digit", "iriencode", "join", "last", "length",
30
                   "length_is", "linebreaks", "linebreaksbr", "linenumbers",
31
                   "ljust", "lower", "make_list", "phone2numeric", "pluralize",
32
                   "pprint", "random", "removetags", "rjust", "safe",
33
                   "safeseq", "slice", "slugify", "stringformat", "striptags",
34
                   "time", "timesince", "timeuntil", "title", "truncatechars",
35
                   "truncatechars_html", "truncatewords", "truncatewords_html",
36
                   "unordered_list", "upper", "urlencode", "urlize",
37
                   "urlizetrunc", "wordcount", "wordwrap", "yesno"],
38
        operators = ["==", "!=", "<", ">", "<=", ">=", "in", "not", "or", "and"];
39
 
40
    keywords = new RegExp("^\\b(" + keywords.join("|") + ")\\b");
41
    filters = new RegExp("^\\b(" + filters.join("|") + ")\\b");
42
    operators = new RegExp("^\\b(" + operators.join("|") + ")\\b");
43
 
44
    // We have to return "null" instead of null, in order to avoid string
45
    // styling as the default, when using Django templates inside HTML
46
    // element attributes
47
    function tokenBase (stream, state) {
48
      // Attempt to identify a variable, template or comment tag respectively
49
      if (stream.match("{{")) {
50
        state.tokenize = inVariable;
51
        return "tag";
52
      } else if (stream.match("{%")) {
53
        state.tokenize = inTag;
54
        return "tag";
55
      } else if (stream.match("{#")) {
56
        state.tokenize = inComment;
57
        return "comment";
58
      }
59
 
60
      // Ignore completely any stream series that do not match the
61
      // Django template opening tags.
62
      while (stream.next() != null && !stream.match("{{", false) && !stream.match("{%", false)) {}
63
      return null;
64
    }
65
 
66
    // A string can be included in either single or double quotes (this is
67
    // the delimeter). Mark everything as a string until the start delimeter
68
    // occurs again.
69
    function inString (delimeter, previousTokenizer) {
70
      return function (stream, state) {
71
        if (!state.escapeNext && stream.eat(delimeter)) {
72
          state.tokenize = previousTokenizer;
73
        } else {
74
          if (state.escapeNext) {
75
            state.escapeNext = false;
76
          }
77
 
78
          var ch = stream.next();
79
 
80
          // Take into account the backslash for escaping characters, such as
81
          // the string delimeter.
82
          if (ch == "\\") {
83
            state.escapeNext = true;
84
          }
85
        }
86
 
87
        return "string";
88
      };
89
    }
90
 
91
    // Apply Django template variable syntax highlighting
92
    function inVariable (stream, state) {
93
      // Attempt to match a dot that precedes a property
94
      if (state.waitDot) {
95
        state.waitDot = false;
96
 
97
        if (stream.peek() != ".") {
98
          return "null";
99
        }
100
 
101
        // Dot folowed by a non-word character should be considered an error.
102
        if (stream.match(/\.\W+/)) {
103
          return "error";
104
        } else if (stream.eat(".")) {
105
          state.waitProperty = true;
106
          return "null";
107
        } else {
108
          throw Error ("Unexpected error while waiting for property.");
109
        }
110
      }
111
 
112
      // Attempt to match a pipe that precedes a filter
113
      if (state.waitPipe) {
114
        state.waitPipe = false;
115
 
116
        if (stream.peek() != "|") {
117
          return "null";
118
        }
119
 
120
        // Pipe folowed by a non-word character should be considered an error.
121
        if (stream.match(/\.\W+/)) {
122
          return "error";
123
        } else if (stream.eat("|")) {
124
          state.waitFilter = true;
125
          return "null";
126
        } else {
127
          throw Error ("Unexpected error while waiting for filter.");
128
        }
129
      }
130
 
131
      // Highlight properties
132
      if (state.waitProperty) {
133
        state.waitProperty = false;
134
        if (stream.match(/\b(\w+)\b/)) {
135
          state.waitDot = true;  // A property can be followed by another property
136
          state.waitPipe = true;  // A property can be followed by a filter
137
          return "property";
138
        }
139
      }
140
 
141
      // Highlight filters
142
      if (state.waitFilter) {
143
          state.waitFilter = false;
144
        if (stream.match(filters)) {
145
          return "variable-2";
146
        }
147
      }
148
 
149
      // Ignore all white spaces
150
      if (stream.eatSpace()) {
151
        state.waitProperty = false;
152
        return "null";
153
      }
154
 
155
      // Identify numbers
156
      if (stream.match(/\b\d+(\.\d+)?\b/)) {
157
        return "number";
158
      }
159
 
160
      // Identify strings
161
      if (stream.match("'")) {
162
        state.tokenize = inString("'", state.tokenize);
163
        return "string";
164
      } else if (stream.match('"')) {
165
        state.tokenize = inString('"', state.tokenize);
166
        return "string";
167
      }
168
 
169
      // Attempt to find the variable
170
      if (stream.match(/\b(\w+)\b/) && !state.foundVariable) {
171
        state.waitDot = true;
172
        state.waitPipe = true;  // A property can be followed by a filter
173
        return "variable";
174
      }
175
 
176
      // If found closing tag reset
177
      if (stream.match("}}")) {
178
        state.waitProperty = null;
179
        state.waitFilter = null;
180
        state.waitDot = null;
181
        state.waitPipe = null;
182
        state.tokenize = tokenBase;
183
        return "tag";
184
      }
185
 
186
      // If nothing was found, advance to the next character
187
      stream.next();
188
      return "null";
189
    }
190
 
191
    function inTag (stream, state) {
192
      // Attempt to match a dot that precedes a property
193
      if (state.waitDot) {
194
        state.waitDot = false;
195
 
196
        if (stream.peek() != ".") {
197
          return "null";
198
        }
199
 
200
        // Dot folowed by a non-word character should be considered an error.
201
        if (stream.match(/\.\W+/)) {
202
          return "error";
203
        } else if (stream.eat(".")) {
204
          state.waitProperty = true;
205
          return "null";
206
        } else {
207
          throw Error ("Unexpected error while waiting for property.");
208
        }
209
      }
210
 
211
      // Attempt to match a pipe that precedes a filter
212
      if (state.waitPipe) {
213
        state.waitPipe = false;
214
 
215
        if (stream.peek() != "|") {
216
          return "null";
217
        }
218
 
219
        // Pipe folowed by a non-word character should be considered an error.
220
        if (stream.match(/\.\W+/)) {
221
          return "error";
222
        } else if (stream.eat("|")) {
223
          state.waitFilter = true;
224
          return "null";
225
        } else {
226
          throw Error ("Unexpected error while waiting for filter.");
227
        }
228
      }
229
 
230
      // Highlight properties
231
      if (state.waitProperty) {
232
        state.waitProperty = false;
233
        if (stream.match(/\b(\w+)\b/)) {
234
          state.waitDot = true;  // A property can be followed by another property
235
          state.waitPipe = true;  // A property can be followed by a filter
236
          return "property";
237
        }
238
      }
239
 
240
      // Highlight filters
241
      if (state.waitFilter) {
242
          state.waitFilter = false;
243
        if (stream.match(filters)) {
244
          return "variable-2";
245
        }
246
      }
247
 
248
      // Ignore all white spaces
249
      if (stream.eatSpace()) {
250
        state.waitProperty = false;
251
        return "null";
252
      }
253
 
254
      // Identify numbers
255
      if (stream.match(/\b\d+(\.\d+)?\b/)) {
256
        return "number";
257
      }
258
 
259
      // Identify strings
260
      if (stream.match("'")) {
261
        state.tokenize = inString("'", state.tokenize);
262
        return "string";
263
      } else if (stream.match('"')) {
264
        state.tokenize = inString('"', state.tokenize);
265
        return "string";
266
      }
267
 
268
      // Attempt to match an operator
269
      if (stream.match(operators)) {
270
        return "operator";
271
      }
272
 
273
      // Attempt to match a keyword
274
      var keywordMatch = stream.match(keywords);
275
      if (keywordMatch) {
276
        if (keywordMatch[0] == "comment") {
277
          state.blockCommentTag = true;
278
        }
279
        return "keyword";
280
      }
281
 
282
      // Attempt to match a variable
283
      if (stream.match(/\b(\w+)\b/)) {
284
        state.waitDot = true;
285
        state.waitPipe = true;  // A property can be followed by a filter
286
        return "variable";
287
      }
288
 
289
      // If found closing tag reset
290
      if (stream.match("%}")) {
291
        state.waitProperty = null;
292
        state.waitFilter = null;
293
        state.waitDot = null;
294
        state.waitPipe = null;
295
        // If the tag that closes is a block comment tag, we want to mark the
296
        // following code as comment, until the tag closes.
297
        if (state.blockCommentTag) {
298
          state.blockCommentTag = false;  // Release the "lock"
299
          state.tokenize = inBlockComment;
300
        } else {
301
          state.tokenize = tokenBase;
302
        }
303
        return "tag";
304
      }
305
 
306
      // If nothing was found, advance to the next character
307
      stream.next();
308
      return "null";
309
    }
310
 
311
    // Mark everything as comment inside the tag and the tag itself.
312
    function inComment (stream, state) {
313
      if (stream.match("#}")) {
314
        state.tokenize = tokenBase;
315
      }
316
      return "comment";
317
    }
318
 
319
    // Mark everything as a comment until the `blockcomment` tag closes.
320
    function inBlockComment (stream, state) {
321
      if (stream.match(/\{%\s*endcomment\s*%\}/, false)) {
322
        state.tokenize = inTag;
323
        stream.match("{%");
324
        return "tag";
325
      } else {
326
        stream.next();
327
        return "comment";
328
      }
329
    }
330
 
331
    return {
332
      startState: function () {
333
        return {tokenize: tokenBase};
334
      },
335
      token: function (stream, state) {
336
        return state.tokenize(stream, state);
337
      },
338
      blockCommentStart: "{% comment %}",
339
      blockCommentEnd: "{% endcomment %}"
340
    };
341
  });
342
 
343
  CodeMirror.defineMode("django", function(config) {
344
    var htmlBase = CodeMirror.getMode(config, "text/html");
345
    var djangoInner = CodeMirror.getMode(config, "django:inner");
346
    return CodeMirror.overlayMode(htmlBase, djangoInner);
347
  });
348
 
349
  CodeMirror.defineMIME("text/x-django", "django");
350
});