Mercurial
comparison .cms/lib/codemirror/mode/django/django.js @ 0:78edf6b517a0 draft
24.10
author | Coffee CMS <info@coffee-cms.ru> |
---|---|
date | Fri, 11 Oct 2024 22:40:23 +0000 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:78edf6b517a0 |
---|---|
1 // CodeMirror, copyright (c) by Marijn Haverbeke and others | |
2 // Distributed under an MIT license: https://codemirror.net/5/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", "filter", "endfilter", | |
18 "loop", "none", "self", "super", "if", "elif", "endif", "as", "else", "import", | |
19 "with", "endwith", "without", "context", "ifequal", "endifequal", "ifnotequal", | |
20 "endifnotequal", "extends", "include", "load", "comment", "endcomment", | |
21 "empty", "url", "static", "trans", "blocktrans", "endblocktrans", "now", | |
22 "regroup", "lorem", "ifchanged", "endifchanged", "firstof", "debug", "cycle", | |
23 "csrf_token", "autoescape", "endautoescape", "spaceless", "endspaceless", | |
24 "ssi", "templatetag", "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 = ["==", "!=", "<", ">", "<=", ">="], | |
39 wordOperators = ["in", "not", "or", "and"]; | |
40 | |
41 keywords = new RegExp("^\\b(" + keywords.join("|") + ")\\b"); | |
42 filters = new RegExp("^\\b(" + filters.join("|") + ")\\b"); | |
43 operators = new RegExp("^\\b(" + operators.join("|") + ")\\b"); | |
44 wordOperators = new RegExp("^\\b(" + wordOperators.join("|") + ")\\b"); | |
45 | |
46 // We have to return "null" instead of null, in order to avoid string | |
47 // styling as the default, when using Django templates inside HTML | |
48 // element attributes | |
49 function tokenBase (stream, state) { | |
50 // Attempt to identify a variable, template or comment tag respectively | |
51 if (stream.match("{{")) { | |
52 state.tokenize = inVariable; | |
53 return "tag"; | |
54 } else if (stream.match("{%")) { | |
55 state.tokenize = inTag; | |
56 return "tag"; | |
57 } else if (stream.match("{#")) { | |
58 state.tokenize = inComment; | |
59 return "comment"; | |
60 } | |
61 | |
62 // Ignore completely any stream series that do not match the | |
63 // Django template opening tags. | |
64 while (stream.next() != null && !stream.match(/\{[{%#]/, false)) {} | |
65 return null; | |
66 } | |
67 | |
68 // A string can be included in either single or double quotes (this is | |
69 // the delimiter). Mark everything as a string until the start delimiter | |
70 // occurs again. | |
71 function inString (delimiter, previousTokenizer) { | |
72 return function (stream, state) { | |
73 if (!state.escapeNext && stream.eat(delimiter)) { | |
74 state.tokenize = previousTokenizer; | |
75 } else { | |
76 if (state.escapeNext) { | |
77 state.escapeNext = false; | |
78 } | |
79 | |
80 var ch = stream.next(); | |
81 | |
82 // Take into account the backslash for escaping characters, such as | |
83 // the string delimiter. | |
84 if (ch == "\\") { | |
85 state.escapeNext = true; | |
86 } | |
87 } | |
88 | |
89 return "string"; | |
90 }; | |
91 } | |
92 | |
93 // Apply Django template variable syntax highlighting | |
94 function inVariable (stream, state) { | |
95 // Attempt to match a dot that precedes a property | |
96 if (state.waitDot) { | |
97 state.waitDot = false; | |
98 | |
99 if (stream.peek() != ".") { | |
100 return "null"; | |
101 } | |
102 | |
103 // Dot followed by a non-word character should be considered an error. | |
104 if (stream.match(/\.\W+/)) { | |
105 return "error"; | |
106 } else if (stream.eat(".")) { | |
107 state.waitProperty = true; | |
108 return "null"; | |
109 } else { | |
110 throw Error ("Unexpected error while waiting for property."); | |
111 } | |
112 } | |
113 | |
114 // Attempt to match a pipe that precedes a filter | |
115 if (state.waitPipe) { | |
116 state.waitPipe = false; | |
117 | |
118 if (stream.peek() != "|") { | |
119 return "null"; | |
120 } | |
121 | |
122 // Pipe followed by a non-word character should be considered an error. | |
123 if (stream.match(/\.\W+/)) { | |
124 return "error"; | |
125 } else if (stream.eat("|")) { | |
126 state.waitFilter = true; | |
127 return "null"; | |
128 } else { | |
129 throw Error ("Unexpected error while waiting for filter."); | |
130 } | |
131 } | |
132 | |
133 // Highlight properties | |
134 if (state.waitProperty) { | |
135 state.waitProperty = false; | |
136 if (stream.match(/\b(\w+)\b/)) { | |
137 state.waitDot = true; // A property can be followed by another property | |
138 state.waitPipe = true; // A property can be followed by a filter | |
139 return "property"; | |
140 } | |
141 } | |
142 | |
143 // Highlight filters | |
144 if (state.waitFilter) { | |
145 state.waitFilter = false; | |
146 if (stream.match(filters)) { | |
147 return "variable-2"; | |
148 } | |
149 } | |
150 | |
151 // Ignore all white spaces | |
152 if (stream.eatSpace()) { | |
153 state.waitProperty = false; | |
154 return "null"; | |
155 } | |
156 | |
157 // Identify numbers | |
158 if (stream.match(/\b\d+(\.\d+)?\b/)) { | |
159 return "number"; | |
160 } | |
161 | |
162 // Identify strings | |
163 if (stream.match("'")) { | |
164 state.tokenize = inString("'", state.tokenize); | |
165 return "string"; | |
166 } else if (stream.match('"')) { | |
167 state.tokenize = inString('"', state.tokenize); | |
168 return "string"; | |
169 } | |
170 | |
171 // Attempt to find the variable | |
172 if (stream.match(/\b(\w+)\b/) && !state.foundVariable) { | |
173 state.waitDot = true; | |
174 state.waitPipe = true; // A property can be followed by a filter | |
175 return "variable"; | |
176 } | |
177 | |
178 // If found closing tag reset | |
179 if (stream.match("}}")) { | |
180 state.waitProperty = null; | |
181 state.waitFilter = null; | |
182 state.waitDot = null; | |
183 state.waitPipe = null; | |
184 state.tokenize = tokenBase; | |
185 return "tag"; | |
186 } | |
187 | |
188 // If nothing was found, advance to the next character | |
189 stream.next(); | |
190 return "null"; | |
191 } | |
192 | |
193 function inTag (stream, state) { | |
194 // Attempt to match a dot that precedes a property | |
195 if (state.waitDot) { | |
196 state.waitDot = false; | |
197 | |
198 if (stream.peek() != ".") { | |
199 return "null"; | |
200 } | |
201 | |
202 // Dot followed by a non-word character should be considered an error. | |
203 if (stream.match(/\.\W+/)) { | |
204 return "error"; | |
205 } else if (stream.eat(".")) { | |
206 state.waitProperty = true; | |
207 return "null"; | |
208 } else { | |
209 throw Error ("Unexpected error while waiting for property."); | |
210 } | |
211 } | |
212 | |
213 // Attempt to match a pipe that precedes a filter | |
214 if (state.waitPipe) { | |
215 state.waitPipe = false; | |
216 | |
217 if (stream.peek() != "|") { | |
218 return "null"; | |
219 } | |
220 | |
221 // Pipe followed by a non-word character should be considered an error. | |
222 if (stream.match(/\.\W+/)) { | |
223 return "error"; | |
224 } else if (stream.eat("|")) { | |
225 state.waitFilter = true; | |
226 return "null"; | |
227 } else { | |
228 throw Error ("Unexpected error while waiting for filter."); | |
229 } | |
230 } | |
231 | |
232 // Highlight properties | |
233 if (state.waitProperty) { | |
234 state.waitProperty = false; | |
235 if (stream.match(/\b(\w+)\b/)) { | |
236 state.waitDot = true; // A property can be followed by another property | |
237 state.waitPipe = true; // A property can be followed by a filter | |
238 return "property"; | |
239 } | |
240 } | |
241 | |
242 // Highlight filters | |
243 if (state.waitFilter) { | |
244 state.waitFilter = false; | |
245 if (stream.match(filters)) { | |
246 return "variable-2"; | |
247 } | |
248 } | |
249 | |
250 // Ignore all white spaces | |
251 if (stream.eatSpace()) { | |
252 state.waitProperty = false; | |
253 return "null"; | |
254 } | |
255 | |
256 // Identify numbers | |
257 if (stream.match(/\b\d+(\.\d+)?\b/)) { | |
258 return "number"; | |
259 } | |
260 | |
261 // Identify strings | |
262 if (stream.match("'")) { | |
263 state.tokenize = inString("'", state.tokenize); | |
264 return "string"; | |
265 } else if (stream.match('"')) { | |
266 state.tokenize = inString('"', state.tokenize); | |
267 return "string"; | |
268 } | |
269 | |
270 // Attempt to match an operator | |
271 if (stream.match(operators)) { | |
272 return "operator"; | |
273 } | |
274 | |
275 // Attempt to match a word operator | |
276 if (stream.match(wordOperators)) { | |
277 return "keyword"; | |
278 } | |
279 | |
280 // Attempt to match a keyword | |
281 var keywordMatch = stream.match(keywords); | |
282 if (keywordMatch) { | |
283 if (keywordMatch[0] == "comment") { | |
284 state.blockCommentTag = true; | |
285 } | |
286 return "keyword"; | |
287 } | |
288 | |
289 // Attempt to match a variable | |
290 if (stream.match(/\b(\w+)\b/)) { | |
291 state.waitDot = true; | |
292 state.waitPipe = true; // A property can be followed by a filter | |
293 return "variable"; | |
294 } | |
295 | |
296 // If found closing tag reset | |
297 if (stream.match("%}")) { | |
298 state.waitProperty = null; | |
299 state.waitFilter = null; | |
300 state.waitDot = null; | |
301 state.waitPipe = null; | |
302 // If the tag that closes is a block comment tag, we want to mark the | |
303 // following code as comment, until the tag closes. | |
304 if (state.blockCommentTag) { | |
305 state.blockCommentTag = false; // Release the "lock" | |
306 state.tokenize = inBlockComment; | |
307 } else { | |
308 state.tokenize = tokenBase; | |
309 } | |
310 return "tag"; | |
311 } | |
312 | |
313 // If nothing was found, advance to the next character | |
314 stream.next(); | |
315 return "null"; | |
316 } | |
317 | |
318 // Mark everything as comment inside the tag and the tag itself. | |
319 function inComment (stream, state) { | |
320 if (stream.match(/^.*?#\}/)) state.tokenize = tokenBase | |
321 else stream.skipToEnd() | |
322 return "comment"; | |
323 } | |
324 | |
325 // Mark everything as a comment until the `blockcomment` tag closes. | |
326 function inBlockComment (stream, state) { | |
327 if (stream.match(/\{%\s*endcomment\s*%\}/, false)) { | |
328 state.tokenize = inTag; | |
329 stream.match("{%"); | |
330 return "tag"; | |
331 } else { | |
332 stream.next(); | |
333 return "comment"; | |
334 } | |
335 } | |
336 | |
337 return { | |
338 startState: function () { | |
339 return {tokenize: tokenBase}; | |
340 }, | |
341 token: function (stream, state) { | |
342 return state.tokenize(stream, state); | |
343 }, | |
344 blockCommentStart: "{% comment %}", | |
345 blockCommentEnd: "{% endcomment %}" | |
346 }; | |
347 }); | |
348 | |
349 CodeMirror.defineMode("django", function(config) { | |
350 var htmlBase = CodeMirror.getMode(config, "text/html"); | |
351 var djangoInner = CodeMirror.getMode(config, "django:inner"); | |
352 return CodeMirror.overlayMode(htmlBase, djangoInner); | |
353 }); | |
354 | |
355 CodeMirror.defineMIME("text/x-django", "django"); | |
356 }); |