Index: third_party/WebKit/Source/devtools/front_end/cm/htmlmixed.js |
diff --git a/third_party/WebKit/Source/devtools/front_end/cm/htmlmixed.js b/third_party/WebKit/Source/devtools/front_end/cm/htmlmixed.js |
index 250ef8cd243fcc7155488ff79b54f3f18ce7e4a5..d74083ee1a3609f668321f3945c1ad540bb721d9 100644 |
--- a/third_party/WebKit/Source/devtools/front_end/cm/htmlmixed.js |
+++ b/third_party/WebKit/Source/devtools/front_end/cm/htmlmixed.js |
@@ -9,113 +9,144 @@ |
else // Plain browser env |
mod(CodeMirror); |
})(function(CodeMirror) { |
-"use strict"; |
- |
-CodeMirror.defineMode("htmlmixed", function(config, parserConfig) { |
- var htmlMode = CodeMirror.getMode(config, {name: "xml", |
- htmlMode: true, |
- multilineTagIndentFactor: parserConfig.multilineTagIndentFactor, |
- multilineTagIndentPastTag: parserConfig.multilineTagIndentPastTag}); |
- var cssMode = CodeMirror.getMode(config, "css"); |
- |
- var scriptTypes = [], scriptTypesConf = parserConfig && parserConfig.scriptTypes; |
- scriptTypes.push({matches: /^(?:text|application)\/(?:x-)?(?:java|ecma)script$|^$/i, |
- mode: CodeMirror.getMode(config, "javascript")}); |
- if (scriptTypesConf) for (var i = 0; i < scriptTypesConf.length; ++i) { |
- var conf = scriptTypesConf[i]; |
- scriptTypes.push({matches: conf.matches, mode: conf.mode && CodeMirror.getMode(config, conf.mode)}); |
- } |
- scriptTypes.push({matches: /./, |
- mode: CodeMirror.getMode(config, "text/plain")}); |
- |
- function html(stream, state) { |
- var tagName = state.htmlState.tagName; |
- if (tagName) tagName = tagName.toLowerCase(); |
- var style = htmlMode.token(stream, state.htmlState); |
- if (tagName == "script" && /\btag\b/.test(style) && stream.current() == ">") { |
- // Script block: mode to change to depends on type attribute |
- var scriptType = stream.string.slice(Math.max(0, stream.pos - 100), stream.pos).match(/\btype\s*=\s*("[^"]+"|'[^']+'|\S+)[^<]*$/i); |
- scriptType = scriptType ? scriptType[1] : ""; |
- if (scriptType && /[\"\']/.test(scriptType.charAt(0))) scriptType = scriptType.slice(1, scriptType.length - 1); |
- for (var i = 0; i < scriptTypes.length; ++i) { |
- var tp = scriptTypes[i]; |
- if (typeof tp.matches == "string" ? scriptType == tp.matches : tp.matches.test(scriptType)) { |
- if (tp.mode) { |
- state.token = script; |
- state.localMode = tp.mode; |
- state.localState = tp.mode.startState && tp.mode.startState(htmlMode.indent(state.htmlState, "")); |
- } |
- break; |
- } |
- } |
- } else if (tagName == "style" && /\btag\b/.test(style) && stream.current() == ">") { |
- state.token = css; |
- state.localMode = cssMode; |
- state.localState = cssMode.startState(htmlMode.indent(state.htmlState, "")); |
- } |
- return style; |
- } |
+ "use strict"; |
+ |
+ var defaultTags = { |
+ script: [ |
+ ["lang", /(javascript|babel)/i, "javascript"], |
+ ["type", /^(?:text|application)\/(?:x-)?(?:java|ecma)script$|^$/i, "javascript"], |
+ ["type", /./, "text/plain"], |
+ [null, null, "javascript"] |
+ ], |
+ style: [ |
+ ["lang", /^css$/i, "css"], |
+ ["type", /^(text\/)?(x-)?(stylesheet|css)$/i, "css"], |
+ ["type", /./, "text/plain"], |
+ [null, null, "css"] |
+ ] |
+ }; |
+ |
function maybeBackup(stream, pat, style) { |
- var cur = stream.current(); |
- var close = cur.search(pat), m; |
- if (close > -1) stream.backUp(cur.length - close); |
- else if (m = cur.match(/<\/?$/)) { |
+ var cur = stream.current(), close = cur.search(pat); |
+ if (close > -1) { |
+ stream.backUp(cur.length - close); |
+ } else if (cur.match(/<\/?$/)) { |
stream.backUp(cur.length); |
if (!stream.match(pat, false)) stream.match(cur); |
} |
return style; |
} |
- function script(stream, state) { |
- if (stream.match(/^<\/\s*script\s*>/i, false)) { |
- state.token = html; |
- state.localState = state.localMode = null; |
- return html(stream, state); |
- } |
- return maybeBackup(stream, /<\/\s*script\s*>/, |
- state.localMode.token(stream, state.localState)); |
+ |
+ var attrRegexpCache = {}; |
+ function getAttrRegexp(attr) { |
+ var regexp = attrRegexpCache[attr]; |
+ if (regexp) return regexp; |
+ return attrRegexpCache[attr] = new RegExp("\\s+" + attr + "\\s*=\\s*('|\")?([^'\"]+)('|\")?\\s*"); |
+ } |
+ |
+ function getAttrValue(text, attr) { |
+ var match = text.match(getAttrRegexp(attr)) |
+ return match ? match[2] : "" |
+ } |
+ |
+ function getTagRegexp(tagName, anchored) { |
+ return new RegExp((anchored ? "^" : "") + "<\/\s*" + tagName + "\s*>", "i"); |
} |
- function css(stream, state) { |
- if (stream.match(/^<\/\s*style\s*>/i, false)) { |
- state.token = html; |
- state.localState = state.localMode = null; |
- return html(stream, state); |
+ |
+ function addTags(from, to) { |
+ for (var tag in from) { |
+ var dest = to[tag] || (to[tag] = []); |
+ var source = from[tag]; |
+ for (var i = source.length - 1; i >= 0; i--) |
+ dest.unshift(source[i]) |
} |
- return maybeBackup(stream, /<\/\s*style\s*>/, |
- cssMode.token(stream, state.localState)); |
} |
- return { |
- startState: function() { |
- var state = htmlMode.startState(); |
- return {token: html, localMode: null, localState: null, htmlState: state}; |
- }, |
- |
- copyState: function(state) { |
- if (state.localState) |
- var local = CodeMirror.copyState(state.localMode, state.localState); |
- return {token: state.token, localMode: state.localMode, localState: local, |
- htmlState: CodeMirror.copyState(htmlMode, state.htmlState)}; |
- }, |
- |
- token: function(stream, state) { |
- return state.token(stream, state); |
- }, |
- |
- indent: function(state, textAfter) { |
- if (!state.localMode || /^\s*<\//.test(textAfter)) |
- return htmlMode.indent(state.htmlState, textAfter); |
- else if (state.localMode.indent) |
- return state.localMode.indent(state.localState, textAfter); |
- else |
- return CodeMirror.Pass; |
- }, |
- |
- innerMode: function(state) { |
- return {state: state.localState || state.htmlState, mode: state.localMode || htmlMode}; |
+ function findMatchingMode(tagInfo, tagText) { |
+ for (var i = 0; i < tagInfo.length; i++) { |
+ var spec = tagInfo[i]; |
+ if (!spec[0] || spec[1].test(getAttrValue(tagText, spec[0]))) return spec[2]; |
} |
- }; |
-}, "xml", "javascript", "css"); |
+ } |
-CodeMirror.defineMIME("text/html", "htmlmixed"); |
+ CodeMirror.defineMode("htmlmixed", function (config, parserConfig) { |
+ var htmlMode = CodeMirror.getMode(config, { |
+ name: "xml", |
+ htmlMode: true, |
+ multilineTagIndentFactor: parserConfig.multilineTagIndentFactor, |
+ multilineTagIndentPastTag: parserConfig.multilineTagIndentPastTag |
+ }); |
+ |
+ var tags = {}; |
+ var configTags = parserConfig && parserConfig.tags, configScript = parserConfig && parserConfig.scriptTypes; |
+ addTags(defaultTags, tags); |
+ if (configTags) addTags(configTags, tags); |
+ if (configScript) for (var i = configScript.length - 1; i >= 0; i--) |
+ tags.script.unshift(["type", configScript[i].matches, configScript[i].mode]) |
+ |
+ function html(stream, state) { |
+ var style = htmlMode.token(stream, state.htmlState), tag = /\btag\b/.test(style), tagName |
+ if (tag && !/[<>\s\/]/.test(stream.current()) && |
+ (tagName = state.htmlState.tagName && state.htmlState.tagName.toLowerCase()) && |
+ tags.hasOwnProperty(tagName)) { |
+ state.inTag = tagName + " " |
+ } else if (state.inTag && tag && />$/.test(stream.current())) { |
+ var inTag = /^([\S]+) (.*)/.exec(state.inTag) |
+ state.inTag = null |
+ var modeSpec = stream.current() == ">" && findMatchingMode(tags[inTag[1]], inTag[2]) |
+ var mode = CodeMirror.getMode(config, modeSpec) |
+ var endTagA = getTagRegexp(inTag[1], true), endTag = getTagRegexp(inTag[1], false); |
+ state.token = function (stream, state) { |
+ if (stream.match(endTagA, false)) { |
+ state.token = html; |
+ state.localState = state.localMode = null; |
+ return null; |
+ } |
+ return maybeBackup(stream, endTag, state.localMode.token(stream, state.localState)); |
+ }; |
+ state.localMode = mode; |
+ state.localState = CodeMirror.startState(mode, htmlMode.indent(state.htmlState, "")); |
+ } else if (state.inTag) { |
+ state.inTag += stream.current() |
+ if (stream.eol()) state.inTag += " " |
+ } |
+ return style; |
+ }; |
+ |
+ return { |
+ startState: function () { |
+ var state = CodeMirror.startState(htmlMode); |
+ return {token: html, inTag: null, localMode: null, localState: null, htmlState: state}; |
+ }, |
+ |
+ copyState: function (state) { |
+ var local; |
+ if (state.localState) { |
+ local = CodeMirror.copyState(state.localMode, state.localState); |
+ } |
+ return {token: state.token, inTag: state.inTag, |
+ localMode: state.localMode, localState: local, |
+ htmlState: CodeMirror.copyState(htmlMode, state.htmlState)}; |
+ }, |
+ |
+ token: function (stream, state) { |
+ return state.token(stream, state); |
+ }, |
+ |
+ indent: function (state, textAfter) { |
+ if (!state.localMode || /^\s*<\//.test(textAfter)) |
+ return htmlMode.indent(state.htmlState, textAfter); |
+ else if (state.localMode.indent) |
+ return state.localMode.indent(state.localState, textAfter); |
+ else |
+ return CodeMirror.Pass; |
+ }, |
+ |
+ innerMode: function (state) { |
+ return {state: state.localState || state.htmlState, mode: state.localMode || htmlMode}; |
+ } |
+ }; |
+ }, "xml", "javascript", "css"); |
+ CodeMirror.defineMIME("text/html", "htmlmixed"); |
}); |