@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/helpers.js CHANGED
@@ -11,6 +11,15 @@ import result from 'lodash-es/result.js';
11
11
  */
12
12
  export class NotImplementedError extends Error {}
13
13
 
14
+ function S4() {
15
+ // Generate four random hex digits.
16
+ return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1);
17
+ }
18
+
19
+ export function guid() {
20
+ // Generate a pseudo-GUID by concatenating random hexadecimal.
21
+ return S4() + S4() + '-' + S4() + '-' + S4() + '-' + S4() + '-' + S4() + S4() + S4();
22
+ }
14
23
 
15
24
  // Helpers
16
25
  // -------
@@ -20,139 +29,164 @@ export class NotImplementedError extends Error {}
20
29
  // class properties to be extended.
21
30
  //
22
31
  export function inherits(protoProps, staticProps) {
23
- const parent = this;
24
- let child;
25
-
26
- // The constructor function for the new subclass is either defined by you
27
- // (the "constructor" property in your `extend` definition), or defaulted
28
- // by us to simply call the parent constructor.
29
- if (protoProps && has(protoProps, 'constructor')) {
30
- child = protoProps.constructor;
31
- } else {
32
- child = function(){ return parent.apply(this, arguments); };
33
- }
34
-
35
- // Add static properties to the constructor function, if supplied.
36
- extend(child, parent, staticProps);
37
-
38
- // Set the prototype chain to inherit from `parent`, without calling
39
- // `parent`'s constructor function and add the prototype properties.
40
- child.prototype = create(parent.prototype, protoProps);
41
- child.prototype.constructor = child;
42
-
43
- // Set a convenience property in case the parent's prototype is needed
44
- // later.
45
- child.__super__ = parent.prototype;
46
-
47
- return child;
48
- }
32
+ // eslint-disable-next-line @typescript-eslint/no-this-alias
33
+ const parent = this;
34
+ let child;
35
+
36
+ // The constructor function for the new subclass is either defined by you
37
+ // (the "constructor" property in your `extend` definition), or defaulted
38
+ // by us to simply call the parent constructor.
39
+ if (protoProps && has(protoProps, 'constructor')) {
40
+ child = protoProps.constructor;
41
+ } else {
42
+ child = function () {
43
+ return parent.apply(this, arguments);
44
+ };
45
+ }
49
46
 
47
+ // Add static properties to the constructor function, if supplied.
48
+ extend(child, parent, staticProps);
50
49
 
51
- export function getResolveablePromise () {
52
- const wrapper = {
53
- isResolved: false,
54
- isPending: true,
55
- isRejected: false
56
- };
57
- const promise = new Promise((resolve, reject) => {
58
- wrapper.resolve = resolve;
59
- wrapper.reject = reject;
60
- })
61
- Object.assign(promise, wrapper);
62
- promise.then(
63
- function (v) {
64
- promise.isResolved = true;
65
- promise.isPending = false;
66
- promise.isRejected = false;
67
- return v;
68
- },
69
- function (e) {
70
- promise.isResolved = false;
71
- promise.isPending = false;
72
- promise.isRejected = true;
73
- throw (e);
74
- }
75
- );
76
- return promise;
50
+ // Set the prototype chain to inherit from `parent`, without calling
51
+ // `parent`'s constructor function and add the prototype properties.
52
+ child.prototype = create(parent.prototype, protoProps);
53
+ child.prototype.constructor = child;
54
+
55
+ // Set a convenience property in case the parent's prototype is needed
56
+ // later.
57
+ child.__super__ = parent.prototype;
58
+
59
+ return child;
77
60
  }
78
61
 
62
+ export function getResolveablePromise() {
63
+ /**
64
+ * @typedef {Object} PromiseWrapper
65
+ * @property {boolean} isResolved
66
+ * @property {boolean} isPending
67
+ * @property {boolean} isRejected
68
+ * @property {Function} resolve
69
+ * @property {Function} reject
70
+ */
71
+
72
+ /** @type {PromiseWrapper} */
73
+ const wrapper = {
74
+ isResolved: false,
75
+ isPending: true,
76
+ isRejected: false,
77
+ resolve: null,
78
+ reject: null,
79
+ };
80
+
81
+ /**
82
+ * @typedef {Promise & PromiseWrapper} ResolveablePromise
83
+ */
84
+
85
+ const promise = /** @type {ResolveablePromise} */ (
86
+ new Promise((resolve, reject) => {
87
+ wrapper.resolve = resolve;
88
+ wrapper.reject = reject;
89
+ })
90
+ );
91
+ Object.assign(promise, wrapper);
92
+ promise.then(
93
+ function (v) {
94
+ promise.isResolved = true;
95
+ promise.isPending = false;
96
+ promise.isRejected = false;
97
+ return v;
98
+ },
99
+ function (e) {
100
+ promise.isResolved = false;
101
+ promise.isPending = false;
102
+ promise.isRejected = true;
103
+ throw e;
104
+ },
105
+ );
106
+ return promise;
107
+ }
79
108
 
80
109
  // Throw an error when a URL is needed, and none is supplied.
