mirror of
				https://github.com/zadam/trilium.git
				synced 2025-11-03 20:06:08 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			576 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			576 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
// CodeMirror, copyright (c) by Marijn Haverbeke and others
 | 
						|
// Distributed under an MIT license: https://codemirror.net/5/LICENSE
 | 
						|
 | 
						|
// Slim Highlighting for CodeMirror copyright (c) HicknHack Software Gmbh
 | 
						|
 | 
						|
(function(mod) {
 | 
						|
  if (typeof exports == "object" && typeof module == "object") // CommonJS
 | 
						|
    mod(require("../../lib/codemirror"), require("../htmlmixed/htmlmixed"), require("../ruby/ruby"));
 | 
						|
  else if (typeof define == "function" && define.amd) // AMD
 | 
						|
    define(["../../lib/codemirror", "../htmlmixed/htmlmixed", "../ruby/ruby"], mod);
 | 
						|
  else // Plain browser env
 | 
						|
    mod(CodeMirror);
 | 
						|
})(function(CodeMirror) {
 | 
						|
"use strict";
 | 
						|
 | 
						|
  CodeMirror.defineMode("slim", function(config) {
 | 
						|
    var htmlMode = CodeMirror.getMode(config, {name: "htmlmixed"});
 | 
						|
    var rubyMode = CodeMirror.getMode(config, "ruby");
 | 
						|
    var modes = { html: htmlMode, ruby: rubyMode };
 | 
						|
    var embedded = {
 | 
						|
      ruby: "ruby",
 | 
						|
      javascript: "javascript",
 | 
						|
      css: "text/css",
 | 
						|
      sass: "text/x-sass",
 | 
						|
      scss: "text/x-scss",
 | 
						|
      less: "text/x-less",
 | 
						|
      styl: "text/x-styl", // no highlighting so far
 | 
						|
      coffee: "coffeescript",
 | 
						|
      asciidoc: "text/x-asciidoc",
 | 
						|
      markdown: "text/x-markdown",
 | 
						|
      textile: "text/x-textile", // no highlighting so far
 | 
						|
      creole: "text/x-creole", // no highlighting so far
 | 
						|
      wiki: "text/x-wiki", // no highlighting so far
 | 
						|
      mediawiki: "text/x-mediawiki", // no highlighting so far
 | 
						|
      rdoc: "text/x-rdoc", // no highlighting so far
 | 
						|
      builder: "text/x-builder", // no highlighting so far
 | 
						|
      nokogiri: "text/x-nokogiri", // no highlighting so far
 | 
						|
      erb: "application/x-erb"
 | 
						|
    };
 | 
						|
    var embeddedRegexp = function(map){
 | 
						|
      var arr = [];
 | 
						|
      for(var key in map) arr.push(key);
 | 
						|
      return new RegExp("^("+arr.join('|')+"):");
 | 
						|
    }(embedded);
 | 
						|
 | 
						|
    var styleMap = {
 | 
						|
      "commentLine": "comment",
 | 
						|
      "slimSwitch": "operator special",
 | 
						|
      "slimTag": "tag",
 | 
						|
      "slimId": "attribute def",
 | 
						|
      "slimClass": "attribute qualifier",
 | 
						|
      "slimAttribute": "attribute",
 | 
						|
      "slimSubmode": "keyword special",
 | 
						|
      "closeAttributeTag": null,
 | 
						|
      "slimDoctype": null,
 | 
						|
      "lineContinuation": null
 | 
						|
    };
 | 
						|
    var closing = {
 | 
						|
      "{": "}",
 | 
						|
      "[": "]",
 | 
						|
      "(": ")"
 | 
						|
    };
 | 
						|
 | 
						|
    var nameStartChar = "_a-zA-Z\xC0-\xD6\xD8-\xF6\xF8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD";
 | 
						|
    var nameChar = nameStartChar + "\\-0-9\xB7\u0300-\u036F\u203F-\u2040";
 | 
						|
    var nameRegexp = new RegExp("^[:"+nameStartChar+"](?::["+nameChar+"]|["+nameChar+"]*)");
 | 
						|
    var attributeNameRegexp = new RegExp("^[:"+nameStartChar+"][:\\."+nameChar+"]*(?=\\s*=)");
 | 
						|
    var wrappedAttributeNameRegexp = new RegExp("^[:"+nameStartChar+"][:\\."+nameChar+"]*");
 | 
						|
    var classNameRegexp = /^\.-?[_a-zA-Z]+[\w\-]*/;
 | 
						|
    var classIdRegexp = /^#[_a-zA-Z]+[\w\-]*/;
 | 
						|
 | 
						|
    function backup(pos, tokenize, style) {
 | 
						|
      var restore = function(stream, state) {
 | 
						|
        state.tokenize = tokenize;
 | 
						|
        if (stream.pos < pos) {
 | 
						|
          stream.pos = pos;
 | 
						|
          return style;
 | 
						|
        }
 | 
						|
        return state.tokenize(stream, state);
 | 
						|
      };
 | 
						|
      return function(stream, state) {
 | 
						|
        state.tokenize = restore;
 | 
						|
        return tokenize(stream, state);
 | 
						|
      };
 | 
						|
    }
 | 
						|
 | 
						|
    function maybeBackup(stream, state, pat, offset, style) {
 | 
						|
      var cur = stream.current();
 | 
						|
      var idx = cur.search(pat);
 | 
						|
      if (idx > -1) {
 | 
						|
        state.tokenize = backup(stream.pos, state.tokenize, style);
 | 
						|
        stream.backUp(cur.length - idx - offset);
 | 
						|
      }
 | 
						|
      return style;
 | 
						|
    }
 | 
						|
 | 
						|
    function continueLine(state, column) {
 | 
						|
      state.stack = {
 | 
						|
        parent: state.stack,
 | 
						|
        style: "continuation",
 | 
						|
        indented: column,
 | 
						|
        tokenize: state.line
 | 
						|
      };
 | 
						|
      state.line = state.tokenize;
 | 
						|
    }
 | 
						|
    function finishContinue(state) {
 | 
						|
      if (state.line == state.tokenize) {
 | 
						|
        state.line = state.stack.tokenize;
 | 
						|
        state.stack = state.stack.parent;
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    function lineContinuable(column, tokenize) {
 | 
						|
      return function(stream, state) {
 | 
						|
        finishContinue(state);
 | 
						|
        if (stream.match(/^\\$/)) {
 | 
						|
          continueLine(state, column);
 | 
						|
          return "lineContinuation";
 | 
						|
        }
 | 
						|
        var style = tokenize(stream, state);
 | 
						|
        if (stream.eol() && stream.current().match(/(?:^|[^\\])(?:\\\\)*\\$/)) {
 | 
						|
          stream.backUp(1);
 | 
						|
        }
 | 
						|
        return style;
 | 
						|
      };
 | 
						|
    }
 | 
						|
    function commaContinuable(column, tokenize) {
 | 
						|
      return function(stream, state) {
 | 
						|
        finishContinue(state);
 | 
						|
        var style = tokenize(stream, state);
 | 
						|
        if (stream.eol() && stream.current().match(/,$/)) {
 | 
						|
          continueLine(state, column);
 | 
						|
        }
 | 
						|
        return style;
 | 
						|
      };
 | 
						|
    }
 | 
						|
 | 
						|
    function rubyInQuote(endQuote, tokenize) {
 | 
						|
      // TODO: add multi line support
 | 
						|
      return function(stream, state) {
 | 
						|
        var ch = stream.peek();
 | 
						|
        if (ch == endQuote && state.rubyState.tokenize.length == 1) {
 | 
						|
          // step out of ruby context as it seems to complete processing all the braces
 | 
						|
          stream.next();
 | 
						|
          state.tokenize = tokenize;
 | 
						|
          return "closeAttributeTag";
 | 
						|
        } else {
 | 
						|
          return ruby(stream, state);
 | 
						|
        }
 | 
						|
      };
 | 
						|
    }
 | 
						|
    function startRubySplat(tokenize) {
 | 
						|
      var rubyState;
 | 
						|
      var runSplat = function(stream, state) {
 | 
						|
        if (state.rubyState.tokenize.length == 1 && !state.rubyState.context.prev) {
 | 
						|
          stream.backUp(1);
 | 
						|
          if (stream.eatSpace()) {
 | 
						|
            state.rubyState = rubyState;
 | 
						|
            state.tokenize = tokenize;
 | 
						|
            return tokenize(stream, state);
 | 
						|
          }
 | 
						|
          stream.next();
 | 
						|
        }
 | 
						|
        return ruby(stream, state);
 | 
						|
      };
 | 
						|
      return function(stream, state) {
 | 
						|
        rubyState = state.rubyState;
 | 
						|
        state.rubyState = CodeMirror.startState(rubyMode);
 | 
						|
        state.tokenize = runSplat;
 | 
						|
        return ruby(stream, state);
 | 
						|
      };
 | 
						|
    }
 | 
						|
 | 
						|
    function ruby(stream, state) {
 | 
						|
      return rubyMode.token(stream, state.rubyState);
 | 
						|
    }
 | 
						|
 | 
						|
    function htmlLine(stream, state) {
 | 
						|
      if (stream.match(/^\\$/)) {
 | 
						|
        return "lineContinuation";
 | 
						|
      }
 | 
						|
      return html(stream, state);
 | 
						|
    }
 | 
						|
    function html(stream, state) {
 | 
						|
      if (stream.match(/^#\{/)) {
 | 
						|
        state.tokenize = rubyInQuote("}", state.tokenize);
 | 
						|
        return null;
 | 
						|
      }
 | 
						|
      return maybeBackup(stream, state, /[^\\]#\{/, 1, htmlMode.token(stream, state.htmlState));
 | 
						|
    }
 | 
						|
 | 
						|
    function startHtmlLine(lastTokenize) {
 | 
						|
      return function(stream, state) {
 | 
						|
        var style = htmlLine(stream, state);
 | 
						|
        if (stream.eol()) state.tokenize = lastTokenize;
 | 
						|
        return style;
 | 
						|
      };
 | 
						|
    }
 | 
						|
 | 
						|
    function startHtmlMode(stream, state, offset) {
 | 
						|
      state.stack = {
 | 
						|
        parent: state.stack,
 | 
						|
        style: "html",
 | 
						|
        indented: stream.column() + offset, // pipe + space
 | 
						|
        tokenize: state.line
 | 
						|
      };
 | 
						|
      state.line = state.tokenize = html;
 | 
						|
      return null;
 | 
						|
    }
 | 
						|
 | 
						|
    function comment(stream, state) {
 | 
						|
      stream.skipToEnd();
 | 
						|
      return state.stack.style;
 | 
						|
    }
 | 
						|
 | 
						|
    function commentMode(stream, state) {
 | 
						|
      state.stack = {
 | 
						|
        parent: state.stack,
 | 
						|
        style: "comment",
 | 
						|
        indented: state.indented + 1,
 | 
						|
        tokenize: state.line
 | 
						|
      };
 | 
						|
      state.line = comment;
 | 
						|
      return comment(stream, state);
 | 
						|
    }
 | 
						|
 | 
						|
    function attributeWrapper(stream, state) {
 | 
						|
      if (stream.eat(state.stack.endQuote)) {
 | 
						|
        state.line = state.stack.line;
 | 
						|
        state.tokenize = state.stack.tokenize;
 | 
						|
        state.stack = state.stack.parent;
 | 
						|
        return null;
 | 
						|
      }
 | 
						|
      if (stream.match(wrappedAttributeNameRegexp)) {
 | 
						|
        state.tokenize = attributeWrapperAssign;
 | 
						|
        return "slimAttribute";
 | 
						|
      }
 | 
						|
      stream.next();
 | 
						|
      return null;
 | 
						|
    }
 | 
						|
    function attributeWrapperAssign(stream, state) {
 | 
						|
      if (stream.match(/^==?/)) {
 | 
						|
        state.tokenize = attributeWrapperValue;
 | 
						|
        return null;
 | 
						|
      }
 | 
						|
      return attributeWrapper(stream, state);
 | 
						|
    }
 | 
						|
    function attributeWrapperValue(stream, state) {
 | 
						|
      var ch = stream.peek();
 | 
						|
      if (ch == '"' || ch == "\'") {
 | 
						|
        state.tokenize = readQuoted(ch, "string", true, false, attributeWrapper);
 | 
						|
        stream.next();
 | 
						|
        return state.tokenize(stream, state);
 | 
						|
      }
 | 
						|
      if (ch == '[') {
 | 
						|
        return startRubySplat(attributeWrapper)(stream, state);
 | 
						|
      }
 | 
						|
      if (stream.match(/^(true|false|nil)\b/)) {
 | 
						|
        state.tokenize = attributeWrapper;
 | 
						|
        return "keyword";
 | 
						|
      }
 | 
						|
      return startRubySplat(attributeWrapper)(stream, state);
 | 
						|
    }
 | 
						|
 | 
						|
    function startAttributeWrapperMode(state, endQuote, tokenize) {
 | 
						|
      state.stack = {
 | 
						|
        parent: state.stack,
 | 
						|
        style: "wrapper",
 | 
						|
        indented: state.indented + 1,
 | 
						|
        tokenize: tokenize,
 | 
						|
        line: state.line,
 | 
						|
        endQuote: endQuote
 | 
						|
      };
 | 
						|
      state.line = state.tokenize = attributeWrapper;
 | 
						|
      return null;
 | 
						|
    }
 | 
						|
 | 
						|
    function sub(stream, state) {
 | 
						|
      if (stream.match(/^#\{/)) {
 | 
						|
        state.tokenize = rubyInQuote("}", state.tokenize);
 | 
						|
        return null;
 | 
						|
      }
 | 
						|
      var subStream = new CodeMirror.StringStream(stream.string.slice(state.stack.indented), stream.tabSize);
 | 
						|
      subStream.pos = stream.pos - state.stack.indented;
 | 
						|
      subStream.start = stream.start - state.stack.indented;
 | 
						|
      subStream.lastColumnPos = stream.lastColumnPos - state.stack.indented;
 | 
						|
      subStream.lastColumnValue = stream.lastColumnValue - state.stack.indented;
 | 
						|
      var style = state.subMode.token(subStream, state.subState);
 | 
						|
      stream.pos = subStream.pos + state.stack.indented;
 | 
						|
      return style;
 | 
						|
    }
 | 
						|
    function firstSub(stream, state) {
 | 
						|
      state.stack.indented = stream.column();
 | 
						|
      state.line = state.tokenize = sub;
 | 
						|
      return state.tokenize(stream, state);
 | 
						|
    }
 | 
						|
 | 
						|
    function createMode(mode) {
 | 
						|
      var query = embedded[mode];
 | 
						|
      var spec = CodeMirror.mimeModes[query];
 | 
						|
      if (spec) {
 | 
						|
        return CodeMirror.getMode(config, spec);
 | 
						|
      }
 | 
						|
      var factory = CodeMirror.modes[query];
 | 
						|
      if (factory) {
 | 
						|
        return factory(config, {name: query});
 | 
						|
      }
 | 
						|
      return CodeMirror.getMode(config, "null");
 | 
						|
    }
 | 
						|
 | 
						|
    function getMode(mode) {
 | 
						|
      if (!modes.hasOwnProperty(mode)) {
 | 
						|
        return modes[mode] = createMode(mode);
 | 
						|
      }
 | 
						|
      return modes[mode];
 | 
						|
    }
 | 
						|
 | 
						|
    function startSubMode(mode, state) {
 | 
						|
      var subMode = getMode(mode);
 | 
						|
      var subState = CodeMirror.startState(subMode);
 | 
						|
 | 
						|
      state.subMode = subMode;
 | 
						|
      state.subState = subState;
 | 
						|
 | 
						|
      state.stack = {
 | 
						|
        parent: state.stack,
 | 
						|
        style: "sub",
 | 
						|
        indented: state.indented + 1,
 | 
						|
        tokenize: state.line
 | 
						|
      };
 | 
						|
      state.line = state.tokenize = firstSub;
 | 
						|
      return "slimSubmode";
 | 
						|
    }
 | 
						|
 | 
						|
    function doctypeLine(stream, _state) {
 | 
						|
      stream.skipToEnd();
 | 
						|
      return "slimDoctype";
 | 
						|
    }
 | 
						|
 | 
						|
    function startLine(stream, state) {
 | 
						|
      var ch = stream.peek();
 | 
						|
      if (ch == '<') {
 | 
						|
        return (state.tokenize = startHtmlLine(state.tokenize))(stream, state);
 | 
						|
      }
 | 
						|
      if (stream.match(/^[|']/)) {
 | 
						|
        return startHtmlMode(stream, state, 1);
 | 
						|
      }
 | 
						|
      if (stream.match(/^\/(!|\[\w+])?/)) {
 | 
						|
        return commentMode(stream, state);
 | 
						|
      }
 | 
						|
      if (stream.match(/^(-|==?[<>]?)/)) {
 | 
						|
        state.tokenize = lineContinuable(stream.column(), commaContinuable(stream.column(), ruby));
 | 
						|
        return "slimSwitch";
 | 
						|
      }
 | 
						|
      if (stream.match(/^doctype\b/)) {
 | 
						|
        state.tokenize = doctypeLine;
 | 
						|
        return "keyword";
 | 
						|
      }
 | 
						|
 | 
						|
      var m = stream.match(embeddedRegexp);
 | 
						|
      if (m) {
 | 
						|
        return startSubMode(m[1], state);
 | 
						|
      }
 | 
						|
 | 
						|
      return slimTag(stream, state);
 | 
						|
    }
 | 
						|
 | 
						|
    function slim(stream, state) {
 | 
						|
      if (state.startOfLine) {
 | 
						|
        return startLine(stream, state);
 | 
						|
      }
 | 
						|
      return slimTag(stream, state);
 | 
						|
    }
 | 
						|
 | 
						|
    function slimTag(stream, state) {
 | 
						|
      if (stream.eat('*')) {
 | 
						|
        state.tokenize = startRubySplat(slimTagExtras);
 | 
						|
        return null;
 | 
						|
      }
 | 
						|
      if (stream.match(nameRegexp)) {
 | 
						|
        state.tokenize = slimTagExtras;
 | 
						|
        return "slimTag";
 | 
						|
      }
 | 
						|
      return slimClass(stream, state);
 | 
						|
    }
 | 
						|
    function slimTagExtras(stream, state) {
 | 
						|
      if (stream.match(/^(<>?|><?)/)) {
 | 
						|
        state.tokenize = slimClass;
 | 
						|
        return null;
 | 
						|
      }
 | 
						|
      return slimClass(stream, state);
 | 
						|
    }
 | 
						|
    function slimClass(stream, state) {
 | 
						|
      if (stream.match(classIdRegexp)) {
 | 
						|
        state.tokenize = slimClass;
 | 
						|
        return "slimId";
 | 
						|
      }
 | 
						|
      if (stream.match(classNameRegexp)) {
 | 
						|
        state.tokenize = slimClass;
 | 
						|
        return "slimClass";
 | 
						|
      }
 | 
						|
      return slimAttribute(stream, state);
 | 
						|
    }
 | 
						|
    function slimAttribute(stream, state) {
 | 
						|
      if (stream.match(/^([\[\{\(])/)) {
 | 
						|
        return startAttributeWrapperMode(state, closing[RegExp.$1], slimAttribute);
 | 
						|
      }
 | 
						|
      if (stream.match(attributeNameRegexp)) {
 | 
						|
        state.tokenize = slimAttributeAssign;
 | 
						|
        return "slimAttribute";
 | 
						|
      }
 | 
						|
      if (stream.peek() == '*') {
 | 
						|
        stream.next();
 | 
						|
        state.tokenize = startRubySplat(slimContent);
 | 
						|
        return null;
 | 
						|
      }
 | 
						|
      return slimContent(stream, state);
 | 
						|
    }
 | 
						|
    function slimAttributeAssign(stream, state) {
 | 
						|
      if (stream.match(/^==?/)) {
 | 
						|
        state.tokenize = slimAttributeValue;
 | 
						|
        return null;
 | 
						|
      }
 | 
						|
      // should never happen, because of forward lookup
 | 
						|
      return slimAttribute(stream, state);
 | 
						|
    }
 | 
						|
 | 
						|
    function slimAttributeValue(stream, state) {
 | 
						|
      var ch = stream.peek();
 | 
						|
      if (ch == '"' || ch == "\'") {
 | 
						|
        state.tokenize = readQuoted(ch, "string", true, false, slimAttribute);
 | 
						|
        stream.next();
 | 
						|
        return state.tokenize(stream, state);
 | 
						|
      }
 | 
						|
      if (ch == '[') {
 | 
						|
        return startRubySplat(slimAttribute)(stream, state);
 | 
						|
      }
 | 
						|
      if (ch == ':') {
 | 
						|
        return startRubySplat(slimAttributeSymbols)(stream, state);
 | 
						|
      }
 | 
						|
      if (stream.match(/^(true|false|nil)\b/)) {
 | 
						|
        state.tokenize = slimAttribute;
 | 
						|
        return "keyword";
 | 
						|
      }
 | 
						|
      return startRubySplat(slimAttribute)(stream, state);
 | 
						|
    }
 | 
						|
    function slimAttributeSymbols(stream, state) {
 | 
						|
      stream.backUp(1);
 | 
						|
      if (stream.match(/^[^\s],(?=:)/)) {
 | 
						|
        state.tokenize = startRubySplat(slimAttributeSymbols);
 | 
						|
        return null;
 | 
						|
      }
 | 
						|
      stream.next();
 | 
						|
      return slimAttribute(stream, state);
 | 
						|
    }
 | 
						|
    function readQuoted(quote, style, embed, unescaped, nextTokenize) {
 | 
						|
      return function(stream, state) {
 | 
						|
        finishContinue(state);
 | 
						|
        var fresh = stream.current().length == 0;
 | 
						|
        if (stream.match(/^\\$/, fresh)) {
 | 
						|
          if (!fresh) return style;
 | 
						|
          continueLine(state, state.indented);
 | 
						|
          return "lineContinuation";
 | 
						|
        }
 | 
						|
        if (stream.match(/^#\{/, fresh)) {
 | 
						|
          if (!fresh) return style;
 | 
						|
          state.tokenize = rubyInQuote("}", state.tokenize);
 | 
						|
          return null;
 | 
						|
        }
 | 
						|
        var escaped = false, ch;
 | 
						|
        while ((ch = stream.next()) != null) {
 | 
						|
          if (ch == quote && (unescaped || !escaped)) {
 | 
						|
            state.tokenize = nextTokenize;
 | 
						|
            break;
 | 
						|
          }
 | 
						|
          if (embed && ch == "#" && !escaped) {
 | 
						|
            if (stream.eat("{")) {
 | 
						|
              stream.backUp(2);
 | 
						|
              break;
 | 
						|
            }
 | 
						|
          }
 | 
						|
          escaped = !escaped && ch == "\\";
 | 
						|
        }
 | 
						|
        if (stream.eol() && escaped) {
 | 
						|
          stream.backUp(1);
 | 
						|
        }
 | 
						|
        return style;
 | 
						|
      };
 | 
						|
    }
 | 
						|
    function slimContent(stream, state) {
 | 
						|
      if (stream.match(/^==?/)) {
 | 
						|
        state.tokenize = ruby;
 | 
						|
        return "slimSwitch";
 | 
						|
      }
 | 
						|
      if (stream.match(/^\/$/)) { // tag close hint
 | 
						|
        state.tokenize = slim;
 | 
						|
        return null;
 | 
						|
      }
 | 
						|
      if (stream.match(/^:/)) { // inline tag
 | 
						|
        state.tokenize = slimTag;
 | 
						|
        return "slimSwitch";
 | 
						|
      }
 | 
						|
      startHtmlMode(stream, state, 0);
 | 
						|
      return state.tokenize(stream, state);
 | 
						|
    }
 | 
						|
 | 
						|
    var mode = {
 | 
						|
      // default to html mode
 | 
						|
      startState: function() {
 | 
						|
        var htmlState = CodeMirror.startState(htmlMode);
 | 
						|
        var rubyState = CodeMirror.startState(rubyMode);
 | 
						|
        return {
 | 
						|
          htmlState: htmlState,
 | 
						|
          rubyState: rubyState,
 | 
						|
          stack: null,
 | 
						|
          last: null,
 | 
						|
          tokenize: slim,
 | 
						|
          line: slim,
 | 
						|
          indented: 0
 | 
						|
        };
 | 
						|
      },
 | 
						|
 | 
						|
      copyState: function(state) {
 | 
						|
        return {
 | 
						|
          htmlState : CodeMirror.copyState(htmlMode, state.htmlState),
 | 
						|
          rubyState: CodeMirror.copyState(rubyMode, state.rubyState),
 | 
						|
          subMode: state.subMode,
 | 
						|
          subState: state.subMode && CodeMirror.copyState(state.subMode, state.subState),
 | 
						|
          stack: state.stack,
 | 
						|
          last: state.last,
 | 
						|
          tokenize: state.tokenize,
 | 
						|
          line: state.line
 | 
						|
        };
 | 
						|
      },
 | 
						|
 | 
						|
      token: function(stream, state) {
 | 
						|
        if (stream.sol()) {
 | 
						|
          state.indented = stream.indentation();
 | 
						|
          state.startOfLine = true;
 | 
						|
          state.tokenize = state.line;
 | 
						|
          while (state.stack && state.stack.indented > state.indented && state.last != "slimSubmode") {
 | 
						|
            state.line = state.tokenize = state.stack.tokenize;
 | 
						|
            state.stack = state.stack.parent;
 | 
						|
            state.subMode = null;
 | 
						|
            state.subState = null;
 | 
						|
          }
 | 
						|
        }
 | 
						|
        if (stream.eatSpace()) return null;
 | 
						|
        var style = state.tokenize(stream, state);
 | 
						|
        state.startOfLine = false;
 | 
						|
        if (style) state.last = style;
 | 
						|
        return styleMap.hasOwnProperty(style) ? styleMap[style] : style;
 | 
						|
      },
 | 
						|
 | 
						|
      blankLine: function(state) {
 | 
						|
        if (state.subMode && state.subMode.blankLine) {
 | 
						|
          return state.subMode.blankLine(state.subState);
 | 
						|
        }
 | 
						|
      },
 | 
						|
 | 
						|
      innerMode: function(state) {
 | 
						|
        if (state.subMode) return {state: state.subState, mode: state.subMode};
 | 
						|
        return {state: state, mode: mode};
 | 
						|
      }
 | 
						|
 | 
						|
      //indent: function(state) {
 | 
						|
      //  return state.indented;
 | 
						|
      //}
 | 
						|
    };
 | 
						|
    return mode;
 | 
						|
  }, "htmlmixed", "ruby");
 | 
						|
 | 
						|
  CodeMirror.defineMIME("text/x-slim", "slim");
 | 
						|
  CodeMirror.defineMIME("application/x-slim", "slim");
 | 
						|
});
 |