| 
									
										
										
										
											2018-01-21 10:33:32 -05:00
										 |  |  | // CodeMirror, copyright (c) by Marijn Haverbeke and others
 | 
					
						
							| 
									
										
										
										
											2022-10-15 12:22:09 +02:00
										 |  |  | // Distributed under an MIT license: https://codemirror.net/5/LICENSE
 | 
					
						
							| 
									
										
										
										
											2018-01-21 10:33:32 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  | // 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"); | 
					
						
							|  |  |  | }); |