[ Index ] |
PHP Cross Reference of Joomla 4.2.2 documentation |
[Summary view] [Print] [Text view]
1 (function (global, factory) { 2 typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : 3 typeof define === 'function' && define.amd ? define(['exports'], factory) : 4 (global = global || self, factory(global.Diff = {})); 5 }(this, (function (exports) { 'use strict'; 6 7 function Diff() {} 8 Diff.prototype = { 9 diff: function diff(oldString, newString) { 10 var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; 11 var callback = options.callback; 12 13 if (typeof options === 'function') { 14 callback = options; 15 options = {}; 16 } 17 18 this.options = options; 19 var self = this; 20 21 function done(value) { 22 if (callback) { 23 setTimeout(function () { 24 callback(undefined, value); 25 }, 0); 26 return true; 27 } else { 28 return value; 29 } 30 } // Allow subclasses to massage the input prior to running 31 32 33 oldString = this.castInput(oldString); 34 newString = this.castInput(newString); 35 oldString = this.removeEmpty(this.tokenize(oldString)); 36 newString = this.removeEmpty(this.tokenize(newString)); 37 var newLen = newString.length, 38 oldLen = oldString.length; 39 var editLength = 1; 40 var maxEditLength = newLen + oldLen; 41 var bestPath = [{ 42 newPos: -1, 43 components: [] 44 }]; // Seed editLength = 0, i.e. the content starts with the same values 45 46 var oldPos = this.extractCommon(bestPath[0], newString, oldString, 0); 47 48 if (bestPath[0].newPos + 1 >= newLen && oldPos + 1 >= oldLen) { 49 // Identity per the equality and tokenizer 50 return done([{ 51 value: this.join(newString), 52 count: newString.length 53 }]); 54 } // Main worker method. checks all permutations of a given edit length for acceptance. 55 56 57 function execEditLength() { 58 for (var diagonalPath = -1 * editLength; diagonalPath <= editLength; diagonalPath += 2) { 59 var basePath = void 0; 60 61 var addPath = bestPath[diagonalPath - 1], 62 removePath = bestPath[diagonalPath + 1], 63 _oldPos = (removePath ? removePath.newPos : 0) - diagonalPath; 64 65 if (addPath) { 66 // No one else is going to attempt to use this value, clear it 67 bestPath[diagonalPath - 1] = undefined; 68 } 69 70 var canAdd = addPath && addPath.newPos + 1 < newLen, 71 canRemove = removePath && 0 <= _oldPos && _oldPos < oldLen; 72 73 if (!canAdd && !canRemove) { 74 // If this path is a terminal then prune 75 bestPath[diagonalPath] = undefined; 76 continue; 77 } // Select the diagonal that we want to branch from. We select the prior 78 // path whose position in the new string is the farthest from the origin 79 // and does not pass the bounds of the diff graph 80 81 82 if (!canAdd || canRemove && addPath.newPos < removePath.newPos) { 83 basePath = clonePath(removePath); 84 self.pushComponent(basePath.components, undefined, true); 85 } else { 86 basePath = addPath; // No need to clone, we've pulled it from the list 87 88 basePath.newPos++; 89 self.pushComponent(basePath.components, true, undefined); 90 } 91 92 _oldPos = self.extractCommon(basePath, newString, oldString, diagonalPath); // If we have hit the end of both strings, then we are done 93 94 if (basePath.newPos + 1 >= newLen && _oldPos + 1 >= oldLen) { 95 return done(buildValues(self, basePath.components, newString, oldString, self.useLongestToken)); 96 } else { 97 // Otherwise track this path as a potential candidate and continue. 98 bestPath[diagonalPath] = basePath; 99 } 100 } 101 102 editLength++; 103 } // Performs the length of edit iteration. Is a bit fugly as this has to support the 104 // sync and async mode which is never fun. Loops over execEditLength until a value 105 // is produced. 106 107 108 if (callback) { 109 (function exec() { 110 setTimeout(function () { 111 // This should not happen, but we want to be safe. 112 113 /* istanbul ignore next */ 114 if (editLength > maxEditLength) { 115 return callback(); 116 } 117 118 if (!execEditLength()) { 119 exec(); 120 } 121 }, 0); 122 })(); 123 } else { 124 while (editLength <= maxEditLength) { 125 var ret = execEditLength(); 126 127 if (ret) { 128 return ret; 129 } 130 } 131 } 132 }, 133 pushComponent: function pushComponent(components, added, removed) { 134 var last = components[components.length - 1]; 135 136 if (last && last.added === added && last.removed === removed) { 137 // We need to clone here as the component clone operation is just 138 // as shallow array clone 139 components[components.length - 1] = { 140 count: last.count + 1, 141 added: added, 142 removed: removed 143 }; 144 } else { 145 components.push({ 146 count: 1, 147 added: added, 148 removed: removed 149 }); 150 } 151 }, 152 extractCommon: function extractCommon(basePath, newString, oldString, diagonalPath) { 153 var newLen = newString.length, 154 oldLen = oldString.length, 155 newPos = basePath.newPos, 156 oldPos = newPos - diagonalPath, 157 commonCount = 0; 158 159 while (newPos + 1 < newLen && oldPos + 1 < oldLen && this.equals(newString[newPos + 1], oldString[oldPos + 1])) { 160 newPos++; 161 oldPos++; 162 commonCount++; 163 } 164 165 if (commonCount) { 166 basePath.components.push({ 167 count: commonCount 168 }); 169 } 170 171 basePath.newPos = newPos; 172 return oldPos; 173 }, 174 equals: function equals(left, right) { 175 if (this.options.comparator) { 176 return this.options.comparator(left, right); 177 } else { 178 return left === right || this.options.ignoreCase && left.toLowerCase() === right.toLowerCase(); 179 } 180 }, 181 removeEmpty: function removeEmpty(array) { 182 var ret = []; 183 184 for (var i = 0; i < array.length; i++) { 185 if (array[i]) { 186 ret.push(array[i]); 187 } 188 } 189 190 return ret; 191 }, 192 castInput: function castInput(value) { 193 return value; 194 }, 195 tokenize: function tokenize(value) { 196 return value.split(''); 197 }, 198 join: function join(chars) { 199 return chars.join(''); 200 } 201 }; 202 203 function buildValues(diff, components, newString, oldString, useLongestToken) { 204 var componentPos = 0, 205 componentLen = components.length, 206 newPos = 0, 207 oldPos = 0; 208 209 for (; componentPos < componentLen; componentPos++) { 210 var component = components[componentPos]; 211 212 if (!component.removed) { 213 if (!component.added && useLongestToken) { 214 var value = newString.slice(newPos, newPos + component.count); 215 value = value.map(function (value, i) { 216 var oldValue = oldString[oldPos + i]; 217 return oldValue.length > value.length ? oldValue : value; 218 }); 219 component.value = diff.join(value); 220 } else { 221 component.value = diff.join(newString.slice(newPos, newPos + component.count)); 222 } 223 224 newPos += component.count; // Common case 225 226 if (!component.added) { 227 oldPos += component.count; 228 } 229 } else { 230 component.value = diff.join(oldString.slice(oldPos, oldPos + component.count)); 231 oldPos += component.count; // Reverse add and remove so removes are output first to match common convention 232 // The diffing algorithm is tied to add then remove output and this is the simplest 233 // route to get the desired output with minimal overhead. 234 235 if (componentPos && components[componentPos - 1].added) { 236 var tmp = components[componentPos - 1]; 237 components[componentPos - 1] = components[componentPos]; 238 components[componentPos] = tmp; 239 } 240 } 241 } // Special case handle for when one terminal is ignored (i.e. whitespace). 242 // For this case we merge the terminal into the prior string and drop the change. 243 // This is only available for string mode. 244 245 246 var lastComponent = components[componentLen - 1]; 247 248 if (componentLen > 1 && typeof lastComponent.value === 'string' && (lastComponent.added || lastComponent.removed) && diff.equals('', lastComponent.value)) { 249 components[componentLen - 2].value += lastComponent.value; 250 components.pop(); 251 } 252 253 return components; 254 } 255 256 function clonePath(path) { 257 return { 258 newPos: path.newPos, 259 components: path.components.slice(0) 260 }; 261 } 262 263 var characterDiff = new Diff(); 264 function diffChars(oldStr, newStr, options) { 265 return characterDiff.diff(oldStr, newStr, options); 266 } 267 268 function generateOptions(options, defaults) { 269 if (typeof options === 'function') { 270 defaults.callback = options; 271 } else if (options) { 272 for (var name in options) { 273 /* istanbul ignore else */ 274 if (options.hasOwnProperty(name)) { 275 defaults[name] = options[name]; 276 } 277 } 278 } 279 280 return defaults; 281 } 282 283 // 284 // Ranges and exceptions: 285 // Latin-1 Supplement, 0080–00FF 286 // - U+00D7 × Multiplication sign 287 // - U+00F7 ÷ Division sign 288 // Latin Extended-A, 0100–017F 289 // Latin Extended-B, 0180–024F 290 // IPA Extensions, 0250–02AF 291 // Spacing Modifier Letters, 02B0–02FF 292 // - U+02C7 ˇ ˇ Caron 293 // - U+02D8 ˘ ˘ Breve 294 // - U+02D9 ˙ ˙ Dot Above 295 // - U+02DA ˚ ˚ Ring Above 296 // - U+02DB ˛ ˛ Ogonek 297 // - U+02DC ˜ ˜ Small Tilde 298 // - U+02DD ˝ ˝ Double Acute Accent 299 // Latin Extended Additional, 1E00–1EFF 300 301 var extendedWordChars = /^[A-Za-z\xC0-\u02C6\u02C8-\u02D7\u02DE-\u02FF\u1E00-\u1EFF]+$/; 302 var reWhitespace = /\S/; 303 var wordDiff = new Diff(); 304 305 wordDiff.equals = function (left, right) { 306 if (this.options.ignoreCase) { 307 left = left.toLowerCase(); 308 right = right.toLowerCase(); 309 } 310 311 return left === right || this.options.ignoreWhitespace && !reWhitespace.test(left) && !reWhitespace.test(right); 312 }; 313 314 wordDiff.tokenize = function (value) { 315 // All whitespace symbols except newline group into one token, each newline - in separate token 316 var tokens = value.split(/([^\S\r\n]+|[()[\]{}'"\r\n]|\b)/); // Join the boundary splits that we do not consider to be boundaries. This is primarily the extended Latin character set. 317 318 for (var i = 0; i < tokens.length - 1; i++) { 319 // If we have an empty string in the next field and we have only word chars before and after, merge 320 if (!tokens[i + 1] && tokens[i + 2] && extendedWordChars.test(tokens[i]) && extendedWordChars.test(tokens[i + 2])) { 321 tokens[i] += tokens[i + 2]; 322 tokens.splice(i + 1, 2); 323 i--; 324 } 325 } 326 327 return tokens; 328 }; 329 330 function diffWords(oldStr, newStr, options) { 331 options = generateOptions(options, { 332 ignoreWhitespace: true 333 }); 334 return wordDiff.diff(oldStr, newStr, options); 335 } 336 function diffWordsWithSpace(oldStr, newStr, options) { 337 return wordDiff.diff(oldStr, newStr, options); 338 } 339 340 var lineDiff = new Diff(); 341 342 lineDiff.tokenize = function (value) { 343 var retLines = [], 344 linesAndNewlines = value.split(/(\n|\r\n)/); // Ignore the final empty token that occurs if the string ends with a new line 345 346 if (!linesAndNewlines[linesAndNewlines.length - 1]) { 347 linesAndNewlines.pop(); 348 } // Merge the content and line separators into single tokens 349 350 351 for (var i = 0; i < linesAndNewlines.length; i++) { 352 var line = linesAndNewlines[i]; 353 354 if (i % 2 && !this.options.newlineIsToken) { 355 retLines[retLines.length - 1] += line; 356 } else { 357 if (this.options.ignoreWhitespace) { 358 line = line.trim(); 359 } 360 361 retLines.push(line); 362 } 363 } 364 365 return retLines; 366 }; 367 368 function diffLines(oldStr, newStr, callback) { 369 return lineDiff.diff(oldStr, newStr, callback); 370 } 371 function diffTrimmedLines(oldStr, newStr, callback) { 372 var options = generateOptions(callback, { 373 ignoreWhitespace: true 374 }); 375 return lineDiff.diff(oldStr, newStr, options); 376 } 377 378 var sentenceDiff = new Diff(); 379 380 sentenceDiff.tokenize = function (value) { 381 return value.split(/(\S.+?[.!?])(?=\s+|$)/); 382 }; 383 384 function diffSentences(oldStr, newStr, callback) { 385 return sentenceDiff.diff(oldStr, newStr, callback); 386 } 387 388 var cssDiff = new Diff(); 389 390 cssDiff.tokenize = function (value) { 391 return value.split(/([{}:;,]|\s+)/); 392 }; 393 394 function diffCss(oldStr, newStr, callback) { 395 return cssDiff.diff(oldStr, newStr, callback); 396 } 397 398 function _typeof(obj) { 399 "@babel/helpers - typeof"; 400 401 if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { 402 _typeof = function (obj) { 403 return typeof obj; 404 }; 405 } else { 406 _typeof = function (obj) { 407 return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; 408 }; 409 } 410 411 return _typeof(obj); 412 } 413 414 function _toConsumableArray(arr) { 415 return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); 416 } 417 418 function _arrayWithoutHoles(arr) { 419 if (Array.isArray(arr)) return _arrayLikeToArray(arr); 420 } 421 422 function _iterableToArray(iter) { 423 if (typeof Symbol !== "undefined" && Symbol.iterator in Object(iter)) return Array.from(iter); 424 } 425 426 function _unsupportedIterableToArray(o, minLen) { 427 if (!o) return; 428 if (typeof o === "string") return _arrayLikeToArray(o, minLen); 429 var n = Object.prototype.toString.call(o).slice(8, -1); 430 if (n === "Object" && o.constructor) n = o.constructor.name; 431 if (n === "Map" || n === "Set") return Array.from(o); 432 if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); 433 } 434 435 function _arrayLikeToArray(arr, len) { 436 if (len == null || len > arr.length) len = arr.length; 437 438 for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; 439 440 return arr2; 441 } 442 443 function _nonIterableSpread() { 444 throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); 445 } 446 447 var objectPrototypeToString = Object.prototype.toString; 448 var jsonDiff = new Diff(); // Discriminate between two lines of pretty-printed, serialized JSON where one of them has a 449 // dangling comma and the other doesn't. Turns out including the dangling comma yields the nicest output: 450 451 jsonDiff.useLongestToken = true; 452 jsonDiff.tokenize = lineDiff.tokenize; 453 454 jsonDiff.castInput = function (value) { 455 var _this$options = this.options, 456 undefinedReplacement = _this$options.undefinedReplacement, 457 _this$options$stringi = _this$options.stringifyReplacer, 458 stringifyReplacer = _this$options$stringi === void 0 ? function (k, v) { 459 return typeof v === 'undefined' ? undefinedReplacement : v; 460 } : _this$options$stringi; 461 return typeof value === 'string' ? value : JSON.stringify(canonicalize(value, null, null, stringifyReplacer), stringifyReplacer, ' '); 462 }; 463 464 jsonDiff.equals = function (left, right) { 465 return Diff.prototype.equals.call(jsonDiff, left.replace(/,([\r\n])/g, '$1'), right.replace(/,([\r\n])/g, '$1')); 466 }; 467 468 function diffJson(oldObj, newObj, options) { 469 return jsonDiff.diff(oldObj, newObj, options); 470 } // This function handles the presence of circular references by bailing out when encountering an 471 // object that is already on the "stack" of items being processed. Accepts an optional replacer 472 473 function canonicalize(obj, stack, replacementStack, replacer, key) { 474 stack = stack || []; 475 replacementStack = replacementStack || []; 476 477 if (replacer) { 478 obj = replacer(key, obj); 479 } 480 481 var i; 482 483 for (i = 0; i < stack.length; i += 1) { 484 if (stack[i] === obj) { 485 return replacementStack[i]; 486 } 487 } 488 489 var canonicalizedObj; 490 491 if ('[object Array]' === objectPrototypeToString.call(obj)) { 492 stack.push(obj); 493 canonicalizedObj = new Array(obj.length); 494 replacementStack.push(canonicalizedObj); 495 496 for (i = 0; i < obj.length; i += 1) { 497 canonicalizedObj[i] = canonicalize(obj[i], stack, replacementStack, replacer, key); 498 } 499 500 stack.pop(); 501 replacementStack.pop(); 502 return canonicalizedObj; 503 } 504 505 if (obj && obj.toJSON) { 506 obj = obj.toJSON(); 507 } 508 509 if (_typeof(obj) === 'object' && obj !== null) { 510 stack.push(obj); 511 canonicalizedObj = {}; 512 replacementStack.push(canonicalizedObj); 513 514 var sortedKeys = [], 515 _key; 516 517 for (_key in obj) { 518 /* istanbul ignore else */ 519 if (obj.hasOwnProperty(_key)) { 520 sortedKeys.push(_key); 521 } 522 } 523 524 sortedKeys.sort(); 525 526 for (i = 0; i < sortedKeys.length; i += 1) { 527 _key = sortedKeys[i]; 528 canonicalizedObj[_key] = canonicalize(obj[_key], stack, replacementStack, replacer, _key); 529 } 530 531 stack.pop(); 532 replacementStack.pop(); 533 } else { 534 canonicalizedObj = obj; 535 } 536 537 return canonicalizedObj; 538 } 539 540 var arrayDiff = new Diff(); 541 542 arrayDiff.tokenize = function (value) { 543 return value.slice(); 544 }; 545 546 arrayDiff.join = arrayDiff.removeEmpty = function (value) { 547 return value; 548 }; 549 550 function diffArrays(oldArr, newArr, callback) { 551 return arrayDiff.diff(oldArr, newArr, callback); 552 } 553 554 function parsePatch(uniDiff) { 555 var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; 556 var diffstr = uniDiff.split(/\r\n|[\n\v\f\r\x85]/), 557 delimiters = uniDiff.match(/\r\n|[\n\v\f\r\x85]/g) || [], 558 list = [], 559 i = 0; 560 561 function parseIndex() { 562 var index = {}; 563 list.push(index); // Parse diff metadata 564 565 while (i < diffstr.length) { 566 var line = diffstr[i]; // File header found, end parsing diff metadata 567 568 if (/^(\-\-\-|\+\+\+|@@)\s/.test(line)) { 569 break; 570 } // Diff index 571 572 573 var header = /^(?:Index:|diff(?: -r \w+)+)\s+(.+?)\s*$/.exec(line); 574 575 if (header) { 576 index.index = header[1]; 577 } 578 579 i++; 580 } // Parse file headers if they are defined. Unified diff requires them, but 581 // there's no technical issues to have an isolated hunk without file header 582 583 584 parseFileHeader(index); 585 parseFileHeader(index); // Parse hunks 586 587 index.hunks = []; 588 589 while (i < diffstr.length) { 590 var _line = diffstr[i]; 591 592 if (/^(Index:|diff|\-\-\-|\+\+\+)\s/.test(_line)) { 593 break; 594 } else if (/^@@/.test(_line)) { 595 index.hunks.push(parseHunk()); 596 } else if (_line && options.strict) { 597 // Ignore unexpected content unless in strict mode 598 throw new Error('Unknown line ' + (i + 1) + ' ' + JSON.stringify(_line)); 599 } else { 600 i++; 601 } 602 } 603 } // Parses the --- and +++ headers, if none are found, no lines 604 // are consumed. 605 606 607 function parseFileHeader(index) { 608 var fileHeader = /^(---|\+\+\+)\s+(.*)$/.exec(diffstr[i]); 609 610 if (fileHeader) { 611 var keyPrefix = fileHeader[1] === '---' ? 'old' : 'new'; 612 var data = fileHeader[2].split('\t', 2); 613 var fileName = data[0].replace(/\\\\/g, '\\'); 614 615 if (/^".*"$/.test(fileName)) { 616 fileName = fileName.substr(1, fileName.length - 2); 617 } 618 619 index[keyPrefix + 'FileName'] = fileName; 620 index[keyPrefix + 'Header'] = (data[1] || '').trim(); 621 i++; 622 } 623 } // Parses a hunk 624 // This assumes that we are at the start of a hunk. 625 626 627 function parseHunk() { 628 var chunkHeaderIndex = i, 629 chunkHeaderLine = diffstr[i++], 630 chunkHeader = chunkHeaderLine.split(/@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@/); 631 var hunk = { 632 oldStart: +chunkHeader[1], 633 oldLines: typeof chunkHeader[2] === 'undefined' ? 1 : +chunkHeader[2], 634 newStart: +chunkHeader[3], 635 newLines: typeof chunkHeader[4] === 'undefined' ? 1 : +chunkHeader[4], 636 lines: [], 637 linedelimiters: [] 638 }; // Unified Diff Format quirk: If the chunk size is 0, 639 // the first number is one lower than one would expect. 640 // https://www.artima.com/weblogs/viewpost.jsp?thread=164293 641 642 if (hunk.oldLines === 0) { 643 hunk.oldStart += 1; 644 } 645 646 if (hunk.newLines === 0) { 647 hunk.newStart += 1; 648 } 649 650 var addCount = 0, 651 removeCount = 0; 652 653 for (; i < diffstr.length; i++) { 654 // Lines starting with '---' could be mistaken for the "remove line" operation 655 // But they could be the header for the next file. Therefore prune such cases out. 656 if (diffstr[i].indexOf('--- ') === 0 && i + 2 < diffstr.length && diffstr[i + 1].indexOf('+++ ') === 0 && diffstr[i + 2].indexOf('@@') === 0) { 657 break; 658 } 659 660 var operation = diffstr[i].length == 0 && i != diffstr.length - 1 ? ' ' : diffstr[i][0]; 661 662 if (operation === '+' || operation === '-' || operation === ' ' || operation === '\\') { 663 hunk.lines.push(diffstr[i]); 664 hunk.linedelimiters.push(delimiters[i] || '\n'); 665 666 if (operation === '+') { 667 addCount++; 668 } else if (operation === '-') { 669 removeCount++; 670 } else if (operation === ' ') { 671 addCount++; 672 removeCount++; 673 } 674 } else { 675 break; 676 } 677 } // Handle the empty block count case 678 679 680 if (!addCount && hunk.newLines === 1) { 681 hunk.newLines = 0; 682 } 683 684 if (!removeCount && hunk.oldLines === 1) { 685 hunk.oldLines = 0; 686 } // Perform optional sanity checking 687 688 689 if (options.strict) { 690 if (addCount !== hunk.newLines) { 691 throw new Error('Added line count did not match for hunk at line ' + (chunkHeaderIndex + 1)); 692 } 693 694 if (removeCount !== hunk.oldLines) { 695 throw new Error('Removed line count did not match for hunk at line ' + (chunkHeaderIndex + 1)); 696 } 697 } 698 699 return hunk; 700 } 701 702 while (i < diffstr.length) { 703 parseIndex(); 704 } 705 706 return list; 707 } 708 709 // Iterator that traverses in the range of [min, max], stepping 710 // by distance from a given start position. I.e. for [0, 4], with 711 // start of 2, this will iterate 2, 3, 1, 4, 0. 712 function distanceIterator (start, minLine, maxLine) { 713 var wantForward = true, 714 backwardExhausted = false, 715 forwardExhausted = false, 716 localOffset = 1; 717 return function iterator() { 718 if (wantForward && !forwardExhausted) { 719 if (backwardExhausted) { 720 localOffset++; 721 } else { 722 wantForward = false; 723 } // Check if trying to fit beyond text length, and if not, check it fits 724 // after offset location (or desired location on first iteration) 725 726 727 if (start + localOffset <= maxLine) { 728 return localOffset; 729 } 730 731 forwardExhausted = true; 732 } 733 734 if (!backwardExhausted) { 735 if (!forwardExhausted) { 736 wantForward = true; 737 } // Check if trying to fit before text beginning, and if not, check it fits 738 // before offset location 739 740 741 if (minLine <= start - localOffset) { 742 return -localOffset++; 743 } 744 745 backwardExhausted = true; 746 return iterator(); 747 } // We tried to fit hunk before text beginning and beyond text length, then 748 // hunk can't fit on the text. Return undefined 749 750 }; 751 } 752 753 function applyPatch(source, uniDiff) { 754 var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; 755 756 if (typeof uniDiff === 'string') { 757 uniDiff = parsePatch(uniDiff); 758 } 759 760 if (Array.isArray(uniDiff)) { 761 if (uniDiff.length > 1) { 762 throw new Error('applyPatch only works with a single input.'); 763 } 764 765 uniDiff = uniDiff[0]; 766 } // Apply the diff to the input 767 768 769 var lines = source.split(/\r\n|[\n\v\f\r\x85]/), 770 delimiters = source.match(/\r\n|[\n\v\f\r\x85]/g) || [], 771 hunks = uniDiff.hunks, 772 compareLine = options.compareLine || function (lineNumber, line, operation, patchContent) { 773 return line === patchContent; 774 }, 775 errorCount = 0, 776 fuzzFactor = options.fuzzFactor || 0, 777 minLine = 0, 778 offset = 0, 779 removeEOFNL, 780 addEOFNL; 781 /** 782 * Checks if the hunk exactly fits on the provided location 783 */ 784 785 786 function hunkFits(hunk, toPos) { 787 for (var j = 0; j < hunk.lines.length; j++) { 788 var line = hunk.lines[j], 789 operation = line.length > 0 ? line[0] : ' ', 790 content = line.length > 0 ? line.substr(1) : line; 791 792 if (operation === ' ' || operation === '-') { 793 // Context sanity check 794 if (!compareLine(toPos + 1, lines[toPos], operation, content)) { 795 errorCount++; 796 797 if (errorCount > fuzzFactor) { 798 return false; 799 } 800 } 801 802 toPos++; 803 } 804 } 805 806 return true; 807 } // Search best fit offsets for each hunk based on the previous ones 808 809 810 for (var i = 0; i < hunks.length; i++) { 811 var hunk = hunks[i], 812 maxLine = lines.length - hunk.oldLines, 813 localOffset = 0, 814 toPos = offset + hunk.oldStart - 1; 815 var iterator = distanceIterator(toPos, minLine, maxLine); 816 817 for (; localOffset !== undefined; localOffset = iterator()) { 818 if (hunkFits(hunk, toPos + localOffset)) { 819 hunk.offset = offset += localOffset; 820 break; 821 } 822 } 823 824 if (localOffset === undefined) { 825 return false; 826 } // Set lower text limit to end of the current hunk, so next ones don't try 827 // to fit over already patched text 828 829 830 minLine = hunk.offset + hunk.oldStart + hunk.oldLines; 831 } // Apply patch hunks 832 833 834 var diffOffset = 0; 835 836 for (var _i = 0; _i < hunks.length; _i++) { 837 var _hunk = hunks[_i], 838 _toPos = _hunk.oldStart + _hunk.offset + diffOffset - 1; 839 840 diffOffset += _hunk.newLines - _hunk.oldLines; 841 842 for (var j = 0; j < _hunk.lines.length; j++) { 843 var line = _hunk.lines[j], 844 operation = line.length > 0 ? line[0] : ' ', 845 content = line.length > 0 ? line.substr(1) : line, 846 delimiter = _hunk.linedelimiters[j]; 847 848 if (operation === ' ') { 849 _toPos++; 850 } else if (operation === '-') { 851 lines.splice(_toPos, 1); 852 delimiters.splice(_toPos, 1); 853 /* istanbul ignore else */ 854 } else if (operation === '+') { 855 lines.splice(_toPos, 0, content); 856 delimiters.splice(_toPos, 0, delimiter); 857 _toPos++; 858 } else if (operation === '\\') { 859 var previousOperation = _hunk.lines[j - 1] ? _hunk.lines[j - 1][0] : null; 860 861 if (previousOperation === '+') { 862 removeEOFNL = true; 863 } else if (previousOperation === '-') { 864 addEOFNL = true; 865 } 866 } 867 } 868 } // Handle EOFNL insertion/removal 869 870 871 if (removeEOFNL) { 872 while (!lines[lines.length - 1]) { 873 lines.pop(); 874 delimiters.pop(); 875 } 876 } else if (addEOFNL) { 877 lines.push(''); 878 delimiters.push('\n'); 879 } 880 881 for (var _k = 0; _k < lines.length - 1; _k++) { 882 lines[_k] = lines[_k] + delimiters[_k]; 883 } 884 885 return lines.join(''); 886 } // Wrapper that supports multiple file patches via callbacks. 887 888 function applyPatches(uniDiff, options) { 889 if (typeof uniDiff === 'string') { 890 uniDiff = parsePatch(uniDiff); 891 } 892 893 var currentIndex = 0; 894 895 function processIndex() { 896 var index = uniDiff[currentIndex++]; 897 898 if (!index) { 899 return options.complete(); 900 } 901 902 options.loadFile(index, function (err, data) { 903 if (err) { 904 return options.complete(err); 905 } 906 907 var updatedContent = applyPatch(data, index, options); 908 options.patched(index, updatedContent, function (err) { 909 if (err) { 910 return options.complete(err); 911 } 912 913 processIndex(); 914 }); 915 }); 916 } 917 918 processIndex(); 919 } 920 921 function structuredPatch(oldFileName, newFileName, oldStr, newStr, oldHeader, newHeader, options) { 922 if (!options) { 923 options = {}; 924 } 925 926 if (typeof options.context === 'undefined') { 927 options.context = 4; 928 } 929 930 var diff = diffLines(oldStr, newStr, options); 931 diff.push({ 932 value: '', 933 lines: [] 934 }); // Append an empty value to make cleanup easier 935 936 function contextLines(lines) { 937 return lines.map(function (entry) { 938 return ' ' + entry; 939 }); 940 } 941 942 var hunks = []; 943 var oldRangeStart = 0, 944 newRangeStart = 0, 945 curRange = [], 946 oldLine = 1, 947 newLine = 1; 948 949 var _loop = function _loop(i) { 950 var current = diff[i], 951 lines = current.lines || current.value.replace(/\n$/, '').split('\n'); 952 current.lines = lines; 953 954 if (current.added || current.removed) { 955 var _curRange; 956 957 // If we have previous context, start with that 958 if (!oldRangeStart) { 959 var prev = diff[i - 1]; 960 oldRangeStart = oldLine; 961 newRangeStart = newLine; 962 963 if (prev) { 964 curRange = options.context > 0 ? contextLines(prev.lines.slice(-options.context)) : []; 965 oldRangeStart -= curRange.length; 966 newRangeStart -= curRange.length; 967 } 968 } // Output our changes 969 970 971 (_curRange = curRange).push.apply(_curRange, _toConsumableArray(lines.map(function (entry) { 972 return (current.added ? '+' : '-') + entry; 973 }))); // Track the updated file position 974 975 976 if (current.added) { 977 newLine += lines.length; 978 } else { 979 oldLine += lines.length; 980 } 981 } else { 982 // Identical context lines. Track line changes 983 if (oldRangeStart) { 984 // Close out any changes that have been output (or join overlapping) 985 if (lines.length <= options.context * 2 && i < diff.length - 2) { 986 var _curRange2; 987 988 // Overlapping 989 (_curRange2 = curRange).push.apply(_curRange2, _toConsumableArray(contextLines(lines))); 990 } else { 991 var _curRange3; 992 993 // end the range and output 994 var contextSize = Math.min(lines.length, options.context); 995 996 (_curRange3 = curRange).push.apply(_curRange3, _toConsumableArray(contextLines(lines.slice(0, contextSize)))); 997 998 var hunk = { 999 oldStart: oldRangeStart, 1000 oldLines: oldLine - oldRangeStart + contextSize, 1001 newStart: newRangeStart, 1002 newLines: newLine - newRangeStart + contextSize, 1003 lines: curRange 1004 }; 1005 1006 if (i >= diff.length - 2 && lines.length <= options.context) { 1007 // EOF is inside this hunk 1008 var oldEOFNewline = /\n$/.test(oldStr); 1009 var newEOFNewline = /\n$/.test(newStr); 1010 var noNlBeforeAdds = lines.length == 0 && curRange.length > hunk.oldLines; 1011 1012 if (!oldEOFNewline && noNlBeforeAdds && oldStr.length > 0) { 1013 // special case: old has no eol and no trailing context; no-nl can end up before adds 1014 // however, if the old file is empty, do not output the no-nl line 1015 curRange.splice(hunk.oldLines, 0, '\\ No newline at end of file'); 1016 } 1017 1018 if (!oldEOFNewline && !noNlBeforeAdds || !newEOFNewline) { 1019 curRange.push('\\ No newline at end of file'); 1020 } 1021 } 1022 1023 hunks.push(hunk); 1024 oldRangeStart = 0; 1025 newRangeStart = 0; 1026 curRange = []; 1027 } 1028 } 1029 1030 oldLine += lines.length; 1031 newLine += lines.length; 1032 } 1033 }; 1034 1035 for (var i = 0; i < diff.length; i++) { 1036 _loop(i); 1037 } 1038 1039 return { 1040 oldFileName: oldFileName, 1041 newFileName: newFileName, 1042 oldHeader: oldHeader, 1043 newHeader: newHeader, 1044 hunks: hunks 1045 }; 1046 } 1047 function formatPatch(diff) { 1048 var ret = []; 1049 1050 if (diff.oldFileName == diff.newFileName) { 1051 ret.push('Index: ' + diff.oldFileName); 1052 } 1053 1054 ret.push('==================================================================='); 1055 ret.push('--- ' + diff.oldFileName + (typeof diff.oldHeader === 'undefined' ? '' : '\t' + diff.oldHeader)); 1056 ret.push('+++ ' + diff.newFileName + (typeof diff.newHeader === 'undefined' ? '' : '\t' + diff.newHeader)); 1057 1058 for (var i = 0; i < diff.hunks.length; i++) { 1059 var hunk = diff.hunks[i]; // Unified Diff Format quirk: If the chunk size is 0, 1060 // the first number is one lower than one would expect. 1061 // https://www.artima.com/weblogs/viewpost.jsp?thread=164293 1062 1063 if (hunk.oldLines === 0) { 1064 hunk.oldStart -= 1; 1065 } 1066 1067 if (hunk.newLines === 0) { 1068 hunk.newStart -= 1; 1069 } 1070 1071 ret.push('@@ -' + hunk.oldStart + ',' + hunk.oldLines + ' +' + hunk.newStart + ',' + hunk.newLines + ' @@'); 1072 ret.push.apply(ret, hunk.lines); 1073 } 1074 1075 return ret.join('\n') + '\n'; 1076 } 1077 function createTwoFilesPatch(oldFileName, newFileName, oldStr, newStr, oldHeader, newHeader, options) { 1078 return formatPatch(structuredPatch(oldFileName, newFileName, oldStr, newStr, oldHeader, newHeader, options)); 1079 } 1080 function createPatch(fileName, oldStr, newStr, oldHeader, newHeader, options) { 1081 return createTwoFilesPatch(fileName, fileName, oldStr, newStr, oldHeader, newHeader, options); 1082 } 1083 1084 function arrayEqual(a, b) { 1085 if (a.length !== b.length) { 1086 return false; 1087 } 1088 1089 return arrayStartsWith(a, b); 1090 } 1091 function arrayStartsWith(array, start) { 1092 if (start.length > array.length) { 1093 return false; 1094 } 1095 1096 for (var i = 0; i < start.length; i++) { 1097 if (start[i] !== array[i]) { 1098 return false; 1099 } 1100 } 1101 1102 return true; 1103 } 1104 1105 function calcLineCount(hunk) { 1106 var _calcOldNewLineCount = calcOldNewLineCount(hunk.lines), 1107 oldLines = _calcOldNewLineCount.oldLines, 1108 newLines = _calcOldNewLineCount.newLines; 1109 1110 if (oldLines !== undefined) { 1111 hunk.oldLines = oldLines; 1112 } else { 1113 delete hunk.oldLines; 1114 } 1115 1116 if (newLines !== undefined) { 1117 hunk.newLines = newLines; 1118 } else { 1119 delete hunk.newLines; 1120 } 1121 } 1122 function merge(mine, theirs, base) { 1123 mine = loadPatch(mine, base); 1124 theirs = loadPatch(theirs, base); 1125 var ret = {}; // For index we just let it pass through as it doesn't have any necessary meaning. 1126 // Leaving sanity checks on this to the API consumer that may know more about the 1127 // meaning in their own context. 1128 1129 if (mine.index || theirs.index) { 1130 ret.index = mine.index || theirs.index; 1131 } 1132 1133 if (mine.newFileName || theirs.newFileName) { 1134 if (!fileNameChanged(mine)) { 1135 // No header or no change in ours, use theirs (and ours if theirs does not exist) 1136 ret.oldFileName = theirs.oldFileName || mine.oldFileName; 1137 ret.newFileName = theirs.newFileName || mine.newFileName; 1138 ret.oldHeader = theirs.oldHeader || mine.oldHeader; 1139 ret.newHeader = theirs.newHeader || mine.newHeader; 1140 } else if (!fileNameChanged(theirs)) { 1141 // No header or no change in theirs, use ours 1142 ret.oldFileName = mine.oldFileName; 1143 ret.newFileName = mine.newFileName; 1144 ret.oldHeader = mine.oldHeader; 1145 ret.newHeader = mine.newHeader; 1146 } else { 1147 // Both changed... figure it out 1148 ret.oldFileName = selectField(ret, mine.oldFileName, theirs.oldFileName); 1149 ret.newFileName = selectField(ret, mine.newFileName, theirs.newFileName); 1150 ret.oldHeader = selectField(ret, mine.oldHeader, theirs.oldHeader); 1151 ret.newHeader = selectField(ret, mine.newHeader, theirs.newHeader); 1152 } 1153 } 1154 1155 ret.hunks = []; 1156 var mineIndex = 0, 1157 theirsIndex = 0, 1158 mineOffset = 0, 1159 theirsOffset = 0; 1160 1161 while (mineIndex < mine.hunks.length || theirsIndex < theirs.hunks.length) { 1162 var mineCurrent = mine.hunks[mineIndex] || { 1163 oldStart: Infinity 1164 }, 1165 theirsCurrent = theirs.hunks[theirsIndex] || { 1166 oldStart: Infinity 1167 }; 1168 1169 if (hunkBefore(mineCurrent, theirsCurrent)) { 1170 // This patch does not overlap with any of the others, yay. 1171 ret.hunks.push(cloneHunk(mineCurrent, mineOffset)); 1172 mineIndex++; 1173 theirsOffset += mineCurrent.newLines - mineCurrent.oldLines; 1174 } else if (hunkBefore(theirsCurrent, mineCurrent)) { 1175 // This patch does not overlap with any of the others, yay. 1176 ret.hunks.push(cloneHunk(theirsCurrent, theirsOffset)); 1177 theirsIndex++; 1178 mineOffset += theirsCurrent.newLines - theirsCurrent.oldLines; 1179 } else { 1180 // Overlap, merge as best we can 1181 var mergedHunk = { 1182 oldStart: Math.min(mineCurrent.oldStart, theirsCurrent.oldStart), 1183 oldLines: 0, 1184 newStart: Math.min(mineCurrent.newStart + mineOffset, theirsCurrent.oldStart + theirsOffset), 1185 newLines: 0, 1186 lines: [] 1187 }; 1188 mergeLines(mergedHunk, mineCurrent.oldStart, mineCurrent.lines, theirsCurrent.oldStart, theirsCurrent.lines); 1189 theirsIndex++; 1190 mineIndex++; 1191 ret.hunks.push(mergedHunk); 1192 } 1193 } 1194 1195 return ret; 1196 } 1197 1198 function loadPatch(param, base) { 1199 if (typeof param === 'string') { 1200 if (/^@@/m.test(param) || /^Index:/m.test(param)) { 1201 return parsePatch(param)[0]; 1202 } 1203 1204 if (!base) { 1205 throw new Error('Must provide a base reference or pass in a patch'); 1206 } 1207 1208 return structuredPatch(undefined, undefined, base, param); 1209 } 1210 1211 return param; 1212 } 1213 1214 function fileNameChanged(patch) { 1215 return patch.newFileName && patch.newFileName !== patch.oldFileName; 1216 } 1217 1218 function selectField(index, mine, theirs) { 1219 if (mine === theirs) { 1220 return mine; 1221 } else { 1222 index.conflict = true; 1223 return { 1224 mine: mine, 1225 theirs: theirs 1226 }; 1227 } 1228 } 1229 1230 function hunkBefore(test, check) { 1231 return test.oldStart < check.oldStart && test.oldStart + test.oldLines < check.oldStart; 1232 } 1233 1234 function cloneHunk(hunk, offset) { 1235 return { 1236 oldStart: hunk.oldStart, 1237 oldLines: hunk.oldLines, 1238 newStart: hunk.newStart + offset, 1239 newLines: hunk.newLines, 1240 lines: hunk.lines 1241 }; 1242 } 1243 1244 function mergeLines(hunk, mineOffset, mineLines, theirOffset, theirLines) { 1245 // This will generally result in a conflicted hunk, but there are cases where the context 1246 // is the only overlap where we can successfully merge the content here. 1247 var mine = { 1248 offset: mineOffset, 1249 lines: mineLines, 1250 index: 0 1251 }, 1252 their = { 1253 offset: theirOffset, 1254 lines: theirLines, 1255 index: 0 1256 }; // Handle any leading content 1257 1258 insertLeading(hunk, mine, their); 1259 insertLeading(hunk, their, mine); // Now in the overlap content. Scan through and select the best changes from each. 1260 1261 while (mine.index < mine.lines.length && their.index < their.lines.length) { 1262 var mineCurrent = mine.lines[mine.index], 1263 theirCurrent = their.lines[their.index]; 1264 1265 if ((mineCurrent[0] === '-' || mineCurrent[0] === '+') && (theirCurrent[0] === '-' || theirCurrent[0] === '+')) { 1266 // Both modified ... 1267 mutualChange(hunk, mine, their); 1268 } else if (mineCurrent[0] === '+' && theirCurrent[0] === ' ') { 1269 var _hunk$lines; 1270 1271 // Mine inserted 1272 (_hunk$lines = hunk.lines).push.apply(_hunk$lines, _toConsumableArray(collectChange(mine))); 1273 } else if (theirCurrent[0] === '+' && mineCurrent[0] === ' ') { 1274 var _hunk$lines2; 1275 1276 // Theirs inserted 1277 (_hunk$lines2 = hunk.lines).push.apply(_hunk$lines2, _toConsumableArray(collectChange(their))); 1278 } else if (mineCurrent[0] === '-' && theirCurrent[0] === ' ') { 1279 // Mine removed or edited 1280 removal(hunk, mine, their); 1281 } else if (theirCurrent[0] === '-' && mineCurrent[0] === ' ') { 1282 // Their removed or edited 1283 removal(hunk, their, mine, true); 1284 } else if (mineCurrent === theirCurrent) { 1285 // Context identity 1286 hunk.lines.push(mineCurrent); 1287 mine.index++; 1288 their.index++; 1289 } else { 1290 // Context mismatch 1291 conflict(hunk, collectChange(mine), collectChange(their)); 1292 } 1293 } // Now push anything that may be remaining 1294 1295 1296 insertTrailing(hunk, mine); 1297 insertTrailing(hunk, their); 1298 calcLineCount(hunk); 1299 } 1300 1301 function mutualChange(hunk, mine, their) { 1302 var myChanges = collectChange(mine), 1303 theirChanges = collectChange(their); 1304 1305 if (allRemoves(myChanges) && allRemoves(theirChanges)) { 1306 // Special case for remove changes that are supersets of one another 1307 if (arrayStartsWith(myChanges, theirChanges) && skipRemoveSuperset(their, myChanges, myChanges.length - theirChanges.length)) { 1308 var _hunk$lines3; 1309 1310 (_hunk$lines3 = hunk.lines).push.apply(_hunk$lines3, _toConsumableArray(myChanges)); 1311 1312 return; 1313 } else if (arrayStartsWith(theirChanges, myChanges) && skipRemoveSuperset(mine, theirChanges, theirChanges.length - myChanges.length)) { 1314 var _hunk$lines4; 1315 1316 (_hunk$lines4 = hunk.lines).push.apply(_hunk$lines4, _toConsumableArray(theirChanges)); 1317 1318 return; 1319 } 1320 } else if (arrayEqual(myChanges, theirChanges)) { 1321 var _hunk$lines5; 1322 1323 (_hunk$lines5 = hunk.lines).push.apply(_hunk$lines5, _toConsumableArray(myChanges)); 1324 1325 return; 1326 } 1327 1328 conflict(hunk, myChanges, theirChanges); 1329 } 1330 1331 function removal(hunk, mine, their, swap) { 1332 var myChanges = collectChange(mine), 1333 theirChanges = collectContext(their, myChanges); 1334 1335 if (theirChanges.merged) { 1336 var _hunk$lines6; 1337 1338 (_hunk$lines6 = hunk.lines).push.apply(_hunk$lines6, _toConsumableArray(theirChanges.merged)); 1339 } else { 1340 conflict(hunk, swap ? theirChanges : myChanges, swap ? myChanges : theirChanges); 1341 } 1342 } 1343 1344 function conflict(hunk, mine, their) { 1345 hunk.conflict = true; 1346 hunk.lines.push({ 1347 conflict: true, 1348 mine: mine, 1349 theirs: their 1350 }); 1351 } 1352 1353 function insertLeading(hunk, insert, their) { 1354 while (insert.offset < their.offset && insert.index < insert.lines.length) { 1355 var line = insert.lines[insert.index++]; 1356 hunk.lines.push(line); 1357 insert.offset++; 1358 } 1359 } 1360 1361 function insertTrailing(hunk, insert) { 1362 while (insert.index < insert.lines.length) { 1363 var line = insert.lines[insert.index++]; 1364 hunk.lines.push(line); 1365 } 1366 } 1367 1368 function collectChange(state) { 1369 var ret = [], 1370 operation = state.lines[state.index][0]; 1371 1372 while (state.index < state.lines.length) { 1373 var line = state.lines[state.index]; // Group additions that are immediately after subtractions and treat them as one "atomic" modify change. 1374 1375 if (operation === '-' && line[0] === '+') { 1376 operation = '+'; 1377 } 1378 1379 if (operation === line[0]) { 1380 ret.push(line); 1381 state.index++; 1382 } else { 1383 break; 1384 } 1385 } 1386 1387 return ret; 1388 } 1389 1390 function collectContext(state, matchChanges) { 1391 var changes = [], 1392 merged = [], 1393 matchIndex = 0, 1394 contextChanges = false, 1395 conflicted = false; 1396 1397 while (matchIndex < matchChanges.length && state.index < state.lines.length) { 1398 var change = state.lines[state.index], 1399 match = matchChanges[matchIndex]; // Once we've hit our add, then we are done 1400 1401 if (match[0] === '+') { 1402 break; 1403 } 1404 1405 contextChanges = contextChanges || change[0] !== ' '; 1406 merged.push(match); 1407 matchIndex++; // Consume any additions in the other block as a conflict to attempt 1408 // to pull in the remaining context after this 1409 1410 if (change[0] === '+') { 1411 conflicted = true; 1412 1413 while (change[0] === '+') { 1414 changes.push(change); 1415 change = state.lines[++state.index]; 1416 } 1417 } 1418 1419 if (match.substr(1) === change.substr(1)) { 1420 changes.push(change); 1421 state.index++; 1422 } else { 1423 conflicted = true; 1424 } 1425 } 1426 1427 if ((matchChanges[matchIndex] || '')[0] === '+' && contextChanges) { 1428 conflicted = true; 1429 } 1430 1431 if (conflicted) { 1432 return changes; 1433 } 1434 1435 while (matchIndex < matchChanges.length) { 1436 merged.push(matchChanges[matchIndex++]); 1437 } 1438 1439 return { 1440 merged: merged, 1441 changes: changes 1442 }; 1443 } 1444 1445 function allRemoves(changes) { 1446 return changes.reduce(function (prev, change) { 1447 return prev && change[0] === '-'; 1448 }, true); 1449 } 1450 1451 function skipRemoveSuperset(state, removeChanges, delta) { 1452 for (var i = 0; i < delta; i++) { 1453 var changeContent = removeChanges[removeChanges.length - delta + i].substr(1); 1454 1455 if (state.lines[state.index + i] !== ' ' + changeContent) { 1456 return false; 1457 } 1458 } 1459 1460 state.index += delta; 1461 return true; 1462 } 1463 1464 function calcOldNewLineCount(lines) { 1465 var oldLines = 0; 1466 var newLines = 0; 1467 lines.forEach(function (line) { 1468 if (typeof line !== 'string') { 1469 var myCount = calcOldNewLineCount(line.mine); 1470 var theirCount = calcOldNewLineCount(line.theirs); 1471 1472 if (oldLines !== undefined) { 1473 if (myCount.oldLines === theirCount.oldLines) { 1474 oldLines += myCount.oldLines; 1475 } else { 1476 oldLines = undefined; 1477 } 1478 } 1479 1480 if (newLines !== undefined) { 1481 if (myCount.newLines === theirCount.newLines) { 1482 newLines += myCount.newLines; 1483 } else { 1484 newLines = undefined; 1485 } 1486 } 1487 } else { 1488 if (newLines !== undefined && (line[0] === '+' || line[0] === ' ')) { 1489 newLines++; 1490 } 1491 1492 if (oldLines !== undefined && (line[0] === '-' || line[0] === ' ')) { 1493 oldLines++; 1494 } 1495 } 1496 }); 1497 return { 1498 oldLines: oldLines, 1499 newLines: newLines 1500 }; 1501 } 1502 1503 // See: http://code.google.com/p/google-diff-match-patch/wiki/API 1504 function convertChangesToDMP(changes) { 1505 var ret = [], 1506 change, 1507 operation; 1508 1509 for (var i = 0; i < changes.length; i++) { 1510 change = changes[i]; 1511 1512 if (change.added) { 1513 operation = 1; 1514 } else if (change.removed) { 1515 operation = -1; 1516 } else { 1517 operation = 0; 1518 } 1519 1520 ret.push([operation, change.value]); 1521 } 1522 1523 return ret; 1524 } 1525 1526 function convertChangesToXML(changes) { 1527 var ret = []; 1528 1529 for (var i = 0; i < changes.length; i++) { 1530 var change = changes[i]; 1531 1532 if (change.added) { 1533 ret.push('<ins>'); 1534 } else if (change.removed) { 1535 ret.push('<del>'); 1536 } 1537 1538 ret.push(escapeHTML(change.value)); 1539 1540 if (change.added) { 1541 ret.push('</ins>'); 1542 } else if (change.removed) { 1543 ret.push('</del>'); 1544 } 1545 } 1546 1547 return ret.join(''); 1548 } 1549 1550 function escapeHTML(s) { 1551 var n = s; 1552 n = n.replace(/&/g, '&'); 1553 n = n.replace(/</g, '<'); 1554 n = n.replace(/>/g, '>'); 1555 n = n.replace(/"/g, '"'); 1556 return n; 1557 } 1558 1559 exports.Diff = Diff; 1560 exports.applyPatch = applyPatch; 1561 exports.applyPatches = applyPatches; 1562 exports.canonicalize = canonicalize; 1563 exports.convertChangesToDMP = convertChangesToDMP; 1564 exports.convertChangesToXML = convertChangesToXML; 1565 exports.createPatch = createPatch; 1566 exports.createTwoFilesPatch = createTwoFilesPatch; 1567 exports.diffArrays = diffArrays; 1568 exports.diffChars = diffChars; 1569 exports.diffCss = diffCss; 1570 exports.diffJson = diffJson; 1571 exports.diffLines = diffLines; 1572 exports.diffSentences = diffSentences; 1573 exports.diffTrimmedLines = diffTrimmedLines; 1574 exports.diffWords = diffWords; 1575 exports.diffWordsWithSpace = diffWordsWithSpace; 1576 exports.merge = merge; 1577 exports.parsePatch = parsePatch; 1578 exports.structuredPatch = structuredPatch; 1579 1580 Object.defineProperty(exports, '__esModule', { value: true }); 1581 1582 })));
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 |