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 8445x 45x 45x         45x 11737x 11737x 11737x 5938x 5938x 12223x 12223x 12223x   9581x 9581x 9581x     2642x 2642x 2642x       5938x 5214x     45x 933x 933x 933x 933x 933x 1313x 933x     45x 11794x 9881x 9881x 9881x 9881x 9881x 15073x 15073x 15073x   9881x     45x       4393x 1497x 564x 564x 564x 564x 729x 729x 729x 729x 436x   564x 564x 564x 763x   763x 408x   564x 468x    
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;
};