gdpr audit implemented, email log, vollmachten, pdf delete cancel data privacy and vollmachten, removed message no id card in engergy car, and other contracts that are not telecom contracts, added insert counter for engery
This commit is contained in:
+189
@@ -0,0 +1,189 @@
|
||||
/// Stores metadata for a part of a change.
|
||||
export class Span<Data = any> {
|
||||
/// @internal
|
||||
constructor(
|
||||
/// The length of this span.
|
||||
readonly length: number,
|
||||
/// The data associated with this span.
|
||||
readonly data: Data
|
||||
) {}
|
||||
|
||||
/// @internal
|
||||
cut(length: number) {
|
||||
return length == this.length ? this : new Span(length, this.data)
|
||||
}
|
||||
|
||||
/// @internal
|
||||
static slice(spans: readonly Span[], from: number, to: number) {
|
||||
if (from == to) return Span.none
|
||||
if (from == 0 && to == Span.len(spans)) return spans
|
||||
let result = []
|
||||
for (let i = 0, off = 0; off < to; i++) {
|
||||
let span = spans[i], end = off + span.length
|
||||
let overlap = Math.min(to, end) - Math.max(from, off)
|
||||
if (overlap > 0) result.push(span.cut(overlap))
|
||||
off = end
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
/// @internal
|
||||
static join<Data>(a: readonly Span<Data>[], b: readonly Span<Data>[], combine: (dataA: Data, dataB: Data) => Data) {
|
||||
if (a.length == 0) return b
|
||||
if (b.length == 0) return a
|
||||
let combined = combine(a[a.length - 1].data, b[0].data)
|
||||
if (combined == null) return a.concat(b)
|
||||
let result = a.slice(0, a.length - 1)
|
||||
result.push(new Span(a[a.length - 1].length + b[0].length, combined))
|
||||
for (let i = 1; i < b.length; i++) result.push(b[i])
|
||||
return result
|
||||
}
|
||||
|
||||
/// @internal
|
||||
static len(spans: readonly Span[]) {
|
||||
let len = 0
|
||||
for (let i = 0; i < spans.length; i++) len += spans[i].length
|
||||
return len
|
||||
}
|
||||
|
||||
/// @internal
|
||||
static none: readonly Span[] = []
|
||||
}
|
||||
|
||||
/// A replaced range with metadata associated with it.
|
||||
export class Change<Data = any> {
|
||||
/// @internal
|
||||
constructor(
|
||||
/// The start of the range deleted/replaced in the old document.
|
||||
readonly fromA: number,
|
||||
/// The end of the range in the old document.
|
||||
readonly toA: number,
|
||||
/// The start of the range inserted in the new document.
|
||||
readonly fromB: number,
|
||||
/// The end of the range in the new document.
|
||||
readonly toB: number,
|
||||
/// Data associated with the deleted content. The length of these
|
||||
/// spans adds up to `this.toA - this.fromA`.
|
||||
readonly deleted: readonly Span<Data>[],
|
||||
/// Data associated with the inserted content. Length adds up to
|
||||
/// `this.toB - this.fromB`.
|
||||
readonly inserted: readonly Span<Data>[]
|
||||
) {}
|
||||
|
||||
/// @internal
|
||||
get lenA() { return this.toA - this.fromA }
|
||||
/// @internal
|
||||
get lenB() { return this.toB - this.fromB }
|
||||
|
||||
/// @internal
|
||||
slice(startA: number, endA: number, startB: number, endB: number): Change<Data> {
|
||||
if (startA == 0 && startB == 0 && endA == this.toA - this.fromA &&
|
||||
endB == this.toB - this.fromB) return this
|
||||
return new Change(this.fromA + startA, this.fromA + endA,
|
||||
this.fromB + startB, this.fromB + endB,
|
||||
Span.slice(this.deleted, startA, endA),
|
||||
Span.slice(this.inserted, startB, endB))
|
||||
}
|
||||
|
||||
/// This merges two changesets (the end document of x should be the
|
||||
/// start document of y) into a single one spanning the start of x to
|
||||
/// the end of y.
|
||||
static merge<Data>(x: readonly Change<Data>[],
|
||||
y: readonly Change<Data>[],
|
||||
combine: (dataA: Data, dataB: Data) => Data): readonly Change<Data>[] {
|
||||
if (x.length == 0) return y
|
||||
if (y.length == 0) return x
|
||||
|
||||
let result = []
|
||||
// Iterate over both sets in parallel, using the middle coordinate
|
||||
// system (B in x, A in y) to synchronize.
|
||||
for (let iX = 0, iY = 0, curX: Change<Data> | null = x[0], curY: Change<Data> | null = y[0];;) {
|
||||
if (!curX && !curY) {
|
||||
return result
|
||||
} else if (curX && (!curY || curX.toB < curY.fromA)) { // curX entirely in front of curY
|
||||
let off = iY ? y[iY - 1].toB - y[iY - 1].toA : 0
|
||||
result.push(off == 0 ? curX :
|
||||
new Change(curX.fromA, curX.toA, curX.fromB + off, curX.toB + off,
|
||||
curX.deleted, curX.inserted))
|
||||
curX = iX++ == x.length ? null : x[iX]
|
||||
} else if (curY && (!curX || curY.toA < curX.fromB)) { // curY entirely in front of curX
|
||||
let off = iX ? x[iX - 1].toB - x[iX - 1].toA : 0
|
||||
result.push(off == 0 ? curY :
|
||||
new Change(curY.fromA - off, curY.toA - off, curY.fromB, curY.toB,
|
||||
curY.deleted, curY.inserted))
|
||||
curY = iY++ == y.length ? null : y[iY]
|
||||
} else { // Touch, need to merge
|
||||
// The rules for merging ranges are that deletions from the
|
||||
// old set and insertions from the new are kept. Areas of the
|
||||
// middle document covered by a but not by b are insertions
|
||||
// from a that need to be added, and areas covered by b but
|
||||
// not a are deletions from b that need to be added.
|
||||
let pos = Math.min(curX!.fromB, curY!.fromA)
|
||||
let fromA = Math.min(curX!.fromA, curY!.fromA - (iX ? x[iX - 1].toB - x[iX - 1].toA : 0)), toA = fromA
|
||||
let fromB = Math.min(curY!.fromB, curX!.fromB + (iY ? y[iY - 1].toB - y[iY - 1].toA : 0)), toB = fromB
|
||||
let deleted = Span.none, inserted = Span.none
|
||||
|
||||
// Used to prevent appending ins/del range for the same Change twice
|
||||
let enteredX = false, enteredY = false
|
||||
|
||||
// Need to have an inner loop since any number of further
|
||||
// ranges might be touching this group
|
||||
for (;;) {
|
||||
let nextX = !curX ? 2e8 : pos >= curX.fromB ? curX.toB : curX.fromB
|
||||
let nextY = !curY ? 2e8 : pos >= curY.fromA ? curY.toA : curY.fromA
|
||||
let next = Math.min(nextX, nextY)
|
||||
let inX = curX && pos >= curX.fromB, inY = curY && pos >= curY.fromA
|
||||
if (!inX && !inY) break
|
||||
if (inX && pos == curX!.fromB && !enteredX) {
|
||||
deleted = Span.join(deleted, curX!.deleted, combine)
|
||||
toA += curX!.lenA
|
||||
enteredX = true
|
||||
}
|
||||
if (inX && !inY) {
|
||||
inserted = Span.join(inserted, Span.slice(curX!.inserted, pos - curX!.fromB, next - curX!.fromB), combine)
|
||||
toB += next - pos
|
||||
}
|
||||
if (inY && pos == curY!.fromA && !enteredY) {
|
||||
inserted = Span.join(inserted, curY!.inserted, combine)
|
||||
toB += curY!.lenB
|
||||
enteredY = true
|
||||
}
|
||||
if (inY && !inX) {
|
||||
deleted = Span.join(deleted, Span.slice(curY!.deleted, pos - curY!.fromA, next - curY!.fromA), combine)
|
||||
toA += next - pos
|
||||
}
|
||||
|
||||
if (inX && next == curX!.toB) {
|
||||
curX = iX++ == x.length ? null : x[iX]
|
||||
enteredX = false
|
||||
}
|
||||
if (inY && next == curY!.toA) {
|
||||
curY = iY++ == y.length ? null : y[iY]
|
||||
enteredY = false
|
||||
}
|
||||
pos = next
|
||||
}
|
||||
if (fromA < toA || fromB < toB)
|
||||
result.push(new Change(fromA, toA, fromB, toB, deleted, inserted))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Deserialize a change from JSON format.
|
||||
static fromJSON<Data>(json: ChangeJSON<Data>) {
|
||||
return new Change(json.fromA, json.toA, json.fromB, json.toB,
|
||||
json.deleted.map(d => new Span(d.length, d.data)),
|
||||
json.inserted.map(d => new Span(d.length, d.data)))
|
||||
}
|
||||
|
||||
/// Returns a JSON-serializeable object to represent this change.
|
||||
toJSON(): ChangeJSON<Data> { return this }
|
||||
}
|
||||
|
||||
/// JSON-serialized form of a change.
|
||||
export type ChangeJSON<Data> = {
|
||||
fromA: number, toA: number,
|
||||
fromB: number, toB: number,
|
||||
deleted: readonly {length: number, data: Data}[],
|
||||
inserted: readonly {length: number, data: Data}[]
|
||||
}
|
||||
Reference in New Issue
Block a user