@joint/core 4.0.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/LICENSE +376 -0
- package/README.md +49 -0
- package/dist/geometry.js +6486 -0
- package/dist/geometry.min.js +8 -0
- package/dist/joint.d.ts +5536 -0
- package/dist/joint.js +39629 -0
- package/dist/joint.min.js +8 -0
- package/dist/joint.nowrap.js +39626 -0
- package/dist/joint.nowrap.min.js +8 -0
- package/dist/vectorizer.js +9135 -0
- package/dist/vectorizer.min.js +8 -0
- package/dist/version.mjs +3 -0
- package/index.js +3 -0
- package/joint.mjs +27 -0
- package/package.json +192 -0
- package/src/V/annotation.mjs +0 -0
- package/src/V/index.mjs +2642 -0
- package/src/anchors/index.mjs +123 -0
- package/src/config/index.mjs +12 -0
- package/src/connectionPoints/index.mjs +202 -0
- package/src/connectionStrategies/index.mjs +73 -0
- package/src/connectors/curve.mjs +553 -0
- package/src/connectors/index.mjs +6 -0
- package/src/connectors/jumpover.mjs +452 -0
- package/src/connectors/normal.mjs +12 -0
- package/src/connectors/rounded.mjs +17 -0
- package/src/connectors/smooth.mjs +44 -0
- package/src/connectors/straight.mjs +110 -0
- package/src/dia/Cell.mjs +945 -0
- package/src/dia/CellView.mjs +1316 -0
- package/src/dia/Element.mjs +519 -0
- package/src/dia/ElementView.mjs +859 -0
- package/src/dia/Graph.mjs +1112 -0
- package/src/dia/HighlighterView.mjs +319 -0
- package/src/dia/Link.mjs +565 -0
- package/src/dia/LinkView.mjs +2207 -0
- package/src/dia/Paper.mjs +3171 -0
- package/src/dia/PaperLayer.mjs +75 -0
- package/src/dia/ToolView.mjs +69 -0
- package/src/dia/ToolsView.mjs +128 -0
- package/src/dia/attributes/calc.mjs +128 -0
- package/src/dia/attributes/connection.mjs +75 -0
- package/src/dia/attributes/defs.mjs +76 -0
- package/src/dia/attributes/eval.mjs +64 -0
- package/src/dia/attributes/index.mjs +69 -0
- package/src/dia/attributes/legacy.mjs +148 -0
- package/src/dia/attributes/offset.mjs +53 -0
- package/src/dia/attributes/props.mjs +30 -0
- package/src/dia/attributes/shape.mjs +92 -0
- package/src/dia/attributes/text.mjs +180 -0
- package/src/dia/index.mjs +13 -0
- package/src/dia/layers/GridLayer.mjs +176 -0
- package/src/dia/ports.mjs +874 -0
- package/src/elementTools/Control.mjs +153 -0
- package/src/elementTools/HoverConnect.mjs +37 -0
- package/src/elementTools/index.mjs +5 -0
- package/src/env/index.mjs +43 -0
- package/src/g/bezier.mjs +175 -0
- package/src/g/curve.mjs +956 -0
- package/src/g/ellipse.mjs +245 -0
- package/src/g/extend.mjs +64 -0
- package/src/g/geometry.helpers.mjs +58 -0
- package/src/g/index.mjs +17 -0
- package/src/g/intersection.mjs +511 -0
- package/src/g/line.bearing.mjs +30 -0
- package/src/g/line.length.mjs +5 -0
- package/src/g/line.mjs +356 -0
- package/src/g/line.squaredLength.mjs +10 -0
- package/src/g/path.mjs +2260 -0
- package/src/g/point.mjs +375 -0
- package/src/g/points.mjs +247 -0
- package/src/g/polygon.mjs +51 -0
- package/src/g/polyline.mjs +523 -0
- package/src/g/rect.mjs +556 -0
- package/src/g/types.mjs +10 -0
- package/src/highlighters/addClass.mjs +27 -0
- package/src/highlighters/index.mjs +5 -0
- package/src/highlighters/list.mjs +111 -0
- package/src/highlighters/mask.mjs +220 -0
- package/src/highlighters/opacity.mjs +17 -0
- package/src/highlighters/stroke.mjs +100 -0
- package/src/layout/index.mjs +4 -0
- package/src/layout/ports/port.mjs +188 -0
- package/src/layout/ports/portLabel.mjs +224 -0
- package/src/linkAnchors/index.mjs +76 -0
- package/src/linkTools/Anchor.mjs +235 -0
- package/src/linkTools/Arrowhead.mjs +103 -0
- package/src/linkTools/Boundary.mjs +48 -0
- package/src/linkTools/Button.mjs +121 -0
- package/src/linkTools/Connect.mjs +85 -0
- package/src/linkTools/HoverConnect.mjs +161 -0
- package/src/linkTools/Segments.mjs +393 -0
- package/src/linkTools/Vertices.mjs +253 -0
- package/src/linkTools/helpers.mjs +33 -0
- package/src/linkTools/index.mjs +8 -0
- package/src/mvc/Collection.mjs +560 -0
- package/src/mvc/Data.mjs +46 -0
- package/src/mvc/Dom/Dom.mjs +587 -0
- package/src/mvc/Dom/Event.mjs +130 -0
- package/src/mvc/Dom/animations.mjs +122 -0
- package/src/mvc/Dom/events.mjs +69 -0
- package/src/mvc/Dom/index.mjs +13 -0
- package/src/mvc/Dom/methods.mjs +392 -0
- package/src/mvc/Dom/props.mjs +77 -0
- package/src/mvc/Dom/vars.mjs +5 -0
- package/src/mvc/Events.mjs +337 -0
- package/src/mvc/Listener.mjs +33 -0
- package/src/mvc/Model.mjs +239 -0
- package/src/mvc/View.mjs +323 -0
- package/src/mvc/ViewBase.mjs +182 -0
- package/src/mvc/index.mjs +9 -0
- package/src/mvc/mvcUtils.mjs +90 -0
- package/src/polyfills/array.js +4 -0
- package/src/polyfills/base64.js +68 -0
- package/src/polyfills/index.mjs +5 -0
- package/src/polyfills/number.js +3 -0
- package/src/polyfills/string.js +3 -0
- package/src/polyfills/typedArray.js +47 -0
- package/src/routers/index.mjs +6 -0
- package/src/routers/manhattan.mjs +856 -0
- package/src/routers/metro.mjs +91 -0
- package/src/routers/normal.mjs +6 -0
- package/src/routers/oneSide.mjs +60 -0
- package/src/routers/orthogonal.mjs +323 -0
- package/src/routers/rightAngle.mjs +1056 -0
- package/src/shapes/index.mjs +3 -0
- package/src/shapes/standard.mjs +755 -0
- package/src/util/cloneCells.mjs +67 -0
- package/src/util/getRectPoint.mjs +65 -0
- package/src/util/index.mjs +5 -0
- package/src/util/svgTagTemplate.mjs +110 -0
- package/src/util/util.mjs +1754 -0
- package/src/util/utilHelpers.mjs +2402 -0
- package/src/util/wrappers.mjs +56 -0
- package/types/geometry.d.ts +815 -0
- package/types/index.d.ts +53 -0
- package/types/joint.d.ts +4391 -0
- package/types/joint.head.d.ts +12 -0
- package/types/vectorizer.d.ts +327 -0
|
@@ -0,0 +1,560 @@
|
|
|
1
|
+
import { Events } from './Events.mjs';
|
|
2
|
+
import { Model } from './Model.mjs';
|
|
3
|
+
import { extend, addMethodsUtil } from './mvcUtils.mjs';
|
|
4
|
+
import {
|
|
5
|
+
assign,
|
|
6
|
+
clone,
|
|
7
|
+
isFunction,
|
|
8
|
+
isString,
|
|
9
|
+
sortBy,
|
|
10
|
+
toArray
|
|
11
|
+
} from '../util/util.mjs';
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
// Collection
|
|
15
|
+
// -------------------
|
|
16
|
+
|
|
17
|
+
// If models tend to represent a single row of data, a Collection is
|
|
18
|
+
// more analogous to a table full of data ... or a small slice or page of that
|
|
19
|
+
// table, or a collection of rows that belong together for a particular reason
|
|
20
|
+
// -- all of the messages in this particular folder, all of the documents
|
|
21
|
+
// belonging to this particular author, and so on. Collections maintain
|
|
22
|
+
// indexes of their models, both in order, and for lookup by `id`.
|
|
23
|
+
|
|
24
|
+
// Create a new **Collection**, perhaps to contain a specific type of `model`.
|
|
25
|
+
// If a `comparator` is specified, the Collection will maintain
|
|
26
|
+
// its models in sort order, as they're added and removed.
|
|
27
|
+
export var Collection = function(models, options) {
|
|
28
|
+
options || (options = {});
|
|
29
|
+
this.preinitialize.apply(this, arguments);
|
|
30
|
+
if (options.model) this.model = options.model;
|
|
31
|
+
if (options.comparator !== void 0) this.comparator = options.comparator;
|
|
32
|
+
this._reset();
|
|
33
|
+
this.initialize.apply(this, arguments);
|
|
34
|
+
if (models) this.reset(models, assign({ silent: true }, options));
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
// Default options for `Collection#set`.
|
|
38
|
+
var setOptions = { add: true, remove: true, merge: true };
|
|
39
|
+
var addOptions = { add: true, remove: false };
|
|
40
|
+
|
|
41
|
+
// Splices `insert` into `array` at index `at`.
|
|
42
|
+
var splice = function(array, insert, at) {
|
|
43
|
+
at = Math.min(Math.max(at, 0), array.length);
|
|
44
|
+
var tail = Array(array.length - at);
|
|
45
|
+
var length = insert.length;
|
|
46
|
+
var i;
|
|
47
|
+
for (i = 0; i < tail.length; i++) tail[i] = array[i + at];
|
|
48
|
+
for (i = 0; i < length; i++) array[i + at] = insert[i];
|
|
49
|
+
for (i = 0; i < tail.length; i++) array[i + length + at] = tail[i];
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
// Define the Collection's inheritable methods.
|
|
53
|
+
assign(Collection.prototype, Events, {
|
|
54
|
+
|
|
55
|
+
// The default model for a collection is just a **Model**.
|
|
56
|
+
// This should be overridden in most cases.
|
|
57
|
+
model: Model,
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
// preinitialize is an empty function by default. You can override it with a function
|
|
61
|
+
// or object. preinitialize will run before any instantiation logic is run in the Collection.
|
|
62
|
+
preinitialize: function(){},
|
|
63
|
+
|
|
64
|
+
// Initialize is an empty function by default. Override it with your own
|
|
65
|
+
// initialization logic.
|
|
66
|
+
initialize: function(){},
|
|
67
|
+
|
|
68
|
+
// The JSON representation of a Collection is an array of the
|
|
69
|
+
// models' attributes.
|
|
70
|
+
toJSON: function(options) {
|
|
71
|
+
return this.map(function(model) { return model.toJSON(options); });
|
|
72
|
+
},
|
|
73
|
+
|
|
74
|
+
// Add a model, or list of models to the set. `models` may be
|
|
75
|
+
// Models or raw JavaScript objects to be converted to Models, or any
|
|
76
|
+
// combination of the two.
|
|
77
|
+
add: function(models, options) {
|
|
78
|
+
return this.set(models, assign({ merge: false }, options, addOptions));
|
|
79
|
+
},
|
|
80
|
+
|
|
81
|
+
// Remove a model, or a list of models from the set.
|
|
82
|
+
remove: function(models, options) {
|
|
83
|
+
options = assign({}, options);
|
|
84
|
+
var singular = !Array.isArray(models);
|
|
85
|
+
models = singular ? [models] : models.slice();
|
|
86
|
+
var removed = this._removeModels(models, options);
|
|
87
|
+
if (!options.silent && removed.length) {
|
|
88
|
+
options.changes = { added: [], merged: [], removed: removed };
|
|
89
|
+
this.trigger('update', this, options);
|
|
90
|
+
}
|
|
91
|
+
return singular ? removed[0] : removed;
|
|
92
|
+
},
|
|
93
|
+
|
|
94
|
+
// Update a collection by `set`-ing a new list of models, adding new ones,
|
|
95
|
+
// removing models that are no longer present, and merging models that
|
|
96
|
+
// already exist in the collection, as necessary. Similar to **Model#set**,
|
|
97
|
+
// the core operation for updating the data contained by the collection.
|
|
98
|
+
set: function(models, options) {
|
|
99
|
+
if (models == null) return;
|
|
100
|
+
|
|
101
|
+
options = assign({}, setOptions, options);
|
|
102
|
+
|
|
103
|
+
var singular = !Array.isArray(models);
|
|
104
|
+
models = singular ? [models] : models.slice();
|
|
105
|
+
|
|
106
|
+
var at = options.at;
|
|
107
|
+
if (at != null) at = +at;
|
|
108
|
+
if (at > this.length) at = this.length;
|
|
109
|
+
if (at < 0) at += this.length + 1;
|
|
110
|
+
|
|
111
|
+
var set = [];
|
|
112
|
+
var toAdd = [];
|
|
113
|
+
var toMerge = [];
|
|
114
|
+
var toRemove = [];
|
|
115
|
+
var modelMap = {};
|
|
116
|
+
|
|
117
|
+
var add = options.add;
|
|
118
|
+
var merge = options.merge;
|
|
119
|
+
var remove = options.remove;
|
|
120
|
+
|
|
121
|
+
var sort = false;
|
|
122
|
+
var sortable = this.comparator && at == null && options.sort !== false;
|
|
123
|
+
var sortAttr = isString(this.comparator) ? this.comparator : null;
|
|
124
|
+
|
|
125
|
+
// Turn bare objects into model references, and prevent invalid models
|
|
126
|
+
// from being added.
|
|
127
|
+
var model, i;
|
|
128
|
+
for (i = 0; i < models.length; i++) {
|
|
129
|
+
model = models[i];
|
|
130
|
+
|
|
131
|
+
// If a duplicate is found, prevent it from being added and
|
|
132
|
+
// optionally merge it into the existing model.
|
|
133
|
+
var existing = this.get(model);
|
|
134
|
+
if (existing) {
|
|
135
|
+
if (merge && model !== existing) {
|
|
136
|
+
var attrs = this._isModel(model) ? model.attributes : model;
|
|
137
|
+
existing.set(attrs, options);
|
|
138
|
+
toMerge.push(existing);
|
|
139
|
+
if (sortable && !sort) sort = existing.hasChanged(sortAttr);
|
|
140
|
+
}
|
|
141
|
+
if (!modelMap[existing.cid]) {
|
|
142
|
+
modelMap[existing.cid] = true;
|
|
143
|
+
set.push(existing);
|
|
144
|
+
}
|
|
145
|
+
models[i] = existing;
|
|
146
|
+
|
|
147
|
+
// If this is a new, valid model, push it to the `toAdd` list.
|
|
148
|
+
} else if (add) {
|
|
149
|
+
model = models[i] = this._prepareModel(model, options);
|
|
150
|
+
if (model) {
|
|
151
|
+
toAdd.push(model);
|
|
152
|
+
this._addReference(model, options);
|
|
153
|
+
modelMap[model.cid] = true;
|
|
154
|
+
set.push(model);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// Remove stale models.
|
|
160
|
+
if (remove) {
|
|
161
|
+
for (i = 0; i < this.length; i++) {
|
|
162
|
+
model = this.models[i];
|
|
163
|
+
if (!modelMap[model.cid]) toRemove.push(model);
|
|
164
|
+
}
|
|
165
|
+
if (toRemove.length) this._removeModels(toRemove, options);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// See if sorting is needed, update `length` and splice in new models.
|
|
169
|
+
var orderChanged = false;
|
|
170
|
+
var replace = !sortable && add && remove;
|
|
171
|
+
if (set.length && replace) {
|
|
172
|
+
orderChanged = this.length !== set.length || this.models.some(function(m, index) {
|
|
173
|
+
return m !== set[index];
|
|
174
|
+
});
|
|
175
|
+
this.models.length = 0;
|
|
176
|
+
splice(this.models, set, 0);
|
|
177
|
+
this.length = this.models.length;
|
|
178
|
+
} else if (toAdd.length) {
|
|
179
|
+
if (sortable) sort = true;
|
|
180
|
+
splice(this.models, toAdd, at == null ? this.length : at);
|
|
181
|
+
this.length = this.models.length;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Silently sort the collection if appropriate.
|
|
185
|
+
if (sort) this.sort({ silent: true });
|
|
186
|
+
|
|
187
|
+
// Unless silenced, it's time to fire all appropriate add/sort/update events.
|
|
188
|
+
if (!options.silent) {
|
|
189
|
+
for (i = 0; i < toAdd.length; i++) {
|
|
190
|
+
if (at != null) options.index = at + i;
|
|
191
|
+
model = toAdd[i];
|
|
192
|
+
model.trigger('add', model, this, options);
|
|
193
|
+
}
|
|
194
|
+
if (sort || orderChanged) this.trigger('sort', this, options);
|
|
195
|
+
if (toAdd.length || toRemove.length || toMerge.length) {
|
|
196
|
+
options.changes = {
|
|
197
|
+
added: toAdd,
|
|
198
|
+
removed: toRemove,
|
|
199
|
+
merged: toMerge
|
|
200
|
+
};
|
|
201
|
+
this.trigger('update', this, options);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// Return the added (or merged) model (or models).
|
|
206
|
+
return singular ? models[0] : models;
|
|
207
|
+
},
|
|
208
|
+
|
|
209
|
+
// When you have more items than you want to add or remove individually,
|
|
210
|
+
// you can reset the entire set with a new list of models, without firing
|
|
211
|
+
// any granular `add` or `remove` events. Fires `reset` when finished.
|
|
212
|
+
// Useful for bulk operations and optimizations.
|
|
213
|
+
reset: function(models, options) {
|
|
214
|
+
options = options ? clone(options) : {};
|
|
215
|
+
for (var i = 0; i < this.models.length; i++) {
|
|
216
|
+
this._removeReference(this.models[i], options);
|
|
217
|
+
}
|
|
218
|
+
options.previousModels = this.models;
|
|
219
|
+
this._reset();
|
|
220
|
+
models = this.add(models, assign({ silent: true }, options));
|
|
221
|
+
if (!options.silent) this.trigger('reset', this, options);
|
|
222
|
+
return models;
|
|
223
|
+
},
|
|
224
|
+
|
|
225
|
+
// Add a model to the end of the collection.
|
|
226
|
+
push: function(model, options) {
|
|
227
|
+
return this.add(model, assign({ at: this.length }, options));
|
|
228
|
+
},
|
|
229
|
+
|
|
230
|
+
// Remove a model from the end of the collection.
|
|
231
|
+
pop: function(options) {
|
|
232
|
+
var model = this.at(this.length - 1);
|
|
233
|
+
return this.remove(model, options);
|
|
234
|
+
},
|
|
235
|
+
|
|
236
|
+
// Add a model to the beginning of the collection.
|
|
237
|
+
unshift: function(model, options) {
|
|
238
|
+
return this.add(model, assign({ at: 0 }, options));
|
|
239
|
+
},
|
|
240
|
+
|
|
241
|
+
// Remove a model from the beginning of the collection.
|
|
242
|
+
shift: function(options) {
|
|
243
|
+
var model = this.at(0);
|
|
244
|
+
return this.remove(model, options);
|
|
245
|
+
},
|
|
246
|
+
|
|
247
|
+
// Slice out a sub-array of models from the collection.
|
|
248
|
+
slice: function() {
|
|
249
|
+
return Array.prototype.slice.apply(this.models, arguments);
|
|
250
|
+
},
|
|
251
|
+
|
|
252
|
+
// Get a model from the set by id, cid, model object with id or cid
|
|
253
|
+
// properties, or an attributes object that is transformed through modelId.
|
|
254
|
+
get: function(obj) {
|
|
255
|
+
if (obj == null) return void 0;
|
|
256
|
+
return this._byId[obj] ||
|
|
257
|
+
this._byId[this.modelId(this._isModel(obj) ? obj.attributes : obj, obj.idAttribute)] ||
|
|
258
|
+
obj.cid && this._byId[obj.cid];
|
|
259
|
+
},
|
|
260
|
+
|
|
261
|
+
// Returns `true` if the model is in the collection.
|
|
262
|
+
has: function(obj) {
|
|
263
|
+
return this.get(obj) != null;
|
|
264
|
+
},
|
|
265
|
+
|
|
266
|
+
// Get the model at the given index.
|
|
267
|
+
at: function(index) {
|
|
268
|
+
if (index < 0) index += this.length;
|
|
269
|
+
return this.models[index];
|
|
270
|
+
},
|
|
271
|
+
|
|
272
|
+
// Force the collection to re-sort itself. You don't need to call this under
|
|
273
|
+
// normal circumstances, as the set will maintain sort order as each item
|
|
274
|
+
// is added.
|
|
275
|
+
sort: function(options) {
|
|
276
|
+
var comparator = this.comparator;
|
|
277
|
+
if (!comparator) throw new Error('Cannot sort a set without a comparator');
|
|
278
|
+
options || (options = {});
|
|
279
|
+
|
|
280
|
+
var length = comparator.length;
|
|
281
|
+
if (isFunction(comparator)) comparator = comparator.bind(this);
|
|
282
|
+
|
|
283
|
+
// Run sort based on type of `comparator`.
|
|
284
|
+
if (length === 1 || isString(comparator)) {
|
|
285
|
+
this.models = this.sortBy(comparator);
|
|
286
|
+
} else {
|
|
287
|
+
this.models.sort(comparator);
|
|
288
|
+
}
|
|
289
|
+
if (!options.silent) this.trigger('sort', this, options);
|
|
290
|
+
return this;
|
|
291
|
+
},
|
|
292
|
+
|
|
293
|
+
// Create a new collection with an identical list of models as this one.
|
|
294
|
+
clone: function() {
|
|
295
|
+
return new this.constructor(this.models, {
|
|
296
|
+
model: this.model,
|
|
297
|
+
comparator: this.comparator
|
|
298
|
+
});
|
|
299
|
+
},
|
|
300
|
+
|
|
301
|
+
// Define how to uniquely identify models in the collection.
|
|
302
|
+
modelId: function(attrs, idAttribute) {
|
|
303
|
+
return attrs[idAttribute || this.model.prototype.idAttribute || 'id'];
|
|
304
|
+
},
|
|
305
|
+
|
|
306
|
+
// Get an iterator of all models in this collection.
|
|
307
|
+
values: function() {
|
|
308
|
+
return new CollectionIterator(this, ITERATOR_VALUES);
|
|
309
|
+
},
|
|
310
|
+
|
|
311
|
+
// Get an iterator of all model IDs in this collection.
|
|
312
|
+
keys: function() {
|
|
313
|
+
return new CollectionIterator(this, ITERATOR_KEYS);
|
|
314
|
+
},
|
|
315
|
+
|
|
316
|
+
// Get an iterator of all [ID, model] tuples in this collection.
|
|
317
|
+
entries: function() {
|
|
318
|
+
return new CollectionIterator(this, ITERATOR_KEYSVALUES);
|
|
319
|
+
},
|
|
320
|
+
|
|
321
|
+
// Iterate over elements of the collection, and invoke fn for each element
|
|
322
|
+
each: function(fn, context) {
|
|
323
|
+
this.models.forEach(fn, context);
|
|
324
|
+
},
|
|
325
|
+
|
|
326
|
+
// Iterate over elements of collection, and return an array of all elements fn returns truthy for
|
|
327
|
+
filter: function(fn, context) {
|
|
328
|
+
return this.models.filter(fn, context);
|
|
329
|
+
},
|
|
330
|
+
|
|
331
|
+
find: function(fn, context) {
|
|
332
|
+
return this.models.find(fn, context);
|
|
333
|
+
},
|
|
334
|
+
|
|
335
|
+
findIndex: function(fn, context) {
|
|
336
|
+
return this.models.findIndex(fn, context);
|
|
337
|
+
},
|
|
338
|
+
|
|
339
|
+
// Return the first model of the collection
|
|
340
|
+
first: function() {
|
|
341
|
+
return this.models[0];
|
|
342
|
+
},
|
|
343
|
+
|
|
344
|
+
// Return true if value is in the collection
|
|
345
|
+
includes: function(value) {
|
|
346
|
+
return this.models.includes(value);
|
|
347
|
+
},
|
|
348
|
+
|
|
349
|
+
// Return the last model of the collection
|
|
350
|
+
last: function() {
|
|
351
|
+
return this.models[this.models.length - 1];
|
|
352
|
+
},
|
|
353
|
+
|
|
354
|
+
// Return true if collection has no elements
|
|
355
|
+
isEmpty: function() {
|
|
356
|
+
return !this.models.length;
|
|
357
|
+
},
|
|
358
|
+
|
|
359
|
+
// Create an array of values by running each element in the collection through fn
|
|
360
|
+
map: function(fn, context) {
|
|
361
|
+
return this.models.map(fn, context);
|
|
362
|
+
},
|
|
363
|
+
|
|
364
|
+
// Runs "reducer" fn over all elements in the collection, in ascending-index order, and accumulates them into a single value
|
|
365
|
+
reduce: function(fn, initAcc = this.first()) {
|
|
366
|
+
return this.models.reduce(fn, initAcc);
|
|
367
|
+
},
|
|
368
|
+
|
|
369
|
+
// Private method to reset all internal state. Called when the collection
|
|
370
|
+
// is first initialized or reset.
|
|
371
|
+
_reset: function() {
|
|
372
|
+
this.length = 0;
|
|
373
|
+
this.models = [];
|
|
374
|
+
this._byId = {};
|
|
375
|
+
},
|
|
376
|
+
|
|
377
|
+
// Prepare a hash of attributes (or other model) to be added to this
|
|
378
|
+
// collection.
|
|
379
|
+
_prepareModel: function(attrs, options) {
|
|
380
|
+
if (this._isModel(attrs)) {
|
|
381
|
+
if (!attrs.collection) attrs.collection = this;
|
|
382
|
+
return attrs;
|
|
383
|
+
}
|
|
384
|
+
options = options ? clone(options) : {};
|
|
385
|
+
options.collection = this;
|
|
386
|
+
|
|
387
|
+
var model;
|
|
388
|
+
if (this.model.prototype) {
|
|
389
|
+
model = new this.model(attrs, options);
|
|
390
|
+
} else {
|
|
391
|
+
// ES class methods didn't have prototype
|
|
392
|
+
model = this.model(attrs, options);
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
if (!model.validationError) return model;
|
|
396
|
+
this.trigger('invalid', this, model.validationError, options);
|
|
397
|
+
return false;
|
|
398
|
+
},
|
|
399
|
+
|
|
400
|
+
// Internal method called by both remove and set.
|
|
401
|
+
_removeModels: function(models, options) {
|
|
402
|
+
var removed = [];
|
|
403
|
+
for (var i = 0; i < models.length; i++) {
|
|
404
|
+
var model = this.get(models[i]);
|
|
405
|
+
if (!model) continue;
|
|
406
|
+
|
|
407
|
+
var index = this.models.indexOf(model);
|
|
408
|
+
this.models.splice(index, 1);
|
|
409
|
+
this.length--;
|
|
410
|
+
|
|
411
|
+
// Remove references before triggering 'remove' event to prevent an
|
|
412
|
+
// infinite loop. #3693
|
|
413
|
+
delete this._byId[model.cid];
|
|
414
|
+
var id = this.modelId(model.attributes, model.idAttribute);
|
|
415
|
+
if (id != null) delete this._byId[id];
|
|
416
|
+
|
|
417
|
+
if (!options.silent) {
|
|
418
|
+
options.index = index;
|
|
419
|
+
model.trigger('remove', model, this, options);
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
removed.push(model);
|
|
423
|
+
this._removeReference(model, options);
|
|
424
|
+
}
|
|
425
|
+
if (models.length > 0 && !options.silent) delete options.index;
|
|
426
|
+
return removed;
|
|
427
|
+
},
|
|
428
|
+
|
|
429
|
+
// Method for checking whether an object should be considered a model for
|
|
430
|
+
// the purposes of adding to the collection.
|
|
431
|
+
_isModel: function(model) {
|
|
432
|
+
return model instanceof Model;
|
|
433
|
+
},
|
|
434
|
+
|
|
435
|
+
// Internal method to create a model's ties to a collection.
|
|
436
|
+
_addReference: function(model, options) {
|
|
437
|
+
this._byId[model.cid] = model;
|
|
438
|
+
var id = this.modelId(model.attributes, model.idAttribute);
|
|
439
|
+
if (id != null) this._byId[id] = model;
|
|
440
|
+
model.on('all', this._onModelEvent, this);
|
|
441
|
+
},
|
|
442
|
+
|
|
443
|
+
// Internal method to sever a model's ties to a collection.
|
|
444
|
+
_removeReference: function(model, options) {
|
|
445
|
+
delete this._byId[model.cid];
|
|
446
|
+
var id = this.modelId(model.attributes, model.idAttribute);
|
|
447
|
+
if (id != null) delete this._byId[id];
|
|
448
|
+
if (this === model.collection) delete model.collection;
|
|
449
|
+
model.off('all', this._onModelEvent, this);
|
|
450
|
+
},
|
|
451
|
+
|
|
452
|
+
// Internal method called every time a model in the set fires an event.
|
|
453
|
+
// Sets need to update their indexes when models change ids. All other
|
|
454
|
+
// events simply proxy through. "add" and "remove" events that originate
|
|
455
|
+
// in other collections are ignored.
|
|
456
|
+
_onModelEvent: function(event, model, collection, options) {
|
|
457
|
+
if (model) {
|
|
458
|
+
if ((event === 'add' || event === 'remove') && collection !== this) return;
|
|
459
|
+
if (event === 'changeId') {
|
|
460
|
+
var prevId = this.modelId(model.previousAttributes(), model.idAttribute);
|
|
461
|
+
var id = this.modelId(model.attributes, model.idAttribute);
|
|
462
|
+
if (prevId != null) delete this._byId[prevId];
|
|
463
|
+
if (id != null) this._byId[id] = model;
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
this.trigger.apply(this, arguments);
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
});
|
|
470
|
+
|
|
471
|
+
// Defining an @@iterator method implements JavaScript's Iterable protocol.
|
|
472
|
+
// In modern ES2015 browsers, this value is found at Symbol.iterator.
|
|
473
|
+
var $$iterator = typeof Symbol === 'function' && Symbol.iterator;
|
|
474
|
+
if ($$iterator) {
|
|
475
|
+
Collection.prototype[$$iterator] = Collection.prototype.values;
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
// CollectionIterator
|
|
479
|
+
// ------------------
|
|
480
|
+
|
|
481
|
+
// A CollectionIterator implements JavaScript's Iterator protocol, allowing the
|
|
482
|
+
// use of `for of` loops in modern browsers and interoperation between
|
|
483
|
+
// Collection and other JavaScript functions and third-party libraries
|
|
484
|
+
// which can operate on Iterables.
|
|
485
|
+
var CollectionIterator = function(collection, kind) {
|
|
486
|
+
this._collection = collection;
|
|
487
|
+
this._kind = kind;
|
|
488
|
+
this._index = 0;
|
|
489
|
+
};
|
|
490
|
+
|
|
491
|
+
// This "enum" defines the three possible kinds of values which can be emitted
|
|
492
|
+
// by a CollectionIterator that correspond to the values(), keys() and entries()
|
|
493
|
+
// methods on Collection, respectively.
|
|
494
|
+
var ITERATOR_VALUES = 1;
|
|
495
|
+
var ITERATOR_KEYS = 2;
|
|
496
|
+
var ITERATOR_KEYSVALUES = 3;
|
|
497
|
+
|
|
498
|
+
// All Iterators should themselves be Iterable.
|
|
499
|
+
if ($$iterator) {
|
|
500
|
+
CollectionIterator.prototype[$$iterator] = function() {
|
|
501
|
+
return this;
|
|
502
|
+
};
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
CollectionIterator.prototype.next = function() {
|
|
506
|
+
if (this._collection) {
|
|
507
|
+
|
|
508
|
+
// Only continue iterating if the iterated collection is long enough.
|
|
509
|
+
if (this._index < this._collection.length) {
|
|
510
|
+
var model = this._collection.at(this._index);
|
|
511
|
+
this._index++;
|
|
512
|
+
|
|
513
|
+
// Construct a value depending on what kind of values should be iterated.
|
|
514
|
+
var value;
|
|
515
|
+
if (this._kind === ITERATOR_VALUES) {
|
|
516
|
+
value = model;
|
|
517
|
+
} else {
|
|
518
|
+
var id = this._collection.modelId(model.attributes, model.idAttribute);
|
|
519
|
+
if (this._kind === ITERATOR_KEYS) {
|
|
520
|
+
value = id;
|
|
521
|
+
} else { // ITERATOR_KEYSVALUES
|
|
522
|
+
value = [id, model];
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
return { value: value, done: false };
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
// Once exhausted, remove the reference to the collection so future
|
|
529
|
+
// calls to the next method always return done.
|
|
530
|
+
this._collection = void 0;
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
return { value: void 0, done: true };
|
|
534
|
+
};
|
|
535
|
+
|
|
536
|
+
// Methods that we want to implement on the Collection.
|
|
537
|
+
var collectionMethods = { toArray: 1, sortBy: 3 };
|
|
538
|
+
|
|
539
|
+
|
|
540
|
+
// Mix in each method as a proxy to `Collection#models`.
|
|
541
|
+
|
|
542
|
+
var config = [ Collection, collectionMethods, 'models' ];
|
|
543
|
+
|
|
544
|
+
function addMethods(config) {
|
|
545
|
+
var Base = config[0],
|
|
546
|
+
methods = config[1],
|
|
547
|
+
attribute = config[2];
|
|
548
|
+
|
|
549
|
+
const methodsToAdd = {
|
|
550
|
+
sortBy,
|
|
551
|
+
toArray
|
|
552
|
+
};
|
|
553
|
+
|
|
554
|
+
addMethodsUtil(Base, methodsToAdd, methods, attribute);
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
addMethods(config);
|
|
558
|
+
|
|
559
|
+
// Set up inheritance for the collection.
|
|
560
|
+
Collection.extend = extend;
|
package/src/mvc/Data.mjs
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
class Data {
|
|
2
|
+
|
|
3
|
+
constructor() {
|
|
4
|
+
this.map = new WeakMap();
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
has(obj, key) {
|
|
8
|
+
if (key === undefined) return this.map.has(obj);
|
|
9
|
+
return key in this.map.get(obj);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
create(obj) {
|
|
13
|
+
if (!this.has(obj)) this.map.set(obj, Object.create(null));
|
|
14
|
+
return this.get(obj);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
get(obj, key) {
|
|
18
|
+
if (!this.has(obj)) return undefined;
|
|
19
|
+
const data = this.map.get(obj);
|
|
20
|
+
if (key === undefined) return data;
|
|
21
|
+
return data[key];
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
set(obj, key, value) {
|
|
25
|
+
if (key === undefined) return;
|
|
26
|
+
const data = this.create(obj);
|
|
27
|
+
if (typeof key === 'string') {
|
|
28
|
+
data[key] = value;
|
|
29
|
+
} else {
|
|
30
|
+
Object.assign(data, key);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
remove(obj, key) {
|
|
35
|
+
if (!this.has(obj)) return;
|
|
36
|
+
if (key === undefined) {
|
|
37
|
+
this.map.delete(obj);
|
|
38
|
+
} else {
|
|
39
|
+
const data = this.map.get(obj);
|
|
40
|
+
delete data[key];
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export default Data;
|
|
46
|
+
|