comparison .cms/lib/codemirror/addon/fold/foldgutter.js @ 0:78edf6b517a0 draft

24.10
author Coffee CMS <info@coffee-cms.ru>
date Fri, 11 Oct 2024 22:40:23 +0000
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:78edf6b517a0
1 // CodeMirror, copyright (c) by Marijn Haverbeke and others
2 // Distributed under an MIT license: https://codemirror.net/5/LICENSE
3
4 (function(mod) {
5 if (typeof exports == "object" && typeof module == "object") // CommonJS
6 mod(require("../../lib/codemirror"), require("./foldcode"));
7 else if (typeof define == "function" && define.amd) // AMD
8 define(["../../lib/codemirror", "./foldcode"], mod);
9 else // Plain browser env
10 mod(CodeMirror);
11 })(function(CodeMirror) {
12 "use strict";
13
14 CodeMirror.defineOption("foldGutter", false, function(cm, val, old) {
15 if (old && old != CodeMirror.Init) {
16 cm.clearGutter(cm.state.foldGutter.options.gutter);
17 cm.state.foldGutter = null;
18 cm.off("gutterClick", onGutterClick);
19 cm.off("changes", onChange);
20 cm.off("viewportChange", onViewportChange);
21 cm.off("fold", onFold);
22 cm.off("unfold", onFold);
23 cm.off("swapDoc", onChange);
24 cm.off("optionChange", optionChange);
25 }
26 if (val) {
27 cm.state.foldGutter = new State(parseOptions(val));
28 updateInViewport(cm);
29 cm.on("gutterClick", onGutterClick);
30 cm.on("changes", onChange);
31 cm.on("viewportChange", onViewportChange);
32 cm.on("fold", onFold);
33 cm.on("unfold", onFold);
34 cm.on("swapDoc", onChange);
35 cm.on("optionChange", optionChange);
36 }
37 });
38
39 var Pos = CodeMirror.Pos;
40
41 function State(options) {
42 this.options = options;
43 this.from = this.to = 0;
44 }
45
46 function parseOptions(opts) {
47 if (opts === true) opts = {};
48 if (opts.gutter == null) opts.gutter = "CodeMirror-foldgutter";
49 if (opts.indicatorOpen == null) opts.indicatorOpen = "CodeMirror-foldgutter-open";
50 if (opts.indicatorFolded == null) opts.indicatorFolded = "CodeMirror-foldgutter-folded";
51 return opts;
52 }
53
54 function isFolded(cm, line) {
55 var marks = cm.findMarks(Pos(line, 0), Pos(line + 1, 0));
56 for (var i = 0; i < marks.length; ++i) {
57 if (marks[i].__isFold) {
58 var fromPos = marks[i].find(-1);
59 if (fromPos && fromPos.line === line)
60 return marks[i];
61 }
62 }
63 }
64
65 function marker(spec) {
66 if (typeof spec == "string") {
67 var elt = document.createElement("div");
68 elt.className = spec + " CodeMirror-guttermarker-subtle";
69 return elt;
70 } else {
71 return spec.cloneNode(true);
72 }
73 }
74
75 function updateFoldInfo(cm, from, to) {
76 var opts = cm.state.foldGutter.options, cur = from - 1;
77 var minSize = cm.foldOption(opts, "minFoldSize");
78 var func = cm.foldOption(opts, "rangeFinder");
79 // we can reuse the built-in indicator element if its className matches the new state
80 var clsFolded = typeof opts.indicatorFolded == "string" && classTest(opts.indicatorFolded);
81 var clsOpen = typeof opts.indicatorOpen == "string" && classTest(opts.indicatorOpen);
82 cm.eachLine(from, to, function(line) {
83 ++cur;
84 var mark = null;
85 var old = line.gutterMarkers;
86 if (old) old = old[opts.gutter];
87 if (isFolded(cm, cur)) {
88 if (clsFolded && old && clsFolded.test(old.className)) return;
89 mark = marker(opts.indicatorFolded);
90 } else {
91 var pos = Pos(cur, 0);
92 var range = func && func(cm, pos);
93 if (range && range.to.line - range.from.line >= minSize) {
94 if (clsOpen && old && clsOpen.test(old.className)) return;
95 mark = marker(opts.indicatorOpen);
96 }
97 }
98 if (!mark && !old) return;
99 cm.setGutterMarker(line, opts.gutter, mark);
100 });
101 }
102
103 // copied from CodeMirror/src/util/dom.js
104 function classTest(cls) { return new RegExp("(^|\\s)" + cls + "(?:$|\\s)\\s*") }
105
106 function updateInViewport(cm) {
107 var vp = cm.getViewport(), state = cm.state.foldGutter;
108 if (!state) return;
109 cm.operation(function() {
110 updateFoldInfo(cm, vp.from, vp.to);
111 });
112 state.from = vp.from; state.to = vp.to;
113 }
114
115 function onGutterClick(cm, line, gutter) {
116 var state = cm.state.foldGutter;
117 if (!state) return;
118 var opts = state.options;
119 if (gutter != opts.gutter) return;
120 var folded = isFolded(cm, line);
121 if (folded) folded.clear();
122 else cm.foldCode(Pos(line, 0), opts);
123 }
124
125 function optionChange(cm, option) {
126 if (option == "mode") onChange(cm)
127 }
128
129 function onChange(cm) {
130 var state = cm.state.foldGutter;
131 if (!state) return;
132 var opts = state.options;
133 state.from = state.to = 0;
134 clearTimeout(state.changeUpdate);
135 state.changeUpdate = setTimeout(function() { updateInViewport(cm); }, opts.foldOnChangeTimeSpan || 600);
136 }
137
138 function onViewportChange(cm) {
139 var state = cm.state.foldGutter;
140 if (!state) return;
141 var opts = state.options;
142 clearTimeout(state.changeUpdate);
143 state.changeUpdate = setTimeout(function() {
144 var vp = cm.getViewport();
145 if (state.from == state.to || vp.from - state.to > 20 || state.from - vp.to > 20) {
146 updateInViewport(cm);
147 } else {
148 cm.operation(function() {
149 if (vp.from < state.from) {
150 updateFoldInfo(cm, vp.from, state.from);
151 state.from = vp.from;
152 }
153 if (vp.to > state.to) {
154 updateFoldInfo(cm, state.to, vp.to);
155 state.to = vp.to;
156 }
157 });
158 }
159 }, opts.updateViewportTimeSpan || 400);
160 }
161
162 function onFold(cm, from) {
163 var state = cm.state.foldGutter;
164 if (!state) return;
165 var line = from.line;
166 if (line >= state.from && line < state.to)
167 updateFoldInfo(cm, line, line + 1);
168 }
169 });