openrat-cms

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README

django.js (11791B)


      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", "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 });