github.com/elliott5/community@v0.14.1-0.20160709191136-823126fb026a/app/public/codemirror/mode/jade/jade.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("../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  });