1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/browser/devtools/sourceeditor/codemirror/fold/xml-fold.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,173 @@ 1.4 +(function() { 1.5 + "use strict"; 1.6 + 1.7 + var Pos = CodeMirror.Pos; 1.8 + function cmp(a, b) { return a.line - b.line || a.ch - b.ch; } 1.9 + 1.10 + 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"; 1.11 + var nameChar = nameStartChar + "\-\:\.0-9\\u00B7\\u0300-\\u036F\\u203F-\\u2040"; 1.12 + var xmlTagStart = new RegExp("<(/?)([" + nameStartChar + "][" + nameChar + "]*)", "g"); 1.13 + 1.14 + function Iter(cm, line, ch, range) { 1.15 + this.line = line; this.ch = ch; 1.16 + this.cm = cm; this.text = cm.getLine(line); 1.17 + this.min = range ? range.from : cm.firstLine(); 1.18 + this.max = range ? range.to - 1 : cm.lastLine(); 1.19 + } 1.20 + 1.21 + function tagAt(iter, ch) { 1.22 + var type = iter.cm.getTokenTypeAt(Pos(iter.line, ch)); 1.23 + return type && /\btag\b/.test(type); 1.24 + } 1.25 + 1.26 + function nextLine(iter) { 1.27 + if (iter.line >= iter.max) return; 1.28 + iter.ch = 0; 1.29 + iter.text = iter.cm.getLine(++iter.line); 1.30 + return true; 1.31 + } 1.32 + function prevLine(iter) { 1.33 + if (iter.line <= iter.min) return; 1.34 + iter.text = iter.cm.getLine(--iter.line); 1.35 + iter.ch = iter.text.length; 1.36 + return true; 1.37 + } 1.38 + 1.39 + function toTagEnd(iter) { 1.40 + for (;;) { 1.41 + var gt = iter.text.indexOf(">", iter.ch); 1.42 + if (gt == -1) { if (nextLine(iter)) continue; else return; } 1.43 + if (!tagAt(iter, gt + 1)) { iter.ch = gt + 1; continue; } 1.44 + var lastSlash = iter.text.lastIndexOf("/", gt); 1.45 + var selfClose = lastSlash > -1 && !/\S/.test(iter.text.slice(lastSlash + 1, gt)); 1.46 + iter.ch = gt + 1; 1.47 + return selfClose ? "selfClose" : "regular"; 1.48 + } 1.49 + } 1.50 + function toTagStart(iter) { 1.51 + for (;;) { 1.52 + var lt = iter.ch ? iter.text.lastIndexOf("<", iter.ch - 1) : -1; 1.53 + if (lt == -1) { if (prevLine(iter)) continue; else return; } 1.54 + if (!tagAt(iter, lt + 1)) { iter.ch = lt; continue; } 1.55 + xmlTagStart.lastIndex = lt; 1.56 + iter.ch = lt; 1.57 + var match = xmlTagStart.exec(iter.text); 1.58 + if (match && match.index == lt) return match; 1.59 + } 1.60 + } 1.61 + 1.62 + function toNextTag(iter) { 1.63 + for (;;) { 1.64 + xmlTagStart.lastIndex = iter.ch; 1.65 + var found = xmlTagStart.exec(iter.text); 1.66 + if (!found) { if (nextLine(iter)) continue; else return; } 1.67 + if (!tagAt(iter, found.index + 1)) { iter.ch = found.index + 1; continue; } 1.68 + iter.ch = found.index + found[0].length; 1.69 + return found; 1.70 + } 1.71 + } 1.72 + function toPrevTag(iter) { 1.73 + for (;;) { 1.74 + var gt = iter.ch ? iter.text.lastIndexOf(">", iter.ch - 1) : -1; 1.75 + if (gt == -1) { if (prevLine(iter)) continue; else return; } 1.76 + if (!tagAt(iter, gt + 1)) { iter.ch = gt; continue; } 1.77 + var lastSlash = iter.text.lastIndexOf("/", gt); 1.78 + var selfClose = lastSlash > -1 && !/\S/.test(iter.text.slice(lastSlash + 1, gt)); 1.79 + iter.ch = gt + 1; 1.80 + return selfClose ? "selfClose" : "regular"; 1.81 + } 1.82 + } 1.83 + 1.84 + function findMatchingClose(iter, tag) { 1.85 + var stack = []; 1.86 + for (;;) { 1.87 + var next = toNextTag(iter), end, startLine = iter.line, startCh = iter.ch - (next ? next[0].length : 0); 1.88 + if (!next || !(end = toTagEnd(iter))) return; 1.89 + if (end == "selfClose") continue; 1.90 + if (next[1]) { // closing tag 1.91 + for (var i = stack.length - 1; i >= 0; --i) if (stack[i] == next[2]) { 1.92 + stack.length = i; 1.93 + break; 1.94 + } 1.95 + if (i < 0 && (!tag || tag == next[2])) return { 1.96 + tag: next[2], 1.97 + from: Pos(startLine, startCh), 1.98 + to: Pos(iter.line, iter.ch) 1.99 + }; 1.100 + } else { // opening tag 1.101 + stack.push(next[2]); 1.102 + } 1.103 + } 1.104 + } 1.105 + function findMatchingOpen(iter, tag) { 1.106 + var stack = []; 1.107 + for (;;) { 1.108 + var prev = toPrevTag(iter); 1.109 + if (!prev) return; 1.110 + if (prev == "selfClose") { toTagStart(iter); continue; } 1.111 + var endLine = iter.line, endCh = iter.ch; 1.112 + var start = toTagStart(iter); 1.113 + if (!start) return; 1.114 + if (start[1]) { // closing tag 1.115 + stack.push(start[2]); 1.116 + } else { // opening tag 1.117 + for (var i = stack.length - 1; i >= 0; --i) if (stack[i] == start[2]) { 1.118 + stack.length = i; 1.119 + break; 1.120 + } 1.121 + if (i < 0 && (!tag || tag == start[2])) return { 1.122 + tag: start[2], 1.123 + from: Pos(iter.line, iter.ch), 1.124 + to: Pos(endLine, endCh) 1.125 + }; 1.126 + } 1.127 + } 1.128 + } 1.129 + 1.130 + CodeMirror.registerHelper("fold", "xml", function(cm, start) { 1.131 + var iter = new Iter(cm, start.line, 0); 1.132 + for (;;) { 1.133 + var openTag = toNextTag(iter), end; 1.134 + if (!openTag || iter.line != start.line || !(end = toTagEnd(iter))) return; 1.135 + if (!openTag[1] && end != "selfClose") { 1.136 + var start = Pos(iter.line, iter.ch); 1.137 + var close = findMatchingClose(iter, openTag[2]); 1.138 + return close && {from: start, to: close.from}; 1.139 + } 1.140 + } 1.141 + }); 1.142 + CodeMirror.tagRangeFinder = CodeMirror.fold.xml; // deprecated 1.143 + 1.144 + CodeMirror.findMatchingTag = function(cm, pos, range) { 1.145 + var iter = new Iter(cm, pos.line, pos.ch, range); 1.146 + if (iter.text.indexOf(">") == -1 && iter.text.indexOf("<") == -1) return; 1.147 + var end = toTagEnd(iter), to = end && Pos(iter.line, iter.ch); 1.148 + var start = end && toTagStart(iter); 1.149 + if (!end || end == "selfClose" || !start || cmp(iter, pos) > 0) return; 1.150 + var here = {from: Pos(iter.line, iter.ch), to: to, tag: start[2]}; 1.151 + 1.152 + if (start[1]) { // closing tag 1.153 + return {open: findMatchingOpen(iter, start[2]), close: here, at: "close"}; 1.154 + } else { // opening tag 1.155 + iter = new Iter(cm, to.line, to.ch, range); 1.156 + return {open: here, close: findMatchingClose(iter, start[2]), at: "open"}; 1.157 + } 1.158 + }; 1.159 + 1.160 + CodeMirror.findEnclosingTag = function(cm, pos, range) { 1.161 + var iter = new Iter(cm, pos.line, pos.ch, range); 1.162 + for (;;) { 1.163 + var open = findMatchingOpen(iter); 1.164 + if (!open) break; 1.165 + var forward = new Iter(cm, pos.line, pos.ch, range); 1.166 + var close = findMatchingClose(forward, open.tag); 1.167 + if (close) return {open: open, close: close}; 1.168 + } 1.169 + }; 1.170 + 1.171 + // Used by addon/edit/closetag.js 1.172 + CodeMirror.scanForClosingTag = function(cm, pos, name, end) { 1.173 + var iter = new Iter(cm, pos.line, pos.ch, end ? {from: 0, to: end} : null); 1.174 + return !!findMatchingClose(iter, name); 1.175 + }; 1.176 +})();