1 |
|
2 |
|
3 |
|
4 | (function(mod) {
|
5 | if (typeof exports == "object" && typeof module == "object")
|
6 | mod(require("../../lib/codemirror"), require("../xml/xml"), require("../javascript/javascript"), require("../css/css"));
|
7 | else if (typeof define == "function" && define.amd)
|
8 | define(["../../lib/codemirror", "../xml/xml", "../javascript/javascript", "../css/css"], mod);
|
9 | else
|
10 | mod(CodeMirror);
|
11 | })(function(CodeMirror) {
|
12 | "use strict";
|
13 |
|
14 | var defaultTags = {
|
15 | script: [
|
16 | ["lang", /(javascript|babel)/i, "javascript"],
|
17 | ["type", /^(?:text|application)\/(?:x-)?(?:java|ecma)script$|^module$|^$/i, "javascript"],
|
18 | ["type", /./, "text/plain"],
|
19 | [null, null, "javascript"]
|
20 | ],
|
21 | style: [
|
22 | ["lang", /^css$/i, "css"],
|
23 | ["type", /^(text\/)?(x-)?(stylesheet|css)$/i, "css"],
|
24 | ["type", /./, "text/plain"],
|
25 | [null, null, "css"]
|
26 | ]
|
27 | };
|
28 |
|
29 | function maybeBackup(stream, pat, style) {
|
30 | var cur = stream.current(), close = cur.search(pat);
|
31 | if (close > -1) {
|
32 | stream.backUp(cur.length - close);
|
33 | } else if (cur.match(/<\/?$/)) {
|
34 | stream.backUp(cur.length);
|
35 | if (!stream.match(pat, false)) stream.match(cur);
|
36 | }
|
37 | return style;
|
38 | }
|
39 |
|
40 | var attrRegexpCache = {};
|
41 | function getAttrRegexp(attr) {
|
42 | var regexp = attrRegexpCache[attr];
|
43 | if (regexp) return regexp;
|
44 | return attrRegexpCache[attr] = new RegExp("\\s+" + attr + "\\s*=\\s*('|\")?([^'\"]+)('|\")?\\s*");
|
45 | }
|
46 |
|
47 | function getAttrValue(text, attr) {
|
48 | var match = text.match(getAttrRegexp(attr))
|
49 | return match ? /^\s*(.*?)\s*$/.exec(match[2])[1] : ""
|
50 | }
|
51 |
|
52 | function getTagRegexp(tagName, anchored) {
|
53 | return new RegExp((anchored ? "^" : "") + "<\/\s*" + tagName + "\s*>", "i");
|
54 | }
|
55 |
|
56 | function addTags(from, to) {
|
57 | for (var tag in from) {
|
58 | var dest = to[tag] || (to[tag] = []);
|
59 | var source = from[tag];
|
60 | for (var i = source.length - 1; i >= 0; i--)
|
61 | dest.unshift(source[i])
|
62 | }
|
63 | }
|
64 |
|
65 | function findMatchingMode(tagInfo, tagText) {
|
66 | for (var i = 0; i < tagInfo.length; i++) {
|
67 | var spec = tagInfo[i];
|
68 | if (!spec[0] || spec[1].test(getAttrValue(tagText, spec[0]))) return spec[2];
|
69 | }
|
70 | }
|
71 |
|
72 | CodeMirror.defineMode("htmlmixed", function (config, parserConfig) {
|
73 | var htmlMode = CodeMirror.getMode(config, {
|
74 | name: "xml",
|
75 | htmlMode: true,
|
76 | multilineTagIndentFactor: parserConfig.multilineTagIndentFactor,
|
77 | multilineTagIndentPastTag: parserConfig.multilineTagIndentPastTag
|
78 | });
|
79 |
|
80 | var tags = {};
|
81 | var configTags = parserConfig && parserConfig.tags, configScript = parserConfig && parserConfig.scriptTypes;
|
82 | addTags(defaultTags, tags);
|
83 | if (configTags) addTags(configTags, tags);
|
84 | if (configScript) for (var i = configScript.length - 1; i >= 0; i--)
|
85 | tags.script.unshift(["type", configScript[i].matches, configScript[i].mode])
|
86 |
|
87 | function html(stream, state) {
|
88 | var style = htmlMode.token(stream, state.htmlState), tag = /\btag\b/.test(style), tagName
|
89 | if (tag && !/[<>\s\/]/.test(stream.current()) &&
|
90 | (tagName = state.htmlState.tagName && state.htmlState.tagName.toLowerCase()) &&
|
91 | tags.hasOwnProperty(tagName)) {
|
92 | state.inTag = tagName + " "
|
93 | } else if (state.inTag && tag && />$/.test(stream.current())) {
|
94 | var inTag = /^([\S]+) (.*)/.exec(state.inTag)
|
95 | state.inTag = null
|
96 | var modeSpec = stream.current() == ">" && findMatchingMode(tags[inTag[1]], inTag[2])
|
97 | var mode = CodeMirror.getMode(config, modeSpec)
|
98 | var endTagA = getTagRegexp(inTag[1], true), endTag = getTagRegexp(inTag[1], false);
|
99 | state.token = function (stream, state) {
|
100 | if (stream.match(endTagA, false)) {
|
101 | state.token = html;
|
102 | state.localState = state.localMode = null;
|
103 | return null;
|
104 | }
|
105 | return maybeBackup(stream, endTag, state.localMode.token(stream, state.localState));
|
106 | };
|
107 | state.localMode = mode;
|
108 | state.localState = CodeMirror.startState(mode, htmlMode.indent(state.htmlState, "", ""));
|
109 | } else if (state.inTag) {
|
110 | state.inTag += stream.current()
|
111 | if (stream.eol()) state.inTag += " "
|
112 | }
|
113 | return style;
|
114 | };
|
115 |
|
116 | return {
|
117 | startState: function () {
|
118 | var state = CodeMirror.startState(htmlMode);
|
119 | return {token: html, inTag: null, localMode: null, localState: null, htmlState: state};
|
120 | },
|
121 |
|
122 | copyState: function (state) {
|
123 | var local;
|
124 | if (state.localState) {
|
125 | local = CodeMirror.copyState(state.localMode, state.localState);
|
126 | }
|
127 | return {token: state.token, inTag: state.inTag,
|
128 | localMode: state.localMode, localState: local,
|
129 | htmlState: CodeMirror.copyState(htmlMode, state.htmlState)};
|
130 | },
|
131 |
|
132 | token: function (stream, state) {
|
133 | return state.token(stream, state);
|
134 | },
|
135 |
|
136 | indent: function (state, textAfter, line) {
|
137 | if (!state.localMode || /^\s*<\//.test(textAfter))
|
138 | return htmlMode.indent(state.htmlState, textAfter, line);
|
139 | else if (state.localMode.indent)
|
140 | return state.localMode.indent(state.localState, textAfter, line);
|
141 | else
|
142 | return CodeMirror.Pass;
|
143 | },
|
144 |
|
145 | innerMode: function (state) {
|
146 | return {state: state.localState || state.htmlState, mode: state.localMode || htmlMode};
|
147 | }
|
148 | };
|
149 | }, "xml", "javascript", "css");
|
150 |
|
151 | CodeMirror.defineMIME("text/html", "htmlmixed");
|
152 | });
|