All files / json-crdt-extensions/quill-delta util.ts

95.18% Statements 79/83
78.94% Branches 15/19
100% Functions 4/4
100% Lines 59/59

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 8451x 51x 51x         51x 11574x 11574x 11574x 5663x 5663x 10036x 10036x 10036x   8173x 8173x 8173x     1863x 1863x 1863x       5663x 5034x     51x 897x 897x 897x 897x 897x 1190x 897x     51x 11935x 10029x 10029x 10029x 10029x 10029x 15329x 15329x 15329x   10029x     51x       4340x 1424x 527x 527x 527x 527x 671x 671x 671x 671x 419x   527x 527x 527x 692x   692x 383x   527x 441x    
import {isEmpty} from '@jsonjoy.com/util/lib/isEmpty';
import {PersistedSlice} from '../peritext/slice/PersistedSlice';
import {SliceStacking} from '../peritext/slice/constants';
import type {OverlayPoint} from '../peritext/overlay/OverlayPoint';
import type {QuillDeltaAttributes} from './types';
import type {PathStep} from '@jsonjoy.com/json-pointer';
 
export const getAttributes = (overlayPoint: OverlayPoint): QuillDeltaAttributes | undefined => {
  const layers = overlayPoint.layers;
  const layerLength = layers.length;
  if (!layerLength) return;
  const attributes: QuillDeltaAttributes = {};
  for (let i = 0; i < layerLength; i++) {
    const slice = layers[i];
    Iif (!(slice instanceof PersistedSlice)) continue;
    switch (slice.stacking) {
      case SliceStacking.One: {
        const tag = slice.type() as PathStep;
        if (tag) attributes[tag] = slice.data();
        break;
      }
      case SliceStacking.Erase: {
        const tag = slice.type() as PathStep;
        if (tag) delete attributes[tag];
        break;
      }
    }
  }
  if (isEmpty(attributes)) return undefined;
  return attributes;
};
 
const eraseAttributes = (attr: QuillDeltaAttributes | undefined): Record<string | number, null> | undefined => {
  Iif (!attr) return;
  const keys = Object.keys(attr);
  const length = keys.length;
  Iif (!length) return;
  const erased: Record<string | number, null> = {};
  for (let i = 0; i < length; i++) erased[keys[i]] = null;
  return erased;
};
 
export const removeErasures = (attr: QuillDeltaAttributes | undefined): QuillDeltaAttributes | undefined => {
  if (!attr) return;
  const keys = Object.keys(attr);
  const length = keys.length;
  Iif (!length) return;
  const cleaned: QuillDeltaAttributes = {};
  for (let i = 0; i < length; i++) {
    const key = keys[i];
    const value = attr[key];
    if (value !== null) cleaned[key] = value;
  }
  return isEmpty(cleaned) ? undefined : cleaned;
};
 
export const diffAttributes = (
  oldAttributes: QuillDeltaAttributes | undefined,
  newAttributes: QuillDeltaAttributes | undefined,
): QuillDeltaAttributes | undefined => {
  if (!oldAttributes) return removeErasures(newAttributes);
  if (!newAttributes) return eraseAttributes(oldAttributes);
  const diff: QuillDeltaAttributes = {};
  const keys = Object.keys(newAttributes);
  const length = keys.length;
  for (let i = 0; i < length; i++) {
    const key = keys[i];
    const newValue = newAttributes[key];
    const oldValue = oldAttributes[key];
    if (newValue === oldValue) continue;
    diff[key] = newValue;
  }
  const oldKeys = Object.keys(oldAttributes);
  const oldLength = oldKeys.length;
  for (let i = 0; i < oldLength; i++) {
    const key = oldKeys[i];
    // tslint:disable-next-line:triple-equals
    if (newAttributes[key] !== undefined) continue;
    diff[key] = null;
  }
  if (isEmpty(diff)) return undefined;
  return diff;
};