81
110
  export function urlError() {
82
- throw new Error('A "url" property or function must be specified');
111
+ throw new Error('A "url" property or function must be specified');
83
112
  }
84
113
 
85
114
  // Wrap an optional error callback with a fallback error event.
86
115
  export function wrapError(model, options) {
87
- const error = options.error;
88
- options.error = function(resp) {
89
- if (error) error.call(options.context, model, resp, options);
90
- model.trigger('error', model, resp, options);
91
- };
116
+ const error = options.error;
117
+ options.error = function (resp) {
118
+ if (error) error.call(options.context, model, resp, options);
119
+ model.trigger('error', model, resp, options);
120
+ };
92
121
  }
93
122
 
94
123
  // Map from CRUD to HTTP for our default `sync` implementation.
95
124
  const methodMap = {
96
- create: 'POST',
97
- update: 'PUT',
98
- patch: 'PATCH',
99
- delete: 'DELETE',
100
- read: 'GET'
125
+ create: 'POST',
126
+ update: 'PUT',
127
+ patch: 'PATCH',
128
+ delete: 'DELETE',
129
+ read: 'GET',
101
130
  };
102
131
 
103
- export function getSyncMethod(model) {
104
- const store = result(model, 'browserStorage') || result(model.collection, 'browserStorage');
105
- return store ? store.sync() : sync;
106
- }
107
-
108
- // sync
109
- // ----
132
+ /**
133
+ * @typedef {import('./model.js').Model} Model
134
+ * @typedef {import('./collection.js').Collection} Collection
135
+ */
110
136
 
