github.com/jancarloviray/community@v0.41.1-0.20170124221257-33a66c87cf2f/app/public/codemirror/mode/django/django.js (about)

     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) && !stream.match("{%", false)) {}
    65        return null;
    66      }
    67  
    68      // A string can be included in either single or double quotes (this is
    69      // the delimeter). Mark everything as a string until the start delimeter
    70      // occurs again.
    71      function inString (delimeter, previousTokenizer) {
    72        return function (stream, state) {
    73          if (!state.escapeNext && stream.eat(delimeter)) {
    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 delimeter.
    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 folowed 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 folowed 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 folowed 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 folowed 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("#}")) {
   321          state.tokenize = tokenBase;
   322        }
   323        return "comment";
   324      }
   325  
   326      // Mark everything as a comment until the `blockcomment` tag closes.
   327      function inBlockComment (stream, state) {
   328        if (stream.match(/\{%\s*endcomment\s*%\}/, false)) {
   329          state.tokenize = inTag;
   330          stream.match("{%");
   331          return "tag";
   332        } else {
   333          stream.next();
   334          return "comment";
   335        }
   336      }
   337  
   338      return {
   339        startState: function () {
   340          return {tokenize: tokenBase};
   341        },
   342        token: function (stream, state) {
   343          return state.tokenize(stream, state);
   344        },
   345        blockCommentStart: "{% comment %}",
   346        blockCommentEnd: "{% endcomment %}"
   347      };
   348    });
   349  
   350    CodeMirror.defineMode("django", function(config) {
   351      var htmlBase = CodeMirror.getMode(config, "text/html");
   352      var djangoInner = CodeMirror.getMode(config, "django:inner");
   353      return CodeMirror.overlayMode(htmlBase, djangoInner);
   354    });
   355  
   356    CodeMirror.defineMIME("text/x-django", "django");
   357  });