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 11543x 11543x 11543x 5663x 5663x 9930x 9930x 9930x   8071x 8071x 8071x     1859x 1859x 1859x       5663x 4976x     51x 894x 894x 894x 894x 894x 1172x 894x     51x 11805x 9824x 9824x 9824x 9824x 9824x 15040x 15040x 15040x   9824x     51x       4296x 1389x 495x 495x 495x 495x 631x 631x 631x 631x 396x   495x 495x 495x 648x   648x 357x   495x 411x    
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;
};