4 CodeMirror.defineOption("foldGutter", false, function(cm, val, old) {
5 if (old && old != CodeMirror.Init) {
6 cm.clearGutter(cm.state.foldGutter.options.gutter);
7 cm.state.foldGutter = null;
8 cm.off("gutterClick", onGutterClick);
9 cm.off("change", onChange);
10 cm.off("viewportChange", onViewportChange);
11 cm.off("fold", onFold);
12 cm.off("unfold", onFold);
15 cm.state.foldGutter = new State(parseOptions(val));
17 cm.on("gutterClick", onGutterClick);
18 cm.on("change", onChange);
19 cm.on("viewportChange", onViewportChange);
20 cm.on("fold", onFold);
21 cm.on("unfold", onFold);
25 var Pos = CodeMirror.Pos;
27 function State(options) {
28 this.options = options;
29 this.from = this.to = 0;
32 function parseOptions(opts) {
33 if (opts === true) opts = {};
34 if (opts.gutter == null) opts.gutter = "CodeMirror-foldgutter";
35 if (opts.indicatorOpen == null) opts.indicatorOpen = "CodeMirror-foldgutter-open";
36 if (opts.indicatorFolded == null) opts.indicatorFolded = "CodeMirror-foldgutter-folded";
40 function isFolded(cm, line) {
41 var marks = cm.findMarksAt(Pos(line));
42 for (var i = 0; i < marks.length; ++i)
43 if (marks[i].__isFold && marks[i].find().from.line == line) return true;
46 function marker(spec) {
47 if (typeof spec == "string") {
48 var elt = document.createElement("div");
52 return spec.cloneNode(true);
56 function updateFoldInfo(cm, from, to) {
57 var opts = cm.state.foldGutter.options, cur = from;
58 cm.eachLine(from, to, function(line) {
60 if (isFolded(cm, cur)) {
61 mark = marker(opts.indicatorFolded);
63 var pos = Pos(cur, 0), func = opts.rangeFinder || cm.getHelper(pos, "fold");
64 var range = func && func(cm, pos);
65 if (range && range.from.line + 1 < range.to.line)
66 mark = marker(opts.indicatorOpen);
68 cm.setGutterMarker(line, opts.gutter, mark);
73 function updateInViewport(cm) {
74 var vp = cm.getViewport(), state = cm.state.foldGutter;
76 cm.operation(function() {
77 updateFoldInfo(cm, vp.from, vp.to);
79 state.from = vp.from; state.to = vp.to;
82 function onGutterClick(cm, line, gutter) {
83 var opts = cm.state.foldGutter.options;
84 if (gutter != opts.gutter) return;
85 cm.foldCode(Pos(line, 0), opts.rangeFinder);
88 function onChange(cm) {
89 var state = cm.state.foldGutter;
90 state.from = state.to = 0;
91 clearTimeout(state.changeUpdate);
92 state.changeUpdate = setTimeout(function() { updateInViewport(cm); }, 600);
95 function onViewportChange(cm) {
96 var state = cm.state.foldGutter;
97 clearTimeout(state.changeUpdate);
98 state.changeUpdate = setTimeout(function() {
99 var vp = cm.getViewport();
100 if (state.from == state.to || vp.from - state.to > 20 || state.from - vp.to > 20) {
101 updateInViewport(cm);
103 cm.operation(function() {
104 if (vp.from < state.from) {
105 updateFoldInfo(cm, vp.from, state.from);
106 state.from = vp.from;
108 if (vp.to > state.to) {
109 updateFoldInfo(cm, state.to, vp.to);
117 function onFold(cm, from) {
118 var state = cm.state.foldGutter, line = from.line;
119 if (line >= state.from && line < state.to)
120 updateFoldInfo(cm, line, line + 1);