// CodeMirror, copyright (c) by Marijn Haverbeke and others // Distributed under an MIT license: https://codemirror.net/LICENSE ;(function (mod) { if (typeof exports == 'object' && typeof module == 'object') // CommonJS mod(require('../../lib/codemirror')) else if (typeof define == 'function' && define.amd) // AMD define(['../../lib/codemirror'], mod) // Plain browser env else mod(CodeMirror) })(function (CodeMirror) { 'use strict' var htmlConfig = { autoSelfClosers: { area: true, base: true, br: true, col: true, command: true, embed: true, frame: true, hr: true, img: true, input: true, keygen: true, link: true, meta: true, param: true, source: true, track: true, wbr: true, menuitem: true, }, implicitlyClosed: { dd: true, li: true, optgroup: true, option: true, p: true, rp: true, rt: true, tbody: true, td: true, tfoot: true, th: true, tr: true }, contextGrabbers: { dd: { dd: true, dt: true }, dt: { dd: true, dt: true }, li: { li: true }, option: { option: true, optgroup: true }, optgroup: { optgroup: true }, p: { address: true, article: true, aside: true, blockquote: true, dir: true, div: true, dl: true, fieldset: true, footer: true, form: true, h1: true, h2: true, h3: true, h4: true, h5: true, h6: true, header: true, hgroup: true, hr: true, menu: true, nav: true, ol: true, p: true, pre: true, section: true, table: true, ul: true, }, rp: { rp: true, rt: true }, rt: { rp: true, rt: true }, tbody: { tbody: true, tfoot: true }, td: { td: true, th: true }, tfoot: { tbody: true }, th: { td: true, th: true }, thead: { tbody: true, tfoot: true }, tr: { tr: true }, }, doNotIndent: { pre: true }, allowUnquoted: true, allowMissing: true, caseFold: true, } var xmlConfig = { autoSelfClosers: {}, implicitlyClosed: {}, contextGrabbers: {}, doNotIndent: {}, allowUnquoted: false, allowMissing: false, allowMissingTagName: false, caseFold: false, } CodeMirror.defineMode('xml', function (editorConf, config_) { var indentUnit = editorConf.indentUnit var config = {} var defaults = config_.htmlMode ? htmlConfig : xmlConfig for (var prop in defaults) config[prop] = defaults[prop] for (var prop in config_) config[prop] = config_[prop] // Return variables for tokenizers var type, setStyle function inText(stream, state) { function chain(parser) { state.tokenize = parser return parser(stream, state) } var ch = stream.next() if (ch == '<') { if (stream.eat('!')) { if (stream.eat('[')) { if (stream.match('CDATA[')) return chain(inBlock('atom', ']]>')) else return null } else if (stream.match('--')) { return chain(inBlock('comment', '-->')) } else if (stream.match('DOCTYPE', true, true)) { stream.eatWhile(/[\w\._\-]/) return chain(doctype(1)) } else { return null } } else if (stream.eat('?')) { stream.eatWhile(/[\w\._\-]/) state.tokenize = inBlock('meta', '?>') return 'meta' } else { type = stream.eat('/') ? 'closeTag' : 'openTag' state.tokenize = inTag return 'tag bracket' } } else if (ch == '&') { var ok if (stream.eat('#')) { if (stream.eat('x')) { ok = stream.eatWhile(/[a-fA-F\d]/) && stream.eat(';') } else { ok = stream.eatWhile(/[\d]/) && stream.eat(';') } } else { ok = stream.eatWhile(/[\w\.\-:]/) && stream.eat(';') } return ok ? 'atom' : 'error' } else { stream.eatWhile(/[^&<]/) return null } } inText.isInText = true function inTag(stream, state) { var ch = stream.next() if (ch == '>' || (ch == '/' && stream.eat('>'))) { state.tokenize = inText type = ch == '>' ? 'endTag' : 'selfcloseTag' return 'tag bracket' } else if (ch == '=') { type = 'equals' return null } else if (ch == '<') { state.tokenize = inText state.state = baseState state.tagName = state.tagStart = null var next = state.tokenize(stream, state) return next ? next + ' tag error' : 'tag error' } else if (/[\'\"]/.test(ch)) { state.tokenize = inAttribute(ch) state.stringStartCol = stream.column() return state.tokenize(stream, state) } else { stream.match(/^[^\s\u00a0=<>\"\']*[^\s\u00a0=<>\"\'\/]/) return 'word' } } function inAttribute(quote) { var closure = function (stream, state) { while (!stream.eol()) { if (stream.next() == quote) { state.tokenize = inTag break } } return 'string' } closure.isInAttribute = true return closure } function inBlock(style, terminator) { return function (stream, state) { while (!stream.eol()) { if (stream.match(terminator)) { state.tokenize = inText break } stream.next() } return style } } function doctype(depth) { return function (stream, state) { var ch while ((ch = stream.next()) != null) { if (ch == '<') { state.tokenize = doctype(depth + 1) return state.tokenize(stream, state) } else if (ch == '>') { if (depth == 1) { state.tokenize = inText break } else { state.tokenize = doctype(depth - 1) return state.tokenize(stream, state) } } } return 'meta' } } function lower(tagName) { return tagName && tagName.toLowerCase() } function Context(state, tagName, startOfLine) { this.prev = state.context this.tagName = tagName || '' this.indent = state.indented this.startOfLine = startOfLine if (config.doNotIndent.hasOwnProperty(tagName) || (state.context && state.context.noIndent)) this.noIndent = true } function popContext(state) { if (state.context) state.context = state.context.prev } function maybePopContext(state, nextTagName) { var parentTagName while (true) { if (!state.context) { return } parentTagName = state.context.tagName if (!config.contextGrabbers.hasOwnProperty(lower(parentTagName)) || !config.contextGrabbers[lower(parentTagName)].hasOwnProperty(lower(nextTagName))) { return } popContext(state) } } function baseState(type, stream, state) { if (type == 'openTag') { state.tagStart = stream.column() return tagNameState } else if (type == 'closeTag') { return closeTagNameState } else { return baseState } } function tagNameState(type, stream, state) { if (type == 'word') { state.tagName = stream.current() setStyle = 'tag' return attrState } else if (config.allowMissingTagName && type == 'endTag') { setStyle = 'tag bracket' return attrState(type, stream, state) } else { setStyle = 'error' return tagNameState } } function closeTagNameState(type, stream, state) { if (type == 'word') { var tagName = stream.current() if (state.context && state.context.tagName != tagName && config.implicitlyClosed.hasOwnProperty(lower(state.context.tagName))) popContext(state) if ((state.context && state.context.tagName == tagName) || config.matchClosing === false) { setStyle = 'tag' return closeState } else { setStyle = 'tag error' return closeStateErr } } else if (config.allowMissingTagName && type == 'endTag') { setStyle = 'tag bracket' return closeState(type, stream, state) } else { setStyle = 'error' return closeStateErr } } function closeState(type, _stream, state) { if (type != 'endTag') { setStyle = 'error' return closeState } popContext(state) return baseState } function closeStateErr(type, stream, state) { setStyle = 'error' return closeState(type, stream, state) } function attrState(type, _stream, state) { if (type == 'word') { setStyle = 'attribute' return attrEqState } else if (type == 'endTag' || type == 'selfcloseTag') { var tagName = state.tagName, tagStart = state.tagStart state.tagName = state.tagStart = null if (type == 'selfcloseTag' || config.autoSelfClosers.hasOwnProperty(lower(tagName))) { maybePopContext(state, tagName) } else { maybePopContext(state, tagName) state.context = new Context(state, tagName, tagStart == state.indented) } return baseState } setStyle = 'error' return attrState } function attrEqState(type, stream, state) { if (type == 'equals') return attrValueState if (!config.allowMissing) setStyle = 'error' return attrState(type, stream, state) } function attrValueState(type, stream, state) { if (type == 'string') return attrContinuedState if (type == 'word' && config.allowUnquoted) { setStyle = 'string' return attrState } setStyle = 'error' return attrState(type, stream, state) } function attrContinuedState(type, stream, state) { if (type == 'string') return attrContinuedState return attrState(type, stream, state) } return { startState: function (baseIndent) { var state = { tokenize: inText, state: baseState, indented: baseIndent || 0, tagName: null, tagStart: null, context: null } if (baseIndent != null) state.baseIndent = baseIndent return state }, token: function (stream, state) { if (!state.tagName && stream.sol()) state.indented = stream.indentation() if (stream.eatSpace()) return null type = null var style = state.tokenize(stream, state) if ((style || type) && style != 'comment') { setStyle = null state.state = state.state(type || style, stream, state) if (setStyle) style = setStyle == 'error' ? style + ' error' : setStyle } return style }, indent: function (state, textAfter, fullLine) { var context = state.context // Indent multi-line strings (e.g. css). if (state.tokenize.isInAttribute) { if (state.tagStart == state.indented) return state.stringStartCol + 1 else return state.indented + indentUnit } if (context && context.noIndent) return CodeMirror.Pass if (state.tokenize != inTag && state.tokenize != inText) return fullLine ? fullLine.match(/^(\s*)/)[0].length : 0 // Indent the starts of attribute names. if (state.tagName) { if (config.multilineTagIndentPastTag !== false) return state.tagStart + state.tagName.length + 2 else return state.tagStart + indentUnit * (config.multilineTagIndentFactor || 1) } if (config.alignCDATA && /$/, blockCommentStart: '', configuration: config.htmlMode ? 'html' : 'xml', helperType: config.htmlMode ? 'html' : 'xml', skipAttribute: function (state) { if (state.state == attrValueState) state.state = attrState }, xmlCurrentTag: function (state) { return state.tagName ? { name: state.tagName, close: state.type == 'closeTag' } : null }, xmlCurrentContext: function (state) { var context = [] for (var cx = state.context; cx; cx = cx.prev) context.push(cx.tagName) return context.reverse() }, } }) CodeMirror.defineMIME('text/xml', 'xml') CodeMirror.defineMIME('application/xml', 'xml') if (!CodeMirror.mimeModes.hasOwnProperty('text/html')) CodeMirror.defineMIME('text/html', { name: 'xml', htmlMode: true }) })