Software zum Installieren eines Smart-Mirror Frameworks , zum Nutzen von hochschulrelevanten Informationen, auf einem Raspberry-Pi.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

index.mjs 44KB

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