111
- // Override this function to change the manner in which Backbone persists
112
- // models to the server. You will be passed the type of request, and the
113
- // model in question. By default, makes a RESTful Ajax request
114
- // to the model's `url()`. Some possible customizations could be:
115
- //
116
- // * Use `setTimeout` to batch rapid-fire updates into a single request.
117
- // * Send up the models as XML instead of JSON.
118
- // * Persist models via WebSockets instead of Ajax.
119
- //
120
- export function sync(method, model, options={}) {
121
- const type = methodMap[method];
122
-
123
- // Default JSON-request options.
124
- const params = {type: type, dataType: 'json'};
125
-
126
- // Ensure that we have a URL.
127
- if (!options.url) {
128
- params.url = result(model, 'url') || urlError();
129
- }
130
-
131
- // Ensure that we have the appropriate request data.
132
- if (options.data == null && model && (method === 'create' || method === 'update' || method === 'patch')) {
133
- params.contentType = 'application/json';
134
- params.data = JSON.stringify(options.attrs || model.toJSON(options));
135
- }
136
-
137
- // Don't process data on a non-GET request.
138
- if (params.type !== 'GET') {
139
- params.processData = false;
140
- }
141
-
142
- // Pass along `textStatus` and `errorThrown` from jQuery.
143
- const error = options.error;
144
- options.error = function(xhr, textStatus, errorThrown) {
145
- options.textStatus = textStatus;
146
- options.errorThrown = errorThrown;
147
- if (error) error.call(options.context, xhr, textStatus, errorThrown);
148
- };
149
137
 
150
- // Make the request, allowing the user to override any Ajax options.
151
- const xhr = options.xhr = ajax(extend(params, options));
152
- model.trigger('request', model, xhr, options);
153
- return xhr;
138
+ /**
139
+ * @param {Model | Collection} model
140
+ */
141
+ export function getSyncMethod(model) {
142
+ const store = result(model, 'browserStorage') || result(/** @type {Model} */(model).collection, 'browserStorage');
143
+ return store ? store.sync() : sync;
154
144
  }
155
145
 
156
- export function ajax() {
157
- return fetch.apply(this, arguments);
146
+ /**
147
+ * @typedef {Object} SyncOptions
148
+ * @property {string} [url]
149
+ * @property {any} [data]
150
+ * @property {any} [attrs]
151
+ * @property {Function} [success]
152
+ * @property {Function} [error]
153
+ * @property {any} [xhr]
154
+ */
155
+
156
+ /**
157
+ * Override this function to change the manner in which Backbone persists
158
+ * models to the server. You will be passed the type of request, and the
159
+ * model in question. By default makes a `fetch()` API call
160
+ * to the model's `url()`.
161
+ *
162
+ * Some possible customizations could be:
163
+ *
164
+ * - Use `setTimeout` to batch rapid-fire updates into a single request.
165
+ * - Persist models via WebSockets instead of Ajax.
166
+ * - Persist models to browser storage
167
+ *
168
+ * @param {'create'|'update'|'patch'} method
169
+ * @param {import('./model.js').Model} model
170
+ * @param {SyncOptions} [options]
171
+ */
172
+ export function sync(method, model, options = {}) {
173
+ let data = options.data;
174
+
175
+ // Ensure that we have the appropriate request data.
176
+ if (data == null && model && (method === 'create' || method === 'update' || method === 'patch')) {
177
+ data = options.attrs || model.toJSON();
178
+ }
179
+
180
+ const type = methodMap[method];
181
+ const params = {
182
+ method: type,
183
+ body: data ? JSON.stringify(data) : '',
184
+ success: options.success,
185
+ error: options.error,
186
+ };
187
+
188
+ const url = options.url || result(model, 'url') || urlError();
189
+ const xhr = (options.xhr = fetch(url, params));
190
+ model.trigger('request', model, xhr, { ...params, xhr });
191
+ return xhr;
158
192
  }
@@ -1,29 +1,24 @@
1
1
  /* global global */
2
- import { ajax, sync } from './helpers.js';
2
+ import ElementView from './element.js';
3
+ import EventEmitter from './eventemitter.js';
4
+ import Storage from './storage.js';
3
5
  import { Collection } from './collection.js';
4
- import { Events } from './events.js';
5
- import History from './history.js';
6
6
  import { Model } from './model.js';
7
- import { Router } from './router.js';
8
- import { View } from './view.js';
7
+ import { sync } from './helpers.js';
9
8
 
10
9
  const skeletor = {
11
10
  Collection,
12
- Events,
13
- History,
11
+ ElementView,
12
+ EventEmitter,
14
13
  Model,
15
- Router,
16
- View,
17
- ajax,
18
- sync
19
- }
20
-
21
- Object.assign(skeletor, Events);
14
+ sync,
15
+ };
22
16
 
23
17
  // Establish the root object, `window` (`self`) in the browser, or `global` on the server.
24
18
  // We use `self` instead of `window` for `WebWorker` support.
25
- const root = typeof self == 'object' && self.self === self && self ||
26
- typeof global == 'object' && global.global === global && global;
19
+ const root =
20
+ (typeof self == 'object' && self.self === self && self) ||
21
+ (typeof global == 'object' && global.global === global && global);
27
22
 
28
23
  // Current version of the library. Keep in sync with `package.json`.
29
24
  skeletor.VERSION = '0.0.1';
@@ -34,10 +29,19 @@ const previousSkeletor = root.Skeletor;
34
29
 
35
30
  // Runs Skeletor.js in *noConflict* mode, returning the `Skeletor` variable
36
31
  // to its previous owner. Returns a reference to this Skeletor object.
37
- skeletor.noConflict = function() {
32
+ skeletor.noConflict = function () {
38
33
  root.Skeletor = previousSkeletor;
39
34
  return this;
40
35
  };
41
36
 
42
37
  root.Skeletor = skeletor;
43
38
  export default skeletor;
39
+
40
+ export {
41
+ Collection,
42
+ ElementView,
43
+ EventEmitter,
44
+ Model,
45
+ Storage,
46
+ sync,
47
+ }
@@ -0,0 +1,81 @@
1
+ import { eventsApi, onApi, offApi } from './utils/events.js';
2
+
3
+ /**
4
+ * A listening class that tracks and cleans up memory bindings
5
+ * when all callbacks have been offed.
6
+ */
7
+ class Listening {
8
+
9
+ /** @typedef {import('./eventemitter.js').default} EventEmitter */
10
+
11
+ /**
12
+ * @param {any} listener
13
+ * @param {any} obj
14
+ */
15
+ constructor(listener, obj) {
16
+ this.id = listener._listenId;
17
+ this.listener = listener;
18
+ this.obj = obj;
19
+ this.interop = true;
20
+ this.count = 0;
21
+ this._events = undefined;
22
+ }
23
+
24
+ /**
25
+ * @param {string} name
26
+ * @param {Function} callback
27
+ * @param {any} context
28
+ * @param {Listening} _listening
29
+ */
30
+ start(name, callback, context, _listening) {
31
+ this._events = eventsApi(onApi, this._events || {}, name, callback, {
32
+ context: this.obj,
33
+ ctx: context,
34
+ listening: _listening,
35
+ });
36
+
37
+ if (_listening) {
38
+ const listeners = this.obj._listeners || (this.obj._listeners = {});
39
+ listeners[this.id] = this;
40
+
41
+ // Allow the listening to use a counter, instead of tracking
42
+ // callbacks for library interop
43
+ this.interop = false;
44
+ }
45
+
46
+ return this;
47
+ }
48
+
49
+ /**
50
+ * Stop's listening to a callback (or several).
51
+ * Uses an optimized counter if the listenee uses Backbone.Events.
52
+ * Otherwise, falls back to manual tracking to support events
53
+ * library interop.
54
+ * @param {string} name
55
+ * @param {Function} callback
56
+ */
57
+ stop(name, callback) {
58
+ let cleanup;
59
+ if (this.interop) {
60
+ this._events = eventsApi(offApi, this._events, name, callback, {
61
+ context: undefined,
62
+ listeners: undefined,
63
+ });
64
+ cleanup = !this._events;
65
+ } else {
66
+ this.count--;
67
+ cleanup = this.count === 0;
68
+ }
69
+ if (cleanup) this.cleanup();
70
+ }
71
+
72
+ /**
73
+ * Cleans up memory bindings between the listener and the listenee.
74
+ */
75
+ cleanup() {
76
+ delete this.listener._listeningTo[this.obj._listenId];
77
+ if (!this.interop) delete this.obj._listeners[this.id];
78
+ }
79
+ }
80
+
81
+ export default Listening;