@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,337 @@
|
|
|
1
|
+
import {
|
|
2
|
+
isEmpty,
|
|
3
|
+
uniqueId
|
|
4
|
+
} from '../util/util.mjs';
|
|
5
|
+
|
|
6
|
+
// Events
|
|
7
|
+
// ---------------
|
|
8
|
+
|
|
9
|
+
// A module that can be mixed in to *any object* in order to provide it with
|
|
10
|
+
// a custom event channel. You may bind a callback to an event with `on` or
|
|
11
|
+
// remove with `off`; `trigger`-ing an event fires all callbacks in
|
|
12
|
+
// succession.
|
|
13
|
+
//
|
|
14
|
+
// const object = {};
|
|
15
|
+
// assign(object, Events);
|
|
16
|
+
// object.on('expand', function(){ alert('expanded'); });
|
|
17
|
+
// object.trigger('expand');
|
|
18
|
+
//
|
|
19
|
+
export var Events = {};
|
|
20
|
+
|
|
21
|
+
// Regular expression used to split event strings.
|
|
22
|
+
var eventSplitter = /\s+/;
|
|
23
|
+
|
|
24
|
+
// A private global variable to share between listeners and listenees.
|
|
25
|
+
var _listening;
|
|
26
|
+
|
|
27
|
+
// Iterates over the standard `event, callback` (as well as the fancy multiple
|
|
28
|
+
// space-separated events `"change blur", callback` and jQuery-style event
|
|
29
|
+
// maps `{event: callback}`).
|
|
30
|
+
var eventsApi = function(iteratee, events, name, callback, opts) {
|
|
31
|
+
var i = 0, names;
|
|
32
|
+
if (name && typeof name === 'object') {
|
|
33
|
+
// Handle event maps.
|
|
34
|
+
if (callback !== void 0 && 'context' in opts && opts.context === void 0) opts.context = callback;
|
|
35
|
+
for (names = Object.keys(name); i < names.length ; i++) {
|
|
36
|
+
events = eventsApi(iteratee, events, names[i], name[names[i]], opts);
|
|
37
|
+
}
|
|
38
|
+
} else if (name && eventSplitter.test(name)) {
|
|
39
|
+
// Handle space-separated event names by delegating them individually.
|
|
40
|
+
for (names = name.split(eventSplitter); i < names.length; i++) {
|
|
41
|
+
events = iteratee(events, names[i], callback, opts);
|
|
42
|
+
}
|
|
43
|
+
} else {
|
|
44
|
+
// Finally, standard events.
|
|
45
|
+
events = iteratee(events, name, callback, opts);
|
|
46
|
+
}
|
|
47
|
+
return events;
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
// Bind an event to a `callback` function. Passing `"all"` will bind
|
|
51
|
+
// the callback to all events fired.
|
|
52
|
+
Events.on = function(name, callback, context) {
|
|
53
|
+
this._events = eventsApi(onApi, this._events || {}, name, callback, {
|
|
54
|
+
context: context,
|
|
55
|
+
ctx: this,
|
|
56
|
+
listening: _listening
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
if (_listening) {
|
|
60
|
+
var listeners = this._listeners || (this._listeners = {});
|
|
61
|
+
listeners[_listening.id] = _listening;
|
|
62
|
+
// Allow the listening to use a counter, instead of tracking
|
|
63
|
+
// callbacks for library interop
|
|
64
|
+
_listening.interop = false;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return this;
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
// Inversion-of-control versions of `on`. Tell *this* object to listen to
|
|
71
|
+
// an event in another object... keeping track of what it's listening to
|
|
72
|
+
// for easier unbinding later.
|
|
73
|
+
Events.listenTo = function(obj, name, callback) {
|
|
74
|
+
if (!obj) return this;
|
|
75
|
+
var id = obj._listenId || (obj._listenId = uniqueId('l'));
|
|
76
|
+
var listeningTo = this._listeningTo || (this._listeningTo = {});
|
|
77
|
+
var listening = _listening = listeningTo[id];
|
|
78
|
+
|
|
79
|
+
// This object is not listening to any other events on `obj` yet.
|
|
80
|
+
// Setup the necessary references to track the listening callbacks.
|
|
81
|
+
if (!listening) {
|
|
82
|
+
this._listenId || (this._listenId = uniqueId('l'));
|
|
83
|
+
listening = _listening = listeningTo[id] = new Listening(this, obj);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Bind callbacks on obj.
|
|
87
|
+
var error = tryCatchOn(obj, name, callback, this);
|
|
88
|
+
_listening = void 0;
|
|
89
|
+
|
|
90
|
+
if (error) throw error;
|
|
91
|
+
// If the target obj is not Events, track events manually.
|
|
92
|
+
if (listening.interop) listening.on(name, callback);
|
|
93
|
+
|
|
94
|
+
return this;
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
// The reducing API that adds a callback to the `events` object.
|
|
98
|
+
var onApi = function(events, name, callback, options) {
|
|
99
|
+
if (callback) {
|
|
100
|
+
var handlers = events[name] || (events[name] = []);
|
|
101
|
+
var context = options.context, ctx = options.ctx, listening = options.listening;
|
|
102
|
+
if (listening) listening.count++;
|
|
103
|
+
|
|
104
|
+
handlers.push({ callback: callback, context: context, ctx: context || ctx, listening: listening });
|
|
105
|
+
}
|
|
106
|
+
return events;
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
// An try-catch guarded #on function, to prevent poisoning the global
|
|
110
|
+
// `_listening` variable.
|
|
111
|
+
var tryCatchOn = function(obj, name, callback, context) {
|
|
112
|
+
try {
|
|
113
|
+
obj.on(name, callback, context);
|
|
114
|
+
} catch (e) {
|
|
115
|
+
return e;
|
|
116
|
+
}
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
// Remove one or many callbacks. If `context` is null, removes all
|
|
120
|
+
// callbacks with that function. If `callback` is null, removes all
|
|
121
|
+
// callbacks for the event. If `name` is null, removes all bound
|
|
122
|
+
// callbacks for all events.
|
|
123
|
+
Events.off = function(name, callback, context) {
|
|
124
|
+
if (!this._events) return this;
|
|
125
|
+
this._events = eventsApi(offApi, this._events, name, callback, {
|
|
126
|
+
context: context,
|
|
127
|
+
listeners: this._listeners
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
return this;
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
// Tell this object to stop listening to either specific events ... or
|
|
134
|
+
// to every object it's currently listening to.
|
|
135
|
+
Events.stopListening = function(obj, name, callback) {
|
|
136
|
+
var listeningTo = this._listeningTo;
|
|
137
|
+
if (!listeningTo) return this;
|
|
138
|
+
|
|
139
|
+
var ids = obj ? [obj._listenId] : Object.keys(listeningTo);
|
|
140
|
+
for (var i = 0; i < ids.length; i++) {
|
|
141
|
+
var listening = listeningTo[ids[i]];
|
|
142
|
+
|
|
143
|
+
// If listening doesn't exist, this object is not currently
|
|
144
|
+
// listening to obj. Break out early.
|
|
145
|
+
if (!listening) break;
|
|
146
|
+
|
|
147
|
+
listening.obj.off(name, callback, this);
|
|
148
|
+
if (listening.interop) listening.off(name, callback);
|
|
149
|
+
}
|
|
150
|
+
if (isEmpty(listeningTo)) this._listeningTo = void 0;
|
|
151
|
+
|
|
152
|
+
return this;
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
// The reducing API that removes a callback from the `events` object.
|
|
156
|
+
var offApi = function(events, name, callback, options) {
|
|
157
|
+
if (!events) return;
|
|
158
|
+
|
|
159
|
+
var context = options.context, listeners = options.listeners;
|
|
160
|
+
var i = 0, names;
|
|
161
|
+
|
|
162
|
+
// Delete all event listeners and "drop" events.
|
|
163
|
+
if (!name && !context && !callback) {
|
|
164
|
+
if(listeners != null) {
|
|
165
|
+
for (names = Object.keys(listeners); i < names.length; i++) {
|
|
166
|
+
listeners[names[i]].cleanup();
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
names = name ? [name] : Object.keys(events);
|
|
172
|
+
for (; i < names.length; i++) {
|
|
173
|
+
name = names[i];
|
|
174
|
+
var handlers = events[name];
|
|
175
|
+
|
|
176
|
+
// Bail out if there are no events stored.
|
|
177
|
+
if (!handlers) break;
|
|
178
|
+
|
|
179
|
+
// Find any remaining events.
|
|
180
|
+
var remaining = [];
|
|
181
|
+
for (var j = 0; j < handlers.length; j++) {
|
|
182
|
+
var handler = handlers[j];
|
|
183
|
+
if (
|
|
184
|
+
callback && callback !== handler.callback &&
|
|
185
|
+
callback !== handler.callback._callback ||
|
|
186
|
+
context && context !== handler.context
|
|
187
|
+
) {
|
|
188
|
+
remaining.push(handler);
|
|
189
|
+
} else {
|
|
190
|
+
var listening = handler.listening;
|
|
191
|
+
if (listening) listening.off(name, callback);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// Replace events if there are any remaining. Otherwise, clean up.
|
|
196
|
+
if (remaining.length) {
|
|
197
|
+
events[name] = remaining;
|
|
198
|
+
} else {
|
|
199
|
+
delete events[name];
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
return events;
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
// Bind an event to only be triggered a single time. After the first time
|
|
207
|
+
// the callback is invoked, its listener will be removed. If multiple events
|
|
208
|
+
// are passed in using the space-separated syntax, the handler will fire
|
|
209
|
+
// once for each event, not once for a combination of all events.
|
|
210
|
+
Events.once = function(name, callback, context) {
|
|
211
|
+
// Map the event into a `{event: once}` object.
|
|
212
|
+
var events = eventsApi(onceMap, {}, name, callback, this.off.bind(this));
|
|
213
|
+
if (typeof name === 'string' && context == null) callback = void 0;
|
|
214
|
+
return this.on(events, callback, context);
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
// Inversion-of-control versions of `once`.
|
|
218
|
+
Events.listenToOnce = function(obj, name, callback) {
|
|
219
|
+
// Map the event into a `{event: once}` object.
|
|
220
|
+
var events = eventsApi(onceMap, {}, name, callback, this.stopListening.bind(this, obj));
|
|
221
|
+
return this.listenTo(obj, events);
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
// Reduces the event callbacks into a map of `{event: onceWrapper}`.
|
|
225
|
+
// `offer` unbinds the `onceWrapper` after it has been called.
|
|
226
|
+
var onceMap = function(map, name, callback, offer) {
|
|
227
|
+
if (callback) {
|
|
228
|
+
var once = map[name] = onceInvoke(function() {
|
|
229
|
+
offer(name, once);
|
|
230
|
+
callback.apply(this, arguments);
|
|
231
|
+
});
|
|
232
|
+
once._callback = callback;
|
|
233
|
+
}
|
|
234
|
+
return map;
|
|
235
|
+
};
|
|
236
|
+
|
|
237
|
+
// Creates a function that is restricted to invoking 'func' once.
|
|
238
|
+
// Repeat calls to the function return the value of the first invocation.
|
|
239
|
+
var onceInvoke = function(func) {
|
|
240
|
+
var result;
|
|
241
|
+
if (typeof func != 'function') {
|
|
242
|
+
throw new TypeError('Expected a function');
|
|
243
|
+
}
|
|
244
|
+
var n = 2;
|
|
245
|
+
return function() {
|
|
246
|
+
if (--n > 0) {
|
|
247
|
+
result = func.apply(this, arguments);
|
|
248
|
+
}
|
|
249
|
+
if (n <= 1) {
|
|
250
|
+
func = undefined;
|
|
251
|
+
}
|
|
252
|
+
return result;
|
|
253
|
+
};
|
|
254
|
+
};
|
|
255
|
+
|
|
256
|
+
// Trigger one or many events, firing all bound callbacks. Callbacks are
|
|
257
|
+
// passed the same arguments as `trigger` is, apart from the event name
|
|
258
|
+
// (unless you're listening on `"all"`, which will cause your callback to
|
|
259
|
+
// receive the true name of the event as the first argument).
|
|
260
|
+
Events.trigger = function(name) {
|
|
261
|
+
if (!this._events) return this;
|
|
262
|
+
|
|
263
|
+
var length = Math.max(0, arguments.length - 1);
|
|
264
|
+
var args = Array(length);
|
|
265
|
+
for (var i = 0; i < length; i++) args[i] = arguments[i + 1];
|
|
266
|
+
|
|
267
|
+
eventsApi(triggerApi, this._events, name, void 0, args);
|
|
268
|
+
return this;
|
|
269
|
+
};
|
|
270
|
+
|
|
271
|
+
// Handles triggering the appropriate event callbacks.
|
|
272
|
+
var triggerApi = function(objEvents, name, callback, args) {
|
|
273
|
+
if (objEvents) {
|
|
274
|
+
var events = objEvents[name];
|
|
275
|
+
var allEvents = objEvents.all;
|
|
276
|
+
if (events && allEvents) allEvents = allEvents.slice();
|
|
277
|
+
if (events) triggerEvents(events, args);
|
|
278
|
+
if (allEvents) triggerEvents(allEvents, [name].concat(args));
|
|
279
|
+
}
|
|
280
|
+
return objEvents;
|
|
281
|
+
};
|
|
282
|
+
|
|
283
|
+
// A difficult-to-believe, but optimized internal dispatch function for
|
|
284
|
+
// triggering events. Tries to keep the usual cases speedy (most internal
|
|
285
|
+
// events have 3 arguments).
|
|
286
|
+
var triggerEvents = function(events, args) {
|
|
287
|
+
var ev, i = -1, l = events.length, a1 = args[0], a2 = args[1], a3 = args[2];
|
|
288
|
+
switch (args.length) {
|
|
289
|
+
case 0: while (++i < l) (ev = events[i]).callback.call(ev.ctx); return;
|
|
290
|
+
case 1: while (++i < l) (ev = events[i]).callback.call(ev.ctx, a1); return;
|
|
291
|
+
case 2: while (++i < l) (ev = events[i]).callback.call(ev.ctx, a1, a2); return;
|
|
292
|
+
case 3: while (++i < l) (ev = events[i]).callback.call(ev.ctx, a1, a2, a3); return;
|
|
293
|
+
default: while (++i < l) (ev = events[i]).callback.apply(ev.ctx, args); return;
|
|
294
|
+
}
|
|
295
|
+
};
|
|
296
|
+
|
|
297
|
+
// A listening class that tracks and cleans up memory bindings
|
|
298
|
+
// when all callbacks have been offed.
|
|
299
|
+
var Listening = function(listener, obj) {
|
|
300
|
+
this.id = listener._listenId;
|
|
301
|
+
this.listener = listener;
|
|
302
|
+
this.obj = obj;
|
|
303
|
+
this.interop = true;
|
|
304
|
+
this.count = 0;
|
|
305
|
+
this._events = void 0;
|
|
306
|
+
};
|
|
307
|
+
|
|
308
|
+
Listening.prototype.on = Events.on;
|
|
309
|
+
|
|
310
|
+
// Offs a callback (or several).
|
|
311
|
+
// Uses an optimized counter if the listenee uses Events.
|
|
312
|
+
// Otherwise, falls back to manual tracking to support events
|
|
313
|
+
// library interop.
|
|
314
|
+
Listening.prototype.off = function(name, callback) {
|
|
315
|
+
var cleanup;
|
|
316
|
+
if (this.interop) {
|
|
317
|
+
this._events = eventsApi(offApi, this._events, name, callback, {
|
|
318
|
+
context: void 0,
|
|
319
|
+
listeners: void 0
|
|
320
|
+
});
|
|
321
|
+
cleanup = !this._events;
|
|
322
|
+
} else {
|
|
323
|
+
this.count--;
|
|
324
|
+
cleanup = this.count === 0;
|
|
325
|
+
}
|
|
326
|
+
if (cleanup) this.cleanup();
|
|
327
|
+
};
|
|
328
|
+
|
|
329
|
+
// Cleans up memory bindings between the listener and the listenee.
|
|
330
|
+
Listening.prototype.cleanup = function() {
|
|
331
|
+
delete this.listener._listeningTo[this.obj._listenId];
|
|
332
|
+
if (!this.interop) delete this.obj._listeners[this.id];
|
|
333
|
+
};
|
|
334
|
+
|
|
335
|
+
// Aliases for backwards compatibility.
|
|
336
|
+
Events.bind = Events.on;
|
|
337
|
+
Events.unbind = Events.off;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import V from '../V/index.mjs';
|
|
2
|
+
import { Events } from './Events.mjs';
|
|
3
|
+
|
|
4
|
+
export class Listener {
|
|
5
|
+
constructor(...callbackArguments) {
|
|
6
|
+
this.callbackArguments = callbackArguments;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
listenTo(object, evt, ...args) {
|
|
10
|
+
const { callbackArguments } = this;
|
|
11
|
+
// signature 1 - (object, eventHashMap, context)
|
|
12
|
+
if (V.isObject(evt)) {
|
|
13
|
+
const [context = null] = args;
|
|
14
|
+
Object.entries(evt).forEach(([eventName, cb]) => {
|
|
15
|
+
if (typeof cb !== 'function') return;
|
|
16
|
+
// Invoke the callback with callbackArguments passed first
|
|
17
|
+
if (context || callbackArguments.length > 0) cb = cb.bind(context, ...callbackArguments);
|
|
18
|
+
Events.listenTo.call(this, object, eventName, cb);
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
// signature 2 - (object, event, callback, context)
|
|
22
|
+
else if (typeof evt === 'string' && typeof args[0] === 'function') {
|
|
23
|
+
let [cb, context = null] = args;
|
|
24
|
+
// Invoke the callback with callbackArguments passed first
|
|
25
|
+
if (context || callbackArguments.length > 0) cb = cb.bind(context, ...callbackArguments);
|
|
26
|
+
Events.listenTo.call(this, object, evt, cb);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
stopListening() {
|
|
31
|
+
Events.stopListening.call(this);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
import { Events } from './Events.mjs';
|
|
2
|
+
import { extend } from './mvcUtils.mjs';
|
|
3
|
+
import {
|
|
4
|
+
assign,
|
|
5
|
+
clone,
|
|
6
|
+
defaults,
|
|
7
|
+
has,
|
|
8
|
+
isEqual,
|
|
9
|
+
isEmpty,
|
|
10
|
+
result,
|
|
11
|
+
uniqueId
|
|
12
|
+
} from '../util/util.mjs';
|
|
13
|
+
|
|
14
|
+
// Model
|
|
15
|
+
// --------------
|
|
16
|
+
|
|
17
|
+
// **Models** are the basic data object in the framework --
|
|
18
|
+
// frequently representing a row in a table in a database on your server.
|
|
19
|
+
// A discrete chunk of data and a bunch of useful, related methods for
|
|
20
|
+
// performing computations and transformations on that data.
|
|
21
|
+
|
|
22
|
+
// Create a new model with the specified attributes. A client id (`cid`)
|
|
23
|
+
// is automatically generated and assigned for you.
|
|
24
|
+
|
|
25
|
+
export var Model = function(attributes, options) {
|
|
26
|
+
var attrs = attributes || {};
|
|
27
|
+
options || (options = {});
|
|
28
|
+
this.preinitialize.apply(this, arguments);
|
|
29
|
+
this.cid = uniqueId(this.cidPrefix);
|
|
30
|
+
this.attributes = {};
|
|
31
|
+
if (options.collection) this.collection = options.collection;
|
|
32
|
+
var attributeDefaults = result(this, 'defaults');
|
|
33
|
+
|
|
34
|
+
// Just _.defaults would work fine, but the additional _.extends
|
|
35
|
+
// is in there for historical reasons. See #3843.
|
|
36
|
+
attrs = defaults(assign({}, attributeDefaults, attrs), attributeDefaults);
|
|
37
|
+
|
|
38
|
+
this.set(attrs, options);
|
|
39
|
+
this.changed = {};
|
|
40
|
+
this.initialize.apply(this, arguments);
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
// Attach all inheritable methods to the Model prototype.
|
|
44
|
+
assign(Model.prototype, Events, {
|
|
45
|
+
|
|
46
|
+
// A hash of attributes whose current and previous value differ.
|
|
47
|
+
changed: null,
|
|
48
|
+
|
|
49
|
+
// The value returned during the last failed validation.
|
|
50
|
+
validationError: null,
|
|
51
|
+
|
|
52
|
+
// The default name for the JSON `id` attribute is `"id"`. MongoDB and
|
|
53
|
+
// CouchDB users may want to set this to `"_id"`.
|
|
54
|
+
idAttribute: 'id',
|
|
55
|
+
|
|
56
|
+
// The prefix is used to create the client id which is used to identify models locally.
|
|
57
|
+
// You may want to override this if you're experiencing name clashes with model ids.
|
|
58
|
+
cidPrefix: 'c',
|
|
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 Model.
|
|
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
|
+
// Return a copy of the model's `attributes` object.
|
|
69
|
+
toJSON: function(options) {
|
|
70
|
+
return clone(this.attributes);
|
|
71
|
+
},
|
|
72
|
+
|
|
73
|
+
// Get the value of an attribute.
|
|
74
|
+
get: function(attr) {
|
|
75
|
+
return this.attributes[attr];
|
|
76
|
+
},
|
|
77
|
+
|
|
78
|
+
// Returns `true` if the attribute contains a value that is not null
|
|
79
|
+
// or undefined.
|
|
80
|
+
has: function(attr) {
|
|
81
|
+
return this.get(attr) != null;
|
|
82
|
+
},
|
|
83
|
+
|
|
84
|
+
// Set a hash of model attributes on the object, firing `"change"`. This is
|
|
85
|
+
// the core primitive operation of a model, updating the data and notifying
|
|
86
|
+
// anyone who needs to know about the change in state. The heart of the beast.
|
|
87
|
+
set: function(key, val, options) {
|
|
88
|
+
if (key == null) return this;
|
|
89
|
+
|
|
90
|
+
// Handle both `"key", value` and `{key: value}` -style arguments.
|
|
91
|
+
var attrs;
|
|
92
|
+
if (typeof key === 'object') {
|
|
93
|
+
attrs = key;
|
|
94
|
+
options = val;
|
|
95
|
+
} else {
|
|
96
|
+
(attrs = {})[key] = val;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
options || (options = {});
|
|
100
|
+
|
|
101
|
+
// Run validation.
|
|
102
|
+
if (!this._validate(attrs, options)) return false;
|
|
103
|
+
|
|
104
|
+
// Extract attributes and options.
|
|
105
|
+
var unset = options.unset;
|
|
106
|
+
var silent = options.silent;
|
|
107
|
+
var changes = [];
|
|
108
|
+
var changing = this._changing;
|
|
109
|
+
this._changing = true;
|
|
110
|
+
|
|
111
|
+
if (!changing) {
|
|
112
|
+
this._previousAttributes = clone(this.attributes);
|
|
113
|
+
this.changed = {};
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
var current = this.attributes;
|
|
117
|
+
var changed = this.changed;
|
|
118
|
+
var prev = this._previousAttributes;
|
|
119
|
+
|
|
120
|
+
// For each `set` attribute, update or delete the current value.
|
|
121
|
+
for (var attr in attrs) {
|
|
122
|
+
val = attrs[attr];
|
|
123
|
+
if (!isEqual(current[attr], val)) changes.push(attr);
|
|
124
|
+
if (!isEqual(prev[attr], val)) {
|
|
125
|
+
changed[attr] = val;
|
|
126
|
+
} else {
|
|
127
|
+
delete changed[attr];
|
|
128
|
+
}
|
|
129
|
+
unset ? delete current[attr] : current[attr] = val;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Update the `id`.
|
|
133
|
+
if (this.idAttribute in attrs) {
|
|
134
|
+
var prevId = this.id;
|
|
135
|
+
this.id = this.get(this.idAttribute);
|
|
136
|
+
this.trigger('changeId', this, prevId, options);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Trigger all relevant attribute changes.
|
|
140
|
+
if (!silent) {
|
|
141
|
+
if (changes.length) this._pending = options;
|
|
142
|
+
for (var i = 0; i < changes.length; i++) {
|
|
143
|
+
this.trigger('change:' + changes[i], this, current[changes[i]], options);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// You might be wondering why there's a `while` loop here. Changes can
|
|
148
|
+
// be recursively nested within `"change"` events.
|
|
149
|
+
if (changing) return this;
|
|
150
|
+
if (!silent) {
|
|
151
|
+
while (this._pending) {
|
|
152
|
+
options = this._pending;
|
|
153
|
+
this._pending = false;
|
|
154
|
+
this.trigger('change', this, options);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
this._pending = false;
|
|
158
|
+
this._changing = false;
|
|
159
|
+
return this;
|
|
160
|
+
},
|
|
161
|
+
|
|
162
|
+
// Remove an attribute from the model, firing `"change"`. `unset` is a noop
|
|
163
|
+
// if the attribute doesn't exist.
|
|
164
|
+
unset: function(attr, options) {
|
|
165
|
+
return this.set(attr, void 0, assign({}, options, { unset: true }));
|
|
166
|
+
},
|
|
167
|
+
|
|
168
|
+
// Clear all attributes on the model, firing `"change"`.
|
|
169
|
+
clear: function(options) {
|
|
170
|
+
var attrs = {};
|
|
171
|
+
for (var key in this.attributes) attrs[key] = void 0;
|
|
172
|
+
return this.set(attrs, assign({}, options, { unset: true }));
|
|
173
|
+
},
|
|
174
|
+
|
|
175
|
+
// Determine if the model has changed since the last `"change"` event.
|
|
176
|
+
// If you specify an attribute name, determine if that attribute has changed.
|
|
177
|
+
hasChanged: function(attr) {
|
|
178
|
+
if (attr == null) return !isEmpty(this.changed);
|
|
179
|
+
return has(this.changed, attr);
|
|
180
|
+
},
|
|
181
|
+
|
|
182
|
+
// Return an object containing all the attributes that have changed, or
|
|
183
|
+
// false if there are no changed attributes. Useful for determining what
|
|
184
|
+
// parts of a view need to be updated and/or what attributes need to be
|
|
185
|
+
// persisted to the server. Unset attributes will be set to undefined.
|
|
186
|
+
// You can also pass an attributes object to diff against the model,
|
|
187
|
+
// determining if there *would be* a change.
|
|
188
|
+
changedAttributes: function(diff) {
|
|
189
|
+
if (!diff) return this.hasChanged() ? clone(this.changed) : false;
|
|
190
|
+
var old = this._changing ? this._previousAttributes : this.attributes;
|
|
191
|
+
var changed = {};
|
|
192
|
+
var hasChanged;
|
|
193
|
+
for (var attr in diff) {
|
|
194
|
+
var val = diff[attr];
|
|
195
|
+
if (isEqual(old[attr], val)) continue;
|
|
196
|
+
changed[attr] = val;
|
|
197
|
+
hasChanged = true;
|
|
198
|
+
}
|
|
199
|
+
return hasChanged ? changed : false;
|
|
200
|
+
},
|
|
201
|
+
|
|
202
|
+
// Get the previous value of an attribute, recorded at the time the last
|
|
203
|
+
// `"change"` event was fired.
|
|
204
|
+
previous: function(attr) {
|
|
205
|
+
if (attr == null || !this._previousAttributes) return null;
|
|
206
|
+
return this._previousAttributes[attr];
|
|
207
|
+
},
|
|
208
|
+
|
|
209
|
+
// Get all of the attributes of the model at the time of the previous
|
|
210
|
+
// `"change"` event.
|
|
211
|
+
previousAttributes: function() {
|
|
212
|
+
return clone(this._previousAttributes);
|
|
213
|
+
},
|
|
214
|
+
|
|
215
|
+
// Create a new model with identical attributes to this one.
|
|
216
|
+
clone: function() {
|
|
217
|
+
return new this.constructor(this.attributes);
|
|
218
|
+
},
|
|
219
|
+
|
|
220
|
+
// Check if the model is currently in a valid state.
|
|
221
|
+
isValid: function(options) {
|
|
222
|
+
return this._validate({}, assign({}, options, { validate: true }));
|
|
223
|
+
},
|
|
224
|
+
|
|
225
|
+
// Run validation against the next complete set of model attributes,
|
|
226
|
+
// returning `true` if all is well. Otherwise, fire an `"invalid"` event.
|
|
227
|
+
_validate: function(attrs, options) {
|
|
228
|
+
if (!options.validate || !this.validate) return true;
|
|
229
|
+
attrs = assign({}, this.attributes, attrs);
|
|
230
|
+
var error = this.validationError = this.validate(attrs, options) || null;
|
|
231
|
+
if (!error) return true;
|
|
232
|
+
this.trigger('invalid', this, error, assign(options, { validationError: error }));
|
|
233
|
+
return false;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
// Set up inheritance for the model.
|
|
239
|
+
Model.extend = extend;
|