in frontend/apply_patch.js [156:213]
function updateListObject(patch, obj, updated) {
const objectId = patch.objectId
if (!updated[objectId]) {
updated[objectId] = cloneListObject(obj, objectId)
}
const list = updated[objectId], conflicts = list[CONFLICTS], elemIds = list[ELEM_IDS]
for (let i = 0; i < patch.edits.length; i++) {
const edit = patch.edits[i]
if (edit.action === 'insert' || edit.action === 'update') {
const oldValue = conflicts[edit.index] && conflicts[edit.index][edit.opId]
let lastValue = getValue(edit.value, oldValue, updated)
let values = {[edit.opId]: lastValue}
// Successive updates for the same index are an indication of a conflict on that list element.
// Edits are sorted in increasing order by Lamport timestamp, so the last value (with the
// greatest timestamp) is the default resolution of the conflict.
while (i < patch.edits.length - 1 && patch.edits[i + 1].index === edit.index &&
patch.edits[i + 1].action === 'update') {
i++
const conflict = patch.edits[i]
const oldValue2 = conflicts[conflict.index] && conflicts[conflict.index][conflict.opId]
lastValue = getValue(conflict.value, oldValue2, updated)
values[conflict.opId] = lastValue
}
if (edit.action === 'insert') {
list.splice(edit.index, 0, lastValue)
conflicts.splice(edit.index, 0, values)
elemIds.splice(edit.index, 0, edit.elemId)
} else {
list[edit.index] = lastValue
conflicts[edit.index] = values
}
} else if (edit.action === 'multi-insert') {
const startElemId = parseOpId(edit.elemId), newElems = [], newValues = [], newConflicts = []
const datatype = edit.datatype
edit.values.forEach((value, index) => {
const elemId = `${startElemId.counter + index}@${startElemId.actorId}`
value = getValue({ value, datatype }, undefined, updated)
newValues.push(value)
newConflicts.push({[elemId]: {value, datatype, type: 'value'}})
newElems.push(elemId)
})
list.splice(edit.index, 0, ...newValues)
conflicts.splice(edit.index, 0, ...newConflicts)
elemIds.splice(edit.index, 0, ...newElems)
} else if (edit.action === 'remove') {
list.splice(edit.index, edit.count)
conflicts.splice(edit.index, edit.count)
elemIds.splice(edit.index, edit.count)
}
}
return list
}