linked-rolls 0.14.0 → 0.15.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/lib/editionOps.d.ts +23 -0
- package/lib/editionOps.js +111 -0
- package/lib/index.d.ts +1 -0
- package/lib/index.js +1 -0
- package/package.json +1 -1
package/lib/editionOps.d.ts
CHANGED
|
@@ -7,6 +7,7 @@ import { Edit } from "./Edit";
|
|
|
7
7
|
import { RollCopy, ScaleReading, Shift } from "./RollCopy";
|
|
8
8
|
import { FeatureSource } from "./FeatureSource";
|
|
9
9
|
import { AnyArgumentation, Certainty } from "./Assumption";
|
|
10
|
+
import { AnyFeature } from "./Feature";
|
|
10
11
|
/**
|
|
11
12
|
* A change to an edition, written onto an immer draft of it. One
|
|
12
13
|
* operation is one undo step, so an operation that has to read the
|
|
@@ -44,6 +45,28 @@ export declare const removeFeatures: (copyId: string, featureIds: readonly strin
|
|
|
44
45
|
* carries, and with every reference the versions made to those symbols.
|
|
45
46
|
*/
|
|
46
47
|
export declare const removeCopy: (copyId: string) => EditionOp;
|
|
48
|
+
/** Why several features cannot be replaced by one. */
|
|
49
|
+
export type MergeObstacle = 'fewer-than-two' | 'different-types' | 'different-tracks' | 'differing-conditions' | 'unlike-features';
|
|
50
|
+
/**
|
|
51
|
+
* What stands in the way of reading the features as one, or nothing
|
|
52
|
+
* where they may be merged. Features may be merged where they differ
|
|
53
|
+
* in nothing but their place along the roll, their depiction and the
|
|
54
|
+
* condition at most one of them states. How far apart they lie is not
|
|
55
|
+
* asked: whether a gap is a bridge of the perforator, a tear or two
|
|
56
|
+
* perforations of their own is the editor's reading.
|
|
57
|
+
*/
|
|
58
|
+
export declare const mergeObstacle: (features: readonly AnyFeature[]) => MergeObstacle | undefined;
|
|
59
|
+
/**
|
|
60
|
+
* Replaces the features of the copy with a single one covering them
|
|
61
|
+
* all, where a scan has split what the editor reads as one feature.
|
|
62
|
+
* The merged feature takes the place of the first it replaces and
|
|
63
|
+
* spans from the first to the last, any gap between them included.
|
|
64
|
+
* What the replaced features carried is carried by the merged one.
|
|
65
|
+
*
|
|
66
|
+
* Throws where the features cannot stand for one; `mergeObstacle`
|
|
67
|
+
* says beforehand whether they can.
|
|
68
|
+
*/
|
|
69
|
+
export declare const mergeFeatures: (copyId: string, featureIds: readonly string[]) => EditionOp;
|
|
47
70
|
/**
|
|
48
71
|
* Bases the child on the parent. A symbol of the child that collates
|
|
49
72
|
* with one the parent hands down adds its carriers to that symbol; the
|
package/lib/editionOps.js
CHANGED
|
@@ -173,6 +173,117 @@ export const removeCopy = (copyId) => onCopy(copyId, (copy, draft) => {
|
|
|
173
173
|
forgetFeatures(draft, featureIdsOf(copy));
|
|
174
174
|
draft.copies = draft.copies.filter(c => c.id !== copyId);
|
|
175
175
|
});
|
|
176
|
+
const isRecord = (value) => typeof value === 'object' && value !== null;
|
|
177
|
+
/**
|
|
178
|
+
* Whether two records of the edition state the same, the identity of a
|
|
179
|
+
* statement left out: two transcriptions reading the same word say the
|
|
180
|
+
* same, whichever ids they were given.
|
|
181
|
+
*/
|
|
182
|
+
const sayTheSame = (a, b) => {
|
|
183
|
+
if (a === b)
|
|
184
|
+
return true;
|
|
185
|
+
if (!isRecord(a) || !isRecord(b) || Array.isArray(a) !== Array.isArray(b))
|
|
186
|
+
return false;
|
|
187
|
+
const keys = new Set([...Object.keys(a), ...Object.keys(b)]);
|
|
188
|
+
keys.delete('id');
|
|
189
|
+
return [...keys].every(key => sayTheSame(a[key], b[key]));
|
|
190
|
+
};
|
|
191
|
+
/** What a feature states beyond its identity, its place along the roll, its depiction and its condition. */
|
|
192
|
+
const nature = (feature) => {
|
|
193
|
+
const { id, horizontal, depiction, condition, ...rest } = feature;
|
|
194
|
+
return rest;
|
|
195
|
+
};
|
|
196
|
+
const conditionsOf = (features) => features.flatMap(feature => feature.condition ? [feature.condition] : []);
|
|
197
|
+
/**
|
|
198
|
+
* What stands in the way of reading the features as one, or nothing
|
|
199
|
+
* where they may be merged. Features may be merged where they differ
|
|
200
|
+
* in nothing but their place along the roll, their depiction and the
|
|
201
|
+
* condition at most one of them states. How far apart they lie is not
|
|
202
|
+
* asked: whether a gap is a bridge of the perforator, a tear or two
|
|
203
|
+
* perforations of their own is the editor's reading.
|
|
204
|
+
*/
|
|
205
|
+
export const mergeObstacle = (features) => {
|
|
206
|
+
if (features.length < 2)
|
|
207
|
+
return 'fewer-than-two';
|
|
208
|
+
const [first, ...rest] = features;
|
|
209
|
+
if (rest.some(feature => feature.type !== first.type))
|
|
210
|
+
return 'different-types';
|
|
211
|
+
if (rest.some(feature => !sayTheSame(feature.vertical, first.vertical)))
|
|
212
|
+
return 'different-tracks';
|
|
213
|
+
if (rest.some(feature => !sayTheSame(nature(feature), nature(first))))
|
|
214
|
+
return 'unlike-features';
|
|
215
|
+
const conditions = conditionsOf(features);
|
|
216
|
+
if (conditions.some(condition => !sayTheSame(condition, conditions[0])))
|
|
217
|
+
return 'differing-conditions';
|
|
218
|
+
return undefined;
|
|
219
|
+
};
|
|
220
|
+
/** The items with the replacement in the place of the first it stands for, the others dropped. */
|
|
221
|
+
const standingFor = (items, stands, replacement) => {
|
|
222
|
+
const first = items.findIndex(stands);
|
|
223
|
+
if (first < 0)
|
|
224
|
+
return items;
|
|
225
|
+
return items.flatMap((item, i) => i === first ? [replacement] : stands(item) ? [] : [item]);
|
|
226
|
+
};
|
|
227
|
+
const spanning = (features) => ({
|
|
228
|
+
unit: 'mm',
|
|
229
|
+
from: mm(Math.min(...features.map(feature => feature.horizontal.from))),
|
|
230
|
+
to: mm(Math.max(...features.map(feature => feature.horizontal.to)))
|
|
231
|
+
});
|
|
232
|
+
/**
|
|
233
|
+
* The feature the merged ones give way to. It spans them all and is in
|
|
234
|
+
* every other respect the one of them that states a condition, the
|
|
235
|
+
* others being alike in all but their place. The depiction goes: a
|
|
236
|
+
* region of the scan showing one part depicts no more than that part.
|
|
237
|
+
*/
|
|
238
|
+
const mergedFrom = (features) => {
|
|
239
|
+
const stating = features.find(feature => feature.condition) ?? features[0];
|
|
240
|
+
const merged = { ...stating, id: v4(), horizontal: spanning(features) };
|
|
241
|
+
delete merged.depiction;
|
|
242
|
+
return merged;
|
|
243
|
+
};
|
|
244
|
+
/** The symbol carried by the merged feature where it named one of those it replaces, and naming it once. */
|
|
245
|
+
const carriedByMerged = (replaced, mergedId) => (symbol) => {
|
|
246
|
+
const names = (carrier) => replaced.has(idOf(carrier));
|
|
247
|
+
const first = symbol.carriers.find(names);
|
|
248
|
+
if (!first)
|
|
249
|
+
return symbol;
|
|
250
|
+
return { ...symbol, carriers: standingFor(symbol.carriers, names, { ...first, id: mergedId }) };
|
|
251
|
+
};
|
|
252
|
+
/** Points the versions at the merged feature wherever they name one it replaces. */
|
|
253
|
+
const carryOver = (draft, replaced, mergedId) => {
|
|
254
|
+
const recarry = carriedByMerged(replaced, mergedId);
|
|
255
|
+
const recarrying = (edit) => {
|
|
256
|
+
const insert = edit.insert && mapped(edit.insert, recarry);
|
|
257
|
+
return insert === edit.insert ? edit : { ...edit, insert };
|
|
258
|
+
};
|
|
259
|
+
const versions = stateOf(draft.versions);
|
|
260
|
+
draft.versions.forEach((version, i) => {
|
|
261
|
+
version.edits = mapped(versions[i].edits, recarrying);
|
|
262
|
+
});
|
|
263
|
+
};
|
|
264
|
+
/**
|
|
265
|
+
* Replaces the features of the copy with a single one covering them
|
|
266
|
+
* all, where a scan has split what the editor reads as one feature.
|
|
267
|
+
* The merged feature takes the place of the first it replaces and
|
|
268
|
+
* spans from the first to the last, any gap between them included.
|
|
269
|
+
* What the replaced features carried is carried by the merged one.
|
|
270
|
+
*
|
|
271
|
+
* Throws where the features cannot stand for one; `mergeObstacle`
|
|
272
|
+
* says beforehand whether they can.
|
|
273
|
+
*/
|
|
274
|
+
export const mergeFeatures = (copyId, featureIds) => onCopy(copyId, (copy, draft) => {
|
|
275
|
+
const replaced = new Set(featureIds);
|
|
276
|
+
const isReplaced = (feature) => replaced.has(feature.id);
|
|
277
|
+
const features = stateOf(copy.features);
|
|
278
|
+
const toMerge = features.filter(isReplaced);
|
|
279
|
+
const obstacle = mergeObstacle(toMerge);
|
|
280
|
+
if (obstacle) {
|
|
281
|
+
throw new Error(`The features of copy ${copyId} cannot be merged: ${obstacle}`);
|
|
282
|
+
}
|
|
283
|
+
const merged = mergedFrom(toMerge);
|
|
284
|
+
copy.features = standingFor(features, isReplaced, merged);
|
|
285
|
+
carryOver(draft, replaced, merged.id);
|
|
286
|
+
});
|
|
176
287
|
/** The carriers of each collated symbol pass to its counterpart. */
|
|
177
288
|
const handOverCarriers = (view, draft, collations) => collations.forEach(({ symbol, counterpart }) => {
|
|
178
289
|
const path = view.getPath(counterpart.id);
|
package/lib/index.d.ts
CHANGED
|
@@ -27,5 +27,6 @@ export * from './constraints';
|
|
|
27
27
|
export * from './context';
|
|
28
28
|
export * from './asJsonLd';
|
|
29
29
|
export * from './importJsonLd';
|
|
30
|
+
export * from './migrate';
|
|
30
31
|
export { readFromStanfordAton, type StanfordAtonOptions } from './readers/stanfordAton';
|
|
31
32
|
export { readFromSpencerBar, readSpencerAnn, paperSpeedOfSpencerAnn, SPENCER_ROWS_PER_INCH, type SpencerBarOptions } from './readers/spencerBar';
|
package/lib/index.js
CHANGED
|
@@ -27,5 +27,6 @@ export * from './constraints';
|
|
|
27
27
|
export * from './context';
|
|
28
28
|
export * from './asJsonLd';
|
|
29
29
|
export * from './importJsonLd';
|
|
30
|
+
export * from './migrate';
|
|
30
31
|
export { readFromStanfordAton } from './readers/stanfordAton';
|
|
31
32
|
export { readFromSpencerBar, readSpencerAnn, paperSpeedOfSpencerAnn, SPENCER_ROWS_PER_INCH } from './readers/spencerBar';
|
package/package.json
CHANGED