linked-rolls 0.57.0 → 0.59.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +54 -367
- package/lib/{constraints.d.ts → analysis/constraints.d.ts} +1 -1
- package/lib/{constraints.js → analysis/constraints.js} +8 -7
- package/lib/analysis/ownPaper.d.ts +26 -0
- package/lib/analysis/ownPaper.js +41 -0
- package/lib/{reservations.d.ts → analysis/reservations.d.ts} +3 -3
- package/lib/{reservations.js → analysis/reservations.js} +3 -3
- package/lib/{sigla.d.ts → analysis/sigla.d.ts} +2 -2
- package/lib/{sigla.js → analysis/sigla.js} +4 -4
- package/lib/{witnesses.d.ts → analysis/witnesses.d.ts} +2 -2
- package/lib/{witnesses.js → analysis/witnesses.js} +2 -2
- package/lib/{Collation.d.ts → collation/Collation.d.ts} +4 -4
- package/lib/{Collation.js → collation/Collation.js} +4 -4
- package/lib/{alignment.d.ts → collation/alignment.d.ts} +4 -4
- package/lib/{alignment.js → collation/alignment.js} +10 -16
- package/lib/{scatter.d.ts → collation/scatter.d.ts} +4 -4
- package/lib/{scatter.js → collation/scatter.js} +3 -3
- package/lib/{statistics.d.ts → collation/statistics.d.ts} +1 -1
- package/lib/{statistics.js → collation/statistics.js} +2 -2
- package/lib/{substitution.d.ts → collation/substitution.d.ts} +9 -24
- package/lib/{substitution.js → collation/substitution.js} +13 -37
- package/lib/emulation/Emulation.d.ts +64 -0
- package/lib/emulation/Emulation.js +282 -0
- package/lib/emulation/negotiation.d.ts +14 -0
- package/lib/emulation/negotiation.js +20 -0
- package/lib/index.d.ts +43 -35
- package/lib/index.js +36 -35
- package/lib/{asJsonLd.d.ts → io/asJsonLd.d.ts} +1 -1
- package/lib/{asJsonLd.js → io/asJsonLd.js} +3 -3
- package/lib/{context.d.ts → io/context.d.ts} +4 -4
- package/lib/{context.js → io/context.js} +4 -4
- package/lib/{importJsonLd.d.ts → io/importJsonLd.d.ts} +1 -1
- package/lib/io/importJsonLd.js +128 -0
- package/lib/{migrate.js → io/migrate.js} +59 -55
- package/lib/{Agent.d.ts → model/Agent.d.ts} +1 -1
- package/lib/{Assumption.d.ts → model/Assumption.d.ts} +1 -1
- package/lib/{Edit.d.ts → model/Edit.d.ts} +1 -1
- package/lib/{Edition.d.ts → model/Edition.d.ts} +2 -2
- package/lib/{Feature.d.ts → model/Feature.d.ts} +1 -1
- package/lib/{FeatureSource.d.ts → model/FeatureSource.d.ts} +1 -1
- package/lib/{Perforator.d.ts → model/Perforator.d.ts} +1 -1
- package/lib/{RollCopy.d.ts → model/RollCopy.d.ts} +3 -10
- package/lib/{RollCopy.js → model/RollCopy.js} +3 -5
- package/lib/{Symbol.d.ts → model/Symbol.d.ts} +1 -1
- package/lib/{Version.d.ts → model/Version.d.ts} +10 -2
- package/lib/{Version.js → model/Version.js} +24 -1
- package/lib/{vocabulary.js → model/vocabulary.js} +2 -2
- package/lib/ops/beliefs.d.ts +9 -0
- package/lib/ops/beliefs.js +31 -0
- package/lib/ops/collation.d.ts +62 -0
- package/lib/ops/collation.js +228 -0
- package/lib/ops/commands.d.ts +9 -0
- package/lib/ops/commands.js +29 -0
- package/lib/ops/copies.d.ts +74 -0
- package/lib/ops/copies.js +135 -0
- package/lib/ops/draft.d.ts +59 -0
- package/lib/ops/draft.js +81 -0
- package/lib/ops/edits.d.ts +10 -0
- package/lib/ops/edits.js +82 -0
- package/lib/ops/features.d.ts +84 -0
- package/lib/ops/features.js +296 -0
- package/lib/ops/forget.d.ts +31 -0
- package/lib/ops/forget.js +110 -0
- package/lib/ops/immutable.d.ts +16 -0
- package/lib/ops/immutable.js +46 -0
- package/lib/ops/index.d.ts +13 -0
- package/lib/ops/index.js +7 -0
- package/lib/ops/versions.d.ts +33 -0
- package/lib/ops/versions.js +95 -0
- package/lib/readers/AtonParser.js +5 -7
- package/lib/readers/phillipsEroll.d.ts +3 -3
- package/lib/readers/phillipsEroll.js +4 -5
- package/lib/readers/spencerBar.d.ts +2 -2
- package/lib/readers/spencerBar.js +2 -3
- package/lib/readers/stanfordAton.d.ts +3 -3
- package/lib/readers/stanfordAton.js +2 -3
- package/lib/schema.json +0 -13
- package/lib/{ReproducingSystem.d.ts → systems/ReproducingSystem.d.ts} +3 -3
- package/lib/{TrackerBar.d.ts → systems/TrackerBar.d.ts} +48 -8
- package/lib/{TrackerBar.js → systems/TrackerBar.js} +11 -2
- package/lib/systems/index.d.ts +8 -2
- package/lib/systems/index.js +7 -1
- package/lib/systems/welte.d.ts +100 -0
- package/lib/systems/welte.js +119 -0
- package/lib/systems/welteLicensee/bar.d.ts +1 -1
- package/lib/systems/welteLicensee/bar.js +5 -2
- package/lib/systems/welteLicensee/system.d.ts +1 -1
- package/lib/systems/welteLicensee/system.js +1 -1
- package/lib/systems/welteT100/bar.d.ts +27 -1
- package/lib/systems/welteT100/bar.js +44 -2
- package/lib/systems/welteT100/system.d.ts +6 -39
- package/lib/systems/welteT100/system.js +19 -128
- package/lib/systems/welteT98/bar.d.ts +1 -1
- package/lib/systems/welteT98/bar.js +18 -2
- package/lib/systems/welteT98/system.d.ts +20 -38
- package/lib/systems/welteT98/system.js +20 -121
- package/lib/validate.d.ts +1 -1
- package/lib/{EditionView.d.ts → view/EditionView.d.ts} +17 -41
- package/lib/{EditionView.js → view/EditionView.js} +26 -75
- package/package.json +7 -2
- package/lib/Emulation.d.ts +0 -36
- package/lib/Emulation.js +0 -250
- package/lib/editionOps.d.ts +0 -260
- package/lib/editionOps.js +0 -1079
- package/lib/importJsonLd.js +0 -57
- /package/lib/{migrate.d.ts → io/migrate.d.ts} +0 -0
- /package/lib/{Agent.js → model/Agent.js} +0 -0
- /package/lib/{Assumption.js → model/Assumption.js} +0 -0
- /package/lib/{ConditionState.d.ts → model/ConditionState.d.ts} +0 -0
- /package/lib/{ConditionState.js → model/ConditionState.js} +0 -0
- /package/lib/{Edit.js → model/Edit.js} +0 -0
- /package/lib/{Edition.js → model/Edition.js} +0 -0
- /package/lib/{Feature.js → model/Feature.js} +0 -0
- /package/lib/{FeatureSource.js → model/FeatureSource.js} +0 -0
- /package/lib/{Perforator.js → model/Perforator.js} +0 -0
- /package/lib/{Quantity.d.ts → model/Quantity.d.ts} +0 -0
- /package/lib/{Quantity.js → model/Quantity.js} +0 -0
- /package/lib/{Symbol.js → model/Symbol.js} +0 -0
- /package/lib/{TrackCalibration.d.ts → model/TrackCalibration.d.ts} +0 -0
- /package/lib/{TrackCalibration.js → model/TrackCalibration.js} +0 -0
- /package/lib/{notes.d.ts → model/notes.d.ts} +0 -0
- /package/lib/{notes.js → model/notes.js} +0 -0
- /package/lib/{procedures.d.ts → model/procedures.d.ts} +0 -0
- /package/lib/{procedures.js → model/procedures.js} +0 -0
- /package/lib/{vocabulary.d.ts → model/vocabulary.d.ts} +0 -0
- /package/lib/{sorted.d.ts → shared/sorted.d.ts} +0 -0
- /package/lib/{sorted.js → shared/sorted.js} +0 -0
- /package/lib/{utils.d.ts → shared/utils.d.ts} +0 -0
- /package/lib/{utils.js → shared/utils.js} +0 -0
- /package/lib/{ReproducingSystem.js → systems/ReproducingSystem.js} +0 -0
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { v4 } from "uuid";
|
|
2
|
+
import { asSymbols, barOf, featuresOf, isPaperStretch } from "../model/RollCopy.js";
|
|
3
|
+
import { systemOf } from "../systems/TrackerBar.js";
|
|
4
|
+
import { applyShift, applyScale, revertShift, revertScale, revertShortening, shortenChains } from "../collation/alignment.js";
|
|
5
|
+
import { idOf } from "../model/Assumption.js";
|
|
6
|
+
import { onCopy, stateOf, insertion, insertedIn, referenceHeld, withoutReferences } from "./draft.js";
|
|
7
|
+
import { without } from "./immutable.js";
|
|
8
|
+
import { featureIdsOf, carriedOnlyOn, forgetFeatures } from "./forget.js";
|
|
9
|
+
/**
|
|
10
|
+
* Puts the copy into the edition with a version of its own, which
|
|
11
|
+
* inserts every symbol the copy's own tracker bar reads on it. The
|
|
12
|
+
* version is a reading in that system's words, so it is coded for the
|
|
13
|
+
* system the copy was cut for.
|
|
14
|
+
*/
|
|
15
|
+
export const createVersion = (copy) => draft => {
|
|
16
|
+
const bar = barOf(copy);
|
|
17
|
+
draft.copies.push(copy);
|
|
18
|
+
draft.versions.push({
|
|
19
|
+
id: v4(),
|
|
20
|
+
system: systemOf(bar),
|
|
21
|
+
edits: asSymbols(featuresOf(copy), bar).map(insertion),
|
|
22
|
+
motivations: []
|
|
23
|
+
});
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* Puts the copy into the edition without a version of its own, as for a
|
|
27
|
+
* copy whose features are not read into symbols: one known only from a
|
|
28
|
+
* recording states instead which versions it carries.
|
|
29
|
+
*/
|
|
30
|
+
export const addCopy = (copy) => draft => {
|
|
31
|
+
draft.copies.push(copy);
|
|
32
|
+
};
|
|
33
|
+
/** States what the scale is put down to, in place of an earlier reading. */
|
|
34
|
+
const readScale = (copy, reading) => {
|
|
35
|
+
if (reading.cause === 'paper') {
|
|
36
|
+
copy.conditions = [...copy.conditions.filter(condition => !isPaperStretch(condition)), reading.condition];
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
if (!copy.production)
|
|
40
|
+
copy.production = {};
|
|
41
|
+
copy.production.speed = reading.speed;
|
|
42
|
+
};
|
|
43
|
+
/**
|
|
44
|
+
* Shifts and then scales the copy's features into line with another
|
|
45
|
+
* copy's, and puts the scale down to what the reading says: the paper,
|
|
46
|
+
* or the speed the copy was cut for.
|
|
47
|
+
*/
|
|
48
|
+
export const alignCopy = (copyId, shift, scale, reading) => onCopy(copyId, copy => {
|
|
49
|
+
applyShift(shift, copy);
|
|
50
|
+
applyScale(scale, copy);
|
|
51
|
+
if (reading)
|
|
52
|
+
readScale(copy, reading);
|
|
53
|
+
});
|
|
54
|
+
/**
|
|
55
|
+
* Puts the copy's features back where they were measured. A paper
|
|
56
|
+
* stretch read off the alignment goes with it; a speed stated stays,
|
|
57
|
+
* being a fact about the copy.
|
|
58
|
+
*/
|
|
59
|
+
export const unalignCopy = (copyId) => onCopy(copyId, copy => {
|
|
60
|
+
revertScale(copy);
|
|
61
|
+
revertShift(copy);
|
|
62
|
+
copy.conditions = without(copy.conditions, isPaperStretch);
|
|
63
|
+
});
|
|
64
|
+
/**
|
|
65
|
+
* Takes the extension a pneumatic reader adds off the ends of the
|
|
66
|
+
* copy's chains of holes, and records how much was taken, so that their lengths
|
|
67
|
+
* can be compared with a scanned copy's at all.
|
|
68
|
+
*/
|
|
69
|
+
export const shortenCopy = (copyId, extension, leaving) => onCopy(copyId, copy => shortenChains(extension, copy, leaving));
|
|
70
|
+
/** Puts the reader's extension back on the copy's chains of holes. */
|
|
71
|
+
export const unshortenCopy = (copyId) => onCopy(copyId, copy => revertShortening(copy));
|
|
72
|
+
/** States what the copy's features were read from, in place of any earlier statement. */
|
|
73
|
+
export const stateSource = (copyId, source) => onCopy(copyId, copy => {
|
|
74
|
+
copy.readFrom = source;
|
|
75
|
+
});
|
|
76
|
+
/** Takes back the statement, leaving the copy silent about its source again. */
|
|
77
|
+
export const clearSource = (copyId) => onCopy(copyId, copy => {
|
|
78
|
+
copy.readFrom = undefined;
|
|
79
|
+
});
|
|
80
|
+
/** Gives the copy the siglum it is referred to by, or takes it away where the siglum given is blank. */
|
|
81
|
+
export const nameCopy = (copyId, siglum) => onCopy(copyId, copy => {
|
|
82
|
+
const trimmed = siglum.trim();
|
|
83
|
+
if (trimmed)
|
|
84
|
+
copy.siglum = trimmed;
|
|
85
|
+
else
|
|
86
|
+
delete copy.siglum;
|
|
87
|
+
});
|
|
88
|
+
/** Takes out the copy's statements that match, and the list itself where none is left. */
|
|
89
|
+
export const dropStatements = (copy, matches) => {
|
|
90
|
+
const statements = stateOf(copy).carries;
|
|
91
|
+
if (!statements?.some(matches))
|
|
92
|
+
return;
|
|
93
|
+
const kept = withoutReferences(statements, matches);
|
|
94
|
+
if (kept)
|
|
95
|
+
copy.carries = kept;
|
|
96
|
+
else
|
|
97
|
+
delete copy.carries;
|
|
98
|
+
};
|
|
99
|
+
/**
|
|
100
|
+
* States that the copy carries the version, under the belief given. It
|
|
101
|
+
* is meant for a copy whose features are not read into symbols, such as
|
|
102
|
+
* one known only from a recording, and `carriageProblems` reports it
|
|
103
|
+
* where features carry symbols already. A second statement about one
|
|
104
|
+
* version is none.
|
|
105
|
+
*/
|
|
106
|
+
export const stateCarriage = (copyId, versionId, belief) => onCopy(copyId, copy => {
|
|
107
|
+
const statements = stateOf(copy).carries ?? [];
|
|
108
|
+
if (statements.some(statement => idOf(statement) === versionId))
|
|
109
|
+
return;
|
|
110
|
+
copy.carries = [...statements, referenceHeld(versionId, belief)];
|
|
111
|
+
});
|
|
112
|
+
/** Takes back the copy's statement that it carries the version. */
|
|
113
|
+
export const clearCarriage = (copyId, versionId) => onCopy(copyId, copy => dropStatements(copy, statement => idOf(statement) === versionId));
|
|
114
|
+
/**
|
|
115
|
+
* Adds a general condition to the copy, beside whatever is stated of it
|
|
116
|
+
* already. The other condition a copy may be in, a paper stretch, is
|
|
117
|
+
* read off an alignment and stated by `alignCopy`.
|
|
118
|
+
*/
|
|
119
|
+
export const addGeneralCondition = (copyId, condition) => onCopy(copyId, copy => {
|
|
120
|
+
copy.conditions.push(condition);
|
|
121
|
+
});
|
|
122
|
+
/** The symbols of the versions that no other copy carries. */
|
|
123
|
+
export const symbolsCarriedOnlyBy = (edition, copyId) => {
|
|
124
|
+
const copy = edition.copies.find(c => c.id === copyId);
|
|
125
|
+
return copy ? insertedIn(edition.versions).filter(carriedOnlyOn(featureIdsOf(copy))) : [];
|
|
126
|
+
};
|
|
127
|
+
/**
|
|
128
|
+
* Takes the copy out of the edition together with the symbols only it
|
|
129
|
+
* carries, and with every reference the versions and the argumentations
|
|
130
|
+
* made to those symbols.
|
|
131
|
+
*/
|
|
132
|
+
export const removeCopy = (copyId) => onCopy(copyId, (copy, draft) => {
|
|
133
|
+
forgetFeatures(draft, featureIdsOf(copy));
|
|
134
|
+
draft.copies = draft.copies.filter(c => c.id !== copyId);
|
|
135
|
+
});
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { EditionView } from "../view/EditionView.js";
|
|
2
|
+
/** What the operations are built from: the type of an operation, how one finds what it writes to on the draft, and the edits it writes. */
|
|
3
|
+
import { Draft } from "immer";
|
|
4
|
+
import { Edition } from "../model/Edition.js";
|
|
5
|
+
import { AnySymbol } from "../model/Symbol.js";
|
|
6
|
+
import { Edit } from "../model/Edit.js";
|
|
7
|
+
import { Motivation, Version } from "../model/Version.js";
|
|
8
|
+
import { RollCopy } from "../model/RollCopy.js";
|
|
9
|
+
import { Belief, ReferenceAssumption } from "../model/Assumption.js";
|
|
10
|
+
import { FeatureOrPatch } from "../model/Feature.js";
|
|
11
|
+
import { WithId } from "../shared/utils.js";
|
|
12
|
+
/**
|
|
13
|
+
* A change to an edition, written onto an immer draft of it. One
|
|
14
|
+
* operation is one undo step, so an operation that has to read the
|
|
15
|
+
* edition first takes an `EditionView` of the state it will be
|
|
16
|
+
* applied to and does all its writing in the one function it returns.
|
|
17
|
+
* Where operations run one after another on one draft, the ones after
|
|
18
|
+
* the first read a view of the draft as it then stands (`reading`).
|
|
19
|
+
*/
|
|
20
|
+
export type EditionOp = (draft: Draft<Edition>) => void;
|
|
21
|
+
export declare const noChange: EditionOp;
|
|
22
|
+
export declare const onCopy: (copyId: string, op: (copy: Draft<RollCopy>, draft: Draft<Edition>) => void) => EditionOp;
|
|
23
|
+
export declare const onVersion: (versionId: string, op: (version: Draft<Version>, draft: Draft<Edition>) => void) => EditionOp;
|
|
24
|
+
export declare const onFeature: (copyId: string, featureId: string, op: (feature: Draft<FeatureOrPatch>) => void) => EditionOp;
|
|
25
|
+
/**
|
|
26
|
+
* The operation built from a view of the state it is applied to. The
|
|
27
|
+
* view given is used where the draft still stands as the view saw it,
|
|
28
|
+
* and a fresh one is taken of the draft where an operation run before
|
|
29
|
+
* on the same draft has changed it: a path or an index read off the
|
|
30
|
+
* view would otherwise name what stood there before.
|
|
31
|
+
*/
|
|
32
|
+
export declare const reading: (view: EditionView, build: (view: EditionView) => EditionOp) => EditionOp;
|
|
33
|
+
/**
|
|
34
|
+
* The state a draft stands at, as plain data. Reading a draft proxies
|
|
35
|
+
* everything it touches, so what is only read is read from this.
|
|
36
|
+
*/
|
|
37
|
+
export declare const stateOf: <T>(draft: Draft<T>) => T;
|
|
38
|
+
/** The ids of the items the rewriting left out. */
|
|
39
|
+
export declare const droppedIds: (before: readonly WithId[], after: readonly WithId[]) => string[];
|
|
40
|
+
export type Ids = ReadonlySet<string>;
|
|
41
|
+
export declare const insertion: (symbol: AnySymbol) => Edit;
|
|
42
|
+
export declare const deletion: (symbolId: string) => Edit;
|
|
43
|
+
/** The edit as a collation leaves it: made by rule, and not yet read by anybody. */
|
|
44
|
+
export declare const asUnchecked: (edit: Edit) => Edit;
|
|
45
|
+
/**
|
|
46
|
+
* The motivations with the unchecked one declared, where any edit
|
|
47
|
+
* names it and the version does not state it yet. An edit naming a
|
|
48
|
+
* motivation the version leaves undeclared points at nothing.
|
|
49
|
+
*/
|
|
50
|
+
export declare const declaring: (motivations: Motivation[], edits: readonly Edit[]) => Motivation[];
|
|
51
|
+
export declare const insertedIn: (versions: readonly Version[]) => AnySymbol[];
|
|
52
|
+
/** The edits with the change applied, less those it emptied; the very same array where it changed none. */
|
|
53
|
+
export declare const edited: (edits: Edit[], change: (edit: Edit) => Edit) => Edit[];
|
|
54
|
+
/** Takes the symbols out of the version's own insertions, and the edits that had nothing else. */
|
|
55
|
+
export declare const dropInsertions: (version: Draft<Version>, symbolIds: Ids) => void;
|
|
56
|
+
/** A reference under the belief given, where one is given. */
|
|
57
|
+
export declare const referenceHeld: (id: string, belief?: Belief) => ReferenceAssumption;
|
|
58
|
+
/** The references less those that match, or nothing where none is left. */
|
|
59
|
+
export declare const withoutReferences: <R extends ReferenceAssumption>(references: readonly R[], matches: (reference: Readonly<R>) => boolean) => R[] | undefined;
|
package/lib/ops/draft.js
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { EditionView } from "../view/EditionView.js";
|
|
2
|
+
/** What the operations are built from: the type of an operation, how one finds what it writes to on the draft, and the edits it writes. */
|
|
3
|
+
import { current, isDraft } from "immer";
|
|
4
|
+
import { v4 } from "uuid";
|
|
5
|
+
import { unchecked, uncheckedMotivation } from "../collation/Collation.js";
|
|
6
|
+
import { insertedBy } from "../model/Version.js";
|
|
7
|
+
import { featuresOf } from "../model/RollCopy.js";
|
|
8
|
+
import { assignReference } from "../model/Assumption.js";
|
|
9
|
+
import { without, pruned } from "./immutable.js";
|
|
10
|
+
export const noChange = () => undefined;
|
|
11
|
+
export const onCopy = (copyId, op) => draft => {
|
|
12
|
+
const copy = draft.copies.find(c => c.id === copyId);
|
|
13
|
+
if (copy)
|
|
14
|
+
op(copy, draft);
|
|
15
|
+
};
|
|
16
|
+
export const onVersion = (versionId, op) => draft => {
|
|
17
|
+
const version = draft.versions.find(v => v.id === versionId);
|
|
18
|
+
if (version)
|
|
19
|
+
op(version, draft);
|
|
20
|
+
};
|
|
21
|
+
export const onFeature = (copyId, featureId, op) => onCopy(copyId, copy => {
|
|
22
|
+
const feature = featuresOf(copy).find(f => f.id === featureId);
|
|
23
|
+
if (feature)
|
|
24
|
+
op(feature);
|
|
25
|
+
});
|
|
26
|
+
/**
|
|
27
|
+
* The operation built from a view of the state it is applied to. The
|
|
28
|
+
* view given is used where the draft still stands as the view saw it,
|
|
29
|
+
* and a fresh one is taken of the draft where an operation run before
|
|
30
|
+
* on the same draft has changed it: a path or an index read off the
|
|
31
|
+
* view would otherwise name what stood there before.
|
|
32
|
+
*/
|
|
33
|
+
export const reading = (view, build) => draft => {
|
|
34
|
+
const state = stateOf(draft);
|
|
35
|
+
build(state === view.edition ? view : new EditionView(state))(draft);
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* The state a draft stands at, as plain data. Reading a draft proxies
|
|
39
|
+
* everything it touches, so what is only read is read from this.
|
|
40
|
+
*/
|
|
41
|
+
export const stateOf = (draft) => isDraft(draft) ? current(draft) : draft;
|
|
42
|
+
/** The ids of the items the rewriting left out. */
|
|
43
|
+
export const droppedIds = (before, after) => {
|
|
44
|
+
const kept = new Set(after.map(item => item.id));
|
|
45
|
+
return before.flatMap(item => kept.has(item.id) ? [] : [item.id]);
|
|
46
|
+
};
|
|
47
|
+
export const insertion = (symbol) => ({ type: 'edit', id: v4(), insert: [symbol] });
|
|
48
|
+
export const deletion = (symbolId) => ({ type: 'edit', id: v4(), delete: [symbolId] });
|
|
49
|
+
/** The edit as a collation leaves it: made by rule, and not yet read by anybody. */
|
|
50
|
+
export const asUnchecked = (edit) => ({ ...edit, motivation: unchecked });
|
|
51
|
+
/**
|
|
52
|
+
* The motivations with the unchecked one declared, where any edit
|
|
53
|
+
* names it and the version does not state it yet. An edit naming a
|
|
54
|
+
* motivation the version leaves undeclared points at nothing.
|
|
55
|
+
*/
|
|
56
|
+
export const declaring = (motivations, edits) => edits.some(edit => edit.motivation === unchecked)
|
|
57
|
+
&& !motivations.some(motivation => motivation.id === unchecked)
|
|
58
|
+
? [...motivations, uncheckedMotivation]
|
|
59
|
+
: motivations;
|
|
60
|
+
const isEmpty = (edit) => !edit.insert?.length && !edit.delete?.length;
|
|
61
|
+
export const insertedIn = (versions) => versions.flatMap(insertedBy);
|
|
62
|
+
/** The edits with the change applied, less those it emptied; the very same array where it changed none. */
|
|
63
|
+
export const edited = (edits, change) => pruned(edits, change, isEmpty);
|
|
64
|
+
/** The edit without the symbols among its insertions, or the very same edit where it inserts none of them. */
|
|
65
|
+
const droppingInsertions = (symbolIds) => (edit) => {
|
|
66
|
+
const insert = edit.insert && without(edit.insert, symbol => symbolIds.has(symbol.id));
|
|
67
|
+
return insert === edit.insert ? edit : { ...edit, insert };
|
|
68
|
+
};
|
|
69
|
+
/** Takes the symbols out of the version's own insertions, and the edits that had nothing else. */
|
|
70
|
+
export const dropInsertions = (version, symbolIds) => {
|
|
71
|
+
const edits = stateOf(version).edits;
|
|
72
|
+
if (edits)
|
|
73
|
+
version.edits = edited(edits, droppingInsertions(symbolIds));
|
|
74
|
+
};
|
|
75
|
+
/** A reference under the belief given, where one is given. */
|
|
76
|
+
export const referenceHeld = (id, belief) => ({ ...assignReference(id), ...(belief && { '@annotation': { id: v4(), belief } }) });
|
|
77
|
+
/** The references less those that match, or nothing where none is left. */
|
|
78
|
+
export const withoutReferences = (references, matches) => {
|
|
79
|
+
const kept = references.filter(reference => !matches(reference));
|
|
80
|
+
return kept.length > 0 ? kept : undefined;
|
|
81
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { EditionView } from "../view/EditionView.js";
|
|
2
|
+
import { Edit } from "../model/Edit.js";
|
|
3
|
+
import { EditionOp } from "./draft.js";
|
|
4
|
+
/**
|
|
5
|
+
* Replaces the edits with a single one carrying all their insertions
|
|
6
|
+
* and deletions, classified by a guess at what the exchange does.
|
|
7
|
+
*/
|
|
8
|
+
export declare const mergeEdits: (given: EditionView, versionId: string, toMerge: readonly Edit[]) => EditionOp;
|
|
9
|
+
/** Replaces the edit with one edit per inserted and one per deleted symbol. */
|
|
10
|
+
export declare const splitEdit: (versionId: string, toSplit: Edit) => EditionOp;
|
package/lib/ops/edits.js
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/** Merging and splitting the edits of a version. */
|
|
2
|
+
import { v4 } from "uuid";
|
|
3
|
+
import { editsOf } from "../model/Version.js";
|
|
4
|
+
import { trackerBarOf } from "../systems/index.js";
|
|
5
|
+
import { distance, mm, subtract } from "../model/Quantity.js";
|
|
6
|
+
import { noChange, onVersion, insertion, deletion, reading } from "./draft.js";
|
|
7
|
+
const sameSequence = (a, b) => a.length === b.length && a.every((value, i) => value === b[i]);
|
|
8
|
+
const expressionTypesOf = (symbols) => symbols.filter((symbol) => symbol.type === 'expression').map(symbol => symbol.expressionType);
|
|
9
|
+
/** The spellings of a single added accent the bar offers, none where no bar is known. */
|
|
10
|
+
const accentsOn = (bar) => bar?.accents ?? [];
|
|
11
|
+
const lengthOf = (span) => subtract(span.to, span.from);
|
|
12
|
+
/** How far apart two onsets may lie for the one symbol to count as a replacement of the other. */
|
|
13
|
+
const REPLACEMENT_TOLERANCE = mm(5);
|
|
14
|
+
/** Shorten or prolong, where the inserted symbol starts about where the deleted one did. */
|
|
15
|
+
const replacementType = (view, inserted, deleted) => {
|
|
16
|
+
const after = view.placeOf(inserted);
|
|
17
|
+
const before = view.placeOf(deleted);
|
|
18
|
+
if (!after || !before || distance(after.from, before.from) >= REPLACEMENT_TOLERANCE)
|
|
19
|
+
return undefined;
|
|
20
|
+
return lengthOf(after) < lengthOf(before) ? 'shorten' : 'prolong';
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* A guess at what an edit does, from the symbols it exchanges and from
|
|
24
|
+
* the systems the version and the one it is based on are coded for.
|
|
25
|
+
*/
|
|
26
|
+
const guessEditType = (view, versionId, edit) => {
|
|
27
|
+
const inserts = edit.insert ?? [];
|
|
28
|
+
const deletes = view.symbols(edit.delete ?? []);
|
|
29
|
+
const inserted = expressionTypesOf(inserts);
|
|
30
|
+
const deleted = expressionTypesOf(deletes);
|
|
31
|
+
const bar = trackerBarOf(view.version(versionId)?.system);
|
|
32
|
+
const parentBar = trackerBarOf(view.predecessorOf(versionId)?.system);
|
|
33
|
+
/**
|
|
34
|
+
* Where the version is coded for another system than its parent, an
|
|
35
|
+
* exchange of expression matter is the transfer being carried out:
|
|
36
|
+
* a red ForzandoOn and ForzandoOff pair giving way to one held green
|
|
37
|
+
* SforzandoForte says the same thing in the other system's words,
|
|
38
|
+
* which is what 'replace-with-equivalent' is for. Calling it a
|
|
39
|
+
* corrected error would say the editor made a mistake.
|
|
40
|
+
*/
|
|
41
|
+
if (bar && parentBar && bar.id !== parentBar.id && inserted.length > 0 && deleted.length > 0) {
|
|
42
|
+
return 'replace-with-equivalent';
|
|
43
|
+
}
|
|
44
|
+
if (deleted.length === 0 && accentsOn(bar).some(accent => sameSequence(inserted, accent)))
|
|
45
|
+
return 'additional-accent';
|
|
46
|
+
if (inserted.length > 1 && sameSequence(inserted, deleted))
|
|
47
|
+
return 'shift';
|
|
48
|
+
if (inserted.length === 0 && deleted.length === 1)
|
|
49
|
+
return 'remove-redundancy';
|
|
50
|
+
if (inserts.length === 1 && deletes.length === 1)
|
|
51
|
+
return replacementType(view, inserts[0], deletes[0]) ?? 'correct-error';
|
|
52
|
+
return 'correct-error';
|
|
53
|
+
};
|
|
54
|
+
/**
|
|
55
|
+
* Replaces the edits with a single one carrying all their insertions
|
|
56
|
+
* and deletions, classified by a guess at what the exchange does.
|
|
57
|
+
*/
|
|
58
|
+
export const mergeEdits = (given, versionId, toMerge) => reading(given, view => {
|
|
59
|
+
if (toMerge.length === 0)
|
|
60
|
+
return noChange;
|
|
61
|
+
const merged = {
|
|
62
|
+
...toMerge[0],
|
|
63
|
+
id: v4(),
|
|
64
|
+
insert: toMerge.flatMap(edit => edit.insert ?? []),
|
|
65
|
+
delete: toMerge.flatMap(edit => edit.delete ?? [])
|
|
66
|
+
};
|
|
67
|
+
merged.editType = guessEditType(view, versionId, merged);
|
|
68
|
+
const mergedIds = new Set(toMerge.map(edit => edit.id));
|
|
69
|
+
return onVersion(versionId, version => {
|
|
70
|
+
version.edits = [...editsOf(version).filter(edit => !mergedIds.has(edit.id)), merged];
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
/** Replaces the edit with one edit per inserted and one per deleted symbol. */
|
|
74
|
+
export const splitEdit = (versionId, toSplit) => {
|
|
75
|
+
const parts = [
|
|
76
|
+
...(toSplit.insert ?? []).map(insertion),
|
|
77
|
+
...(toSplit.delete ?? []).map(deletion)
|
|
78
|
+
];
|
|
79
|
+
return onVersion(versionId, version => {
|
|
80
|
+
version.edits = [...editsOf(version).filter(edit => edit.id !== toSplit.id), ...parts];
|
|
81
|
+
});
|
|
82
|
+
};
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { EditionView } from "../view/EditionView.js";
|
|
2
|
+
import { ModificationPurpose } from "../model/RollCopy.js";
|
|
3
|
+
import { FeatureConditionAssignment, FeatureOrPatch, NestedFeature } from "../model/Feature.js";
|
|
4
|
+
import { EditionOp } from "./draft.js";
|
|
5
|
+
/**
|
|
6
|
+
* States the condition of the feature, in place of any earlier
|
|
7
|
+
* statement. Throws where the kind of feature is in no such condition;
|
|
8
|
+
* `conditions` says which conditions each kind of feature may be in.
|
|
9
|
+
*/
|
|
10
|
+
export declare const stateFeatureCondition: (copyId: string, featureId: string, condition: FeatureConditionAssignment) => EditionOp;
|
|
11
|
+
/**
|
|
12
|
+
* The act a feature an editor states belongs to: the punching of the
|
|
13
|
+
* copy, the act that brought another feature about, or, where neither
|
|
14
|
+
* is said, an act of its own for the purpose given. A patch is never
|
|
15
|
+
* punched, being glued on, so `punched` says nothing of one.
|
|
16
|
+
*/
|
|
17
|
+
export interface FeatureAct {
|
|
18
|
+
/** The copy came from its punching with it, which is where a reading of a scan puts every chain of holes. */
|
|
19
|
+
punched?: boolean;
|
|
20
|
+
/** The act that brought this feature about brought the new one about as well. */
|
|
21
|
+
beside?: string;
|
|
22
|
+
/** What the act was for, where a new one is made for the feature. */
|
|
23
|
+
purpose?: ModificationPurpose;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* States that the copy bears the feature, in the act that brought it
|
|
27
|
+
* about: a patch was glued on by an attachment, anything else was
|
|
28
|
+
* produced, by the punching or by a later act. Which act, `FeatureAct`
|
|
29
|
+
* says; a `beside` that names nothing the copy bears is passed over and
|
|
30
|
+
* the feature goes into an act of its own.
|
|
31
|
+
*/
|
|
32
|
+
export declare const addFeature: (copyId: string, feature: FeatureOrPatch, act?: FeatureAct) => EditionOp;
|
|
33
|
+
/**
|
|
34
|
+
* States that the patch bears the feature. What a patch bears came onto
|
|
35
|
+
* the copy with the patch, so it belongs to no act of its own and
|
|
36
|
+
* states no place: it stands where the patch stands.
|
|
37
|
+
*/
|
|
38
|
+
export declare const addBorneFeature: (copyId: string, patchId: string, feature: NestedFeature) => EditionOp;
|
|
39
|
+
/**
|
|
40
|
+
* Takes the features off the copy, and out of the versions with what
|
|
41
|
+
* only they carried. A feature may be named wherever the copy bears it:
|
|
42
|
+
* a patch goes with everything glued onto it, and a feature of a patch
|
|
43
|
+
* may be taken back on its own, the patch staying where it is. An act
|
|
44
|
+
* left having produced or added nothing goes as well.
|
|
45
|
+
*/
|
|
46
|
+
export declare const removeFeatures: (copyId: string, featureIds: readonly string[]) => EditionOp;
|
|
47
|
+
/** Why several features cannot be replaced by one. */
|
|
48
|
+
export type MergeObstacle = 'fewer-than-two' | 'different-types' | 'different-tracks' | 'differing-conditions' | 'unlike-features' | 'different-acts';
|
|
49
|
+
/**
|
|
50
|
+
* What stands in the way of reading the features as one, or nothing
|
|
51
|
+
* where they may be merged. Features may be merged where they differ
|
|
52
|
+
* in nothing but their place along the roll, their depiction and the
|
|
53
|
+
* condition at most one of them states. How far apart they lie is not
|
|
54
|
+
* asked: whether a gap is a bridge of the perforator, a tear or two
|
|
55
|
+
* perforations of their own is the editor's reading.
|
|
56
|
+
*/
|
|
57
|
+
export declare const mergeObstacle: (features: readonly FeatureOrPatch[]) => MergeObstacle | undefined;
|
|
58
|
+
/**
|
|
59
|
+
* What stands in the way of reading the features the ids name as one,
|
|
60
|
+
* as `mergeFeatures` will find it. `mergeObstacle` asks only what the
|
|
61
|
+
* features themselves say, which cannot reach `different-acts`: one
|
|
62
|
+
* feature is the work of one act, and where the features stand is the
|
|
63
|
+
* edition's business rather than theirs.
|
|
64
|
+
*/
|
|
65
|
+
export declare const mergeObstacleIn: (view: EditionView, featureIds: readonly string[]) => MergeObstacle | undefined;
|
|
66
|
+
/**
|
|
67
|
+
* Replaces the features of the copy with a single one covering them
|
|
68
|
+
* all, where a scan has split what the editor reads as one feature.
|
|
69
|
+
* The merged feature takes the place of the first it replaces and
|
|
70
|
+
* spans from the first to the last, any gap between them included.
|
|
71
|
+
* What the replaced features carried is carried by the merged one, and
|
|
72
|
+
* whatever else named them, a modification or a comprehension, names
|
|
73
|
+
* the merged one in their stead.
|
|
74
|
+
*
|
|
75
|
+
* Only the features the copy bears itself are merged. A feature of a
|
|
76
|
+
* patch states no place along the roll of its own, so there is nothing
|
|
77
|
+
* for a merge to span, and an id naming one is passed over as an id the
|
|
78
|
+
* copy does not bear is. Features of two different acts are not merged
|
|
79
|
+
* either: one feature is the work of one act.
|
|
80
|
+
*
|
|
81
|
+
* Throws where the features cannot stand for one; `mergeObstacle`
|
|
82
|
+
* says beforehand whether they can.
|
|
83
|
+
*/
|
|
84
|
+
export declare const mergeFeatures: (copyId: string, featureIds: readonly string[]) => EditionOp;
|