Merge branch '2.0'
[GitHub/WoltLab/WCF.git] / wcfsetup / install / files / js / 3rdParty / codemirror / addon / fold / foldgutter.js
1 (function(mod) {
2 if (typeof exports == "object" && typeof module == "object") // CommonJS
3 mod(require("../../lib/codemirror"), require("./foldcode"));
4 else if (typeof define == "function" && define.amd) // AMD
5 define(["../../lib/codemirror", "./foldcode"], mod);
6 else // Plain browser env
7 mod(CodeMirror);
8 })(function(CodeMirror) {
9 "use strict";
10
11 CodeMirror.defineOption("foldGutter", false, function(cm, val, old) {
12 if (old && old != CodeMirror.Init) {
13 cm.clearGutter(cm.state.foldGutter.options.gutter);
14 cm.state.foldGutter = null;
15 cm.off("gutterClick", onGutterClick);
16 cm.off("change", onChange);
17 cm.off("viewportChange", onViewportChange);
18 cm.off("fold", onFold);
19 cm.off("unfold", onFold);
20 cm.off("swapDoc", updateInViewport);
21 }
22 if (val) {
23 cm.state.foldGutter = new State(parseOptions(val));
24 updateInViewport(cm);
25 cm.on("gutterClick", onGutterClick);
26 cm.on("change", onChange);
27 cm.on("viewportChange", onViewportChange);
28 cm.on("fold", onFold);
29 cm.on("unfold", onFold);
30 cm.on("swapDoc", updateInViewport);
31 }
32 });
33
34 var Pos = CodeMirror.Pos;
35
36 function State(options) {
37 this.options = options;
38 this.from = this.to = 0;
39 }
40
41 function parseOptions(opts) {
42 if (opts === true) opts = {};
43 if (opts.gutter == null) opts.gutter = "CodeMirror-foldgutter";
44 if (opts.indicatorOpen == null) opts.indicatorOpen = "CodeMirror-foldgutter-open";
45 if (opts.indicatorFolded == null) opts.indicatorFolded = "CodeMirror-foldgutter-folded";
46 return opts;
47 }
48
49 function isFolded(cm, line) {
50 var marks = cm.findMarksAt(Pos(line));
51 for (var i = 0; i < marks.length; ++i)
52 if (marks[i].__isFold && marks[i].find().from.line == line) return true;
53 }
54
55 function marker(spec) {
56 if (typeof spec == "string") {
57 var elt = document.createElement("div");
58 elt.className = spec;
59 return elt;
60 } else {
61 return spec.cloneNode(true);
62 }
63 }
64
65 function updateFoldInfo(cm, from, to) {
66 var opts = cm.state.foldGutter.options, cur = from;
67 cm.eachLine(from, to, function(line) {
68 var mark = null;
69 if (isFolded(cm, cur)) {
70 mark = marker(opts.indicatorFolded);
71 } else {
72 var pos = Pos(cur, 0), func = opts.rangeFinder || CodeMirror.fold.auto;
73 var range = func && func(cm, pos);
74 if (range && range.from.line + 1 < range.to.line)
75 mark = marker(opts.indicatorOpen);
76 }
77 cm.setGutterMarker(line, opts.gutter, mark);
78 ++cur;
79 });
80 }
81
82 function updateInViewport(cm) {
83 var vp = cm.getViewport(), state = cm.state.foldGutter;
84 if (!state) return;
85 cm.operation(function() {
86 updateFoldInfo(cm, vp.from, vp.to);
87 });
88 state.from = vp.from; state.to = vp.to;
89 }
90
91 function onGutterClick(cm, line, gutter) {
92 var opts = cm.state.foldGutter.options;
93 if (gutter != opts.gutter) return;
94 cm.foldCode(Pos(line, 0), opts.rangeFinder);
95 }
96
97 function onChange(cm) {
98 var state = cm.state.foldGutter, opts = cm.state.foldGutter.options;
99 state.from = state.to = 0;
100 clearTimeout(state.changeUpdate);
101 state.changeUpdate = setTimeout(function() { updateInViewport(cm); }, opts.foldOnChangeTimeSpan || 600);
102 }
103
104 function onViewportChange(cm) {
105 var state = cm.state.foldGutter, opts = cm.state.foldGutter.options;
106 clearTimeout(state.changeUpdate);
107 state.changeUpdate = setTimeout(function() {
108 var vp = cm.getViewport();
109 if (state.from == state.to || vp.from - state.to > 20 || state.from - vp.to > 20) {
110 updateInViewport(cm);
111 } else {
112 cm.operation(function() {
113 if (vp.from < state.from) {
114 updateFoldInfo(cm, vp.from, state.from);
115 state.from = vp.from;
116 }
117 if (vp.to > state.to) {
118 updateFoldInfo(cm, state.to, vp.to);
119 state.to = vp.to;
120 }
121 });
122 }
123 }, opts.updateViewportTimeSpan || 400);
124 }
125
126 function onFold(cm, from) {
127 var state = cm.state.foldGutter, line = from.line;
128 if (line >= state.from && line < state.to)
129 updateFoldInfo(cm, line, line + 1);
130 }
131 });