@converse/skeletor 0.0.7 → 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.
Files changed (44) hide show
  1. package/README.md +38 -25
  2. package/dist/skeletor.js +10413 -6875
  3. package/dist/skeletor.js.map +1 -1
  4. package/dist/skeletor.min.js +74 -3
  5. package/dist/skeletor.min.js.map +1 -1
  6. package/package.json +23 -12
  7. package/src/collection.js +540 -395
  8. package/src/drivers/sessionStorage.js +226 -232
  9. package/src/element.js +132 -82
  10. package/src/eventemitter.js +187 -0
  11. package/src/helpers.js +148 -114
  12. package/src/{main.js → index.js} +21 -17
  13. package/src/listening.js +81 -0
  14. package/src/model.js +340 -240
  15. package/src/storage.js +223 -241
  16. package/src/types/collection.d.ts +366 -0
  17. package/src/types/collection.d.ts.map +1 -0
  18. package/src/types/drivers/sessionStorage.d.ts +37 -0
  19. package/src/types/drivers/sessionStorage.d.ts.map +1 -0
  20. package/src/types/element.d.ts +115 -0
  21. package/src/types/element.d.ts.map +1 -0
  22. package/src/types/eventemitter.d.ts +93 -0
  23. package/src/types/eventemitter.d.ts.map +1 -0
  24. package/src/types/helpers.d.ts +62 -0
  25. package/src/types/helpers.d.ts.map +1 -0
  26. package/src/types/index.d.ts +19 -0
  27. package/src/types/index.d.ts.map +1 -0
  28. package/src/types/listening.d.ts +40 -0
  29. package/src/types/listening.d.ts.map +1 -0
  30. package/src/types/model.d.ts +230 -0
  31. package/src/types/model.d.ts.map +1 -0
  32. package/src/types/storage.d.ts +35 -0
  33. package/src/types/storage.d.ts.map +1 -0
  34. package/src/types/utils/events.d.ts +28 -0
  35. package/src/types/utils/events.d.ts.map +1 -0
  36. package/src/utils/events.js +174 -0
  37. package/src/drivers/indexeddb-getitems.js~ +0 -1116
  38. package/src/drivers/indexeddb.js~ +0 -1115
  39. package/src/events.js +0 -328
  40. package/src/history.js +0 -301
  41. package/src/overview.js +0 -191
  42. package/src/router.js +0 -119
  43. package/src/storage.js~ +0 -269
  44. package/src/view.js +0 -267
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
- });
package/src/storage.js~ DELETED
@@ -1,269 +0,0 @@
1
- /**
2
- * IndexedDB, localStorage and sessionStorage adapter
3
- */
4
- import * as memoryDriver from 'localforage-driver-memory';
5
- import cloneDeep from 'lodash-es/cloneDeep.js';
6
- import isString from 'lodash-es/isString.js';
7
- import localForage from "localforage/src/localforage";
8
- import mergebounce from 'mergebounce';
9
- import sessionStorageWrapper from "./drivers/sessionStorage.js";
10
- import { extendPrototype as extendPrototypeWithSetItems } from 'localforage-setitems';
11
- import { extendPrototype as extendPrototypeWithGetItems } from 'localforage-getitems';
12
-
13
- const IN_MEMORY = memoryDriver._driver
14
- localForage.defineDriver(memoryDriver);
15
- extendPrototypeWithSetItems(localForage);
16
- extendPrototypeWithGetItems(localForage);
17
-
18
- function S4() {
19
- // Generate four random hex digits.
20
- return (((1+Math.random())*0x10000)|0).toString(16).substring(1);
21
- }
22
-
23
- function guid() {
24
- // Generate a pseudo-GUID by concatenating random hexadecimal.
25
- return (S4()+S4()+"-"+S4()+"-"+S4()+"-"+S4()+"-"+S4()+S4()+S4());
26
- }
27
-
28
-
29
- class Storage {
30
-
31
- constructor (id, type, batchedWrites=false) {
32
- if (type === 'local' && !window.localStorage ) {
33
- throw new Error("Skeletor.storage: Environment does not support localStorage.");
34
- } else if (type === 'session' && !window.sessionStorage ) {
35
- throw new Error("Skeletor.storage: Environment does not support sessionStorage.");
36
- }
37
- if (isString(type)) {
38
- this.storeInitialized = this.initStore(type, batchedWrites);
39
- } else {
40
- this.store = type;
41
- if (batchedWrites) {
42
- this.store.debouncedSetItems = mergebounce(
43
- items => this.store.setItems(items),
44
- 50,
45
- {'promise': true}
46
- );
47
- }
48
- this.storeInitialized = Promise.resolve();
49
- }
50
- this.name = id;
51
- }
52
-
53
- async initStore (type, batchedWrites) {
54
- if (type === 'session') {
55
- localForage.setDriver(sessionStorageWrapper._driver);
56
- } else if (type === 'local') {
57
- await localForage.config({'driver': localForage.LOCALSTORAGE});
58
- } else if (type === 'in_memory') {
59
- localForage.config({'driver': IN_MEMORY});
60
- } else if (type !== 'indexed') {
61
- throw new Error("Skeletor.storage: No storage type was specified");
62
- }
63
- this.store = localForage;
64
- if (batchedWrites) {
65
- this.store.debouncedSetItems = mergebounce(
66
- items => this.store.setItems(items),
67
- 50,
68
- {'promise': true}
69
- );
70
- }
71
- }
72
-
73
- flush () {
74
- return this.store.debouncedSetItems?.flush();
75
- }
76
-
77
- async clear () {
78
- await this.store.removeItem(this.name).catch(e => console.error(e));
79
- const re = new RegExp(`^${this.name}-`);
80
- const keys = await this.store.keys();
81
- const removed_keys = keys.filter(k => re.test(k));
82
- await Promise.all(removed_keys.map(k => this.store.removeItem(k).catch(e => console.error(e))));
83
- }
84
-
85
- sync () {
86
- const that = this;
87
-
88
- async function localSync (method, model, options) {
89
- let resp, errorMessage, promise, new_attributes;
90
-
91
- // We get the collection (and if necessary the model attribute.
92
- // Waiting for storeInitialized will cause another iteration of
93
- // the event loop, after which the collection reference will
94
- // be removed from the model.
95
- const collection = model.collection;
96
- if (['patch', 'update'].includes(method)) {
97
- new_attributes = cloneDeep(model.attributes);
98
- }
99
- await that.storeInitialized;
100
- try {
101
- const original_attributes = model.attributes;
102
- switch (method) {
103
- case "read":
104
- if (model.id !== undefined) {
105
- resp = await that.find(model);
106
- } else {
107
- resp = await that.findAll();
108
- }
109
- break;
110
- case "create":
111
- resp = await that.create(model, options);
112
- break;
113
- case 'patch':
114
- case "update":
115
- if (options.wait) {
116
- // When `wait` is set to true, Skeletor waits until
117
- // confirmation of storage before setting the values on
118
- // the model.
119
- // However, the new attributes needs to be sent, so it
120
- // sets them manually on the model and then removes
121
- // them after calling `sync`.
122
- // Because our `sync` method is asynchronous and we
123
- // wait for `storeInitialized`, the attributes are
124
- // already restored once we get here, so we need to do
125
- // the attributes dance again.
126
- model.attributes = new_attributes;
127
- }
128
- promise = that.update(model, options);
129
- if (options.wait) {
130
- model.attributes = original_attributes;
131
- }
132
- resp = await promise;
133
- break;
134
- case "delete":
135
- resp = await that.destroy(model, collection);
136
- break;
137
- }
138
- } catch (error) {
139
- if (error.code === 22 && that.getStorageSize() === 0) {
140
- errorMessage = "Private browsing is unsupported";
141
- } else {
142
- errorMessage = error.message;
143
- }
144
- }
145
-
146
- if (resp) {
147
- if (options && options.success) {
148
- // When storing, we don't pass back the response (which is
149
- // the set attributes returned from localforage because
150
- // Skeletor sets them again on the model and due to the async
151
- // nature of localforage it can cause stale attributes to be
152
- // set on a model after it's been updated in the meantime.
153
- const data = (method === "read") ? resp : null;
154
- options.success(data, options);
155
- }
156
- } else {
157
- errorMessage = errorMessage ? errorMessage : "Record Not Found";
158
- if (options && options.error) {
159
- options.error(errorMessage);
160
- }
161
- }
162
- }
163
- localSync.__name__ = 'localSync';
164
- return localSync;
165
- }
166
-
167
- removeCollectionReference (model, collection) {
168
- if (!collection) {
169
- return;
170
- }
171
- const ids = collection
172
- .filter(m => (m.id !== model.id))
173
- .map(m => this.getItemName(m.id));
174
-
175
- return this.store.setItem(this.name, ids);
176
- }
177
-
178
- addCollectionReference (model, collection) {
179
- if (!collection) {
180
- return;
181
- }
182
- const ids = collection.map(m => this.getItemName(m.id));
183
- const new_id = this.getItemName(model.id);
184
- if (!ids.includes(new_id)) {
185
- ids.push(new_id);
186
- }
187
- return this.store.setItem(this.name, ids);
188
- }
189
-
190
- getCollectionReferenceData (model) {
191
- if (!model.collection) {
192
- return {};
193
- }
194
- const ids = model.collection.map(m => this.getItemName(m.id));
195
- const new_id = this.getItemName(model.id);
196
- if (!ids.includes(new_id)) {
197
- ids.push(new_id);
198
- }
199
- const result = {};
200
- result[this.name] = ids;
201
- return result;
202
- }
203
-
204
- async save (model) {
205
- if (this.store.setItems) {
206
- const items = {}
207
- items[this.getItemName(model.id)] = model.toJSON();
208
- Object.assign(items, this.getCollectionReferenceData(model));
209
- return (this.store.debouncedSetItems) ?
210
- this.store.debouncedSetItems(items) :
211
- this.store.setItems(items);
212
- } else {
213
- const key = this.getItemName(model.id);
214
- const data = await this.store.setItem(key, model.toJSON());
215
- await this.addCollectionReference(model, model.collection);
216
- return data;
217
- }
218
- }
219
-
220
- create (model, options) {
221
- /* Add a model, giving it a (hopefully)-unique GUID, if it doesn't already
222
- * have an id of it's own.
223
- */
224
- if (!model.id) {
225
- model.id = guid();
226
- model.set(model.idAttribute, model.id, options);
227
- }
228
- return this.save(model);
229
- }
230
-
231
- update (model) {
232
- return this.save(model);
233
- }
234
-
235
- find (model) {
236
- return this.store.getItem(this.getItemName(model.id));
237
- }
238
-
239
- async findAll () {
240
- /* Return the array of all models currently in storage.
241
- */
242
- const keys = await this.store.getItem(this.name);
243
- if (keys?.length) {
244
- const items = await this.store.getItems(keys);
245
- debugger;
246
- return Object.values(items);
247
- }
248
- return [];
249
- }
250
-
251
- async destroy (model, collection) {
252
- await this.flush();
253
- await this.store.removeItem(this.getItemName(model.id));
254
- await this.removeCollectionReference(model, collection);
255
- return model;
256
- }
257
-
258
- getStorageSize () {
259
- return this.store.length;
260
- }
261
-
262
- getItemName (id) {
263
- return this.name+"-"+id;
264
- }
265
- }
266
-
267
- Storage.sessionStorageInitialized = localForage.defineDriver(sessionStorageWrapper);
268
- Storage.localForage = localForage;
269
- export default Storage;