(function() { "use strict"; var Pos = CodeMirror.Pos; CodeMirror.xmlHint = function(cm, options) { var tags = options && options.schemaInfo; var quote = (options && options.quoteChar) || '"'; if (!tags) return; var cur = cm.getCursor(), token = cm.getTokenAt(cur); var inner = CodeMirror.innerMode(cm.getMode(), token.state); if (inner.mode.name != "xml") return; var result = [], replaceToken = false, prefix; var isTag = token.string.charAt(0) == "<"; if (!inner.state.tagName || isTag) { // Tag completion if (isTag) { prefix = token.string.slice(1); replaceToken = true; } var cx = inner.state.context, curTag = cx && tags[cx.tagName]; var childList = cx ? curTag && curTag.children : tags["!top"]; if (childList) { for (var i = 0; i < childList.length; ++i) if (!prefix || childList[i].indexOf(prefix) == 0) result.push("<" + childList[i]); } else { for (var name in tags) if (tags.hasOwnProperty(name) && name != "!top" && (!prefix || name.indexOf(prefix) == 0)) result.push("<" + name); } if (cx && (!prefix || ("/" + cx.tagName).indexOf(prefix) == 0)) result.push(""); } else { // Attribute completion var curTag = tags[inner.state.tagName], attrs = curTag && curTag.attrs; if (!attrs) return; if (token.type == "string" || token.string == "=") { // A value var before = cm.getRange(Pos(cur.line, Math.max(0, cur.ch - 60)), Pos(cur.line, token.type == "string" ? token.start : token.end)); var atName = before.match(/([^\s\u00a0=<>\"\']+)=$/), atValues; if (!atName || !attrs.hasOwnProperty(atName[1]) || !(atValues = attrs[atName[1]])) return; if (token.type == "string") { prefix = token.string; if (/['"]/.test(token.string.charAt(0))) { quote = token.string.charAt(0); prefix = token.string.slice(1); } replaceToken = true; } for (var i = 0; i < atValues.length; ++i) if (!prefix || atValues[i].indexOf(prefix) == 0) result.push(quote + atValues[i] + quote); } else { // An attribute name if (token.type == "attribute") { prefix = token.string; replaceToken = true; } for (var attr in attrs) if (attrs.hasOwnProperty(attr) && (!prefix || attr.indexOf(prefix) == 0)) result.push(attr); } } return { list: result, from: replaceToken ? Pos(cur.line, token.start) : cur, to: replaceToken ? Pos(cur.line, token.end) : cur }; }; })();