2 if (typeof exports
== "object" && typeof module
== "object") // CommonJS
3 mod(require("../../lib/codemirror"));
4 else if (typeof define
== "function" && define
.amd
) // AMD
5 define(["../../lib/codemirror"], mod
);
6 else // Plain browser env
8 })(function(CodeMirror
) {
11 var Pos
= CodeMirror
.Pos
;
12 function cmp(a
, b
) { return a
.line
- b
.line
|| a
.ch
- b
.ch
; }
14 var nameStartChar
= "A-Z_a-z\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02FF\\u0370-\\u037D\\u037F-\\u1FFF\\u200C-\\u200D\\u2070-\\u218F\\u2C00-\\u2FEF\\u3001-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFFD";
15 var nameChar
= nameStartChar
+ "\-\:\.0-9\\u00B7\\u0300-\\u036F\\u203F-\\u2040";
16 var xmlTagStart
= new RegExp("<(/?)([" + nameStartChar
+ "][" + nameChar
+ "]*)", "g");
18 function Iter(cm
, line
, ch
, range
) {
19 this.line
= line
; this.ch
= ch
;
20 this.cm
= cm
; this.text
= cm
.getLine(line
);
21 this.min
= range
? range
.from : cm
.firstLine();
22 this.max
= range
? range
.to
- 1 : cm
.lastLine();
25 function tagAt(iter
, ch
) {
26 var type
= iter
.cm
.getTokenTypeAt(Pos(iter
.line
, ch
));
27 return type
&& /\btag\b/.test(type
);
30 function nextLine(iter
) {
31 if (iter
.line
>= iter
.max
) return;
33 iter
.text
= iter
.cm
.getLine(++iter
.line
);
36 function prevLine(iter
) {
37 if (iter
.line
<= iter
.min
) return;
38 iter
.text
= iter
.cm
.getLine(--iter
.line
);
39 iter
.ch
= iter
.text
.length
;
43 function toTagEnd(iter
) {
45 var gt
= iter
.text
.indexOf(">", iter
.ch
);
46 if (gt
== -1) { if (nextLine(iter
)) continue; else return; }
47 if (!tagAt(iter
, gt
+ 1)) { iter
.ch
= gt
+ 1; continue; }
48 var lastSlash
= iter
.text
.lastIndexOf("/", gt
);
49 var selfClose
= lastSlash
> -1 && !/\S/.test(iter
.text
.slice(lastSlash
+ 1, gt
));
51 return selfClose
? "selfClose" : "regular";
54 function toTagStart(iter
) {
56 var lt
= iter
.ch
? iter
.text
.lastIndexOf("<", iter
.ch
- 1) : -1;
57 if (lt
== -1) { if (prevLine(iter
)) continue; else return; }
58 if (!tagAt(iter
, lt
+ 1)) { iter
.ch
= lt
; continue; }
59 xmlTagStart
.lastIndex
= lt
;
61 var match
= xmlTagStart
.exec(iter
.text
);
62 if (match
&& match
.index
== lt
) return match
;
66 function toNextTag(iter
) {
68 xmlTagStart
.lastIndex
= iter
.ch
;
69 var found
= xmlTagStart
.exec(iter
.text
);
70 if (!found
) { if (nextLine(iter
)) continue; else return; }
71 if (!tagAt(iter
, found
.index
+ 1)) { iter
.ch
= found
.index
+ 1; continue; }
72 iter
.ch
= found
.index
+ found
[0].length
;
76 function toPrevTag(iter
) {
78 var gt
= iter
.ch
? iter
.text
.lastIndexOf(">", iter
.ch
- 1) : -1;
79 if (gt
== -1) { if (prevLine(iter
)) continue; else return; }
80 if (!tagAt(iter
, gt
+ 1)) { iter
.ch
= gt
; continue; }
81 var lastSlash
= iter
.text
.lastIndexOf("/", gt
);
82 var selfClose
= lastSlash
> -1 && !/\S/.test(iter
.text
.slice(lastSlash
+ 1, gt
));
84 return selfClose
? "selfClose" : "regular";
88 function findMatchingClose(iter
, tag
) {
91 var next
= toNextTag(iter
), end
, startLine
= iter
.line
, startCh
= iter
.ch
- (next
? next
[0].length
: 0);
92 if (!next
|| !(end
= toTagEnd(iter
))) return;
93 if (end
== "selfClose") continue;
94 if (next
[1]) { // closing tag
95 for (var i
= stack
.length
- 1; i
>= 0; --i
) if (stack
[i
] == next
[2]) {
99 if (i
< 0 && (!tag
|| tag
== next
[2])) return {
101 from: Pos(startLine
, startCh
),
102 to
: Pos(iter
.line
, iter
.ch
)
104 } else { // opening tag
109 function findMatchingOpen(iter
, tag
) {
112 var prev
= toPrevTag(iter
);
114 if (prev
== "selfClose") { toTagStart(iter
); continue; }
115 var endLine
= iter
.line
, endCh
= iter
.ch
;
116 var start
= toTagStart(iter
);
118 if (start
[1]) { // closing tag
119 stack
.push(start
[2]);
120 } else { // opening tag
121 for (var i
= stack
.length
- 1; i
>= 0; --i
) if (stack
[i
] == start
[2]) {
125 if (i
< 0 && (!tag
|| tag
== start
[2])) return {
127 from: Pos(iter
.line
, iter
.ch
),
128 to
: Pos(endLine
, endCh
)
134 CodeMirror
.registerHelper("fold", "xml", function(cm
, start
) {
135 var iter
= new Iter(cm
, start
.line
, 0);
137 var openTag
= toNextTag(iter
), end
;
138 if (!openTag
|| iter
.line
!= start
.line
|| !(end
= toTagEnd(iter
))) return;
139 if (!openTag
[1] && end
!= "selfClose") {
140 var start
= Pos(iter
.line
, iter
.ch
);
141 var close
= findMatchingClose(iter
, openTag
[2]);
142 return close
&& {from: start
, to
: close
.from};
146 CodeMirror
.findMatchingTag = function(cm
, pos
, range
) {
147 var iter
= new Iter(cm
, pos
.line
, pos
.ch
, range
);
148 if (iter
.text
.indexOf(">") == -1 && iter
.text
.indexOf("<") == -1) return;
149 var end
= toTagEnd(iter
), to
= end
&& Pos(iter
.line
, iter
.ch
);
150 var start
= end
&& toTagStart(iter
);
151 if (!end
|| end
== "selfClose" || !start
|| cmp(iter
, pos
) > 0) return;
152 var here
= {from: Pos(iter
.line
, iter
.ch
), to
: to
, tag
: start
[2]};
154 if (start
[1]) { // closing tag
155 return {open
: findMatchingOpen(iter
, start
[2]), close
: here
, at
: "close"};
156 } else { // opening tag
157 iter
= new Iter(cm
, to
.line
, to
.ch
, range
);
158 return {open
: here
, close
: findMatchingClose(iter
, start
[2]), at
: "open"};
162 CodeMirror
.findEnclosingTag = function(cm
, pos
, range
) {
163 var iter
= new Iter(cm
, pos
.line
, pos
.ch
, range
);
165 var open
= findMatchingOpen(iter
);
167 var forward
= new Iter(cm
, pos
.line
, pos
.ch
, range
);
168 var close
= findMatchingClose(forward
, open
.tag
);
169 if (close
) return {open
: open
, close
: close
};
173 // Used by addon/edit/closetag.js
174 CodeMirror
.scanForClosingTag = function(cm
, pos
, name
, end
) {
175 var iter
= new Iter(cm
, pos
.line
, pos
.ch
, end
? {from: 0, to
: end
} : null);
176 return !!findMatchingClose(iter
, name
);