| 1 | CodeMirror.defineMode("htmlmixed", function(config) { |
|---|
| 2 | var htmlMode = CodeMirror.getMode(config, {name: "xml", htmlMode: true}); |
|---|
| 3 | var jsMode = CodeMirror.getMode(config, "javascript"); |
|---|
| 4 | var cssMode = CodeMirror.getMode(config, "css"); |
|---|
| 5 | |
|---|
| 6 | function html(stream, state) { |
|---|
| 7 | var style = htmlMode.token(stream, state.htmlState); |
|---|
| 8 | if (style == "tag" && stream.current() == ">" && state.htmlState.context) { |
|---|
| 9 | if (/^script$/i.test(state.htmlState.context.tagName)) { |
|---|
| 10 | state.token = javascript; |
|---|
| 11 | state.localState = jsMode.startState(htmlMode.indent(state.htmlState, "")); |
|---|
| 12 | } |
|---|
| 13 | else if (/^style$/i.test(state.htmlState.context.tagName)) { |
|---|
| 14 | state.token = css; |
|---|
| 15 | state.localState = cssMode.startState(htmlMode.indent(state.htmlState, "")); |
|---|
| 16 | } |
|---|
| 17 | } |
|---|
| 18 | return style; |
|---|
| 19 | } |
|---|
| 20 | function maybeBackup(stream, pat, style) { |
|---|
| 21 | var cur = stream.current(); |
|---|
| 22 | var close = cur.search(pat), m; |
|---|
| 23 | if (close > -1) stream.backUp(cur.length - close); |
|---|
| 24 | else if (m = cur.match(/<\/?$/)) { |
|---|
| 25 | stream.backUp(cur[0].length); |
|---|
| 26 | if (!stream.match(pat, false)) stream.match(cur[0]); |
|---|
| 27 | } |
|---|
| 28 | return style; |
|---|
| 29 | } |
|---|
| 30 | function javascript(stream, state) { |
|---|
| 31 | if (stream.match(/^<\/\s*script\s*>/i, false)) { |
|---|
| 32 | state.token = html; |
|---|
| 33 | state.localState = null; |
|---|
| 34 | return html(stream, state); |
|---|
| 35 | } |
|---|
| 36 | return maybeBackup(stream, /<\/\s*script\s*>/, |
|---|
| 37 | jsMode.token(stream, state.localState)); |
|---|
| 38 | } |
|---|
| 39 | function css(stream, state) { |
|---|
| 40 | if (stream.match(/^<\/\s*style\s*>/i, false)) { |
|---|
| 41 | state.token = html; |
|---|
| 42 | state.localState = null; |
|---|
| 43 | return html(stream, state); |
|---|
| 44 | } |
|---|
| 45 | return maybeBackup(stream, /<\/\s*style\s*>/, |
|---|
| 46 | cssMode.token(stream, state.localState)); |
|---|
| 47 | } |
|---|
| 48 | |
|---|
| 49 | return { |
|---|
| 50 | startState: function() { |
|---|
| 51 | var state = htmlMode.startState(); |
|---|
| 52 | return {token: html, localState: null, mode: "html", htmlState: state}; |
|---|
| 53 | }, |
|---|
| 54 | |
|---|
| 55 | copyState: function(state) { |
|---|
| 56 | if (state.localState) |
|---|
| 57 | var local = CodeMirror.copyState(state.token == css ? cssMode : jsMode, state.localState); |
|---|
| 58 | return {token: state.token, localState: local, mode: state.mode, |
|---|
| 59 | htmlState: CodeMirror.copyState(htmlMode, state.htmlState)}; |
|---|
| 60 | }, |
|---|
| 61 | |
|---|
| 62 | token: function(stream, state) { |
|---|
| 63 | return state.token(stream, state); |
|---|
| 64 | }, |
|---|
| 65 | |
|---|
| 66 | indent: function(state, textAfter) { |
|---|
| 67 | if (state.token == html || /^\s*<\//.test(textAfter)) |
|---|
| 68 | return htmlMode.indent(state.htmlState, textAfter); |
|---|
| 69 | else if (state.token == javascript) |
|---|
| 70 | return jsMode.indent(state.localState, textAfter); |
|---|
| 71 | else |
|---|
| 72 | return cssMode.indent(state.localState, textAfter); |
|---|
| 73 | }, |
|---|
| 74 | |
|---|
| 75 | electricChars: "/{}:", |
|---|
| 76 | |
|---|
| 77 | innerMode: function(state) { |
|---|
| 78 | var mode = state.token == html ? htmlMode : state.token == javascript ? jsMode : cssMode; |
|---|
| 79 | return {state: state.localState || state.htmlState, mode: mode}; |
|---|
| 80 | } |
|---|
| 81 | }; |
|---|
| 82 | }, "xml", "javascript", "css"); |
|---|
| 83 | |
|---|
| 84 | CodeMirror.defineMIME("text/html", "htmlmixed"); |
|---|