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 8448x 48x 48x         48x 11764x 11764x 11764x 5977x 5977x 11727x 11727x 11727x   9135x 9135x 9135x     2592x 2592x 2592x       5977x 5221x     48x 1030x 1030x 1030x 1030x 1030x 1401x 1030x     48x 11923x 9948x 9948x 9948x 9948x 9948x 15257x 15257x 15257x   9948x     48x       4529x 1616x 586x 586x 586x 586x 771x 771x 771x 771x 490x   586x 586x 586x 773x   773x 425x   586x 503x    
import {isEmpty} from '@jsonjoy.com/util/lib/isEmpty';
import {PersistedSlice} from '../peritext/slice/PersistedSlice';
import {SliceBehavior} 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.behavior) {
      case SliceBehavior.One: {
        const tag = slice.type as PathStep;
        if (tag) attributes[tag] = slice.data();
        break;
      }
      case SliceBehavior.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;
};