[ Index ] |
PHP Cross Reference of Joomla 4.2.2 documentation |
[Summary view] [Print] [Text view]
1 // CodeMirror, copyright (c) by Marijn Haverbeke and others 2 // Distributed under an MIT license: https://codemirror.net/5/LICENSE 3 4 // declare global: diff_match_patch, DIFF_INSERT, DIFF_DELETE, DIFF_EQUAL 5 6 (function(mod) { 7 if (typeof exports == "object" && typeof module == "object") // CommonJS 8 mod(require("../../lib/codemirror")); // Note non-packaged dependency diff_match_patch 9 else if (typeof define == "function" && define.amd) // AMD 10 define(["../../lib/codemirror", "diff_match_patch"], mod); 11 else // Plain browser env 12 mod(CodeMirror); 13 })(function(CodeMirror) { 14 "use strict"; 15 var Pos = CodeMirror.Pos; 16 var svgNS = "http://www.w3.org/2000/svg"; 17 18 function DiffView(mv, type) { 19 this.mv = mv; 20 this.type = type; 21 this.classes = type == "left" 22 ? {chunk: "CodeMirror-merge-l-chunk", 23 start: "CodeMirror-merge-l-chunk-start", 24 end: "CodeMirror-merge-l-chunk-end", 25 insert: "CodeMirror-merge-l-inserted", 26 del: "CodeMirror-merge-l-deleted", 27 connect: "CodeMirror-merge-l-connect"} 28 : {chunk: "CodeMirror-merge-r-chunk", 29 start: "CodeMirror-merge-r-chunk-start", 30 end: "CodeMirror-merge-r-chunk-end", 31 insert: "CodeMirror-merge-r-inserted", 32 del: "CodeMirror-merge-r-deleted", 33 connect: "CodeMirror-merge-r-connect"}; 34 } 35 36 DiffView.prototype = { 37 constructor: DiffView, 38 init: function(pane, orig, options) { 39 this.edit = this.mv.edit; 40 ;(this.edit.state.diffViews || (this.edit.state.diffViews = [])).push(this); 41 this.orig = CodeMirror(pane, copyObj({value: orig, readOnly: !this.mv.options.allowEditingOriginals}, copyObj(options))); 42 if (this.mv.options.connect == "align") { 43 if (!this.edit.state.trackAlignable) this.edit.state.trackAlignable = new TrackAlignable(this.edit) 44 this.orig.state.trackAlignable = new TrackAlignable(this.orig) 45 } 46 this.lockButton.title = this.edit.phrase("Toggle locked scrolling"); 47 this.lockButton.setAttribute("aria-label", this.lockButton.title); 48 49 this.orig.state.diffViews = [this]; 50 var classLocation = options.chunkClassLocation || "background"; 51 if (Object.prototype.toString.call(classLocation) != "[object Array]") classLocation = [classLocation] 52 this.classes.classLocation = classLocation 53 54 this.diff = getDiff(asString(orig), asString(options.value), this.mv.options.ignoreWhitespace); 55 this.chunks = getChunks(this.diff); 56 this.diffOutOfDate = this.dealigned = false; 57 this.needsScrollSync = null 58 59 this.showDifferences = options.showDifferences !== false; 60 }, 61 registerEvents: function(otherDv) { 62 this.forceUpdate = registerUpdate(this); 63 setScrollLock(this, true, false); 64 registerScroll(this, otherDv); 65 }, 66 setShowDifferences: function(val) { 67 val = val !== false; 68 if (val != this.showDifferences) { 69 this.showDifferences = val; 70 this.forceUpdate("full"); 71 } 72 } 73 }; 74 75 function ensureDiff(dv) { 76 if (dv.diffOutOfDate) { 77 dv.diff = getDiff(dv.orig.getValue(), dv.edit.getValue(), dv.mv.options.ignoreWhitespace); 78 dv.chunks = getChunks(dv.diff); 79 dv.diffOutOfDate = false; 80 CodeMirror.signal(dv.edit, "updateDiff", dv.diff); 81 } 82 } 83 84 var updating = false; 85 function registerUpdate(dv) { 86 var edit = {from: 0, to: 0, marked: []}; 87 var orig = {from: 0, to: 0, marked: []}; 88 var debounceChange, updatingFast = false; 89 function update(mode) { 90 updating = true; 91 updatingFast = false; 92 if (mode == "full") { 93 if (dv.svg) clear(dv.svg); 94 if (dv.copyButtons) clear(dv.copyButtons); 95 clearMarks(dv.edit, edit.marked, dv.classes); 96 clearMarks(dv.orig, orig.marked, dv.classes); 97 edit.from = edit.to = orig.from = orig.to = 0; 98 } 99 ensureDiff(dv); 100 if (dv.showDifferences) { 101 updateMarks(dv.edit, dv.diff, edit, DIFF_INSERT, dv.classes); 102 updateMarks(dv.orig, dv.diff, orig, DIFF_DELETE, dv.classes); 103 } 104 105 if (dv.mv.options.connect == "align") 106 alignChunks(dv); 107 makeConnections(dv); 108 if (dv.needsScrollSync != null) syncScroll(dv, dv.needsScrollSync) 109 110 updating = false; 111 } 112 function setDealign(fast) { 113 if (updating) return; 114 dv.dealigned = true; 115 set(fast); 116 } 117 function set(fast) { 118 if (updating || updatingFast) return; 119 clearTimeout(debounceChange); 120 if (fast === true) updatingFast = true; 121 debounceChange = setTimeout(update, fast === true ? 20 : 250); 122 } 123 function change(_cm, change) { 124 if (!dv.diffOutOfDate) { 125 dv.diffOutOfDate = true; 126 edit.from = edit.to = orig.from = orig.to = 0; 127 } 128 // Update faster when a line was added/removed 129 setDealign(change.text.length - 1 != change.to.line - change.from.line); 130 } 131 function swapDoc() { 132 dv.diffOutOfDate = true; 133 dv.dealigned = true; 134 update("full"); 135 } 136 dv.edit.on("change", change); 137 dv.orig.on("change", change); 138 dv.edit.on("swapDoc", swapDoc); 139 dv.orig.on("swapDoc", swapDoc); 140 if (dv.mv.options.connect == "align") { 141 CodeMirror.on(dv.edit.state.trackAlignable, "realign", setDealign) 142 CodeMirror.on(dv.orig.state.trackAlignable, "realign", setDealign) 143 } 144 dv.edit.on("viewportChange", function() { set(false); }); 145 dv.orig.on("viewportChange", function() { set(false); }); 146 update(); 147 return update; 148 } 149 150 function registerScroll(dv, otherDv) { 151 dv.edit.on("scroll", function() { 152 syncScroll(dv, true) && makeConnections(dv); 153 }); 154 dv.orig.on("scroll", function() { 155 syncScroll(dv, false) && makeConnections(dv); 156 if (otherDv) syncScroll(otherDv, true) && makeConnections(otherDv); 157 }); 158 } 159 160 function syncScroll(dv, toOrig) { 161 // Change handler will do a refresh after a timeout when diff is out of date 162 if (dv.diffOutOfDate) { 163 if (dv.lockScroll && dv.needsScrollSync == null) dv.needsScrollSync = toOrig 164 return false 165 } 166 dv.needsScrollSync = null 167 if (!dv.lockScroll) return true; 168 var editor, other, now = +new Date; 169 if (toOrig) { editor = dv.edit; other = dv.orig; } 170 else { editor = dv.orig; other = dv.edit; } 171 // Don't take action if the position of this editor was recently set 172 // (to prevent feedback loops) 173 if (editor.state.scrollSetBy == dv && (editor.state.scrollSetAt || 0) + 250 > now) return false; 174 175 var sInfo = editor.getScrollInfo(); 176 if (dv.mv.options.connect == "align") { 177 targetPos = sInfo.top; 178 } else { 179 var halfScreen = .5 * sInfo.clientHeight, midY = sInfo.top + halfScreen; 180 var mid = editor.lineAtHeight(midY, "local"); 181 var around = chunkBoundariesAround(dv.chunks, mid, toOrig); 182 var off = getOffsets(editor, toOrig ? around.edit : around.orig); 183 var offOther = getOffsets(other, toOrig ? around.orig : around.edit); 184 var ratio = (midY - off.top) / (off.bot - off.top); 185 var targetPos = (offOther.top - halfScreen) + ratio * (offOther.bot - offOther.top); 186 187 var botDist, mix; 188 // Some careful tweaking to make sure no space is left out of view 189 // when scrolling to top or bottom. 190 if (targetPos > sInfo.top && (mix = sInfo.top / halfScreen) < 1) { 191 targetPos = targetPos * mix + sInfo.top * (1 - mix); 192 } else if ((botDist = sInfo.height - sInfo.clientHeight - sInfo.top) < halfScreen) { 193 var otherInfo = other.getScrollInfo(); 194 var botDistOther = otherInfo.height - otherInfo.clientHeight - targetPos; 195 if (botDistOther > botDist && (mix = botDist / halfScreen) < 1) 196 targetPos = targetPos * mix + (otherInfo.height - otherInfo.clientHeight - botDist) * (1 - mix); 197 } 198 } 199 200 other.scrollTo(sInfo.left, targetPos); 201 other.state.scrollSetAt = now; 202 other.state.scrollSetBy = dv; 203 return true; 204 } 205 206 function getOffsets(editor, around) { 207 var bot = around.after; 208 if (bot == null) bot = editor.lastLine() + 1; 209 return {top: editor.heightAtLine(around.before || 0, "local"), 210 bot: editor.heightAtLine(bot, "local")}; 211 } 212 213 function setScrollLock(dv, val, action) { 214 dv.lockScroll = val; 215 if (val && action != false) syncScroll(dv, DIFF_INSERT) && makeConnections(dv); 216 (val ? CodeMirror.addClass : CodeMirror.rmClass)(dv.lockButton, "CodeMirror-merge-scrolllock-enabled"); 217 } 218 219 // Updating the marks for editor content 220 221 function removeClass(editor, line, classes) { 222 var locs = classes.classLocation 223 for (var i = 0; i < locs.length; i++) { 224 editor.removeLineClass(line, locs[i], classes.chunk); 225 editor.removeLineClass(line, locs[i], classes.start); 226 editor.removeLineClass(line, locs[i], classes.end); 227 } 228 } 229 230 function clearMarks(editor, arr, classes) { 231 for (var i = 0; i < arr.length; ++i) { 232 var mark = arr[i]; 233 if (mark instanceof CodeMirror.TextMarker) 234 mark.clear(); 235 else if (mark.parent) 236 removeClass(editor, mark, classes); 237 } 238 arr.length = 0; 239 } 240 241 // FIXME maybe add a margin around viewport to prevent too many updates 242 function updateMarks(editor, diff, state, type, classes) { 243 var vp = editor.getViewport(); 244 editor.operation(function() { 245 if (state.from == state.to || vp.from - state.to > 20 || state.from - vp.to > 20) { 246 clearMarks(editor, state.marked, classes); 247 markChanges(editor, diff, type, state.marked, vp.from, vp.to, classes); 248 state.from = vp.from; state.to = vp.to; 249 } else { 250 if (vp.from < state.from) { 251 markChanges(editor, diff, type, state.marked, vp.from, state.from, classes); 252 state.from = vp.from; 253 } 254 if (vp.to > state.to) { 255 markChanges(editor, diff, type, state.marked, state.to, vp.to, classes); 256 state.to = vp.to; 257 } 258 } 259 }); 260 } 261 262 function addClass(editor, lineNr, classes, main, start, end) { 263 var locs = classes.classLocation, line = editor.getLineHandle(lineNr); 264 for (var i = 0; i < locs.length; i++) { 265 if (main) editor.addLineClass(line, locs[i], classes.chunk); 266 if (start) editor.addLineClass(line, locs[i], classes.start); 267 if (end) editor.addLineClass(line, locs[i], classes.end); 268 } 269 return line; 270 } 271 272 function markChanges(editor, diff, type, marks, from, to, classes) { 273 var pos = Pos(0, 0); 274 var top = Pos(from, 0), bot = editor.clipPos(Pos(to - 1)); 275 var cls = type == DIFF_DELETE ? classes.del : classes.insert; 276 function markChunk(start, end) { 277 var bfrom = Math.max(from, start), bto = Math.min(to, end); 278 for (var i = bfrom; i < bto; ++i) 279 marks.push(addClass(editor, i, classes, true, i == start, i == end - 1)); 280 // When the chunk is empty, make sure a horizontal line shows up 281 if (start == end && bfrom == end && bto == end) { 282 if (bfrom) 283 marks.push(addClass(editor, bfrom - 1, classes, false, false, true)); 284 else 285 marks.push(addClass(editor, bfrom, classes, false, true, false)); 286 } 287 } 288 289 var chunkStart = 0, pending = false; 290 for (var i = 0; i < diff.length; ++i) { 291 var part = diff[i], tp = part[0], str = part[1]; 292 if (tp == DIFF_EQUAL) { 293 var cleanFrom = pos.line + (startOfLineClean(diff, i) ? 0 : 1); 294 moveOver(pos, str); 295 var cleanTo = pos.line + (endOfLineClean(diff, i) ? 1 : 0); 296 if (cleanTo > cleanFrom) { 297 if (pending) { markChunk(chunkStart, cleanFrom); pending = false } 298 chunkStart = cleanTo; 299 } 300 } else { 301 pending = true 302 if (tp == type) { 303 var end = moveOver(pos, str, true); 304 var a = posMax(top, pos), b = posMin(bot, end); 305 if (!posEq(a, b)) 306 marks.push(editor.markText(a, b, {className: cls})); 307 pos = end; 308 } 309 } 310 } 311 if (pending) markChunk(chunkStart, pos.line + 1); 312 } 313 314 // Updating the gap between editor and original 315 316 function makeConnections(dv) { 317 if (!dv.showDifferences) return; 318 319 if (dv.svg) { 320 clear(dv.svg); 321 var w = dv.gap.offsetWidth; 322 attrs(dv.svg, "width", w, "height", dv.gap.offsetHeight); 323 } 324 if (dv.copyButtons) clear(dv.copyButtons); 325 326 var vpEdit = dv.edit.getViewport(), vpOrig = dv.orig.getViewport(); 327 var outerTop = dv.mv.wrap.getBoundingClientRect().top 328 var sTopEdit = outerTop - dv.edit.getScrollerElement().getBoundingClientRect().top + dv.edit.getScrollInfo().top 329 var sTopOrig = outerTop - dv.orig.getScrollerElement().getBoundingClientRect().top + dv.orig.getScrollInfo().top; 330 for (var i = 0; i < dv.chunks.length; i++) { 331 var ch = dv.chunks[i]; 332 if (ch.editFrom <= vpEdit.to && ch.editTo >= vpEdit.from && 333 ch.origFrom <= vpOrig.to && ch.origTo >= vpOrig.from) 334 drawConnectorsForChunk(dv, ch, sTopOrig, sTopEdit, w); 335 } 336 } 337 338 function getMatchingOrigLine(editLine, chunks) { 339 var editStart = 0, origStart = 0; 340 for (var i = 0; i < chunks.length; i++) { 341 var chunk = chunks[i]; 342 if (chunk.editTo > editLine && chunk.editFrom <= editLine) return null; 343 if (chunk.editFrom > editLine) break; 344 editStart = chunk.editTo; 345 origStart = chunk.origTo; 346 } 347 return origStart + (editLine - editStart); 348 } 349 350 // Combines information about chunks and widgets/markers to return 351 // an array of lines, in a single editor, that probably need to be 352 // aligned with their counterparts in the editor next to it. 353 function alignableFor(cm, chunks, isOrig) { 354 var tracker = cm.state.trackAlignable 355 var start = cm.firstLine(), trackI = 0 356 var result = [] 357 for (var i = 0;; i++) { 358 var chunk = chunks[i] 359 var chunkStart = !chunk ? 1e9 : isOrig ? chunk.origFrom : chunk.editFrom 360 for (; trackI < tracker.alignable.length; trackI += 2) { 361 var n = tracker.alignable[trackI] + 1 362 if (n <= start) continue 363 if (n <= chunkStart) result.push(n) 364 else break 365 } 366 if (!chunk) break 367 result.push(start = isOrig ? chunk.origTo : chunk.editTo) 368 } 369 return result 370 } 371 372 // Given information about alignable lines in two editors, fill in 373 // the result (an array of three-element arrays) to reflect the 374 // lines that need to be aligned with each other. 375 function mergeAlignable(result, origAlignable, chunks, setIndex) { 376 var rI = 0, origI = 0, chunkI = 0, diff = 0 377 outer: for (;; rI++) { 378 var nextR = result[rI], nextO = origAlignable[origI] 379 if (!nextR && nextO == null) break 380 381 var rLine = nextR ? nextR[0] : 1e9, oLine = nextO == null ? 1e9 : nextO 382 while (chunkI < chunks.length) { 383 var chunk = chunks[chunkI] 384 if (chunk.origFrom <= oLine && chunk.origTo > oLine) { 385 origI++ 386 rI-- 387 continue outer; 388 } 389 if (chunk.editTo > rLine) { 390 if (chunk.editFrom <= rLine) continue outer; 391 break 392 } 393 diff += (chunk.origTo - chunk.origFrom) - (chunk.editTo - chunk.editFrom) 394 chunkI++ 395 } 396 if (rLine == oLine - diff) { 397 nextR[setIndex] = oLine 398 origI++ 399 } else if (rLine < oLine - diff) { 400 nextR[setIndex] = rLine + diff 401 } else { 402 var record = [oLine - diff, null, null] 403 record[setIndex] = oLine 404 result.splice(rI, 0, record) 405 origI++ 406 } 407 } 408 } 409 410 function findAlignedLines(dv, other) { 411 var alignable = alignableFor(dv.edit, dv.chunks, false), result = [] 412 if (other) for (var i = 0, j = 0; i < other.chunks.length; i++) { 413 var n = other.chunks[i].editTo 414 while (j < alignable.length && alignable[j] < n) j++ 415 if (j == alignable.length || alignable[j] != n) alignable.splice(j++, 0, n) 416 } 417 for (var i = 0; i < alignable.length; i++) 418 result.push([alignable[i], null, null]) 419 420 mergeAlignable(result, alignableFor(dv.orig, dv.chunks, true), dv.chunks, 1) 421 if (other) 422 mergeAlignable(result, alignableFor(other.orig, other.chunks, true), other.chunks, 2) 423 424 return result 425 } 426 427 function alignChunks(dv, force) { 428 if (!dv.dealigned && !force) return; 429 if (!dv.orig.curOp) return dv.orig.operation(function() { 430 alignChunks(dv, force); 431 }); 432 433 dv.dealigned = false; 434 var other = dv.mv.left == dv ? dv.mv.right : dv.mv.left; 435 if (other) { 436 ensureDiff(other); 437 other.dealigned = false; 438 } 439 var linesToAlign = findAlignedLines(dv, other); 440 441 // Clear old aligners 442 var aligners = dv.mv.aligners; 443 for (var i = 0; i < aligners.length; i++) 444 aligners[i].clear(); 445 aligners.length = 0; 446 447 var cm = [dv.edit, dv.orig], scroll = [], offset = [] 448 if (other) cm.push(other.orig); 449 for (var i = 0; i < cm.length; i++) { 450 scroll.push(cm[i].getScrollInfo().top); 451 offset.push(-cm[i].getScrollerElement().getBoundingClientRect().top) 452 } 453 454 if (offset[0] != offset[1] || cm.length == 3 && offset[1] != offset[2]) 455 alignLines(cm, offset, [0, 0, 0], aligners) 456 for (var ln = 0; ln < linesToAlign.length; ln++) 457 alignLines(cm, offset, linesToAlign[ln], aligners); 458 459 for (var i = 0; i < cm.length; i++) 460 cm[i].scrollTo(null, scroll[i]); 461 } 462 463 function alignLines(cm, cmOffset, lines, aligners) { 464 var maxOffset = -1e8, offset = []; 465 for (var i = 0; i < cm.length; i++) if (lines[i] != null) { 466 var off = cm[i].heightAtLine(lines[i], "local") - cmOffset[i]; 467 offset[i] = off; 468 maxOffset = Math.max(maxOffset, off); 469 } 470 for (var i = 0; i < cm.length; i++) if (lines[i] != null) { 471 var diff = maxOffset - offset[i]; 472 if (diff > 1) 473 aligners.push(padAbove(cm[i], lines[i], diff)); 474 } 475 } 476 477 function padAbove(cm, line, size) { 478 var above = true; 479 if (line > cm.lastLine()) { 480 line--; 481 above = false; 482 } 483 var elt = document.createElement("div"); 484 elt.className = "CodeMirror-merge-spacer"; 485 elt.style.height = size + "px"; elt.style.minWidth = "1px"; 486 return cm.addLineWidget(line, elt, {height: size, above: above, mergeSpacer: true, handleMouseEvents: true}); 487 } 488 489 function drawConnectorsForChunk(dv, chunk, sTopOrig, sTopEdit, w) { 490 var flip = dv.type == "left"; 491 var top = dv.orig.heightAtLine(chunk.origFrom, "local", true) - sTopOrig; 492 if (dv.svg) { 493 var topLpx = top; 494 var topRpx = dv.edit.heightAtLine(chunk.editFrom, "local", true) - sTopEdit; 495 if (flip) { var tmp = topLpx; topLpx = topRpx; topRpx = tmp; } 496 var botLpx = dv.orig.heightAtLine(chunk.origTo, "local", true) - sTopOrig; 497 var botRpx = dv.edit.heightAtLine(chunk.editTo, "local", true) - sTopEdit; 498 if (flip) { var tmp = botLpx; botLpx = botRpx; botRpx = tmp; } 499 var curveTop = " C " + w/2 + " " + topRpx + " " + w/2 + " " + topLpx + " " + (w + 2) + " " + topLpx; 500 var curveBot = " C " + w/2 + " " + botLpx + " " + w/2 + " " + botRpx + " -1 " + botRpx; 501 attrs(dv.svg.appendChild(document.createElementNS(svgNS, "path")), 502 "d", "M -1 " + topRpx + curveTop + " L " + (w + 2) + " " + botLpx + curveBot + " z", 503 "class", dv.classes.connect); 504 } 505 if (dv.copyButtons) { 506 var copy = dv.copyButtons.appendChild(elt("div", dv.type == "left" ? "\u21dd" : "\u21dc", 507 "CodeMirror-merge-copy")); 508 var editOriginals = dv.mv.options.allowEditingOriginals; 509 copy.title = dv.edit.phrase(editOriginals ? "Push to left" : "Revert chunk"); 510 copy.chunk = chunk; 511 copy.style.top = (chunk.origTo > chunk.origFrom ? top : dv.edit.heightAtLine(chunk.editFrom, "local") - sTopEdit) + "px"; 512 copy.setAttribute("role", "button"); 513 copy.setAttribute("tabindex", "0"); 514 copy.setAttribute("aria-label", copy.title); 515 516 if (editOriginals) { 517 var topReverse = dv.edit.heightAtLine(chunk.editFrom, "local") - sTopEdit; 518 var copyReverse = dv.copyButtons.appendChild(elt("div", dv.type == "right" ? "\u21dd" : "\u21dc", 519 "CodeMirror-merge-copy-reverse")); 520 copyReverse.title = "Push to right"; 521 copyReverse.chunk = {editFrom: chunk.origFrom, editTo: chunk.origTo, 522 origFrom: chunk.editFrom, origTo: chunk.editTo}; 523 copyReverse.style.top = topReverse + "px"; 524 dv.type == "right" ? copyReverse.style.left = "2px" : copyReverse.style.right = "2px"; 525 copyReverse.setAttribute("role", "button"); 526 copyReverse.setAttribute("tabindex", "0"); 527 copyReverse.setAttribute("aria-label", copyReverse.title); 528 } 529 } 530 } 531 532 function copyChunk(dv, to, from, chunk) { 533 if (dv.diffOutOfDate) return; 534 var origStart = chunk.origTo > from.lastLine() ? Pos(chunk.origFrom - 1) : Pos(chunk.origFrom, 0) 535 var origEnd = Pos(chunk.origTo, 0) 536 var editStart = chunk.editTo > to.lastLine() ? Pos(chunk.editFrom - 1) : Pos(chunk.editFrom, 0) 537 var editEnd = Pos(chunk.editTo, 0) 538 var handler = dv.mv.options.revertChunk 539 if (handler) 540 handler(dv.mv, from, origStart, origEnd, to, editStart, editEnd) 541 else 542 to.replaceRange(from.getRange(origStart, origEnd), editStart, editEnd) 543 } 544 545 // Merge view, containing 0, 1, or 2 diff views. 546 547 var MergeView = CodeMirror.MergeView = function(node, options) { 548 if (!(this instanceof MergeView)) return new MergeView(node, options); 549 550 this.options = options; 551 var origLeft = options.origLeft, origRight = options.origRight == null ? options.orig : options.origRight; 552 553 var hasLeft = origLeft != null, hasRight = origRight != null; 554 var panes = 1 + (hasLeft ? 1 : 0) + (hasRight ? 1 : 0); 555 var wrap = [], left = this.left = null, right = this.right = null; 556 var self = this; 557 558 if (hasLeft) { 559 left = this.left = new DiffView(this, "left"); 560 var leftPane = elt("div", null, "CodeMirror-merge-pane CodeMirror-merge-left"); 561 wrap.push(leftPane); 562 wrap.push(buildGap(left)); 563 } 564 565 var editPane = elt("div", null, "CodeMirror-merge-pane CodeMirror-merge-editor"); 566 wrap.push(editPane); 567 568 if (hasRight) { 569 right = this.right = new DiffView(this, "right"); 570 wrap.push(buildGap(right)); 571 var rightPane = elt("div", null, "CodeMirror-merge-pane CodeMirror-merge-right"); 572 wrap.push(rightPane); 573 } 574 575 (hasRight ? rightPane : editPane).className += " CodeMirror-merge-pane-rightmost"; 576 577 wrap.push(elt("div", null, null, "height: 0; clear: both;")); 578 579 var wrapElt = this.wrap = node.appendChild(elt("div", wrap, "CodeMirror-merge CodeMirror-merge-" + panes + "pane")); 580 this.edit = CodeMirror(editPane, copyObj(options)); 581 582 if (left) left.init(leftPane, origLeft, options); 583 if (right) right.init(rightPane, origRight, options); 584 if (options.collapseIdentical) 585 this.editor().operation(function() { 586 collapseIdenticalStretches(self, options.collapseIdentical); 587 }); 588 if (options.connect == "align") { 589 this.aligners = []; 590 alignChunks(this.left || this.right, true); 591 } 592 if (left) left.registerEvents(right) 593 if (right) right.registerEvents(left) 594 595 596 var onResize = function() { 597 if (left) makeConnections(left); 598 if (right) makeConnections(right); 599 }; 600 CodeMirror.on(window, "resize", onResize); 601 var resizeInterval = setInterval(function() { 602 for (var p = wrapElt.parentNode; p && p != document.body; p = p.parentNode) {} 603 if (!p) { clearInterval(resizeInterval); CodeMirror.off(window, "resize", onResize); } 604 }, 5000); 605 }; 606 607 function buildGap(dv) { 608 var lock = dv.lockButton = elt("div", null, "CodeMirror-merge-scrolllock"); 609 lock.setAttribute("role", "button"); 610 lock.setAttribute("tabindex", "0"); 611 var lockWrap = elt("div", [lock], "CodeMirror-merge-scrolllock-wrap"); 612 CodeMirror.on(lock, "click", function() { setScrollLock(dv, !dv.lockScroll); }); 613 CodeMirror.on(lock, "keyup", function(e) { e.key === "Enter" && setScrollLock(dv, !dv.lockScroll); }); 614 var gapElts = [lockWrap]; 615 if (dv.mv.options.revertButtons !== false) { 616 dv.copyButtons = elt("div", null, "CodeMirror-merge-copybuttons-" + dv.type); 617 function copyButtons(e) { 618 var node = e.target || e.srcElement; 619 if (!node.chunk) return; 620 if (node.className == "CodeMirror-merge-copy-reverse") { 621 copyChunk(dv, dv.orig, dv.edit, node.chunk); 622 return; 623 } 624 copyChunk(dv, dv.edit, dv.orig, node.chunk); 625 } 626 CodeMirror.on(dv.copyButtons, "click", copyButtons); 627 CodeMirror.on(dv.copyButtons, "keyup", function(e) { e.key === "Enter" && copyButtons(e); }); 628 gapElts.unshift(dv.copyButtons); 629 } 630 if (dv.mv.options.connect != "align") { 631 var svg = document.createElementNS && document.createElementNS(svgNS, "svg"); 632 if (svg && !svg.createSVGRect) svg = null; 633 dv.svg = svg; 634 if (svg) gapElts.push(svg); 635 } 636 637 return dv.gap = elt("div", gapElts, "CodeMirror-merge-gap"); 638 } 639 640 MergeView.prototype = { 641 constructor: MergeView, 642 editor: function() { return this.edit; }, 643 rightOriginal: function() { return this.right && this.right.orig; }, 644 leftOriginal: function() { return this.left && this.left.orig; }, 645 setShowDifferences: function(val) { 646 if (this.right) this.right.setShowDifferences(val); 647 if (this.left) this.left.setShowDifferences(val); 648 }, 649 rightChunks: function() { 650 if (this.right) { ensureDiff(this.right); return this.right.chunks; } 651 }, 652 leftChunks: function() { 653 if (this.left) { ensureDiff(this.left); return this.left.chunks; } 654 } 655 }; 656 657 function asString(obj) { 658 if (typeof obj == "string") return obj; 659 else return obj.getValue(); 660 } 661 662 // Operations on diffs 663 var dmp; 664 function getDiff(a, b, ignoreWhitespace) { 665 if (!dmp) dmp = new diff_match_patch(); 666 667 var diff = dmp.diff_main(a, b); 668 // The library sometimes leaves in empty parts, which confuse the algorithm 669 for (var i = 0; i < diff.length; ++i) { 670 var part = diff[i]; 671 if (ignoreWhitespace ? !/[^ \t]/.test(part[1]) : !part[1]) { 672 diff.splice(i--, 1); 673 } else if (i && diff[i - 1][0] == part[0]) { 674 diff.splice(i--, 1); 675 diff[i][1] += part[1]; 676 } 677 } 678 return diff; 679 } 680 681 function getChunks(diff) { 682 var chunks = []; 683 if (!diff.length) return chunks; 684 var startEdit = 0, startOrig = 0; 685 var edit = Pos(0, 0), orig = Pos(0, 0); 686 for (var i = 0; i < diff.length; ++i) { 687 var part = diff[i], tp = part[0]; 688 if (tp == DIFF_EQUAL) { 689 var startOff = !startOfLineClean(diff, i) || edit.line < startEdit || orig.line < startOrig ? 1 : 0; 690 var cleanFromEdit = edit.line + startOff, cleanFromOrig = orig.line + startOff; 691 moveOver(edit, part[1], null, orig); 692 var endOff = endOfLineClean(diff, i) ? 1 : 0; 693 var cleanToEdit = edit.line + endOff, cleanToOrig = orig.line + endOff; 694 if (cleanToEdit > cleanFromEdit) { 695 if (i) chunks.push({origFrom: startOrig, origTo: cleanFromOrig, 696 editFrom: startEdit, editTo: cleanFromEdit}); 697 startEdit = cleanToEdit; startOrig = cleanToOrig; 698 } 699 } else { 700 moveOver(tp == DIFF_INSERT ? edit : orig, part[1]); 701 } 702 } 703 if (startEdit <= edit.line || startOrig <= orig.line) 704 chunks.push({origFrom: startOrig, origTo: orig.line + 1, 705 editFrom: startEdit, editTo: edit.line + 1}); 706 return chunks; 707 } 708 709 function endOfLineClean(diff, i) { 710 if (i == diff.length - 1) return true; 711 var next = diff[i + 1][1]; 712 if ((next.length == 1 && i < diff.length - 2) || next.charCodeAt(0) != 10) return false; 713 if (i == diff.length - 2) return true; 714 next = diff[i + 2][1]; 715 return (next.length > 1 || i == diff.length - 3) && next.charCodeAt(0) == 10; 716 } 717 718 function startOfLineClean(diff, i) { 719 if (i == 0) return true; 720 var last = diff[i - 1][1]; 721 if (last.charCodeAt(last.length - 1) != 10) return false; 722 if (i == 1) return true; 723 last = diff[i - 2][1]; 724 return last.charCodeAt(last.length - 1) == 10; 725 } 726 727 function chunkBoundariesAround(chunks, n, nInEdit) { 728 var beforeE, afterE, beforeO, afterO; 729 for (var i = 0; i < chunks.length; i++) { 730 var chunk = chunks[i]; 731 var fromLocal = nInEdit ? chunk.editFrom : chunk.origFrom; 732 var toLocal = nInEdit ? chunk.editTo : chunk.origTo; 733 if (afterE == null) { 734 if (fromLocal > n) { afterE = chunk.editFrom; afterO = chunk.origFrom; } 735 else if (toLocal > n) { afterE = chunk.editTo; afterO = chunk.origTo; } 736 } 737 if (toLocal <= n) { beforeE = chunk.editTo; beforeO = chunk.origTo; } 738 else if (fromLocal <= n) { beforeE = chunk.editFrom; beforeO = chunk.origFrom; } 739 } 740 return {edit: {before: beforeE, after: afterE}, orig: {before: beforeO, after: afterO}}; 741 } 742 743 function collapseSingle(cm, from, to) { 744 cm.addLineClass(from, "wrap", "CodeMirror-merge-collapsed-line"); 745 var widget = document.createElement("span"); 746 widget.className = "CodeMirror-merge-collapsed-widget"; 747 widget.title = cm.phrase("Identical text collapsed. Click to expand."); 748 var mark = cm.markText(Pos(from, 0), Pos(to - 1), { 749 inclusiveLeft: true, 750 inclusiveRight: true, 751 replacedWith: widget, 752 clearOnEnter: true 753 }); 754 function clear() { 755 mark.clear(); 756 cm.removeLineClass(from, "wrap", "CodeMirror-merge-collapsed-line"); 757 } 758 if (mark.explicitlyCleared) clear(); 759 CodeMirror.on(widget, "click", clear); 760 mark.on("clear", clear); 761 CodeMirror.on(widget, "click", clear); 762 return {mark: mark, clear: clear}; 763 } 764 765 function collapseStretch(size, editors) { 766 var marks = []; 767 function clear() { 768 for (var i = 0; i < marks.length; i++) marks[i].clear(); 769 } 770 for (var i = 0; i < editors.length; i++) { 771 var editor = editors[i]; 772 var mark = collapseSingle(editor.cm, editor.line, editor.line + size); 773 marks.push(mark); 774 mark.mark.on("clear", clear); 775 } 776 return marks[0].mark; 777 } 778 779 function unclearNearChunks(dv, margin, off, clear) { 780 for (var i = 0; i < dv.chunks.length; i++) { 781 var chunk = dv.chunks[i]; 782 for (var l = chunk.editFrom - margin; l < chunk.editTo + margin; l++) { 783 var pos = l + off; 784 if (pos >= 0 && pos < clear.length) clear[pos] = false; 785 } 786 } 787 } 788 789 function collapseIdenticalStretches(mv, margin) { 790 if (typeof margin != "number") margin = 2; 791 var clear = [], edit = mv.editor(), off = edit.firstLine(); 792 for (var l = off, e = edit.lastLine(); l <= e; l++) clear.push(true); 793 if (mv.left) unclearNearChunks(mv.left, margin, off, clear); 794 if (mv.right) unclearNearChunks(mv.right, margin, off, clear); 795 796 for (var i = 0; i < clear.length; i++) { 797 if (clear[i]) { 798 var line = i + off; 799 for (var size = 1; i < clear.length - 1 && clear[i + 1]; i++, size++) {} 800 if (size > margin) { 801 var editors = [{line: line, cm: edit}]; 802 if (mv.left) editors.push({line: getMatchingOrigLine(line, mv.left.chunks), cm: mv.left.orig}); 803 if (mv.right) editors.push({line: getMatchingOrigLine(line, mv.right.chunks), cm: mv.right.orig}); 804 var mark = collapseStretch(size, editors); 805 if (mv.options.onCollapse) mv.options.onCollapse(mv, line, size, mark); 806 } 807 } 808 } 809 } 810 811 // General utilities 812 813 function elt(tag, content, className, style) { 814 var e = document.createElement(tag); 815 if (className) e.className = className; 816 if (style) e.style.cssText = style; 817 if (typeof content == "string") e.appendChild(document.createTextNode(content)); 818 else if (content) for (var i = 0; i < content.length; ++i) e.appendChild(content[i]); 819 return e; 820 } 821 822 function clear(node) { 823 for (var count = node.childNodes.length; count > 0; --count) 824 node.removeChild(node.firstChild); 825 } 826 827 function attrs(elt) { 828 for (var i = 1; i < arguments.length; i += 2) 829 elt.setAttribute(arguments[i], arguments[i+1]); 830 } 831 832 function copyObj(obj, target) { 833 if (!target) target = {}; 834 for (var prop in obj) if (obj.hasOwnProperty(prop)) target[prop] = obj[prop]; 835 return target; 836 } 837 838 function moveOver(pos, str, copy, other) { 839 var out = copy ? Pos(pos.line, pos.ch) : pos, at = 0; 840 for (;;) { 841 var nl = str.indexOf("\n", at); 842 if (nl == -1) break; 843 ++out.line; 844 if (other) ++other.line; 845 at = nl + 1; 846 } 847 out.ch = (at ? 0 : out.ch) + (str.length - at); 848 if (other) other.ch = (at ? 0 : other.ch) + (str.length - at); 849 return out; 850 } 851 852 // Tracks collapsed markers and line widgets, in order to be able to 853 // accurately align the content of two editors. 854 855 var F_WIDGET = 1, F_WIDGET_BELOW = 2, F_MARKER = 4 856 857 function TrackAlignable(cm) { 858 this.cm = cm 859 this.alignable = [] 860 this.height = cm.doc.height 861 var self = this 862 cm.on("markerAdded", function(_, marker) { 863 if (!marker.collapsed) return 864 var found = marker.find(1) 865 if (found != null) self.set(found.line, F_MARKER) 866 }) 867 cm.on("markerCleared", function(_, marker, _min, max) { 868 if (max != null && marker.collapsed) 869 self.check(max, F_MARKER, self.hasMarker) 870 }) 871 cm.on("markerChanged", this.signal.bind(this)) 872 cm.on("lineWidgetAdded", function(_, widget, lineNo) { 873 if (widget.mergeSpacer) return 874 if (widget.above) self.set(lineNo - 1, F_WIDGET_BELOW) 875 else self.set(lineNo, F_WIDGET) 876 }) 877 cm.on("lineWidgetCleared", function(_, widget, lineNo) { 878 if (widget.mergeSpacer) return 879 if (widget.above) self.check(lineNo - 1, F_WIDGET_BELOW, self.hasWidgetBelow) 880 else self.check(lineNo, F_WIDGET, self.hasWidget) 881 }) 882 cm.on("lineWidgetChanged", this.signal.bind(this)) 883 cm.on("change", function(_, change) { 884 var start = change.from.line, nBefore = change.to.line - change.from.line 885 var nAfter = change.text.length - 1, end = start + nAfter 886 if (nBefore || nAfter) self.map(start, nBefore, nAfter) 887 self.check(end, F_MARKER, self.hasMarker) 888 if (nBefore || nAfter) self.check(change.from.line, F_MARKER, self.hasMarker) 889 }) 890 cm.on("viewportChange", function() { 891 if (self.cm.doc.height != self.height) self.signal() 892 }) 893 } 894 895 TrackAlignable.prototype = { 896 signal: function() { 897 CodeMirror.signal(this, "realign") 898 this.height = this.cm.doc.height 899 }, 900 901 set: function(n, flags) { 902 var pos = -1 903 for (; pos < this.alignable.length; pos += 2) { 904 var diff = this.alignable[pos] - n 905 if (diff == 0) { 906 if ((this.alignable[pos + 1] & flags) == flags) return 907 this.alignable[pos + 1] |= flags 908 this.signal() 909 return 910 } 911 if (diff > 0) break 912 } 913 this.signal() 914 this.alignable.splice(pos, 0, n, flags) 915 }, 916 917 find: function(n) { 918 for (var i = 0; i < this.alignable.length; i += 2) 919 if (this.alignable[i] == n) return i 920 return -1 921 }, 922 923 check: function(n, flag, pred) { 924 var found = this.find(n) 925 if (found == -1 || !(this.alignable[found + 1] & flag)) return 926 if (!pred.call(this, n)) { 927 this.signal() 928 var flags = this.alignable[found + 1] & ~flag 929 if (flags) this.alignable[found + 1] = flags 930 else this.alignable.splice(found, 2) 931 } 932 }, 933 934 hasMarker: function(n) { 935 var handle = this.cm.getLineHandle(n) 936 if (handle.markedSpans) for (var i = 0; i < handle.markedSpans.length; i++) 937 if (handle.markedSpans[i].marker.collapsed && handle.markedSpans[i].to != null) 938 return true 939 return false 940 }, 941 942 hasWidget: function(n) { 943 var handle = this.cm.getLineHandle(n) 944 if (handle.widgets) for (var i = 0; i < handle.widgets.length; i++) 945 if (!handle.widgets[i].above && !handle.widgets[i].mergeSpacer) return true 946 return false 947 }, 948 949 hasWidgetBelow: function(n) { 950 if (n == this.cm.lastLine()) return false 951 var handle = this.cm.getLineHandle(n + 1) 952 if (handle.widgets) for (var i = 0; i < handle.widgets.length; i++) 953 if (handle.widgets[i].above && !handle.widgets[i].mergeSpacer) return true 954 return false 955 }, 956 957 map: function(from, nBefore, nAfter) { 958 var diff = nAfter - nBefore, to = from + nBefore, widgetFrom = -1, widgetTo = -1 959 for (var i = 0; i < this.alignable.length; i += 2) { 960 var n = this.alignable[i] 961 if (n == from && (this.alignable[i + 1] & F_WIDGET_BELOW)) widgetFrom = i 962 if (n == to && (this.alignable[i + 1] & F_WIDGET_BELOW)) widgetTo = i 963 if (n <= from) continue 964 else if (n < to) this.alignable.splice(i--, 2) 965 else this.alignable[i] += diff 966 } 967 if (widgetFrom > -1) { 968 var flags = this.alignable[widgetFrom + 1] 969 if (flags == F_WIDGET_BELOW) this.alignable.splice(widgetFrom, 2) 970 else this.alignable[widgetFrom + 1] = flags & ~F_WIDGET_BELOW 971 } 972 if (widgetTo > -1 && nAfter) 973 this.set(from + nAfter, F_WIDGET_BELOW) 974 } 975 } 976 977 function posMin(a, b) { return (a.line - b.line || a.ch - b.ch) < 0 ? a : b; } 978 function posMax(a, b) { return (a.line - b.line || a.ch - b.ch) > 0 ? a : b; } 979 function posEq(a, b) { return a.line == b.line && a.ch == b.ch; } 980 981 function findPrevDiff(chunks, start, isOrig) { 982 for (var i = chunks.length - 1; i >= 0; i--) { 983 var chunk = chunks[i]; 984 var to = (isOrig ? chunk.origTo : chunk.editTo) - 1; 985 if (to < start) return to; 986 } 987 } 988 989 function findNextDiff(chunks, start, isOrig) { 990 for (var i = 0; i < chunks.length; i++) { 991 var chunk = chunks[i]; 992 var from = (isOrig ? chunk.origFrom : chunk.editFrom); 993 if (from > start) return from; 994 } 995 } 996 997 function goNearbyDiff(cm, dir) { 998 var found = null, views = cm.state.diffViews, line = cm.getCursor().line; 999 if (views) for (var i = 0; i < views.length; i++) { 1000 var dv = views[i], isOrig = cm == dv.orig; 1001 ensureDiff(dv); 1002 var pos = dir < 0 ? findPrevDiff(dv.chunks, line, isOrig) : findNextDiff(dv.chunks, line, isOrig); 1003 if (pos != null && (found == null || (dir < 0 ? pos > found : pos < found))) 1004 found = pos; 1005 } 1006 if (found != null) 1007 cm.setCursor(found, 0); 1008 else 1009 return CodeMirror.Pass; 1010 } 1011 1012 CodeMirror.commands.goNextDiff = function(cm) { 1013 return goNearbyDiff(cm, 1); 1014 }; 1015 CodeMirror.commands.goPrevDiff = function(cm) { 1016 return goNearbyDiff(cm, -1); 1017 }; 1018 });
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Wed Sep 7 05:41:13 2022 | Chilli.vc Blog - For Webmaster,Blog-Writer,System Admin and Domainer |