Merge pull request #5987 from WoltLab/acp-dahsboard-box-hight
[GitHub/WoltLab/WCF.git] / wcfsetup / install / files / js / 3rdParty / codemirror / addon / lint / lint.js
CommitLineData
44f4c339 1// CodeMirror, copyright (c) by Marijn Haverbeke and others
373d1232 2// Distributed under an MIT license: https://codemirror.net/LICENSE
44f4c339 3
837afb80
TD
4(function(mod) {
5 if (typeof exports == "object" && typeof module == "object") // CommonJS
6 mod(require("../../lib/codemirror"));
7 else if (typeof define == "function" && define.amd) // AMD
8 define(["../../lib/codemirror"], mod);
9 else // Plain browser env
10 mod(CodeMirror);
11})(function(CodeMirror) {
12 "use strict";
77b7b761 13 var GUTTER_ID = "CodeMirror-lint-markers";
77b7b761 14
704cb1bf 15 function showTooltip(cm, e, content) {
77b7b761 16 var tt = document.createElement("div");
704cb1bf 17 tt.className = "CodeMirror-lint-tooltip cm-s-" + cm.options.theme;
77b7b761 18 tt.appendChild(content.cloneNode(true));
704cb1bf
MS
19 if (cm.state.lint.options.selfContain)
20 cm.getWrapperElement().appendChild(tt);
21 else
22 document.body.appendChild(tt);
77b7b761
TD
23
24 function position(e) {
25 if (!tt.parentNode) return CodeMirror.off(document, "mousemove", position);
26 tt.style.top = Math.max(0, e.clientY - tt.offsetHeight - 5) + "px";
27 tt.style.left = (e.clientX + 5) + "px";
28 }
29 CodeMirror.on(document, "mousemove", position);
30 position(e);
31 if (tt.style.opacity != null) tt.style.opacity = 1;
32 return tt;
33 }
34 function rm(elt) {
35 if (elt.parentNode) elt.parentNode.removeChild(elt);
36 }
37 function hideTooltip(tt) {
38 if (!tt.parentNode) return;
39 if (tt.style.opacity == null) rm(tt);
40 tt.style.opacity = 0;
41 setTimeout(function() { rm(tt); }, 600);
42 }
43
704cb1bf
MS
44 function showTooltipFor(cm, e, content, node) {
45 var tooltip = showTooltip(cm, e, content);
77b7b761
TD
46 function hide() {
47 CodeMirror.off(node, "mouseout", hide);
48 if (tooltip) { hideTooltip(tooltip); tooltip = null; }
49 }
50 var poll = setInterval(function() {
51 if (tooltip) for (var n = node;; n = n.parentNode) {
44f4c339 52 if (n && n.nodeType == 11) n = n.host;
77b7b761
TD
53 if (n == document.body) return;
54 if (!n) { hide(); break; }
55 }
56 if (!tooltip) return clearInterval(poll);
57 }, 400);
58 CodeMirror.on(node, "mouseout", hide);
59 }
60
61 function LintState(cm, options, hasGutter) {
62 this.marked = [];
63 this.options = options;
64 this.timeout = null;
65 this.hasGutter = hasGutter;
66 this.onMouseOver = function(e) { onMouseOver(cm, e); };
44f4c339 67 this.waitingFor = 0
77b7b761
TD
68 }
69
44f4c339 70 function parseOptions(_cm, options) {
77b7b761 71 if (options instanceof Function) return {getAnnotations: options};
837afb80 72 if (!options || options === true) options = {};
77b7b761
TD
73 return options;
74 }
75
76 function clearMarks(cm) {
77 var state = cm.state.lint;
78 if (state.hasGutter) cm.clearGutter(GUTTER_ID);
79 for (var i = 0; i < state.marked.length; ++i)
80 state.marked[i].clear();
81 state.marked.length = 0;
82 }
83
704cb1bf 84 function makeMarker(cm, labels, severity, multiple, tooltips) {
77b7b761 85 var marker = document.createElement("div"), inner = marker;
704cb1bf 86 marker.className = "CodeMirror-lint-marker CodeMirror-lint-marker-" + severity;
77b7b761
TD
87 if (multiple) {
88 inner = marker.appendChild(document.createElement("div"));
704cb1bf 89 inner.className = "CodeMirror-lint-marker CodeMirror-lint-marker-multiple";
77b7b761
TD
90 }
91
92 if (tooltips != false) CodeMirror.on(inner, "mouseover", function(e) {
704cb1bf 93 showTooltipFor(cm, e, labels, inner);
77b7b761
TD
94 });
95
96 return marker;
97 }
98
99 function getMaxSeverity(a, b) {
100 if (a == "error") return a;
101 else return b;
102 }
103
104 function groupByLine(annotations) {
105 var lines = [];
106 for (var i = 0; i < annotations.length; ++i) {
107 var ann = annotations[i], line = ann.from.line;
108 (lines[line] || (lines[line] = [])).push(ann);
109 }
110 return lines;
111 }
112
113 function annotationTooltip(ann) {
114 var severity = ann.severity;
44f4c339 115 if (!severity) severity = "error";
77b7b761 116 var tip = document.createElement("div");
704cb1bf 117 tip.className = "CodeMirror-lint-message CodeMirror-lint-message-" + severity;
373d1232 118 if (typeof ann.messageHTML != 'undefined') {
704cb1bf 119 tip.innerHTML = ann.messageHTML;
373d1232 120 } else {
704cb1bf 121 tip.appendChild(document.createTextNode(ann.message));
373d1232 122 }
77b7b761
TD
123 return tip;
124 }
125
44f4c339
AE
126 function lintAsync(cm, getAnnotations, passOptions) {
127 var state = cm.state.lint
128 var id = ++state.waitingFor
129 function abort() {
130 id = -1
131 cm.off("change", abort)
132 }
133 cm.on("change", abort)
134 getAnnotations(cm.getValue(), function(annotations, arg2) {
135 cm.off("change", abort)
136 if (state.waitingFor != id) return
137 if (arg2 && annotations instanceof CodeMirror) annotations = arg2
373d1232 138 cm.operation(function() {updateLinting(cm, annotations)})
44f4c339
AE
139 }, passOptions, cm);
140 }
141
77b7b761
TD
142 function startLinting(cm) {
143 var state = cm.state.lint, options = state.options;
373d1232
TD
144 /*
145 * Passing rules in `options` property prevents JSHint (and other linters) from complaining
146 * about unrecognized rules like `onUpdateLinting`, `delay`, `lintOnChange`, etc.
147 */
148 var passOptions = options.options || options;
44f4c339
AE
149 var getAnnotations = options.getAnnotations || cm.getHelper(CodeMirror.Pos(0, 0), "lint");
150 if (!getAnnotations) return;
151 if (options.async || getAnnotations.async) {
152 lintAsync(cm, getAnnotations, passOptions)
153 } else {
373d1232
TD
154 var annotations = getAnnotations(cm.getValue(), passOptions, cm);
155 if (!annotations) return;
156 if (annotations.then) annotations.then(function(issues) {
157 cm.operation(function() {updateLinting(cm, issues)})
158 });
159 else cm.operation(function() {updateLinting(cm, annotations)})
44f4c339 160 }
77b7b761
TD
161 }
162
163 function updateLinting(cm, annotationsNotSorted) {
164 clearMarks(cm);
165 var state = cm.state.lint, options = state.options;
166
167 var annotations = groupByLine(annotationsNotSorted);
168
169 for (var line = 0; line < annotations.length; ++line) {
170 var anns = annotations[line];
171 if (!anns) continue;
172
704cb1bf
MS
173 // filter out duplicate messages
174 var message = [];
175 anns = anns.filter(function(item) { return message.indexOf(item.message) > -1 ? false : message.push(item.message) });
176
77b7b761
TD
177 var maxSeverity = null;
178 var tipLabel = state.hasGutter && document.createDocumentFragment();
179
180 for (var i = 0; i < anns.length; ++i) {
181 var ann = anns[i];
182 var severity = ann.severity;
44f4c339 183 if (!severity) severity = "error";
77b7b761
TD
184 maxSeverity = getMaxSeverity(maxSeverity, severity);
185
186 if (options.formatAnnotation) ann = options.formatAnnotation(ann);
187 if (state.hasGutter) tipLabel.appendChild(annotationTooltip(ann));
188
189 if (ann.to) state.marked.push(cm.markText(ann.from, ann.to, {
704cb1bf 190 className: "CodeMirror-lint-mark CodeMirror-lint-mark-" + severity,
77b7b761
TD
191 __annotation: ann
192 }));
193 }
704cb1bf 194 // use original annotations[line] to show multiple messages
77b7b761 195 if (state.hasGutter)
704cb1bf 196 cm.setGutterMarker(line, GUTTER_ID, makeMarker(cm, tipLabel, maxSeverity, annotations[line].length > 1,
77b7b761
TD
197 state.options.tooltips));
198 }
199 if (options.onUpdateLinting) options.onUpdateLinting(annotationsNotSorted, annotations, cm);
200 }
201
202 function onChange(cm) {
203 var state = cm.state.lint;
44f4c339 204 if (!state) return;
77b7b761
TD
205 clearTimeout(state.timeout);
206 state.timeout = setTimeout(function(){startLinting(cm);}, state.options.delay || 500);
207 }
208
704cb1bf 209 function popupTooltips(cm, annotations, e) {
77b7b761 210 var target = e.target || e.srcElement;
44f4c339
AE
211 var tooltip = document.createDocumentFragment();
212 for (var i = 0; i < annotations.length; i++) {
213 var ann = annotations[i];
214 tooltip.appendChild(annotationTooltip(ann));
215 }
704cb1bf 216 showTooltipFor(cm, e, tooltip, target);
77b7b761
TD
217 }
218
77b7b761 219 function onMouseOver(cm, e) {
44f4c339
AE
220 var target = e.target || e.srcElement;
221 if (!/\bCodeMirror-lint-mark-/.test(target.className)) return;
222 var box = target.getBoundingClientRect(), x = (box.left + box.right) / 2, y = (box.top + box.bottom) / 2;
223 var spans = cm.findMarksAt(cm.coordsChar({left: x, top: y}, "client"));
224
225 var annotations = [];
226 for (var i = 0; i < spans.length; ++i) {
227 var ann = spans[i].__annotation;
228 if (ann) annotations.push(ann);
77b7b761 229 }
704cb1bf 230 if (annotations.length) popupTooltips(cm, annotations, e);
77b7b761
TD
231 }
232
837afb80 233 CodeMirror.defineOption("lint", false, function(cm, val, old) {
77b7b761
TD
234 if (old && old != CodeMirror.Init) {
235 clearMarks(cm);
44f4c339
AE
236 if (cm.state.lint.options.lintOnChange !== false)
237 cm.off("change", onChange);
77b7b761 238 CodeMirror.off(cm.getWrapperElement(), "mouseover", cm.state.lint.onMouseOver);
44f4c339 239 clearTimeout(cm.state.lint.timeout);
77b7b761
TD
240 delete cm.state.lint;
241 }
242
243 if (val) {
244 var gutters = cm.getOption("gutters"), hasLintGutter = false;
245 for (var i = 0; i < gutters.length; ++i) if (gutters[i] == GUTTER_ID) hasLintGutter = true;
837afb80 246 var state = cm.state.lint = new LintState(cm, parseOptions(cm, val), hasLintGutter);
44f4c339
AE
247 if (state.options.lintOnChange !== false)
248 cm.on("change", onChange);
373d1232 249 if (state.options.tooltips != false && state.options.tooltips != "gutter")
77b7b761
TD
250 CodeMirror.on(cm.getWrapperElement(), "mouseover", state.onMouseOver);
251
252 startLinting(cm);
253 }
254 });
44f4c339
AE
255
256 CodeMirror.defineExtension("performLint", function() {
257 if (this.state.lint) startLinting(this);
258 });
837afb80 259});