(() => { let initialized = false; let globalNodeMap = new EditingHistory.GlobalNodeMap(); let topLevelUpdates = []; let currentChildUpdates = []; let isProcessingTopLevelUpdate = false; let lastKnownSelectionState = null; let mutationObserver = new MutationObserver(records => appendDOMUpdatesFromRecords(records)); function beginProcessingTopLevelUpdate() { isProcessingTopLevelUpdate = true; } function endProcessingTopLevelUpdate(topLevelUpdate) { topLevelUpdates.push(topLevelUpdate); currentChildUpdates = []; isProcessingTopLevelUpdate = false; } function appendDOMUpdatesFromRecords(records) { if (!records.length) return; let newUpdates = EditingHistory.DOMUpdate.fromRecords(records, globalNodeMap); if (isProcessingTopLevelUpdate) currentChildUpdates = currentChildUpdates.concat(newUpdates); else topLevelUpdates = topLevelUpdates.concat(newUpdates); } function appendSelectionUpdateIfNecessary() { let newSelectionState = EditingHistory.SelectionState.fromSelection(getSelection(), globalNodeMap); if (newSelectionState.isEqual(lastKnownSelectionState)) return; let update = new EditingHistory.SelectionUpdate(globalNodeMap, newSelectionState); if (isProcessingTopLevelUpdate) currentChildUpdates.push(update); else topLevelUpdates.push(update); lastKnownSelectionState = newSelectionState; } document.body.addEventListener("focus", () => { if (initialized) return; initialized = true; EditingHistory.getEditingHistoryAsJSONString = (formatted) => { let record = {}; record.updates = topLevelUpdates.map(update => update.toObject()); record.globalNodeMap = globalNodeMap.toObject(); return formatted ? JSON.stringify(record, null, 4) : JSON.stringify(record); }; document.addEventListener("selectionchange", () => { appendSelectionUpdateIfNecessary(); }); document.addEventListener("beforeinput", event => { appendDOMUpdatesFromRecords(mutationObserver.takeRecords()); beginProcessingTopLevelUpdate(); }); document.addEventListener("input", event => { appendDOMUpdatesFromRecords(mutationObserver.takeRecords()); let eventData = event.dataTransfer ? event.dataTransfer.getData("text/html") : event.data; lastKnownSelectionState = null; endProcessingTopLevelUpdate(new EditingHistory.InputEventUpdate(globalNodeMap, currentChildUpdates, event.inputType, eventData, event.timeStamp)); }); mutationObserver.observe(document, { childList: true, attributes: true, characterData: true, subtree: true, attributeOldValue: true, characterDataOldValue: true, }); }); })();