/* eslint-disable camelcase, no-proto, no-unused-vars, */ // TODO: This file was created by bulk-decaffeinate. // Fix any style issues and re-enable lint. /* * decaffeinate suggestions: * DS101: Remove unnecessary use of Array.from * DS102: Remove unnecessary code created because of implicit returns * DS207: Consider shorter variations of null checks * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md */ let DiffGenerator; var ConsistencyError = function(message) { const error = new Error(message); error.name = "ConsistencyError"; error.__proto__ = ConsistencyError.prototype; return error; }; ConsistencyError.prototype.__proto__ = Error.prototype; const logger = require("logger-sharelatex"); module.exports = (DiffGenerator = { ConsistencyError, rewindUpdate(content, update) { for (let j = update.op.length - 1, i = j; j >= 0; j--, i = j) { const op = update.op[i]; if (op.broken !== true) { try { content = DiffGenerator.rewindOp(content, op); } catch (e) { if (e instanceof ConsistencyError && (i = update.op.length - 1)) { // catch known case where the last op in an array has been // merged into a later op logger.error({err: e, update, op: JSON.stringify(op)}, "marking op as broken"); op.broken = true; } else { throw e; // rethrow the execption } } } } return content; }, rewindOp(content, op) { let p; if (op.i != null) { // ShareJS will accept an op where p > content.length when applied, // and it applies as though p == content.length. However, the op is // passed to us with the original p > content.length. Detect if that // is the case with this op, and shift p back appropriately to match // ShareJS if so. ({ p } = op); const max_p = content.length - op.i.length; if (p > max_p) { logger.warn({max_p, p}, "truncating position to content length"); p = max_p; } const textToBeRemoved = content.slice(p, p + op.i.length); if (op.i !== textToBeRemoved) { throw new ConsistencyError( `Inserted content, '${op.i}', does not match text to be removed, '${textToBeRemoved}'` ); } return content.slice(0, p) + content.slice(p + op.i.length); } else if (op.d != null) { return content.slice(0, op.p) + op.d + content.slice(op.p); } else { return content; } }, rewindUpdates(content, updates) { for (const update of Array.from(updates.reverse())) { try { content = DiffGenerator.rewindUpdate(content, update); } catch (e) { e.attempted_update = update; // keep a record of the attempted update throw e; // rethrow the exception } } return content; }, buildDiff(initialContent, updates) { let diff = [ {u: initialContent} ]; for (const update of Array.from(updates)) { diff = DiffGenerator.applyUpdateToDiff(diff, update); } diff = DiffGenerator.compressDiff(diff); return diff; }, compressDiff(diff) { const newDiff = []; for (const part of Array.from(diff)) { const lastPart = newDiff[newDiff.length - 1]; if ((lastPart != null) && ((lastPart.meta != null ? lastPart.meta.user : undefined) != null) && ((part.meta != null ? part.meta.user : undefined) != null)) { if ((lastPart.i != null) && (part.i != null) && (lastPart.meta.user.id === part.meta.user.id)) { lastPart.i += part.i; lastPart.meta.start_ts = Math.min(lastPart.meta.start_ts, part.meta.start_ts); lastPart.meta.end_ts = Math.max(lastPart.meta.end_ts, part.meta.end_ts); } else if ((lastPart.d != null) && (part.d != null) && (lastPart.meta.user.id === part.meta.user.id)) { lastPart.d += part.d; lastPart.meta.start_ts = Math.min(lastPart.meta.start_ts, part.meta.start_ts); lastPart.meta.end_ts = Math.max(lastPart.meta.end_ts, part.meta.end_ts); } else { newDiff.push(part); } } else { newDiff.push(part); } } return newDiff; }, applyOpToDiff(diff, op, meta) { let consumedDiff; const position = 0; let remainingDiff = diff.slice(); ({consumedDiff, remainingDiff} = DiffGenerator._consumeToOffset(remainingDiff, op.p)); const newDiff = consumedDiff; if (op.i != null) { newDiff.push({ i: op.i, meta }); } else if (op.d != null) { ({consumedDiff, remainingDiff} = DiffGenerator._consumeDiffAffectedByDeleteOp(remainingDiff, op, meta)); newDiff.push(...Array.from(consumedDiff || [])); } newDiff.push(...Array.from(remainingDiff || [])); return newDiff; }, applyUpdateToDiff(diff, update) { for (const op of Array.from(update.op)) { if (op.broken !== true) { diff = DiffGenerator.applyOpToDiff(diff, op, update.meta); } } return diff; }, _consumeToOffset(remainingDiff, totalOffset) { let part; const consumedDiff = []; let position = 0; while ((part = remainingDiff.shift())) { const length = DiffGenerator._getLengthOfDiffPart(part); if (part.d != null) { consumedDiff.push(part); } else if ((position + length) >= totalOffset) { const partOffset = totalOffset - position; if (partOffset > 0) { consumedDiff.push(DiffGenerator._slicePart(part, 0, partOffset)); } if (partOffset < length) { remainingDiff.unshift(DiffGenerator._slicePart(part, partOffset)); } break; } else { position += length; consumedDiff.push(part); } } return { consumedDiff, remainingDiff }; }, _consumeDiffAffectedByDeleteOp(remainingDiff, deleteOp, meta) { const consumedDiff = []; let remainingOp = deleteOp; while (remainingOp && (remainingDiff.length > 0)) { let newPart; ({newPart, remainingDiff, remainingOp} = DiffGenerator._consumeDeletedPart(remainingDiff, remainingOp, meta)); if (newPart != null) { consumedDiff.push(newPart); } } return { consumedDiff, remainingDiff }; }, _consumeDeletedPart(remainingDiff, op, meta) { let deletedContent, newPart, remainingOp; const part = remainingDiff.shift(); const partLength = DiffGenerator._getLengthOfDiffPart(part); if (part.d != null) { // Skip existing deletes remainingOp = op; newPart = part; } else if (partLength > op.d.length) { // Only the first bit of the part has been deleted const remainingPart = DiffGenerator._slicePart(part, op.d.length); remainingDiff.unshift(remainingPart); deletedContent = DiffGenerator._getContentOfPart(part).slice(0, op.d.length); if (deletedContent !== op.d) { throw new ConsistencyError(`deleted content, '${deletedContent}', does not match delete op, '${op.d}'`); } if (part.u != null) { newPart = { d: op.d, meta }; } else if (part.i != null) { newPart = null; } remainingOp = null; } else if (partLength === op.d.length) { // The entire part has been deleted, but it is the last part deletedContent = DiffGenerator._getContentOfPart(part); if (deletedContent !== op.d) { throw new ConsistencyError(`deleted content, '${deletedContent}', does not match delete op, '${op.d}'`); } if (part.u != null) { newPart = { d: op.d, meta }; } else if (part.i != null) { newPart = null; } remainingOp = null; } else if (partLength < op.d.length) { // The entire part has been deleted and there is more deletedContent = DiffGenerator._getContentOfPart(part); const opContent = op.d.slice(0, deletedContent.length); if (deletedContent !== opContent) { throw new ConsistencyError(`deleted content, '${deletedContent}', does not match delete op, '${opContent}'`); } if (part.u) { newPart = { d: part.u, meta }; } else if (part.i != null) { newPart = null; } remainingOp = {p: op.p, d: op.d.slice(DiffGenerator._getLengthOfDiffPart(part))}; } return { newPart, remainingDiff, remainingOp }; }, _slicePart(basePart, from, to) { let part; if (basePart.u != null) { part = { u: basePart.u.slice(from, to) }; } else if (basePart.i != null) { part = { i: basePart.i.slice(from, to) }; } if (basePart.meta != null) { part.meta = basePart.meta; } return part; }, _getLengthOfDiffPart(part) { return (part.u || part.d || part.i || '').length; }, _getContentOfPart(part) { return part.u || part.d || part.i || ''; } });