@converse/skeletor 0.0.8 → 0.0.9
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 +38 -25
- package/dist/skeletor.js +9232 -5096
- package/dist/skeletor.js.map +1 -1
- package/dist/skeletor.min.js +74 -3
- package/dist/skeletor.min.js.map +1 -1
- package/package.json +21 -11
- package/src/collection.js +540 -395
- package/src/drivers/sessionStorage.js +222 -219
- package/src/element.js +132 -82
- package/src/eventemitter.js +187 -0
- package/src/helpers.js +143 -118
- package/src/{main.js → index.js} +21 -17
- package/src/listening.js +81 -0
- package/src/model.js +340 -238
- package/src/storage.js +222 -230
- package/src/types/collection.d.ts +366 -0
- package/src/types/collection.d.ts.map +1 -0
- package/src/types/drivers/sessionStorage.d.ts +37 -0
- package/src/types/drivers/sessionStorage.d.ts.map +1 -0
- package/src/types/element.d.ts +115 -0
- package/src/types/element.d.ts.map +1 -0
- package/src/types/eventemitter.d.ts +93 -0
- package/src/types/eventemitter.d.ts.map +1 -0
- package/src/types/helpers.d.ts +62 -0
- package/src/types/helpers.d.ts.map +1 -0
- package/src/types/index.d.ts +19 -0
- package/src/types/index.d.ts.map +1 -0
- package/src/types/listening.d.ts +40 -0
- package/src/types/listening.d.ts.map +1 -0
- package/src/types/model.d.ts +230 -0
- package/src/types/model.d.ts.map +1 -0
- package/src/types/storage.d.ts +35 -0
- package/src/types/storage.d.ts.map +1 -0
- package/src/types/utils/events.d.ts +28 -0
- package/src/types/utils/events.d.ts.map +1 -0
- package/src/utils/events.js +174 -0
- package/src/drivers/sessionStorage.js~ +0 -307
- package/src/events.js +0 -328
- package/src/history.js +0 -301
- package/src/overview.js +0 -191
- package/src/router.js +0 -119
- package/src/view.js +0 -267
|
@@ -1,307 +0,0 @@
|
|
|
1
|
-
// Copyright 2014 Mozilla
|
|
2
|
-
// Copyright 2015 Thodoris Greasidis
|
|
3
|
-
// Copyright 2018 JC Brand
|
|
4
|
-
//
|
|
5
|
-
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
-
// you may not use this file except in compliance with the License.
|
|
7
|
-
// You may obtain a copy of the License at
|
|
8
|
-
//
|
|
9
|
-
// http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
-
//
|
|
11
|
-
// Unless required by applicable law or agreed to in writing, software
|
|
12
|
-
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
-
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
-
// See the License for the specific language governing permissions and
|
|
15
|
-
// limitations under the License.
|
|
16
|
-
|
|
17
|
-
import executeCallback from 'localforage/src/utils/executeCallback';
|
|
18
|
-
import getCallback from 'localforage/src/utils/getCallback';
|
|
19
|
-
import normalizeKey from 'localforage/src/utils/normalizeKey';
|
|
20
|
-
import serializer from 'localforage/src/utils/serializer';
|
|
21
|
-
|
|
22
|
-
const serialize = serializer["serialize"];
|
|
23
|
-
const deserialize = serializer["deserialize"];
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
function isSessionStorageValid () {
|
|
27
|
-
// If the app is running inside a Google Chrome packaged webapp, or some
|
|
28
|
-
// other context where sessionStorage isn't available, we don't use
|
|
29
|
-
// sessionStorage. This feature detection is preferred over the old
|
|
30
|
-
// `if (window.chrome && window.chrome.runtime)` code.
|
|
31
|
-
// See: https://github.com/mozilla/localForage/issues/68
|
|
32
|
-
try {
|
|
33
|
-
// If sessionStorage isn't available, we get outta here!
|
|
34
|
-
// This should be inside a try catch
|
|
35
|
-
if (sessionStorage && ('setItem' in sessionStorage)) {
|
|
36
|
-
return true;
|
|
37
|
-
}
|
|
38
|
-
} catch (e) {
|
|
39
|
-
console.log(e);
|
|
40
|
-
}
|
|
41
|
-
return false;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
function _getKeyPrefix(options, defaultConfig) {
|
|
45
|
-
let keyPrefix = options.name + '/';
|
|
46
|
-
|
|
47
|
-
if (options.storeName !== defaultConfig.storeName) {
|
|
48
|
-
keyPrefix += options.storeName + '/';
|
|
49
|
-
}
|
|
50
|
-
return keyPrefix;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
const dbInfo = {
|
|
54
|
-
'serializer': {
|
|
55
|
-
'serialize': serialize,
|
|
56
|
-
'deserialize': deserialize
|
|
57
|
-
}
|
|
58
|
-
};
|
|
59
|
-
|
|
60
|
-
function _initStorage(options) {
|
|
61
|
-
dbInfo.keyPrefix = _getKeyPrefix(options, this._defaultConfig);
|
|
62
|
-
if (options) {
|
|
63
|
-
for (const i in options) { // eslint-disable-line guard-for-in
|
|
64
|
-
dbInfo[i] = options[i];
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
// Remove all keys from the datastore, effectively destroying all data in
|
|
70
|
-
// the app's key/value store!
|
|
71
|
-
function clear(callback) {
|
|
72
|
-
const promise = this.ready().then(function() {
|
|
73
|
-
const keyPrefix = dbInfo.keyPrefix;
|
|
74
|
-
|
|
75
|
-
for (let i = sessionStorage.length - 1; i >= 0; i--) {
|
|
76
|
-
const key = sessionStorage.key(i);
|
|
77
|
-
|
|
78
|
-
if (key.indexOf(keyPrefix) === 0) {
|
|
79
|
-
sessionStorage.removeItem(key);
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
executeCallback(promise, callback);
|
|
85
|
-
return promise;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
// Retrieve an item from the store. Unlike the original async_storage
|
|
89
|
-
// library in Gaia, we don't modify return values at all. If a key's value
|
|
90
|
-
// is `undefined`, we pass that value to the callback function.
|
|
91
|
-
function getItem(key, callback) {
|
|
92
|
-
key = normalizeKey(key);
|
|
93
|
-
|
|
94
|
-
const promise = this.ready().then(function() {
|
|
95
|
-
let result = sessionStorage.getItem(dbInfo.keyPrefix + key);
|
|
96
|
-
// If a result was found, parse it from the serialized
|
|
97
|
-
// string into a JS object. If result isn't truthy, the key
|
|
98
|
-
// is likely undefined and we'll pass it straight to the
|
|
99
|
-
// callback.
|
|
100
|
-
if (result) {
|
|
101
|
-
result = dbInfo.serializer.deserialize(result);
|
|
102
|
-
}
|
|
103
|
-
return result;
|
|
104
|
-
});
|
|
105
|
-
executeCallback(promise, callback);
|
|
106
|
-
return promise;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
// Iterate over all items in the store.
|
|
110
|
-
function iterate(iterator, callback) {
|
|
111
|
-
const self = this;
|
|
112
|
-
|
|
113
|
-
const promise = self.ready().then(function() {
|
|
114
|
-
const keyPrefix = dbInfo.keyPrefix;
|
|
115
|
-
const keyPrefixLength = keyPrefix.length;
|
|
116
|
-
const length = sessionStorage.length;
|
|
117
|
-
|
|
118
|
-
// We use a dedicated iterator instead of the `i` variable below
|
|
119
|
-
// so other keys we fetch in sessionStorage aren't counted in
|
|
120
|
-
// the `iterationNumber` argument passed to the `iterate()`
|
|
121
|
-
// callback.
|
|
122
|
-
//
|
|
123
|
-
// See: github.com/mozilla/localForage/pull/435#discussion_r38061530
|
|
124
|
-
let iterationNumber = 1;
|
|
125
|
-
|
|
126
|
-
for (let i = 0; i < length; i++) {
|
|
127
|
-
const key = sessionStorage.key(i);
|
|
128
|
-
if (key.indexOf(keyPrefix) !== 0) {
|
|
129
|
-
continue;
|
|
130
|
-
}
|
|
131
|
-
let value = sessionStorage.getItem(key);
|
|
132
|
-
|
|
133
|
-
// If a result was found, parse it from the serialized
|
|
134
|
-
// string into a JS object. If result isn't truthy, the
|
|
135
|
-
// key is likely undefined and we'll pass it straight
|
|
136
|
-
// to the iterator.
|
|
137
|
-
if (value) {
|
|
138
|
-
value = dbInfo.serializer.deserialize(value);
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
value = iterator(
|
|
142
|
-
value,
|
|
143
|
-
key.substring(keyPrefixLength),
|
|
144
|
-
iterationNumber++
|
|
145
|
-
);
|
|
146
|
-
|
|
147
|
-
if (value !== void 0) { // eslint-disable-line no-void
|
|
148
|
-
return value;
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
});
|
|
152
|
-
|
|
153
|
-
executeCallback(promise, callback);
|
|
154
|
-
return promise;
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
// Same as sessionStorage's key() method, except takes a callback.
|
|
158
|
-
function key(n, callback) {
|
|
159
|
-
const self = this;
|
|
160
|
-
const promise = self.ready().then(function() {
|
|
161
|
-
let result;
|
|
162
|
-
try {
|
|
163
|
-
result = sessionStorage.key(n);
|
|
164
|
-
} catch (error) {
|
|
165
|
-
result = null;
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
// Remove the prefix from the key, if a key is found.
|
|
169
|
-
if (result) {
|
|
170
|
-
result = result.substring(dbInfo.keyPrefix.length);
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
return result;
|
|
174
|
-
});
|
|
175
|
-
|
|
176
|
-
executeCallback(promise, callback);
|
|
177
|
-
return promise;
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
function keys(callback) {
|
|
181
|
-
const self = this;
|
|
182
|
-
const promise = self.ready().then(function() {
|
|
183
|
-
const length = sessionStorage.length;
|
|
184
|
-
const keys = [];
|
|
185
|
-
|
|
186
|
-
for (let i = 0; i < length; i++) {
|
|
187
|
-
const itemKey = sessionStorage.key(i);
|
|
188
|
-
if (itemKey.indexOf(dbInfo.keyPrefix) === 0) {
|
|
189
|
-
keys.push(itemKey.substring(dbInfo.keyPrefix.length));
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
return keys;
|
|
193
|
-
});
|
|
194
|
-
|
|
195
|
-
executeCallback(promise, callback);
|
|
196
|
-
return promise;
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
// Supply the number of keys in the datastore to the callback function.
|
|
200
|
-
function length(callback) {
|
|
201
|
-
const self = this;
|
|
202
|
-
const promise = self.keys().then(function(keys) {
|
|
203
|
-
return keys.length;
|
|
204
|
-
});
|
|
205
|
-
|
|
206
|
-
executeCallback(promise, callback);
|
|
207
|
-
return promise;
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
// Remove an item from the store, nice and simple.
|
|
211
|
-
function removeItem(key, callback) {
|
|
212
|
-
key = normalizeKey(key);
|
|
213
|
-
const promise = this.ready().then(function() {
|
|
214
|
-
sessionStorage.removeItem(dbInfo.keyPrefix + key);
|
|
215
|
-
});
|
|
216
|
-
executeCallback(promise, callback);
|
|
217
|
-
return promise;
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
// Set a key's value and run an optional callback once the value is set.
|
|
221
|
-
// Unlike Gaia's implementation, the callback function is passed the value,
|
|
222
|
-
// in case you want to operate on that value only after you're sure it
|
|
223
|
-
// saved, or something like that.
|
|
224
|
-
async function setItem(key, value, callback) {
|
|
225
|
-
key = normalizeKey(key);
|
|
226
|
-
await this.ready();
|
|
227
|
-
|
|
228
|
-
// Convert undefined values to null.
|
|
229
|
-
// https://github.com/mozilla/localForage/pull/42
|
|
230
|
-
value = value ?? null;
|
|
231
|
-
|
|
232
|
-
// Save the original value to pass to the callback.
|
|
233
|
-
const originalValue = value;
|
|
234
|
-
|
|
235
|
-
dbInfo.serializer.serialize(value, (value, error) => {
|
|
236
|
-
if (error) {
|
|
237
|
-
throw error;
|
|
238
|
-
} else {
|
|
239
|
-
try {
|
|
240
|
-
sessionStorage.setItem(dbInfo.keyPrefix + key, value);
|
|
241
|
-
executeCallback(Promise.resolve(originalValue), callback);
|
|
242
|
-
} catch (e) {
|
|
243
|
-
if (
|
|
244
|
-
e.name === 'QuotaExceededError' ||
|
|
245
|
-
e.name === 'NS_ERROR_DOM_QUOTA_REACHED'
|
|
246
|
-
) {
|
|
247
|
-
console.error("Your sesionStorage capacity is used up.");
|
|
248
|
-
throw e;
|
|
249
|
-
}
|
|
250
|
-
throw e;
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
});
|
|
254
|
-
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
function dropInstance(options, callback) {
|
|
258
|
-
callback = getCallback.apply(this, arguments);
|
|
259
|
-
|
|
260
|
-
options = (typeof options !== 'function' && options) || {};
|
|
261
|
-
if (!options.name) {
|
|
262
|
-
const currentConfig = this.config();
|
|
263
|
-
options.name = options.name || currentConfig.name;
|
|
264
|
-
options.storeName = options.storeName || currentConfig.storeName;
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
const self = this;
|
|
268
|
-
let promise;
|
|
269
|
-
if (!options.name) {
|
|
270
|
-
promise = Promise.reject(new Error('Invalid arguments'));
|
|
271
|
-
} else {
|
|
272
|
-
promise = new Promise(function(resolve) {
|
|
273
|
-
if (!options.storeName) {
|
|
274
|
-
resolve(`${options.name}/`);
|
|
275
|
-
} else {
|
|
276
|
-
resolve(_getKeyPrefix(options, self._defaultConfig));
|
|
277
|
-
}
|
|
278
|
-
}).then(function(keyPrefix) {
|
|
279
|
-
for (let i = sessionStorage.length - 1; i >= 0; i--) {
|
|
280
|
-
const key = sessionStorage.key(i);
|
|
281
|
-
if (key.indexOf(keyPrefix) === 0) {
|
|
282
|
-
sessionStorage.removeItem(key);
|
|
283
|
-
}
|
|
284
|
-
}
|
|
285
|
-
});
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
executeCallback(promise, callback);
|
|
289
|
-
return promise;
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
const sessionStorageWrapper = {
|
|
293
|
-
_driver: 'sessionStorageWrapper',
|
|
294
|
-
_initStorage: _initStorage,
|
|
295
|
-
_support: isSessionStorageValid(),
|
|
296
|
-
iterate: iterate,
|
|
297
|
-
getItem: getItem,
|
|
298
|
-
setItem: setItem,
|
|
299
|
-
removeItem: removeItem,
|
|
300
|
-
clear: clear,
|
|
301
|
-
length: length,
|
|
302
|
-
key: key,
|
|
303
|
-
keys: keys,
|
|
304
|
-
dropInstance: dropInstance
|
|
305
|
-
};
|
|
306
|
-
|
|
307
|
-
export default sessionStorageWrapper;
|
package/src/events.js
DELETED
|
@@ -1,328 +0,0 @@
|
|
|
1
|
-
// Backbone.js 1.4.0
|
|
2
|
-
// (c) 2010-2019 Jeremy Ashkenas and DocumentCloud
|
|
3
|
-
// Backbone may be freely distributed under the MIT license.
|
|
4
|
-
|
|
5
|
-
// Events
|
|
6
|
-
// ------
|
|
7
|
-
|
|
8
|
-
// A module that can be mixed in to *any object* in order to provide it with
|
|
9
|
-
// a custom event channel. You may bind a callback to an event with `on` or
|
|
10
|
-
// remove with `off`; `trigger`-ing an event fires all callbacks in
|
|
11
|
-
// succession.
|
|
12
|
-
//
|
|
13
|
-
// let object = {};
|
|
14
|
-
// extend(object, Backbone.Events);
|
|
15
|
-
// object.on('expand', function(){ alert('expanded'); });
|
|
16
|
-
// object.trigger('expand');
|
|
17
|
-
//
|
|
18
|
-
|
|
19
|
-
import isEmpty from 'lodash-es/isEmpty.js';
|
|
20
|
-
import keys from 'lodash-es/keys.js';
|
|
21
|
-
import once from 'lodash-es/once.js';
|
|
22
|
-
import uniqueId from 'lodash-es/uniqueId.js';
|
|
23
|
-
|
|
24
|
-
export const Events = {};
|
|
25
|
-
|
|
26
|
-
// Regular expression used to split event strings.
|
|
27
|
-
const eventSplitter = /\s+/;
|
|
28
|
-
|
|
29
|
-
// A private global variable to share between listeners and listenees.
|
|
30
|
-
let _listening;
|
|
31
|
-
|
|
32
|
-
// Iterates over the standard `event, callback` (as well as the fancy multiple
|
|
33
|
-
// space-separated events `"change blur", callback` and jQuery-style event
|
|
34
|
-
// maps `{event: callback}`).
|
|
35
|
-
const eventsApi = function(iteratee, events, name, callback, opts) {
|
|
36
|
-
let i = 0, names;
|
|
37
|
-
if (name && typeof name === 'object') {
|
|
38
|
-
// Handle event maps.
|
|
39
|
-
if (callback !== undefined && 'context' in opts && opts.context === undefined) opts.context = callback;
|
|
40
|
-
for (names = keys(name); i < names.length ; i++) {
|
|
41
|
-
events = eventsApi(iteratee, events, names[i], name[names[i]], opts);
|
|
42
|
-
}
|
|
43
|
-
} else if (name && eventSplitter.test(name)) {
|
|
44
|
-
// Handle space-separated event names by delegating them individually.
|
|
45
|
-
for (names = name.split(eventSplitter); i < names.length; i++) {
|
|
46
|
-
events = iteratee(events, names[i], callback, opts);
|
|
47
|
-
}
|
|
48
|
-
} else {
|
|
49
|
-
// Finally, standard events.
|
|
50
|
-
events = iteratee(events, name, callback, opts);
|
|
51
|
-
}
|
|
52
|
-
return events;
|
|
53
|
-
};
|
|
54
|
-
|
|
55
|
-
// Bind an event to a `callback` function. Passing `"all"` will bind
|
|
56
|
-
// the callback to all events fired.
|
|
57
|
-
Events.on = function(name, callback, context) {
|
|
58
|
-
this._events = eventsApi(onApi, this._events || {}, name, callback, {
|
|
59
|
-
context: context,
|
|
60
|
-
ctx: this,
|
|
61
|
-
listening: _listening
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
if (_listening) {
|
|
65
|
-
const listeners = this._listeners || (this._listeners = {});
|
|
66
|
-
listeners[_listening.id] = _listening;
|
|
67
|
-
// Allow the listening to use a counter, instead of tracking
|
|
68
|
-
// callbacks for library interop
|
|
69
|
-
_listening.interop = false;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
return this;
|
|
73
|
-
};
|
|
74
|
-
|
|
75
|
-
// Inversion-of-control versions of `on`. Tell *this* object to listen to
|
|
76
|
-
// an event in another object... keeping track of what it's listening to
|
|
77
|
-
// for easier unbinding later.
|
|
78
|
-
Events.listenTo = function(obj, name, callback) {
|
|
79
|
-
if (!obj) return this;
|
|
80
|
-
const id = obj._listenId || (obj._listenId = uniqueId('l'));
|
|
81
|
-
const listeningTo = this._listeningTo || (this._listeningTo = {});
|
|
82
|
-
let listening = _listening = listeningTo[id];
|
|
83
|
-
|
|
84
|
-
// This object is not listening to any other events on `obj` yet.
|
|
85
|
-
// Setup the necessary references to track the listening callbacks.
|
|
86
|
-
if (!listening) {
|
|
87
|
-
this._listenId || (this._listenId = uniqueId('l'));
|
|
88
|
-
listening = _listening = listeningTo[id] = new Listening(this, obj);
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
// Bind callbacks on obj.
|
|
92
|
-
const error = tryCatchOn(obj, name, callback, this);
|
|
93
|
-
_listening = undefined;
|
|
94
|
-
|
|
95
|
-
if (error) throw error;
|
|
96
|
-
// If the target obj is not Backbone.Events, track events manually.
|
|
97
|
-
if (listening.interop) listening.on(name, callback);
|
|
98
|
-
|
|
99
|
-
return this;
|
|
100
|
-
};
|
|
101
|
-
|
|
102
|
-
// The reducing API that adds a callback to the `events` object.
|
|
103
|
-
const onApi = function(events, name, callback, options) {
|
|
104
|
-
if (callback) {
|
|
105
|
-
const handlers = events[name] || (events[name] = []);
|
|
106
|
-
const context = options.context, ctx = options.ctx, listening = options.listening;
|
|
107
|
-
if (listening) listening.count++;
|
|
108
|
-
|
|
109
|
-
handlers.push({callback: callback, context: context, ctx: context || ctx, listening: listening});
|
|
110
|
-
}
|
|
111
|
-
return events;
|
|
112
|
-
};
|
|
113
|
-
|
|
114
|
-
// An try-catch guarded #on function, to prevent poisoning the global
|
|
115
|
-
// `_listening` variable.
|
|
116
|
-
const tryCatchOn = function(obj, name, callback, context) {
|
|
117
|
-
try {
|
|
118
|
-
obj.on(name, callback, context);
|
|
119
|
-
} catch (e) {
|
|
120
|
-
return e;
|
|
121
|
-
}
|
|
122
|
-
};
|
|
123
|
-
|
|
124
|
-
// Remove one or many callbacks. If `context` is null, removes all
|
|
125
|
-
// callbacks with that function. If `callback` is null, removes all
|
|
126
|
-
// callbacks for the event. If `name` is null, removes all bound
|
|
127
|
-
// callbacks for all events.
|
|
128
|
-
Events.off = function(name, callback, context) {
|
|
129
|
-
if (!this._events) return this;
|
|
130
|
-
this._events = eventsApi(offApi, this._events, name, callback, {
|
|
131
|
-
context: context,
|
|
132
|
-
listeners: this._listeners
|
|
133
|
-
});
|
|
134
|
-
|
|
135
|
-
return this;
|
|
136
|
-
};
|
|
137
|
-
|
|
138
|
-
// Tell this object to stop listening to either specific events ... or
|
|
139
|
-
// to every object it's currently listening to.
|
|
140
|
-
Events.stopListening = function(obj, name, callback) {
|
|
141
|
-
const listeningTo = this._listeningTo;
|
|
142
|
-
if (!listeningTo) return this;
|
|
143
|
-
|
|
144
|
-
const ids = obj ? [obj._listenId] : keys(listeningTo);
|
|
145
|
-
for (let i = 0; i < ids.length; i++) {
|
|
146
|
-
const listening = listeningTo[ids[i]];
|
|
147
|
-
|
|
148
|
-
// If listening doesn't exist, this object is not currently
|
|
149
|
-
// listening to obj. Break out early.
|
|
150
|
-
if (!listening) break;
|
|
151
|
-
|
|
152
|
-
listening.obj.off(name, callback, this);
|
|
153
|
-
if (listening.interop) listening.off(name, callback);
|
|
154
|
-
}
|
|
155
|
-
if (isEmpty(listeningTo)) this._listeningTo = undefined;
|
|
156
|
-
|
|
157
|
-
return this;
|
|
158
|
-
};
|
|
159
|
-
|
|
160
|
-
// The reducing API that removes a callback from the `events` object.
|
|
161
|
-
const offApi = function(events, name, callback, options) {
|
|
162
|
-
if (!events) return;
|
|
163
|
-
|
|
164
|
-
const context = options.context, listeners = options.listeners;
|
|
165
|
-
let i = 0, names;
|
|
166
|
-
|
|
167
|
-
// Delete all event listeners and "drop" events.
|
|
168
|
-
if (!name && !context && !callback) {
|
|
169
|
-
for (names = keys(listeners); i < names.length; i++) {
|
|
170
|
-
listeners[names[i]].cleanup();
|
|
171
|
-
}
|
|
172
|
-
return;
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
names = name ? [name] : keys(events);
|
|
176
|
-
for (; i < names.length; i++) {
|
|
177
|
-
name = names[i];
|
|
178
|
-
const handlers = events[name];
|
|
179
|
-
|
|
180
|
-
// Bail out if there are no events stored.
|
|
181
|
-
if (!handlers) {
|
|
182
|
-
break;
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
// Find any remaining events.
|
|
186
|
-
const remaining = [];
|
|
187
|
-
for (let j = 0; j < handlers.length; j++) {
|
|
188
|
-
const handler = handlers[j];
|
|
189
|
-
if (
|
|
190
|
-
callback && callback !== handler.callback &&
|
|
191
|
-
callback !== handler.callback._callback ||
|
|
192
|
-
context && context !== handler.context
|
|
193
|
-
) {
|
|
194
|
-
remaining.push(handler);
|
|
195
|
-
} else {
|
|
196
|
-
const listening = handler.listening;
|
|
197
|
-
if (listening) listening.off(name, callback);
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
// Replace events if there are any remaining. Otherwise, clean up.
|
|
202
|
-
if (remaining.length) {
|
|
203
|
-
events[name] = remaining;
|
|
204
|
-
} else {
|
|
205
|
-
delete events[name];
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
return events;
|
|
210
|
-
};
|
|
211
|
-
|
|
212
|
-
// Bind an event to only be triggered a single time. After the first time
|
|
213
|
-
// the callback is invoked, its listener will be removed. If multiple events
|
|
214
|
-
// are passed in using the space-separated syntax, the handler will fire
|
|
215
|
-
// once for each event, not once for a combination of all events.
|
|
216
|
-
Events.once = function(name, callback, context) {
|
|
217
|
-
// Map the event into a `{event: once}` object.
|
|
218
|
-
const events = eventsApi(onceMap, {}, name, callback, this.off.bind(this));
|
|
219
|
-
if (typeof name === 'string' && (context === null || context === undefined)) callback = undefined;
|
|
220
|
-
return this.on(events, callback, context);
|
|
221
|
-
};
|
|
222
|
-
|
|
223
|
-
// Inversion-of-control versions of `once`.
|
|
224
|
-
Events.listenToOnce = function(obj, name, callback) {
|
|
225
|
-
// Map the event into a `{event: once}` object.
|
|
226
|
-
const events = eventsApi(onceMap, {}, name, callback, this.stopListening.bind(this, obj));
|
|
227
|
-
return this.listenTo(obj, events);
|
|
228
|
-
};
|
|
229
|
-
|
|
230
|
-
// Reduces the event callbacks into a map of `{event: onceWrapper}`.
|
|
231
|
-
// `offer` unbinds the `onceWrapper` after it has been called.
|
|
232
|
-
const onceMap = function(map, name, callback, offer) {
|
|
233
|
-
if (callback) {
|
|
234
|
-
const _once = map[name] = once(function() {
|
|
235
|
-
offer(name, _once);
|
|
236
|
-
callback.apply(this, arguments);
|
|
237
|
-
});
|
|
238
|
-
_once._callback = callback;
|
|
239
|
-
}
|
|
240
|
-
return map;
|
|
241
|
-
};
|
|
242
|
-
|
|
243
|
-
// Trigger one or many events, firing all bound callbacks. Callbacks are
|
|
244
|
-
// passed the same arguments as `trigger` is, apart from the event name
|
|
245
|
-
// (unless you're listening on `"all"`, which will cause your callback to
|
|
246
|
-
// receive the true name of the event as the first argument).
|
|
247
|
-
Events.trigger = function(name) {
|
|
248
|
-
if (!this._events) return this;
|
|
249
|
-
|
|
250
|
-
const length = Math.max(0, arguments.length - 1);
|
|
251
|
-
const args = Array(length);
|
|
252
|
-
for (let i = 0; i < length; i++) args[i] = arguments[i + 1];
|
|
253
|
-
|
|
254
|
-
eventsApi(triggerApi, this._events, name, undefined, args);
|
|
255
|
-
return this;
|
|
256
|
-
};
|
|
257
|
-
|
|
258
|
-
// Handles triggering the appropriate event callbacks.
|
|
259
|
-
const triggerApi = function(objEvents, name, callback, args) {
|
|
260
|
-
if (objEvents) {
|
|
261
|
-
const events = objEvents[name];
|
|
262
|
-
let allEvents = objEvents.all;
|
|
263
|
-
if (events && allEvents) allEvents = allEvents.slice();
|
|
264
|
-
if (events) triggerEvents(events, args);
|
|
265
|
-
if (allEvents) triggerEvents(allEvents, [name].concat(args));
|
|
266
|
-
}
|
|
267
|
-
return objEvents;
|
|
268
|
-
};
|
|
269
|
-
|
|
270
|
-
// A difficult-to-believe, but optimized internal dispatch function for
|
|
271
|
-
// triggering events. Tries to keep the usual cases speedy (most internal
|
|
272
|
-
// Backbone events have 3 arguments).
|
|
273
|
-
const triggerEvents = function(events, args) {
|
|
274
|
-
let ev, i = -1;
|
|
275
|
-
const l = events.length,
|
|
276
|
-
a1 = args[0],
|
|
277
|
-
a2 = args[1],
|
|
278
|
-
a3 = args[2];
|
|
279
|
-
switch (args.length) {
|
|
280
|
-
case 0: while (++i < l) (ev = events[i]).callback.call(ev.ctx); return;
|
|
281
|
-
case 1: while (++i < l) (ev = events[i]).callback.call(ev.ctx, a1); return;
|
|
282
|
-
case 2: while (++i < l) (ev = events[i]).callback.call(ev.ctx, a1, a2); return;
|
|
283
|
-
case 3: while (++i < l) (ev = events[i]).callback.call(ev.ctx, a1, a2, a3); return;
|
|
284
|
-
default: while (++i < l) (ev = events[i]).callback.apply(ev.ctx, args); return;
|
|
285
|
-
}
|
|
286
|
-
};
|
|
287
|
-
|
|
288
|
-
// A listening class that tracks and cleans up memory bindings
|
|
289
|
-
// when all callbacks have been offed.
|
|
290
|
-
const Listening = function(listener, obj) {
|
|
291
|
-
this.id = listener._listenId;
|
|
292
|
-
this.listener = listener;
|
|
293
|
-
this.obj = obj;
|
|
294
|
-
this.interop = true;
|
|
295
|
-
this.count = 0;
|
|
296
|
-
this._events = undefined;
|
|
297
|
-
};
|
|
298
|
-
|
|
299
|
-
Listening.prototype.on = Events.on;
|
|
300
|
-
|
|
301
|
-
// Offs a callback (or several).
|
|
302
|
-
// Uses an optimized counter if the listenee uses Backbone.Events.
|
|
303
|
-
// Otherwise, falls back to manual tracking to support events
|
|
304
|
-
// library interop.
|
|
305
|
-
Listening.prototype.off = function(name, callback) {
|
|
306
|
-
let cleanup;
|
|
307
|
-
if (this.interop) {
|
|
308
|
-
this._events = eventsApi(offApi, this._events, name, callback, {
|
|
309
|
-
context: undefined,
|
|
310
|
-
listeners: undefined
|
|
311
|
-
});
|
|
312
|
-
cleanup = !this._events;
|
|
313
|
-
} else {
|
|
314
|
-
this.count--;
|
|
315
|
-
cleanup = this.count === 0;
|
|
316
|
-
}
|
|
317
|
-
if (cleanup) this.cleanup();
|
|
318
|
-
};
|
|
319
|
-
|
|
320
|
-
// Cleans up memory bindings between the listener and the listenee.
|
|
321
|
-
Listening.prototype.cleanup = function() {
|
|
322
|
-
delete this.listener._listeningTo[this.obj._listenId];
|
|
323
|
-
if (!this.interop) delete this.obj._listeners[this.id];
|
|
324
|
-
};
|
|
325
|
-
|
|
326
|
-
// Aliases for backwards compatibility.
|
|
327
|
-
Events.bind = Events.on;
|
|
328
|
-
Events.unbind = Events.off;
|