PluginProbe
User Profile Builder – Beautiful User Registration Forms, User Profiles & User Role Editor / 3.9.9
User Profile Builder – Beautiful User Registration Forms, User Profiles & User Role Editor v3.9.9
4.0.3 4.0.2 4.0.1 4.0.0 3.16.6 3.16.5 3.16.4 3.16.3 3.16.2 3.16.1 3.16.0 3.15.9 3.9.9 3.9.5 3.9.6 3.9.7 3.9.8 1.1.7 1.1.8 1.1.9 2.0.2 2.0.3 2.0.4 2.0.5 2.0.6 All 341 releases
profile-builder / assets / lib / codemirror / addon / merge / merge.js

merge.js in User Profile Builder – Beautiful User Registration Forms, User Profiles & User Role Editor 3.9.9, at assets/lib/codemirror/addon/merge/merge.js

474 lines 17.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 (function() {
2 "use strict";
3
4 var Pos = CodeMirror.Pos;
5 var svgNS = "http://www.w3.org/2000/svg";
6
7 function DiffView(mv, type) {
8 this.mv = mv;
9 this.type = type;
10 this.classes = type == "left"
11 ? {chunk: "CodeMirror-merge-l-chunk",
12 start: "CodeMirror-merge-l-chunk-start",
13 end: "CodeMirror-merge-l-chunk-end",
14 insert: "CodeMirror-merge-l-inserted",
15 del: "CodeMirror-merge-l-deleted",
16 connect: "CodeMirror-merge-l-connect"}
17 : {chunk: "CodeMirror-merge-r-chunk",
18 start: "CodeMirror-merge-r-chunk-start",
19 end: "CodeMirror-merge-r-chunk-end",
20 insert: "CodeMirror-merge-r-inserted",
21 del: "CodeMirror-merge-r-deleted",
22 connect: "CodeMirror-merge-r-connect"};
23 }
24
25 DiffView.prototype = {
26 constructor: DiffView,
27 init: function(pane, orig, options) {
28 this.edit = this.mv.edit;
29 this.orig = CodeMirror(pane, copyObj({value: orig, readOnly: true}, copyObj(options)));
30
31 this.diff = getDiff(orig, options.value);
32 this.diffOutOfDate = false;
33
34 this.showDifferences = options.showDifferences !== false;
35 this.forceUpdate = registerUpdate(this);
36 setScrollLock(this, true, false);
37 registerScroll(this);
38 },
39 setShowDifferences: function(val) {
40 val = val !== false;
41 if (val != this.showDifferences) {
42 this.showDifferences = val;
43 this.forceUpdate("full");
44 }
45 }
46 };
47
48 function registerUpdate(dv) {
49 var edit = {from: 0, to: 0, marked: []};
50 var orig = {from: 0, to: 0, marked: []};
51 var debounceChange;
52 function update(mode) {
53 if (mode == "full") {
54 if (dv.svg) clear(dv.svg);
55 clear(dv.copyButtons);
56 clearMarks(dv.edit, edit.marked, dv.classes);
57 clearMarks(dv.orig, orig.marked, dv.classes);
58 edit.from = edit.to = orig.from = orig.to = 0;
59 }
60 if (dv.diffOutOfDate) {
61 dv.diff = getDiff(dv.orig.getValue(), dv.edit.getValue());
62 dv.diffOutOfDate = false;
63 CodeMirror.signal(dv.edit, "updateDiff", dv.diff);
64 }
65 if (dv.showDifferences) {
66 updateMarks(dv.edit, dv.diff, edit, DIFF_INSERT, dv.classes);
67 updateMarks(dv.orig, dv.diff, orig, DIFF_DELETE, dv.classes);
68 }
69 drawConnectors(dv);
70 }
71 function set(slow) {
72 clearTimeout(debounceChange);
73 debounceChange = setTimeout(update, slow == true ? 250 : 100);
74 }
75 function change() {
76 if (!dv.diffOutOfDate) {
77 dv.diffOutOfDate = true;
78 edit.from = edit.to = orig.from = orig.to = 0;
79 }
80 set(true);
81 }
82 dv.edit.on("change", change);
83 dv.orig.on("change", change);
84 dv.edit.on("viewportChange", set);
85 dv.orig.on("viewportChange", set);
86 update();
87 return update;
88 }
89
90 function registerScroll(dv) {
91 dv.edit.on("scroll", function() {
92 syncScroll(dv, DIFF_INSERT) && drawConnectors(dv);
93 });
94 dv.orig.on("scroll", function() {
95 syncScroll(dv, DIFF_DELETE) && drawConnectors(dv);
96 });
97 }
98
99 function syncScroll(dv, type) {
100 // Change handler will do a refresh after a timeout when diff is out of date
101 if (dv.diffOutOfDate) return false;
102 if (!dv.lockScroll) return true;
103 var editor, other, now = +new Date;
104 if (type == DIFF_INSERT) { editor = dv.edit; other = dv.orig; }
105 else { editor = dv.orig; other = dv.edit; }
106 // Don't take action if the position of this editor was recently set
107 // (to prevent feedback loops)
108 if (editor.state.scrollSetBy == dv && (editor.state.scrollSetAt || 0) + 50 > now) return false;
109
110 var sInfo = editor.getScrollInfo(), halfScreen = .5 * sInfo.clientHeight, midY = sInfo.top + halfScreen;
111 var mid = editor.lineAtHeight(midY, "local");
112 var around = chunkBoundariesAround(dv.diff, mid, type == DIFF_INSERT);
113 var off = getOffsets(editor, type == DIFF_INSERT ? around.edit : around.orig);
114 var offOther = getOffsets(other, type == DIFF_INSERT ? around.orig : around.edit);
115 var ratio = (midY - off.top) / (off.bot - off.top);
116 var targetPos = (offOther.top - halfScreen) + ratio * (offOther.bot - offOther.top);
117
118 var botDist, mix;
119 // Some careful tweaking to make sure no space is left out of view
120 // when scrolling to top or bottom.
121 if (targetPos > sInfo.top && (mix = sInfo.top / halfScreen) < 1) {
122 targetPos = targetPos * mix + sInfo.top * (1 - mix);
123 } else if ((botDist = sInfo.height - sInfo.clientHeight - sInfo.top) < halfScreen) {
124 var otherInfo = other.getScrollInfo();
125 var botDistOther = otherInfo.height - otherInfo.clientHeight - targetPos;
126 if (botDistOther > botDist && (mix = botDist / halfScreen) < 1)
127 targetPos = targetPos * mix + (otherInfo.height - otherInfo.clientHeight - botDist) * (1 - mix);
128 }
129
130 other.scrollTo(sInfo.left, targetPos);
131 other.state.scrollSetAt = now;
132 other.state.scrollSetBy = dv;
133 return true;
134 }
135
136 function getOffsets(editor, around) {
137 var bot = around.after;
138 if (bot == null) bot = editor.lastLine() + 1;
139 return {top: editor.heightAtLine(around.before || 0, "local"),
140 bot: editor.heightAtLine(bot, "local")};
141 }
142
143 function setScrollLock(dv, val, action) {
144 dv.lockScroll = val;
145 if (val && action != false) syncScroll(dv, DIFF_INSERT) && drawConnectors(dv);
146 dv.lockButton.innerHTML = val ? "\u21db\u21da" : "\u21db&nbsp;&nbsp;\u21da";
147 }
148
149 // Updating the marks for editor content
150
151 function clearMarks(editor, arr, classes) {
152 for (var i = 0; i < arr.length; ++i) {
153 var mark = arr[i];
154 if (mark instanceof CodeMirror.TextMarker) {
155 mark.clear();
156 } else {
157 editor.removeLineClass(mark, "background", classes.chunk);
158 editor.removeLineClass(mark, "background", classes.start);
159 editor.removeLineClass(mark, "background", classes.end);
160 }
161 }
162 arr.length = 0;
163 }
164
165 // FIXME maybe add a margin around viewport to prevent too many updates
166 function updateMarks(editor, diff, state, type, classes) {
167 var vp = editor.getViewport();
168 editor.operation(function() {
169 if (state.from == state.to || vp.from - state.to > 20 || state.from - vp.to > 20) {
170 clearMarks(editor, state.marked, classes);
171 markChanges(editor, diff, type, state.marked, vp.from, vp.to, classes);
172 state.from = vp.from; state.to = vp.to;
173 } else {
174 if (vp.from < state.from) {
175 markChanges(editor, diff, type, state.marked, vp.from, state.from, classes);
176 state.from = vp.from;
177 }
178 if (vp.to > state.to) {
179 markChanges(editor, diff, type, state.marked, state.to, vp.to, classes);
180 state.to = vp.to;
181 }
182 }
183 });
184 }
185
186 function markChanges(editor, diff, type, marks, from, to, classes) {
187 var pos = Pos(0, 0);
188 var top = Pos(from, 0), bot = editor.clipPos(Pos(to - 1));
189 var cls = type == DIFF_DELETE ? classes.del : classes.insert;
190 function markChunk(start, end) {
191 var bfrom = Math.max(from, start), bto = Math.min(to, end);
192 for (var i = bfrom; i < bto; ++i) {
193 var line = editor.addLineClass(i, "background", classes.chunk);
194 if (i == start) editor.addLineClass(line, "background", classes.start);
195 if (i == end - 1) editor.addLineClass(line, "background", classes.end);
196 marks.push(line);
197 }
198 // When the chunk is empty, make sure a horizontal line shows up
199 if (start == end && bfrom == end && bto == end) {
200 if (bfrom)
201 marks.push(editor.addLineClass(bfrom - 1, "background", classes.end));
202 else
203 marks.push(editor.addLineClass(bfrom, "background", classes.start));
204 }
205 }
206
207 var chunkStart = 0;
208 for (var i = 0; i < diff.length; ++i) {
209 var part = diff[i], tp = part[0], str = part[1];
210 if (tp == DIFF_EQUAL) {
211 var cleanFrom = pos.line + (startOfLineClean(diff, i) ? 0 : 1);
212 moveOver(pos, str);
213 var cleanTo = pos.line + (endOfLineClean(diff, i) ? 1 : 0);
214 if (cleanTo > cleanFrom) {
215 if (i) markChunk(chunkStart, cleanFrom);
216 chunkStart = cleanTo;
217 }
218 } else {
219 if (tp == type) {
220 var end = moveOver(pos, str, true);
221 var a = posMax(top, pos), b = posMin(bot, end);
222 if (!posEq(a, b))
223 marks.push(editor.markText(a, b, {className: cls}));
224 pos = end;
225 }
226 }
227 }
228 if (chunkStart <= pos.line) markChunk(chunkStart, pos.line + 1);
229 }
230
231 // Updating the gap between editor and original
232
233 function drawConnectors(dv) {
234 if (!dv.showDifferences) return;
235
236 if (dv.svg) {
237 clear(dv.svg);
238 var w = dv.gap.offsetWidth;
239 attrs(dv.svg, "width", w, "height", dv.gap.offsetHeight);
240 }
241 clear(dv.copyButtons);
242
243 var flip = dv.type == "left";
244 var vpEdit = dv.edit.getViewport(), vpOrig = dv.orig.getViewport();
245 var sTopEdit = dv.edit.getScrollInfo().top, sTopOrig = dv.orig.getScrollInfo().top;
246 iterateChunks(dv.diff, function(topOrig, botOrig, topEdit, botEdit) {
247 if (topEdit > vpEdit.to || botEdit < vpEdit.from ||
248 topOrig > vpOrig.to || botOrig < vpOrig.from)
249 return;
250 var topLpx = dv.orig.heightAtLine(topOrig, "local") - sTopOrig, top = topLpx;
251 if (dv.svg) {
252 var topRpx = dv.edit.heightAtLine(topEdit, "local") - sTopEdit;
253 if (flip) { var tmp = topLpx; topLpx = topRpx; topRpx = tmp; }
254 var botLpx = dv.orig.heightAtLine(botOrig, "local") - sTopOrig;
255 var botRpx = dv.edit.heightAtLine(botEdit, "local") - sTopEdit;
256 if (flip) { var tmp = botLpx; botLpx = botRpx; botRpx = tmp; }
257 var curveTop = " C " + w/2 + " " + topRpx + " " + w/2 + " " + topLpx + " " + (w + 2) + " " + topLpx;
258 var curveBot = " C " + w/2 + " " + botLpx + " " + w/2 + " " + botRpx + " -1 " + botRpx;
259 attrs(dv.svg.appendChild(document.createElementNS(svgNS, "path")),
260 "d", "M -1 " + topRpx + curveTop + " L " + (w + 2) + " " + botLpx + curveBot + " z",
261 "class", dv.classes.connect);
262 }
263 var copy = dv.copyButtons.appendChild(elt("div", dv.type == "left" ? "\u21dd" : "\u21dc",
264 "CodeMirror-merge-copy"));
265 copy.title = "Revert chunk";
266 copy.chunk = {topEdit: topEdit, botEdit: botEdit, topOrig: topOrig, botOrig: botOrig};
267 copy.style.top = top + "px";
268 });
269 }
270
271 function copyChunk(dv, chunk) {
272 if (dv.diffOutOfDate) return;
273 dv.edit.replaceRange(dv.orig.getRange(Pos(chunk.topOrig, 0), Pos(chunk.botOrig, 0)),
274 Pos(chunk.topEdit, 0), Pos(chunk.botEdit, 0));
275 }
276
277 // Merge view, containing 0, 1, or 2 diff views.
278
279 var MergeView = CodeMirror.MergeView = function(node, options) {
280 if (!(this instanceof MergeView)) return new MergeView(node, options);
281
282 var origLeft = options.origLeft, origRight = options.origRight == null ? options.orig : options.origRight;
283 var hasLeft = origLeft != null, hasRight = origRight != null;
284 var panes = 1 + (hasLeft ? 1 : 0) + (hasRight ? 1 : 0);
285 var wrap = [], left = this.left = null, right = this.right = null;
286
287 if (hasLeft) {
288 left = this.left = new DiffView(this, "left");
289 var leftPane = elt("div", null, "CodeMirror-merge-pane");
290 wrap.push(leftPane);
291 wrap.push(buildGap(left));
292 }
293
294 var editPane = elt("div", null, "CodeMirror-merge-pane");
295 wrap.push(editPane);
296
297 if (hasRight) {
298 right = this.right = new DiffView(this, "right");
299 wrap.push(buildGap(right));
300 var rightPane = elt("div", null, "CodeMirror-merge-pane");
301 wrap.push(rightPane);
302 }
303
304 (hasRight ? rightPane : editPane).className += " CodeMirror-merge-pane-rightmost";
305
306 wrap.push(elt("div", null, null, "height: 0; clear: both;"));
307 var wrapElt = this.wrap = node.appendChild(elt("div", wrap, "CodeMirror-merge CodeMirror-merge-" + panes + "pane"));
308 this.edit = CodeMirror(editPane, copyObj(options));
309
310 if (left) left.init(leftPane, origLeft, options);
311 if (right) right.init(rightPane, origRight, options);
312
313 var onResize = function() {
314 if (left) drawConnectors(left);
315 if (right) drawConnectors(right);
316 };
317 CodeMirror.on(window, "resize", onResize);
318 var resizeInterval = setInterval(function() {
319 for (var p = wrapElt.parentNode; p && p != document.body; p = p.parentNode) {}
320 if (!p) { clearInterval(resizeInterval); CodeMirror.off(window, "resize", onResize); }
321 }, 5000);
322 };
323
324 function buildGap(dv) {
325 var lock = dv.lockButton = elt("div", null, "CodeMirror-merge-scrolllock");
326 lock.title = "Toggle locked scrolling";
327 var lockWrap = elt("div", [lock], "CodeMirror-merge-scrolllock-wrap");
328 CodeMirror.on(lock, "click", function() { setScrollLock(dv, !dv.lockScroll); });
329 dv.copyButtons = elt("div", null, "CodeMirror-merge-copybuttons-" + dv.type);
330 CodeMirror.on(dv.copyButtons, "click", function(e) {
331 var node = e.target || e.srcElement;
332 if (node.chunk) copyChunk(dv, node.chunk);
333 });
334 var gapElts = [dv.copyButtons, lockWrap];
335 var svg = document.createElementNS && document.createElementNS(svgNS, "svg");
336 if (svg && !svg.createSVGRect) svg = null;
337 dv.svg = svg;
338 if (svg) gapElts.push(svg);
339
340 return dv.gap = elt("div", gapElts, "CodeMirror-merge-gap");
341 }
342
343 MergeView.prototype = {
344 constuctor: MergeView,
345 editor: function() { return this.edit; },
346 rightOriginal: function() { return this.right && this.right.orig; },
347 leftOriginal: function() { return this.left && this.left.orig; },
348 setShowDifferences: function(val) {
349 if (this.right) this.right.setShowDifferences(val);
350 if (this.left) this.left.setShowDifferences(val);
351 }
352 };
353
354 // Operations on diffs
355
356 var dmp = new diff_match_patch();
357 function getDiff(a, b) {
358 var diff = dmp.diff_main(a, b);
359 dmp.diff_cleanupSemantic(diff);
360 // The library sometimes leaves in empty parts, which confuse the algorithm
361 for (var i = 0; i < diff.length; ++i) {
362 var part = diff[i];
363 if (!part[1]) {
364 diff.splice(i--, 1);
365 } else if (i && diff[i - 1][0] == part[0]) {
366 diff.splice(i--, 1);
367 diff[i][1] += part[1];
368 }
369 }
370 return diff;
371 }
372
373 function iterateChunks(diff, f) {
374 var startEdit = 0, startOrig = 0;
375 var edit = Pos(0, 0), orig = Pos(0, 0);
376 for (var i = 0; i < diff.length; ++i) {
377 var part = diff[i], tp = part[0];
378 if (tp == DIFF_EQUAL) {
379 var startOff = startOfLineClean(diff, i) ? 0 : 1;
380 var cleanFromEdit = edit.line + startOff, cleanFromOrig = orig.line + startOff;
381 moveOver(edit, part[1], null, orig);
382 var endOff = endOfLineClean(diff, i) ? 1 : 0;
383 var cleanToEdit = edit.line + endOff, cleanToOrig = orig.line + endOff;
384 if (cleanToEdit > cleanFromEdit) {
385 if (i) f(startOrig, cleanFromOrig, startEdit, cleanFromEdit);
386 startEdit = cleanToEdit; startOrig = cleanToOrig;
387 }
388 } else {
389 moveOver(tp == DIFF_INSERT ? edit : orig, part[1]);
390 }
391 }
392 if (startEdit <= edit.line || startOrig <= orig.line)
393 f(startOrig, orig.line + 1, startEdit, edit.line + 1);
394 }
395
396 function endOfLineClean(diff, i) {
397 if (i == diff.length - 1) return true;
398 var next = diff[i + 1][1];
399 if (next.length == 1 || next.charCodeAt(0) != 10) return false;
400 if (i == diff.length - 2) return true;
401 next = diff[i + 2][1];
402 return next.length > 1 && next.charCodeAt(0) == 10;
403 }
404
405 function startOfLineClean(diff, i) {
406 if (i == 0) return true;
407 var last = diff[i - 1][1];
408 if (last.charCodeAt(last.length - 1) != 10) return false;
409 if (i == 1) return true;
410 last = diff[i - 2][1];
411 return last.charCodeAt(last.length - 1) == 10;
412 }
413
414 function chunkBoundariesAround(diff, n, nInEdit) {
415 var beforeE, afterE, beforeO, afterO;
416 iterateChunks(diff, function(fromOrig, toOrig, fromEdit, toEdit) {
417 var fromLocal = nInEdit ? fromEdit : fromOrig;
418 var toLocal = nInEdit ? toEdit : toOrig;
419 if (afterE == null) {
420 if (fromLocal > n) { afterE = fromEdit; afterO = fromOrig; }
421 else if (toLocal > n) { afterE = toEdit; afterO = toOrig; }
422 }
423 if (toLocal <= n) { beforeE = toEdit; beforeO = toOrig; }
424 else if (fromLocal <= n) { beforeE = fromEdit; beforeO = fromOrig; }
425 });
426 return {edit: {before: beforeE, after: afterE}, orig: {before: beforeO, after: afterO}};
427 }
428
429 // General utilities
430
431 function elt(tag, content, className, style) {
432 var e = document.createElement(tag);
433 if (className) e.className = className;
434 if (style) e.style.cssText = style;
435 if (typeof content == "string") e.appendChild(document.createTextNode(content));
436 else if (content) for (var i = 0; i < content.length; ++i) e.appendChild(content[i]);
437 return e;
438 }
439
440 function clear(node) {
441 for (var count = node.childNodes.length; count > 0; --count)
442 node.removeChild(node.firstChild);
443 }
444
445 function attrs(elt) {
446 for (var i = 1; i < arguments.length; i += 2)
447 elt.setAttribute(arguments[i], arguments[i+1]);
448 }
449
450 function copyObj(obj, target) {
451 if (!target) target = {};
452 for (var prop in obj) if (obj.hasOwnProperty(prop)) target[prop] = obj[prop];
453 return target;
454 }
455
456 function moveOver(pos, str, copy, other) {
457 var out = copy ? Pos(pos.line, pos.ch) : pos, at = 0;
458 for (;;) {
459 var nl = str.indexOf("\n", at);
460 if (nl == -1) break;
461 ++out.line;
462 if (other) ++other.line;
463 at = nl + 1;
464 }
465 out.ch = (at ? 0 : out.ch) + (str.length - at);
466 if (other) other.ch = (at ? 0 : other.ch) + (str.length - at);
467 return out;
468 }
469
470 function posMin(a, b) { return (a.line - b.line || a.ch - b.ch) < 0 ? a : b; }
471 function posMax(a, b) { return (a.line - b.line || a.ch - b.ch) > 0 ? a : b; }
472 function posEq(a, b) { return a.line == b.line && a.ch == b.ch; }
473 })();
474