]>
Commit | Line | Data |
---|---|---|
06a3d686 BB |
1 | /** |
2 | * Tag-closer extension for CodeMirror. | |
3 | * | |
4 | * This extension adds an "autoCloseTags" option that can be set to | |
5 | * either true to get the default behavior, or an object to further | |
6 | * configure its behavior. | |
7 | * | |
8 | * These are supported options: | |
9 | * | |
10 | * `whenClosing` (default true) | |
11 | * Whether to autoclose when the '/' of a closing tag is typed. | |
12 | * `whenOpening` (default true) | |
13 | * Whether to autoclose the tag when the final '>' of an opening | |
14 | * tag is typed. | |
15 | * `dontCloseTags` (default is empty tags for HTML, none for XML) | |
16 | * An array of tag names that should not be autoclosed. | |
17 | * `indentTags` (default is block tags for HTML, none for XML) | |
18 | * An array of tag names that should, when opened, cause a | |
19 | * blank line to be added inside the tag, and the blank line and | |
20 | * closing line to be indented. | |
21 | * | |
22 | * See demos/closetag.html for a usage example. | |
23 | */ | |
24 | ||
25 | (function() { | |
26 | CodeMirror.defineOption("autoCloseTags", false, function(cm, val, old) { | |
27 | if (val && (old == CodeMirror.Init || !old)) { | |
28 | var map = {name: "autoCloseTags"}; | |
29 | if (typeof val != "object" || val.whenClosing) | |
30 | map["'/'"] = function(cm) { autoCloseTag(cm, '/'); }; | |
31 | if (typeof val != "object" || val.whenOpening) | |
32 | map["'>'"] = function(cm) { autoCloseTag(cm, '>'); }; | |
33 | cm.addKeyMap(map); | |
34 | } else if (!val && (old != CodeMirror.Init && old)) { | |
35 | cm.removeKeyMap("autoCloseTags"); | |
36 | } | |
37 | }); | |
38 | ||
39 | var htmlDontClose = ["area", "base", "br", "col", "command", "embed", "hr", "img", "input", "keygen", "link", "meta", "param", | |
40 | "source", "track", "wbr"]; | |
41 | var htmlIndent = ["applet", "blockquote", "body", "button", "div", "dl", "fieldset", "form", "frameset", "h1", "h2", "h3", "h4", | |
42 | "h5", "h6", "head", "html", "iframe", "layer", "legend", "object", "ol", "p", "select", "table", "ul"]; | |
43 | ||
44 | function autoCloseTag(cm, ch) { | |
45 | var pos = cm.getCursor(), tok = cm.getTokenAt(pos); | |
46 | var inner = CodeMirror.innerMode(cm.getMode(), tok.state), state = inner.state; | |
47 | if (inner.mode.name != "xml") throw CodeMirror.Pass; | |
48 | ||
49 | var opt = cm.getOption("autoCloseTags"), html = inner.mode.configuration == "html"; | |
50 | var dontCloseTags = (typeof opt == "object" && opt.dontCloseTags) || (html && htmlDontClose); | |
51 | var indentTags = (typeof opt == "object" && opt.indentTags) || (html && htmlIndent); | |
52 | ||
53 | if (ch == ">" && state.tagName) { | |
54 | var tagName = state.tagName; | |
55 | if (tok.end > pos.ch) tagName = tagName.slice(0, tagName.length - tok.end + pos.ch); | |
56 | var lowerTagName = tagName.toLowerCase(); | |
57 | // Don't process the '>' at the end of an end-tag or self-closing tag | |
58 | if (tok.type == "tag" && state.type == "closeTag" || | |
59 | /\/\s*$/.test(tok.string) || | |
60 | dontCloseTags && indexOf(dontCloseTags, lowerTagName) > -1) | |
61 | throw CodeMirror.Pass; | |
62 | ||
63 | var doIndent = indentTags && indexOf(indentTags, lowerTagName) > -1; | |
64 | cm.replaceSelection(">" + (doIndent ? "\n\n" : "") + "</" + tagName + ">", | |
65 | doIndent ? {line: pos.line + 1, ch: 0} : {line: pos.line, ch: pos.ch + 1}); | |
66 | if (doIndent) { | |
67 | cm.indentLine(pos.line + 1); | |
68 | cm.indentLine(pos.line + 2); | |
69 | } | |
70 | return; | |
71 | } else if (ch == "/" && tok.type == "tag" && tok.string == "<") { | |
72 | var tagName = state.context && state.context.tagName; | |
73 | if (tagName) cm.replaceSelection("/" + tagName + ">", "end"); | |
74 | return; | |
75 | } | |
76 | throw CodeMirror.Pass; | |
77 | } | |
78 | ||
79 | function indexOf(collection, elt) { | |
80 | if (collection.indexOf) return collection.indexOf(elt); | |
81 | for (var i = 0, e = collection.length; i < e; ++i) | |
82 | if (collection[i] == elt) return i; | |
83 | return -1; | |
84 | } | |
85 | })(); |