@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
package/src/history.js
DELETED
|
@@ -1,301 +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
|
-
import extend from 'lodash-es/extend.js';
|
|
6
|
-
import some from 'lodash-es/some.js';
|
|
7
|
-
import { Events } from './events.js';
|
|
8
|
-
import { inherits } from './helpers.js';
|
|
9
|
-
|
|
10
|
-
// History
|
|
11
|
-
// -------
|
|
12
|
-
|
|
13
|
-
// Handles cross-browser history management, based on either
|
|
14
|
-
// [pushState](http://diveintohtml5.info/history.html) and real URLs, or
|
|
15
|
-
// [onhashchange](https://developer.mozilla.org/en-US/docs/DOM/window.onhashchange)
|
|
16
|
-
// and URL fragments. If the browser supports neither (old IE, natch),
|
|
17
|
-
// falls back to polling.
|
|
18
|
-
const History = function() {
|
|
19
|
-
this.handlers = [];
|
|
20
|
-
this.checkUrl = this.checkUrl.bind(this);
|
|
21
|
-
|
|
22
|
-
// Ensure that `History` can be used outside of the browser.
|
|
23
|
-
if (typeof window !== 'undefined') {
|
|
24
|
-
this.location = window.location;
|
|
25
|
-
this.history = window.history;
|
|
26
|
-
}
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
History.extend = inherits;
|
|
30
|
-
|
|
31
|
-
// Cached regex for stripping a leading hash/slash and trailing space.
|
|
32
|
-
const routeStripper = /^[#\/]|\s+$/g;
|
|
33
|
-
// Cached regex for stripping leading and trailing slashes.
|
|
34
|
-
const rootStripper = /^\/+|\/+$/g;
|
|
35
|
-
// Cached regex for stripping urls of hash.
|
|
36
|
-
const pathStripper = /#.*$/;
|
|
37
|
-
|
|
38
|
-
// Has the history handling already been started?
|
|
39
|
-
History.started = false;
|
|
40
|
-
|
|
41
|
-
// Set up all inheritable **History** properties and methods.
|
|
42
|
-
Object.assign(History.prototype, Events, {
|
|
43
|
-
|
|
44
|
-
// The default interval to poll for hash changes, if necessary, is
|
|
45
|
-
// twenty times a second.
|
|
46
|
-
interval: 50,
|
|
47
|
-
|
|
48
|
-
// Are we at the app root?
|
|
49
|
-
atRoot: function() {
|
|
50
|
-
const path = this.location.pathname.replace(/[^\/]$/, '$&/');
|
|
51
|
-
return path === this.root && !this.getSearch();
|
|
52
|
-
},
|
|
53
|
-
|
|
54
|
-
// Does the pathname match the root?
|
|
55
|
-
matchRoot: function() {
|
|
56
|
-
const path = this.decodeFragment(this.location.pathname);
|
|
57
|
-
const rootPath = path.slice(0, this.root.length - 1) + '/';
|
|
58
|
-
return rootPath === this.root;
|
|
59
|
-
},
|
|
60
|
-
|
|
61
|
-
// Unicode characters in `location.pathname` are percent encoded so they're
|
|
62
|
-
// decoded for comparison. `%25` should not be decoded since it may be part
|
|
63
|
-
// of an encoded parameter.
|
|
64
|
-
decodeFragment: function(fragment) {
|
|
65
|
-
return decodeURI(fragment.replace(/%25/g, '%2525'));
|
|
66
|
-
},
|
|
67
|
-
|
|
68
|
-
// In IE6, the hash fragment and search params are incorrect if the
|
|
69
|
-
// fragment contains `?`.
|
|
70
|
-
getSearch: function() {
|
|
71
|
-
const match = this.location.href.replace(/#.*/, '').match(/\?.+/);
|
|
72
|
-
return match ? match[0] : '';
|
|
73
|
-
},
|
|
74
|
-
|
|
75
|
-
// Gets the true hash value. Cannot use location.hash directly due to bug
|
|
76
|
-
// in Firefox where location.hash will always be decoded.
|
|
77
|
-
getHash: function(window) {
|
|
78
|
-
const match = (window || this).location.href.match(/#(.*)$/);
|
|
79
|
-
return match ? match[1] : '';
|
|
80
|
-
},
|
|
81
|
-
|
|
82
|
-
// Get the pathname and search params, without the root.
|
|
83
|
-
getPath: function() {
|
|
84
|
-
const path = this.decodeFragment(
|
|
85
|
-
this.location.pathname + this.getSearch()
|
|
86
|
-
).slice(this.root.length - 1);
|
|
87
|
-
return path.charAt(0) === '/' ? path.slice(1) : path;
|
|
88
|
-
},
|
|
89
|
-
|
|
90
|
-
// Get the cross-browser normalized URL fragment from the path or hash.
|
|
91
|
-
getFragment: function(fragment) {
|
|
92
|
-
if (fragment == null) {
|
|
93
|
-
if (this._usePushState || !this._wantsHashChange) {
|
|
94
|
-
fragment = this.getPath();
|
|
95
|
-
} else {
|
|
96
|
-
fragment = this.getHash();
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
return fragment.replace(routeStripper, '');
|
|
100
|
-
},
|
|
101
|
-
|
|
102
|
-
// Start the hash change handling, returning `true` if the current URL matches
|
|
103
|
-
// an existing route, and `false` otherwise.
|
|
104
|
-
start: function(options) {
|
|
105
|
-
if (History.started) throw new Error('history has already been started');
|
|
106
|
-
History.started = true;
|
|
107
|
-
|
|
108
|
-
// Figure out the initial configuration. Do we need an iframe?
|
|
109
|
-
// Is pushState desired ... is it available?
|
|
110
|
-
this.options = extend({root: '/'}, this.options, options);
|
|
111
|
-
this.root = this.options.root;
|
|
112
|
-
this._wantsHashChange = this.options.hashChange !== false;
|
|
113
|
-
this._hasHashChange = 'onhashchange' in window && (document.documentMode === undefined|| document.documentMode > 7);
|
|
114
|
-
this._useHashChange = this._wantsHashChange && this._hasHashChange;
|
|
115
|
-
this._wantsPushState = !!this.options.pushState;
|
|
116
|
-
this._hasPushState = !!(this.history && this.history.pushState);
|
|
117
|
-
this._usePushState = this._wantsPushState && this._hasPushState;
|
|
118
|
-
this.fragment = this.getFragment();
|
|
119
|
-
|
|
120
|
-
// Normalize root to always include a leading and trailing slash.
|
|
121
|
-
this.root = ('/' + this.root + '/').replace(rootStripper, '/');
|
|
122
|
-
|
|
123
|
-
// Transition from hashChange to pushState or vice versa if both are
|
|
124
|
-
// requested.
|
|
125
|
-
if (this._wantsHashChange && this._wantsPushState) {
|
|
126
|
-
|
|
127
|
-
// If we've started off with a route from a `pushState`-enabled
|
|
128
|
-
// browser, but we're currently in a browser that doesn't support it...
|
|
129
|
-
if (!this._hasPushState && !this.atRoot()) {
|
|
130
|
-
const rootPath = this.root.slice(0, -1) || '/';
|
|
131
|
-
this.location.replace(rootPath + '#' + this.getPath());
|
|
132
|
-
// Return immediately as browser will do redirect to new url
|
|
133
|
-
return true;
|
|
134
|
-
|
|
135
|
-
// Or if we've started out with a hash-based route, but we're currently
|
|
136
|
-
// in a browser where it could be `pushState`-based instead...
|
|
137
|
-
} else if (this._hasPushState && this.atRoot()) {
|
|
138
|
-
this.navigate(this.getHash(), {replace: true});
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
// Proxy an iframe to handle location events if the browser doesn't
|
|
144
|
-
// support the `hashchange` event, HTML5 history, or the user wants
|
|
145
|
-
// `hashChange` but not `pushState`.
|
|
146
|
-
if (!this._hasHashChange && this._wantsHashChange && !this._usePushState) {
|
|
147
|
-
this.iframe = document.createElement('iframe');
|
|
148
|
-
this.iframe.src = 'javascript:0';
|
|
149
|
-
this.iframe.style.display = 'none';
|
|
150
|
-
this.iframe.tabIndex = -1;
|
|
151
|
-
const body = document.body;
|
|
152
|
-
// Using `appendChild` will throw on IE < 9 if the document is not ready.
|
|
153
|
-
const iWindow = body.insertBefore(this.iframe, body.firstChild).contentWindow;
|
|
154
|
-
iWindow.document.open();
|
|
155
|
-
iWindow.document.close();
|
|
156
|
-
iWindow.location.hash = '#' + this.fragment;
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
// Depending on whether we're using pushState or hashes, and whether
|
|
160
|
-
// 'onhashchange' is supported, determine how we check the URL state.
|
|
161
|
-
if (this._usePushState) {
|
|
162
|
-
addEventListener('popstate', this.checkUrl, false);
|
|
163
|
-
} else if (this._useHashChange && !this.iframe) {
|
|
164
|
-
addEventListener('hashchange', this.checkUrl, false);
|
|
165
|
-
} else if (this._wantsHashChange) {
|
|
166
|
-
this._checkUrlInterval = setInterval(this.checkUrl, this.interval);
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
if (!this.options.silent) return this.loadUrl();
|
|
170
|
-
},
|
|
171
|
-
|
|
172
|
-
// Disable history, perhaps temporarily. Not useful in a real app,
|
|
173
|
-
// but possibly useful for unit testing Routers.
|
|
174
|
-
stop: function() {
|
|
175
|
-
// Remove window listeners.
|
|
176
|
-
if (this._usePushState) {
|
|
177
|
-
removeEventListener('popstate', this.checkUrl, false);
|
|
178
|
-
} else if (this._useHashChange && !this.iframe) {
|
|
179
|
-
removeEventListener('hashchange', this.checkUrl, false);
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
// Clean up the iframe if necessary.
|
|
183
|
-
if (this.iframe) {
|
|
184
|
-
document.body.removeChild(this.iframe);
|
|
185
|
-
this.iframe = null;
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
// Some environments will throw when clearing an undefined interval.
|
|
189
|
-
if (this._checkUrlInterval) clearInterval(this._checkUrlInterval);
|
|
190
|
-
History.started = false;
|
|
191
|
-
},
|
|
192
|
-
|
|
193
|
-
// Add a route to be tested when the fragment changes. Routes added later
|
|
194
|
-
// may override previous routes.
|
|
195
|
-
route: function(route, callback) {
|
|
196
|
-
this.handlers.unshift({route: route, callback: callback});
|
|
197
|
-
},
|
|
198
|
-
|
|
199
|
-
// Checks the current URL to see if it has changed, and if it has,
|
|
200
|
-
// calls `loadUrl`, normalizing across the hidden iframe.
|
|
201
|
-
checkUrl: function(e) {
|
|
202
|
-
let current = this.getFragment();
|
|
203
|
-
|
|
204
|
-
// If the user pressed the back button, the iframe's hash will have
|
|
205
|
-
// changed and we should use that for comparison.
|
|
206
|
-
if (current === this.fragment && this.iframe) {
|
|
207
|
-
current = this.getHash(this.iframe.contentWindow);
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
if (current === this.fragment) return false;
|
|
211
|
-
if (this.iframe) this.navigate(current);
|
|
212
|
-
this.loadUrl();
|
|
213
|
-
},
|
|
214
|
-
|
|
215
|
-
// Attempt to load the current URL fragment. If a route succeeds with a
|
|
216
|
-
// match, returns `true`. If no defined routes matches the fragment,
|
|
217
|
-
// returns `false`.
|
|
218
|
-
loadUrl: function(fragment) {
|
|
219
|
-
// If the root doesn't match, no routes can match either.
|
|
220
|
-
if (!this.matchRoot()) return false;
|
|
221
|
-
fragment = this.fragment = this.getFragment(fragment);
|
|
222
|
-
return some(this.handlers, function(handler) {
|
|
223
|
-
if (handler.route.test(fragment)) {
|
|
224
|
-
handler.callback(fragment);
|
|
225
|
-
return true;
|
|
226
|
-
}
|
|
227
|
-
});
|
|
228
|
-
},
|
|
229
|
-
|
|
230
|
-
// Save a fragment into the hash history, or replace the URL state if the
|
|
231
|
-
// 'replace' option is passed. You are responsible for properly URL-encoding
|
|
232
|
-
// the fragment in advance.
|
|
233
|
-
//
|
|
234
|
-
// The options object can contain `trigger: true` if you wish to have the
|
|
235
|
-
// route callback be fired (not usually desirable), or `replace: true`, if
|
|
236
|
-
// you wish to modify the current URL without adding an entry to the history.
|
|
237
|
-
navigate: function(fragment, options) {
|
|
238
|
-
if (!History.started) return false;
|
|
239
|
-
if (!options || options === true) options = {trigger: !!options};
|
|
240
|
-
|
|
241
|
-
// Normalize the fragment.
|
|
242
|
-
fragment = this.getFragment(fragment || '');
|
|
243
|
-
|
|
244
|
-
// Don't include a trailing slash on the root.
|
|
245
|
-
let rootPath = this.root;
|
|
246
|
-
if (fragment === '' || fragment.charAt(0) === '?') {
|
|
247
|
-
rootPath = rootPath.slice(0, -1) || '/';
|
|
248
|
-
}
|
|
249
|
-
const url = rootPath + fragment;
|
|
250
|
-
|
|
251
|
-
// Strip the fragment of the query and hash for matching.
|
|
252
|
-
fragment = fragment.replace(pathStripper, '');
|
|
253
|
-
|
|
254
|
-
// Decode for matching.
|
|
255
|
-
const decodedFragment = this.decodeFragment(fragment);
|
|
256
|
-
|
|
257
|
-
if (this.fragment === decodedFragment) return;
|
|
258
|
-
this.fragment = decodedFragment;
|
|
259
|
-
|
|
260
|
-
// If pushState is available, we use it to set the fragment as a real URL.
|
|
261
|
-
if (this._usePushState) {
|
|
262
|
-
this.history[options.replace ? 'replaceState' : 'pushState']({}, document.title, url);
|
|
263
|
-
|
|
264
|
-
// If hash changes haven't been explicitly disabled, update the hash
|
|
265
|
-
// fragment to store history.
|
|
266
|
-
} else if (this._wantsHashChange) {
|
|
267
|
-
this._updateHash(this.location, fragment, options.replace);
|
|
268
|
-
if (this.iframe && fragment !== this.getHash(this.iframe.contentWindow)) {
|
|
269
|
-
const iWindow = this.iframe.contentWindow;
|
|
270
|
-
|
|
271
|
-
// Opening and closing the iframe tricks IE7 and earlier to push a
|
|
272
|
-
// history entry on hash-tag change. When replace is true, we don't
|
|
273
|
-
// want this.
|
|
274
|
-
if (!options.replace) {
|
|
275
|
-
iWindow.document.open();
|
|
276
|
-
iWindow.document.close();
|
|
277
|
-
}
|
|
278
|
-
this._updateHash(iWindow.location, fragment, options.replace);
|
|
279
|
-
}
|
|
280
|
-
// If you've told us that you explicitly don't want fallback hashchange-
|
|
281
|
-
// based history, then `navigate` becomes a page refresh.
|
|
282
|
-
} else {
|
|
283
|
-
return this.location.assign(url);
|
|
284
|
-
}
|
|
285
|
-
if (options.trigger) return this.loadUrl(fragment);
|
|
286
|
-
},
|
|
287
|
-
|
|
288
|
-
// Update the hash location, either replacing the current entry, or adding
|
|
289
|
-
// a new one to the browser history.
|
|
290
|
-
_updateHash: function(location, fragment, replace) {
|
|
291
|
-
if (replace) {
|
|
292
|
-
const href = location.href.replace(/(javascript:|#).*$/, '');
|
|
293
|
-
location.replace(href + '#' + fragment);
|
|
294
|
-
} else {
|
|
295
|
-
// Some browsers require that `hash` contains a leading #.
|
|
296
|
-
location.hash = '#' + fragment;
|
|
297
|
-
}
|
|
298
|
-
}
|
|
299
|
-
});
|
|
300
|
-
|
|
301
|
-
export default History;
|
package/src/overview.js
DELETED
|
@@ -1,191 +0,0 @@
|
|
|
1
|
-
/*!
|
|
2
|
-
* Copyright (c) JC Brand <jc@opkode.com>
|
|
3
|
-
*/
|
|
4
|
-
import debounce from 'lodash-es/debounce.js';
|
|
5
|
-
import difference from 'lodash-es/difference.js';
|
|
6
|
-
import drop from 'lodash-es/drop.js';
|
|
7
|
-
import every from 'lodash-es/every.js';
|
|
8
|
-
import extend from 'lodash-es/extend.js';
|
|
9
|
-
import filter from 'lodash-es/filter.js';
|
|
10
|
-
import find from 'lodash-es/find.js';
|
|
11
|
-
import first from 'lodash-es/first.js';
|
|
12
|
-
import forEach from 'lodash-es/forEach.js';
|
|
13
|
-
import get from 'lodash-es/get.js';
|
|
14
|
-
import head from 'lodash-es/head.js';
|
|
15
|
-
import includes from 'lodash-es/includes.js';
|
|
16
|
-
import indexOf from 'lodash-es/indexOf.js';
|
|
17
|
-
import initial from 'lodash-es/initial.js';
|
|
18
|
-
import invoke from 'lodash-es/invoke.js';
|
|
19
|
-
import isEmpty from 'lodash-es/isEmpty.js';
|
|
20
|
-
import last from 'lodash-es/last.js';
|
|
21
|
-
import lastIndexOf from 'lodash-es/lastIndexOf.js';
|
|
22
|
-
import map from 'lodash-es/map.js';
|
|
23
|
-
import max from 'lodash-es/max.js';
|
|
24
|
-
import min from 'lodash-es/min.js';
|
|
25
|
-
import reduce from 'lodash-es/reduce.js';
|
|
26
|
-
import reduceRight from 'lodash-es/reduceRight.js';
|
|
27
|
-
import reject from 'lodash-es/reject.js';
|
|
28
|
-
import rest from 'lodash-es/rest.js';
|
|
29
|
-
import sample from 'lodash-es/sample.js';
|
|
30
|
-
import shuffle from 'lodash-es/shuffle.js';
|
|
31
|
-
import size from 'lodash-es/size.js';
|
|
32
|
-
import some from 'lodash-es/some.js';
|
|
33
|
-
import sortBy from 'lodash-es/sortBy.js';
|
|
34
|
-
import tail from 'lodash-es/tail.js';
|
|
35
|
-
import take from 'lodash-es/take.js';
|
|
36
|
-
import toArray from 'lodash-es/toArray.js';
|
|
37
|
-
import without from 'lodash-es/without.js';
|
|
38
|
-
import { View } from "./view";
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
const Overview = function (options) {
|
|
42
|
-
/* An Overview is a View that contains and keeps track of sub-views.
|
|
43
|
-
* Kind of like what a Collection is to a Model.
|
|
44
|
-
*/
|
|
45
|
-
this.views = {};
|
|
46
|
-
this.keys = () => Object.keys(this.views);
|
|
47
|
-
this.getAll = () => this.views;
|
|
48
|
-
this.get = id => this.views[id];
|
|
49
|
-
|
|
50
|
-
/* Exclusive get. Returns all instances except the given id. */
|
|
51
|
-
this.xget = id => {
|
|
52
|
-
return this.keys()
|
|
53
|
-
.filter(k => (k !== id))
|
|
54
|
-
.reduce((acc, k) => {
|
|
55
|
-
acc[k] = this.views[k]
|
|
56
|
-
return acc;
|
|
57
|
-
}, {});
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
this.add = (id, view) => {
|
|
61
|
-
this.views[id] = view;
|
|
62
|
-
return view;
|
|
63
|
-
};
|
|
64
|
-
|
|
65
|
-
this.remove = id => {
|
|
66
|
-
if (typeof id === "undefined") {
|
|
67
|
-
new View().remove.apply(this);
|
|
68
|
-
}
|
|
69
|
-
const view = this.views[id];
|
|
70
|
-
if (view) {
|
|
71
|
-
delete this.views[id];
|
|
72
|
-
view.remove();
|
|
73
|
-
return view;
|
|
74
|
-
}
|
|
75
|
-
};
|
|
76
|
-
|
|
77
|
-
this.removeAll = () => {
|
|
78
|
-
this.keys().forEach(id => this.remove(id));
|
|
79
|
-
return this;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
View.apply(this, Array.prototype.slice.apply(arguments));
|
|
83
|
-
};
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
const methods = {
|
|
87
|
-
includes, difference, drop,
|
|
88
|
-
every, filter, find,
|
|
89
|
-
first, forEach, head,
|
|
90
|
-
indexOf, initial, invoke, isEmpty,
|
|
91
|
-
last, lastIndexOf, map, max, min, reduce,
|
|
92
|
-
reduceRight, reject, rest, sample,
|
|
93
|
-
shuffle, size, some, sortBy, tail, take,
|
|
94
|
-
toArray, without
|
|
95
|
-
}
|
|
96
|
-
Object.keys(methods).forEach(name => {
|
|
97
|
-
Overview.prototype[name] = function() {
|
|
98
|
-
const args = Array.prototype.slice.call(arguments);
|
|
99
|
-
args.unshift(this.views);
|
|
100
|
-
return methods[name].apply(this, args);
|
|
101
|
-
};
|
|
102
|
-
});
|
|
103
|
-
|
|
104
|
-
Object.assign(Overview.prototype, View.prototype);
|
|
105
|
-
Overview.extend = View.extend;
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
const OrderedListView = Overview.extend({
|
|
109
|
-
/* An OrderedListView is a special type of Overview which adds some
|
|
110
|
-
* methods and conventions for rendering an ordered list of elements.
|
|
111
|
-
*/
|
|
112
|
-
// The `listItems` attribute denotes the path (from this View) to the
|
|
113
|
-
// list of items.
|
|
114
|
-
listItems: 'model',
|
|
115
|
-
// The `sortEvent` attribute specifies the event which should cause the
|
|
116
|
-
// ordered list to be sorted.
|
|
117
|
-
sortEvent: 'change',
|
|
118
|
-
// If false, we debounce sorting and inserting the new item
|
|
119
|
-
// (for improved performance when a large amount of items get added all at once)
|
|
120
|
-
// Otherwise we immediately sort the items and insert the new item.
|
|
121
|
-
sortImmediatelyOnAdd: false,
|
|
122
|
-
// The `listSelector` is the selector used to query for the DOM list
|
|
123
|
-
// element which contains the ordered items.
|
|
124
|
-
listSelector: '.ordered-items',
|
|
125
|
-
// The `itemView` is constructor which should be called to create a
|
|
126
|
-
// View for a new item.
|
|
127
|
-
ItemView: undefined,
|
|
128
|
-
// The `subviewIndex` is the attribute of the list element model which
|
|
129
|
-
// acts as the index of the subview in the overview.
|
|
130
|
-
// An overview is a "Collection" of views, and they can be retrieved
|
|
131
|
-
// via an index. By default this is the 'id' attribute, but it could be
|
|
132
|
-
// set to something else.
|
|
133
|
-
subviewIndex: 'id',
|
|
134
|
-
|
|
135
|
-
initialize () {
|
|
136
|
-
this.sortEventually = debounce(() => this.sortAndPositionAllItems(), 100);
|
|
137
|
-
this.items = get(this, this.listItems);
|
|
138
|
-
this.items.on('remove', this.removeView, this);
|
|
139
|
-
this.items.on('reset', this.removeAll, this);
|
|
140
|
-
|
|
141
|
-
this.items.on('add', (a, b) => {
|
|
142
|
-
if (this.sortImmediatelyOnAdd) {
|
|
143
|
-
this.sortAndPositionAllItems();
|
|
144
|
-
} else {
|
|
145
|
-
this.sortEventually();
|
|
146
|
-
}
|
|
147
|
-
});
|
|
148
|
-
|
|
149
|
-
if (this.sortEvent) {
|
|
150
|
-
this.items.on(this.sortEvent, this.sortEventually, this);
|
|
151
|
-
}
|
|
152
|
-
},
|
|
153
|
-
|
|
154
|
-
createItemView (item) {
|
|
155
|
-
let item_view = this.get(item.get(this.subviewIndex));
|
|
156
|
-
if (!item_view) {
|
|
157
|
-
item_view = new this.ItemView({model: item});
|
|
158
|
-
this.add(item.get(this.subviewIndex), item_view);
|
|
159
|
-
} else {
|
|
160
|
-
item_view.model = item;
|
|
161
|
-
item_view.initialize();
|
|
162
|
-
}
|
|
163
|
-
item_view.render();
|
|
164
|
-
return item_view;
|
|
165
|
-
},
|
|
166
|
-
|
|
167
|
-
removeView (item) {
|
|
168
|
-
this.remove(item.get(this.subviewIndex));
|
|
169
|
-
},
|
|
170
|
-
|
|
171
|
-
sortAndPositionAllItems () {
|
|
172
|
-
if (!this.items.length) {
|
|
173
|
-
return;
|
|
174
|
-
}
|
|
175
|
-
this.items.sort();
|
|
176
|
-
|
|
177
|
-
const list_el = this.el.querySelector(this.listSelector);
|
|
178
|
-
const div = document.createElement('div');
|
|
179
|
-
list_el.parentNode.replaceChild(div, list_el);
|
|
180
|
-
this.items.forEach(item => {
|
|
181
|
-
let view = this.get(item.get(this.subviewIndex));
|
|
182
|
-
if (!view) {
|
|
183
|
-
view = this.createItemView(item)
|
|
184
|
-
}
|
|
185
|
-
list_el.insertAdjacentElement('beforeend', view.el);
|
|
186
|
-
});
|
|
187
|
-
div.parentNode.replaceChild(list_el, div);
|
|
188
|
-
}
|
|
189
|
-
});
|
|
190
|
-
|
|
191
|
-
export { OrderedListView, Overview }
|
package/src/router.js
DELETED
|
@@ -1,119 +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
|
-
// Router
|
|
6
|
-
// ------
|
|
7
|
-
|
|
8
|
-
import History from './history.js';
|
|
9
|
-
import extend from 'lodash-es/extend.js';
|
|
10
|
-
import isFunction from 'lodash-es/isFunction.js';
|
|
11
|
-
import isRegExp from 'lodash-es/isRegExp.js';
|
|
12
|
-
import keys from 'lodash-es/keys.js';
|
|
13
|
-
import result from 'lodash-es/result.js';
|
|
14
|
-
import { Events } from './events.js';
|
|
15
|
-
import { inherits } from './helpers.js';
|
|
16
|
-
|
|
17
|
-
// Routers map faux-URLs to actions, and fire events when routes are
|
|
18
|
-
// matched. Creating a new one sets its `routes` hash, if not set statically.
|
|
19
|
-
export const Router = function(options={}) {
|
|
20
|
-
this.history = options.history || new History();
|
|
21
|
-
this.preinitialize.apply(this, arguments);
|
|
22
|
-
if (options.routes) this.routes = options.routes;
|
|
23
|
-
this._bindRoutes();
|
|
24
|
-
this.initialize.apply(this, arguments);
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
Router.extend = inherits;
|
|
28
|
-
|
|
29
|
-
// Cached regular expressions for matching named param parts and splatted
|
|
30
|
-
// parts of route strings.
|
|
31
|
-
const optionalParam = /\((.*?)\)/g;
|
|
32
|
-
const namedParam = /(\(\?)?:\w+/g;
|
|
33
|
-
const splatParam = /\*\w+/g;
|
|
34
|
-
const escapeRegExp = /[\-{}\[\]+?.,\\\^$|#\s]/g;
|
|
35
|
-
|
|
36
|
-
// Set up all inheritable **Router** properties and methods.
|
|
37
|
-
Object.assign(Router.prototype, Events, {
|
|
38
|
-
|
|
39
|
-
// preinitialize is an empty function by default. You can override it with a function
|
|
40
|
-
// or object. preinitialize will run before any instantiation logic is run in the Router.
|
|
41
|
-
preinitialize: function(){},
|
|
42
|
-
|
|
43
|
-
// Initialize is an empty function by default. Override it with your own
|
|
44
|
-
// initialization logic.
|
|
45
|
-
initialize: function(){},
|
|
46
|
-
|
|
47
|
-
// Manually bind a single named route to a callback. For example:
|
|
48
|
-
//
|
|
49
|
-
// this.route('search/:query/p:num', 'search', function(query, num) {
|
|
50
|
-
// ...
|
|
51
|
-
// });
|
|
52
|
-
//
|
|
53
|
-
route: function(route, name, callback) {
|
|
54
|
-
if (!isRegExp(route)) route = this._routeToRegExp(route);
|
|
55
|
-
if (isFunction(name)) {
|
|
56
|
-
callback = name;
|
|
57
|
-
name = '';
|
|
58
|
-
}
|
|
59
|
-
if (!callback) callback = this[name];
|
|
60
|
-
this.history.route(route, (fragment) => {
|
|
61
|
-
const args = this._extractParameters(route, fragment);
|
|
62
|
-
if (this.execute(callback, args, name) !== false) {
|
|
63
|
-
this.trigger.apply(this, ['route:' + name].concat(args));
|
|
64
|
-
this.trigger('route', name, args);
|
|
65
|
-
this.history.trigger('route', this, name, args);
|
|
66
|
-
}
|
|
67
|
-
});
|
|
68
|
-
return this;
|
|
69
|
-
},
|
|
70
|
-
|
|
71
|
-
// Execute a route handler with the provided parameters. This is an
|
|
72
|
-
// excellent place to do pre-route setup or post-route cleanup.
|
|
73
|
-
execute: function(callback, args, name) {
|
|
74
|
-
if (callback) callback.apply(this, args);
|
|
75
|
-
},
|
|
76
|
-
|
|
77
|
-
// Simple proxy to `history` to save a fragment into the history.
|
|
78
|
-
navigate: function(fragment, options) {
|
|
79
|
-
this.history.navigate(fragment, options);
|
|
80
|
-
return this;
|
|
81
|
-
},
|
|
82
|
-
|
|
83
|
-
// Bind all defined routes to `history`. We have to reverse the
|
|
84
|
-
// order of the routes here to support behavior where the most general
|
|
85
|
-
// routes can be defined at the bottom of the route map.
|
|
86
|
-
_bindRoutes: function() {
|
|
87
|
-
if (!this.routes) return;
|
|
88
|
-
this.routes = result(this, 'routes');
|
|
89
|
-
let route;
|
|
90
|
-
const routes = keys(this.routes);
|
|
91
|
-
while ((route = routes.pop()) != null) {
|
|
92
|
-
this.route(route, this.routes[route]);
|
|
93
|
-
}
|
|
94
|
-
},
|
|
95
|
-
|
|
96
|
-
// Convert a route string into a regular expression, suitable for matching
|
|
97
|
-
// against the current location hash.
|
|
98
|
-
_routeToRegExp: function(route) {
|
|
99
|
-
route = route.replace(escapeRegExp, '\\$&')
|
|
100
|
-
.replace(optionalParam, '(?:$1)?')
|
|
101
|
-
.replace(namedParam, function(match, optional) {
|
|
102
|
-
return optional ? match : '([^/?]+)';
|
|
103
|
-
})
|
|
104
|
-
.replace(splatParam, '([^?]*?)');
|
|
105
|
-
return new RegExp('^' + route + '(?:\\?([\\s\\S]*))?$');
|
|
106
|
-
},
|
|
107
|
-
|
|
108
|
-
// Given a route, and a URL fragment that it matches, return the array of
|
|
109
|
-
// extracted decoded parameters. Empty or unmatched parameters will be
|
|
110
|
-
// treated as `null` to normalize cross-browser behavior.
|
|
111
|
-
_extractParameters: function(route, fragment) {
|
|
112
|
-
const params = route.exec(fragment).slice(1);
|
|
113
|
-
return params.map(function(param, i) {
|
|
114
|
-
// Don't decode the search params.
|
|
115
|
-
if (i === params.length - 1) return param || null;
|
|
116
|
-
return param ? decodeURIComponent(param) : null;
|
|
117
|
-
});
|
|
118
|
-
}
|
|
119
|
-
});
|