@webqit/observer 1.7.5 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.gitignore +3 -3
- package/LICENSE +21 -0
- package/README.md +202 -199
- package/dist/main.js +2 -2
- package/dist/main.js.map +7 -1
- package/package.json +68 -68
- package/src/actors.js +175 -0
- package/src/core/Descriptor.js +23 -0
- package/src/core/ListenerRegistration.js +54 -0
- package/src/core/ListenerRegistry.js +41 -0
- package/src/core/Registration.js +35 -0
- package/src/core/Registry.js +96 -0
- package/src/core/TrapsRegistration.js +35 -0
- package/src/core/TrapsRegistry.js +51 -0
- package/src/index.js +10 -74
- package/src/main.js +547 -0
- package/src/targets.browser.js +9 -0
- package/test/reactions.test.js +326 -337
- package/webpack.config.cjs +5 -5
- package/src/actions/_exec.js +0 -33
- package/src/actions/_setOrDefine.js +0 -136
- package/src/actions/apply.js +0 -19
- package/src/actions/construct.js +0 -19
- package/src/actions/defineProperty.js +0 -20
- package/src/actions/deleteProperty.js +0 -80
- package/src/actions/get.js +0 -53
- package/src/actions/getOwnPropertyDescriptor.js +0 -18
- package/src/actions/getPrototypeOf.js +0 -17
- package/src/actions/has.js +0 -18
- package/src/actions/isExtensible.js +0 -17
- package/src/actions/ownKeys.js +0 -17
- package/src/actions/preventExtensions.js +0 -17
- package/src/actions/set.js +0 -21
- package/src/actions/setPrototypeOf.js +0 -18
- package/src/actors/accessorize.js +0 -162
- package/src/actors/proxy.js +0 -59
- package/src/actors/unaccessorize.js +0 -26
- package/src/actors/unproxy.js +0 -17
- package/src/browser-entry.js +0 -11
- package/src/connectors/build.js +0 -64
- package/src/connectors/link.js +0 -76
- package/src/connectors/unlink.js +0 -35
- package/src/core/Action.js +0 -33
- package/src/core/Delta.js +0 -46
- package/src/core/Event.js +0 -141
- package/src/core/Fireable.js +0 -34
- package/src/core/Firebase.js +0 -136
- package/src/core/Interceptor.js +0 -33
- package/src/core/Interceptors.js +0 -61
- package/src/core/Mutation.js +0 -46
- package/src/core/Observer.js +0 -99
- package/src/core/Observers.js +0 -75
- package/src/core/utils.js +0 -51
- package/src/reactions/closure.js +0 -88
- package/src/reactions/intercept.js +0 -53
- package/src/reactions/observe.js +0 -45
- package/src/reactions/unintercept.js +0 -43
- package/src/reactions/unobserve.js +0 -36
package/package.json
CHANGED
|
@@ -1,68 +1,68 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@webqit/observer",
|
|
3
|
-
"title": "Observer",
|
|
4
|
-
"description": "A simple set of functions for intercepting and observing JavaScript objects and arrays.",
|
|
5
|
-
"keywords": [
|
|
6
|
-
"observer",
|
|
7
|
-
"reflection",
|
|
8
|
-
"interception",
|
|
9
|
-
"pub/sub pattern",
|
|
10
|
-
"object.observe",
|
|
11
|
-
"array.observe",
|
|
12
|
-
"events"
|
|
13
|
-
],
|
|
14
|
-
"homepage": "https://webqit.io/tooling/observer",
|
|
15
|
-
"version": "
|
|
16
|
-
"license": "MIT",
|
|
17
|
-
"repository": {
|
|
18
|
-
"type": "git",
|
|
19
|
-
"url": "git+https://github.com/webqit/observer.git"
|
|
20
|
-
},
|
|
21
|
-
"bugs": {
|
|
22
|
-
"url": "https://github.com/webqit/observer/issues"
|
|
23
|
-
},
|
|
24
|
-
"type": "module",
|
|
25
|
-
"sideEffects": false,
|
|
26
|
-
"main": "src/index.js",
|
|
27
|
-
"scripts": {
|
|
28
|
-
"test": "mocha --extension .test.js --exit",
|
|
29
|
-
"test:coverage": "c8 --reporter=text-lcov npm run test | coveralls",
|
|
30
|
-
"build": "
|
|
31
|
-
"preversion": "npm run test && npm run build && git add -A dist",
|
|
32
|
-
"postversion": "npm publish",
|
|
33
|
-
"postpublish": "git push && git push --tags"
|
|
34
|
-
},
|
|
35
|
-
"dependencies": {
|
|
36
|
-
"@webqit/util": "^0.8.7"
|
|
37
|
-
},
|
|
38
|
-
"devDependencies": {
|
|
39
|
-
"chai": "^4.3.4",
|
|
40
|
-
"coveralls": "^3.1.1",
|
|
41
|
-
"
|
|
42
|
-
"
|
|
43
|
-
"
|
|
44
|
-
},
|
|
45
|
-
"author": "Oxford Harrison <oxharris.dev@gmail.com>",
|
|
46
|
-
"maintainers": [
|
|
47
|
-
"Oxford Harrison <oxharris.dev@gmail.com>"
|
|
48
|
-
],
|
|
49
|
-
"contributors": [],
|
|
50
|
-
"funding": {
|
|
51
|
-
"type": "patreon",
|
|
52
|
-
"url": "https://patreon.com/ox_harris"
|
|
53
|
-
},
|
|
54
|
-
"badges": {
|
|
55
|
-
"list": [
|
|
56
|
-
"npmversion",
|
|
57
|
-
"npmdownloads",
|
|
58
|
-
"patreon"
|
|
59
|
-
],
|
|
60
|
-
"config": {
|
|
61
|
-
"patreonUsername": "ox_harris",
|
|
62
|
-
"githubUsername": "webqit",
|
|
63
|
-
"githubRepository": "observer",
|
|
64
|
-
"githubSlug": "webqit/observer",
|
|
65
|
-
"npmPackageName": "@webqit/observer"
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@webqit/observer",
|
|
3
|
+
"title": "Observer",
|
|
4
|
+
"description": "A simple set of functions for intercepting and observing JavaScript objects and arrays.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"observer",
|
|
7
|
+
"reflection",
|
|
8
|
+
"interception",
|
|
9
|
+
"pub/sub pattern",
|
|
10
|
+
"object.observe",
|
|
11
|
+
"array.observe",
|
|
12
|
+
"events"
|
|
13
|
+
],
|
|
14
|
+
"homepage": "https://webqit.io/tooling/observer",
|
|
15
|
+
"version": "2.0.0",
|
|
16
|
+
"license": "MIT",
|
|
17
|
+
"repository": {
|
|
18
|
+
"type": "git",
|
|
19
|
+
"url": "git+https://github.com/webqit/observer.git"
|
|
20
|
+
},
|
|
21
|
+
"bugs": {
|
|
22
|
+
"url": "https://github.com/webqit/observer/issues"
|
|
23
|
+
},
|
|
24
|
+
"type": "module",
|
|
25
|
+
"sideEffects": false,
|
|
26
|
+
"main": "src/index.js",
|
|
27
|
+
"scripts": {
|
|
28
|
+
"test": "mocha --extension .test.js --exit",
|
|
29
|
+
"test:coverage": "c8 --reporter=text-lcov npm run test | coveralls",
|
|
30
|
+
"build": "esbuild main=src/targets.browser.js --bundle --minify --sourcemap --outdir=dist",
|
|
31
|
+
"preversion": "npm run test && npm run build && git add -A dist",
|
|
32
|
+
"postversion": "npm publish",
|
|
33
|
+
"postpublish": "git push && git push --tags"
|
|
34
|
+
},
|
|
35
|
+
"dependencies": {
|
|
36
|
+
"@webqit/util": "^0.8.7"
|
|
37
|
+
},
|
|
38
|
+
"devDependencies": {
|
|
39
|
+
"chai": "^4.3.4",
|
|
40
|
+
"coveralls": "^3.1.1",
|
|
41
|
+
"esbuild": "^0.17.9",
|
|
42
|
+
"mocha": "^10.2.0",
|
|
43
|
+
"mocha-lcov-reporter": "^1.3.0"
|
|
44
|
+
},
|
|
45
|
+
"author": "Oxford Harrison <oxharris.dev@gmail.com>",
|
|
46
|
+
"maintainers": [
|
|
47
|
+
"Oxford Harrison <oxharris.dev@gmail.com>"
|
|
48
|
+
],
|
|
49
|
+
"contributors": [],
|
|
50
|
+
"funding": {
|
|
51
|
+
"type": "patreon",
|
|
52
|
+
"url": "https://patreon.com/ox_harris"
|
|
53
|
+
},
|
|
54
|
+
"badges": {
|
|
55
|
+
"list": [
|
|
56
|
+
"npmversion",
|
|
57
|
+
"npmdownloads",
|
|
58
|
+
"patreon"
|
|
59
|
+
],
|
|
60
|
+
"config": {
|
|
61
|
+
"patreonUsername": "ox_harris",
|
|
62
|
+
"githubUsername": "webqit",
|
|
63
|
+
"githubRepository": "observer",
|
|
64
|
+
"githubSlug": "webqit/observer",
|
|
65
|
+
"npmPackageName": "@webqit/observer"
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
package/src/actors.js
ADDED
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
|
|
2
|
+
/**
|
|
3
|
+
* @imports
|
|
4
|
+
*/
|
|
5
|
+
import { _from as _arrFrom } from '@webqit/util/arr/index.js';
|
|
6
|
+
import { _isClass, _isFunction, _isTypeObject, _getType, _internals } from '@webqit/util/js/index.js';
|
|
7
|
+
import { set, deleteProperty, has, get, ownKeys, defineProperty, getOwnPropertyDescriptor } from "./main.js";
|
|
8
|
+
import { apply, construct, getPrototypeOf, setPrototypeOf, isExtensible, preventExtensions } from "./main.js";
|
|
9
|
+
|
|
10
|
+
/* ---------------ACCESSORIZE METHODS--------------- */
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Accessorizes props.
|
|
14
|
+
*
|
|
15
|
+
* @param Array|Object target
|
|
16
|
+
* @param String|Array props
|
|
17
|
+
* @param Object params
|
|
18
|
+
*
|
|
19
|
+
* @return Array
|
|
20
|
+
*/
|
|
21
|
+
export function accessorize( target, props, params = {} ) {
|
|
22
|
+
target = resolveTarget( target );
|
|
23
|
+
const accessorizedProps = _internals( target, 'accessorizedProps' );
|
|
24
|
+
// ---------
|
|
25
|
+
function getDescriptorDeep( prop ) {
|
|
26
|
+
let descriptor, proto = target;
|
|
27
|
+
while ( !descriptor && ( proto = Object.getPrototypeOf( proto ) ) ) {
|
|
28
|
+
descriptor = Object.getOwnPropertyDescriptor( proto, prop );
|
|
29
|
+
}
|
|
30
|
+
return descriptor
|
|
31
|
+
? { proto, descriptor }
|
|
32
|
+
: { descriptor: { value: undefined } };
|
|
33
|
+
}
|
|
34
|
+
// ---------
|
|
35
|
+
function accessorizeProp( prop ) {
|
|
36
|
+
if ( accessorizedProps.has( prop ) ) return true;
|
|
37
|
+
// ------------------
|
|
38
|
+
// Current Descriptor Record
|
|
39
|
+
const currentDescriptorRecord = getDescriptorDeep( prop );
|
|
40
|
+
currentDescriptorRecord.getValue = function() {
|
|
41
|
+
return 'get' in this.descriptor ? this.descriptor.get() : this.descriptor.value;
|
|
42
|
+
};
|
|
43
|
+
currentDescriptorRecord.setValue = function( value ) {
|
|
44
|
+
return 'set' in this.descriptor ? this.descriptor.set( value ) : ( this.descriptor.value = value )
|
|
45
|
+
};
|
|
46
|
+
currentDescriptorRecord.intact = function() {
|
|
47
|
+
const currentDescriptor = Object.getOwnPropertyDescriptor( target, prop );
|
|
48
|
+
return currentDescriptor.get === accessorization.get
|
|
49
|
+
&& currentDescriptor.set === accessorization.set
|
|
50
|
+
&& accessorizedProps.get( prop ) === this;
|
|
51
|
+
};
|
|
52
|
+
currentDescriptorRecord.restore = function() {
|
|
53
|
+
if ( !this.intact() ) return false;
|
|
54
|
+
if ( this.proto !== target ) { delete target[ prop ]; }
|
|
55
|
+
else { Object.defineProperty( target, prop, this.descriptor ); }
|
|
56
|
+
accessorizedProps.delete( prop );
|
|
57
|
+
return true;
|
|
58
|
+
};
|
|
59
|
+
accessorizedProps.set( prop, currentDescriptorRecord );
|
|
60
|
+
// ------------------
|
|
61
|
+
// enumerable, configurable
|
|
62
|
+
const { enumerable = true, configurable = true } = currentDescriptorRecord.descriptor;
|
|
63
|
+
const accessorization = { enumerable, configurable };
|
|
64
|
+
// set, get
|
|
65
|
+
if ( [ 'value', 'set' ].some( x => x in currentDescriptorRecord.descriptor ) ) {
|
|
66
|
+
accessorization.set = function ( value ) { return set( this, prop, value, params ); };
|
|
67
|
+
}
|
|
68
|
+
if ( [ 'value', 'get' ].some( x => x in currentDescriptorRecord.descriptor ) ) {
|
|
69
|
+
accessorization.get = function () { return get( this, prop, params ); };
|
|
70
|
+
}
|
|
71
|
+
try {
|
|
72
|
+
Object.defineProperty( target, prop, accessorization );
|
|
73
|
+
return true;
|
|
74
|
+
} catch( e ) {
|
|
75
|
+
accessorizedProps.delete( prop );
|
|
76
|
+
return false;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
const _props = Array.isArray( props ) ? props : (
|
|
80
|
+
props === undefined ? Object.keys( target ) : [ props ]
|
|
81
|
+
);
|
|
82
|
+
const statuses = _props.map( accessorizeProp );
|
|
83
|
+
return props === undefined || Array.isArray( props )
|
|
84
|
+
? statuses
|
|
85
|
+
: statuses[ 0 ];
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Unaccessorizes previously accessorized props.
|
|
90
|
+
*
|
|
91
|
+
* @param Array|Object target
|
|
92
|
+
* @param String|Array props
|
|
93
|
+
* @param Object params
|
|
94
|
+
*
|
|
95
|
+
* @return Array
|
|
96
|
+
*/
|
|
97
|
+
export function unaccessorize( target, props, params = {} ) {
|
|
98
|
+
target = resolveTarget( target );
|
|
99
|
+
const accessorizedProps = _internals( target, 'accessorizedProps' );
|
|
100
|
+
function unaccessorizeProp( prop ) {
|
|
101
|
+
if ( !accessorizedProps.has( prop ) ) return true;
|
|
102
|
+
return accessorizedProps.get( prop ).restore();
|
|
103
|
+
}
|
|
104
|
+
const _props = Array.isArray( props ) ? props : (
|
|
105
|
+
props === undefined ? Object.keys( target ) : [ props ]
|
|
106
|
+
);
|
|
107
|
+
const statuses = _props.map( unaccessorizeProp );
|
|
108
|
+
return props === undefined || Array.isArray( props )
|
|
109
|
+
? statuses
|
|
110
|
+
: statuses[ 0 ];
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/* ---------------PROXY METHODS--------------- */
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Returns an object as a proxy and binds all instance methods
|
|
117
|
+
* to the proxy instead of the object itself.
|
|
118
|
+
*
|
|
119
|
+
* @param Array|Object target
|
|
120
|
+
* @param Object params
|
|
121
|
+
*
|
|
122
|
+
* @return Proxy
|
|
123
|
+
*/
|
|
124
|
+
export function proxy( target, params = {} ) {
|
|
125
|
+
target = resolveTarget( target );
|
|
126
|
+
const proxy = new Proxy( target, {
|
|
127
|
+
apply: ( target, thisArgument, argumentsList ) => apply( target, thisArgument, argumentsList, params ),
|
|
128
|
+
construct: ( target, argumentsList, newTarget = null ) => construct( target, argumentsList, newTarget, params ),
|
|
129
|
+
defineProperty: ( target, propertyKey, attributes ) => defineProperty( target, propertyKey, attributes, params ),
|
|
130
|
+
deleteProperty: ( target, propertyKey ) => deleteProperty( target, propertyKey, params ),
|
|
131
|
+
get: ( target, propertyKey, receiver = null ) => {
|
|
132
|
+
const val = get( target, propertyKey, { ...params, receiver } );
|
|
133
|
+
if ( params.proxyAutoBinding !== false && _isFunction( val ) && !_isClass( val )) {
|
|
134
|
+
return val.bind( proxy );
|
|
135
|
+
}
|
|
136
|
+
return val;
|
|
137
|
+
},
|
|
138
|
+
getOwnPropertyDescriptor: ( target, propertyKey ) => getOwnPropertyDescriptor( target, propertyKey, params ),
|
|
139
|
+
getPrototypeOf: target => getPrototypeOf( target, params ),
|
|
140
|
+
has: ( target, propertyKey ) => has( target, propertyKey, params ),
|
|
141
|
+
isExtensible: target => isExtensible( target, params ),
|
|
142
|
+
ownKeys: target => ownKeys( target, params ),
|
|
143
|
+
preventExtensions: target => preventExtensions( target, params ),
|
|
144
|
+
set: ( target, propertyKey, value, receiver = null ) => set( target, propertyKey, value, { ...params, receiver } ),
|
|
145
|
+
setPrototypeOf: ( target, prototype ) => setPrototypeOf( target, prototype, params ),
|
|
146
|
+
});
|
|
147
|
+
_internals( proxy ).set( proxy, target );
|
|
148
|
+
return proxy;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Returns the original object earlier proxied by proxy().
|
|
153
|
+
*
|
|
154
|
+
* @param Proxy|Any target
|
|
155
|
+
*
|
|
156
|
+
* @return Any
|
|
157
|
+
*/
|
|
158
|
+
export function unproxy( target ) {
|
|
159
|
+
// Proxy targets are mapped to their own instances internally
|
|
160
|
+
return _internals( target, false ).get( target ) || target;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/* ---------------HELPERS--------------- */
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Ensures target object is an object or array.
|
|
167
|
+
*
|
|
168
|
+
* @param Array|Object target
|
|
169
|
+
*
|
|
170
|
+
* @return Array|Object
|
|
171
|
+
*/
|
|
172
|
+
function resolveTarget( target ) {
|
|
173
|
+
if ( !target || !_isTypeObject( target ) ) throw new Error('Target must be of type object!');
|
|
174
|
+
return unproxy( target );
|
|
175
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
|
|
2
|
+
/**
|
|
3
|
+
* ---------------------------
|
|
4
|
+
* The Descriptor class
|
|
5
|
+
* ---------------------------
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export default class Descriptor {
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Initializes the instance.
|
|
12
|
+
*
|
|
13
|
+
* @param array|object target
|
|
14
|
+
* @param object dfn
|
|
15
|
+
*
|
|
16
|
+
* @return void
|
|
17
|
+
*/
|
|
18
|
+
constructor( target, dfn ) {
|
|
19
|
+
this.target = target;
|
|
20
|
+
if ( !( dfn.type ) ) throw new Error( 'Descriptor type must be given in definition!' );
|
|
21
|
+
Object.assign( this, dfn );
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
|
|
2
|
+
/**
|
|
3
|
+
* @imports
|
|
4
|
+
*/
|
|
5
|
+
import { _from as _arrFrom } from '@webqit/util/arr/index.js';
|
|
6
|
+
import Registration from './Registration.js';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* ---------------------------
|
|
10
|
+
* The ListenerRegistration class
|
|
11
|
+
* ---------------------------
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
export default class ListenerRegistration extends Registration {
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* @constructor
|
|
18
|
+
*/
|
|
19
|
+
constructor() {
|
|
20
|
+
super( ...arguments );
|
|
21
|
+
Object.defineProperty( this, 'abortController', { value: new AbortController } );
|
|
22
|
+
Object.defineProperty( this, 'signal', { value: this.abortController.signal } );
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* De-registers the instance.
|
|
27
|
+
*
|
|
28
|
+
* @return Void
|
|
29
|
+
*/
|
|
30
|
+
remove() {
|
|
31
|
+
this.abortController.abort();
|
|
32
|
+
super.remove();
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Calls the observer's handler function
|
|
37
|
+
* on matching with the event's fields.
|
|
38
|
+
*
|
|
39
|
+
* @param Array events
|
|
40
|
+
*
|
|
41
|
+
* @return Any
|
|
42
|
+
*/
|
|
43
|
+
fire( events ) {
|
|
44
|
+
let matches = events, filter = this.filter;
|
|
45
|
+
if ( filter !== Infinity && ( filter = _arrFrom( filter ) ) ) {
|
|
46
|
+
matches = events.filter( event => filter.includes( event.key ) );
|
|
47
|
+
}
|
|
48
|
+
if ( matches.length ) {
|
|
49
|
+
return this.filter === Infinity || Array.isArray( this.filter )
|
|
50
|
+
? this.handler( matches, this )
|
|
51
|
+
: this.handler( matches[ 0 ], this );
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
|
|
2
|
+
/**
|
|
3
|
+
* @imports
|
|
4
|
+
*/
|
|
5
|
+
import ListenerRegistration from './ListenerRegistration.js';
|
|
6
|
+
import Registry from './Registry.js';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* ---------------------------
|
|
10
|
+
* The ListenerRegistry class
|
|
11
|
+
* ---------------------------
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
export default class ListenerRegistry extends Registry {
|
|
15
|
+
|
|
16
|
+
static getInstance( target, createIfNotExists = true, namespace = null ) {
|
|
17
|
+
return super._getInstance( 'listeners', ...arguments );
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
static namespace( namespace, ImplementationClass = null ) {
|
|
21
|
+
return super._namespace( 'listeners', ...arguments );
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* @inheritdoc
|
|
26
|
+
*/
|
|
27
|
+
addRegistration( filter, handler, params ) {
|
|
28
|
+
return super.addRegistration( new ListenerRegistration( this, { filter, handler, params } ) );
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Fires all observers with the given evt (change).
|
|
33
|
+
*
|
|
34
|
+
* @param Arrayn events
|
|
35
|
+
*
|
|
36
|
+
* @return Void
|
|
37
|
+
*/
|
|
38
|
+
emit( events ) {
|
|
39
|
+
this.entries.forEach( listener => listener.fire( events ) );
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
|
|
2
|
+
/**
|
|
3
|
+
* ---------------------------
|
|
4
|
+
* The Registration class
|
|
5
|
+
* ---------------------------
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export default class Registration {
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Initializes the instance.
|
|
12
|
+
*
|
|
13
|
+
* @param Registry registry
|
|
14
|
+
* @param object dfn
|
|
15
|
+
*
|
|
16
|
+
* @return void
|
|
17
|
+
*/
|
|
18
|
+
constructor( registry, dfn ) {
|
|
19
|
+
this.registry = registry;
|
|
20
|
+
Object.assign( this, { ...dfn, target: registry.target } );
|
|
21
|
+
if ( this.params.signal ) {
|
|
22
|
+
this.params.signal.addEventListener( 'abort', () => this.remove() );
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Sets a "disconnected" flag on the Registration.
|
|
28
|
+
*
|
|
29
|
+
* @return void
|
|
30
|
+
*/
|
|
31
|
+
remove() {
|
|
32
|
+
this.removed = true;
|
|
33
|
+
return this.registry.removeRegistration( this );
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
|
|
2
|
+
/**
|
|
3
|
+
* @imports
|
|
4
|
+
*/
|
|
5
|
+
import { _isTypeObject, _getType, _internals } from '@webqit/util/js/index.js';
|
|
6
|
+
import { _from as _arrFrom, _intersect, _equals as _arrEquals } from '@webqit/util/arr/index.js';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* ---------------------------
|
|
10
|
+
* The Registry class
|
|
11
|
+
* ---------------------------
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
export default class Registry {
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Initializes the instance.
|
|
18
|
+
*
|
|
19
|
+
* @param object target
|
|
20
|
+
*
|
|
21
|
+
* @return void
|
|
22
|
+
*/
|
|
23
|
+
constructor( target ) {
|
|
24
|
+
this.target = target;
|
|
25
|
+
this.entries = [];
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Adds an Registration instance
|
|
30
|
+
* with optional tags.
|
|
31
|
+
*
|
|
32
|
+
* @param Registration registration
|
|
33
|
+
*
|
|
34
|
+
* @return Registration
|
|
35
|
+
*/
|
|
36
|
+
addRegistration( registration ) {
|
|
37
|
+
this.entries.push( registration );
|
|
38
|
+
return registration;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Removes registrations by reference.
|
|
43
|
+
*
|
|
44
|
+
* @param Registration registration
|
|
45
|
+
*
|
|
46
|
+
* @return void
|
|
47
|
+
*/
|
|
48
|
+
removeRegistration( registration ) {
|
|
49
|
+
this.entries = this.entries.filter( _entry => _entry !== registration );
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Returns a observer-specific object embedded on an element.
|
|
54
|
+
*
|
|
55
|
+
* @param string type
|
|
56
|
+
* @param array|object target
|
|
57
|
+
* @param bool createIfNotExists
|
|
58
|
+
* @param string namespace
|
|
59
|
+
*
|
|
60
|
+
* @return Registry
|
|
61
|
+
*/
|
|
62
|
+
static _getInstance( type, target, createIfNotExists = true, namespace = this.__namespace ) {
|
|
63
|
+
if ( !_isTypeObject( target ) ) throw new Error( `Subject must be of type object; "${ _getType( target ) }" given!` );
|
|
64
|
+
let ImplementationClass = this;
|
|
65
|
+
if ( namespace && globalThis.WebQitObserverNamespaceRegistry.has( type + '-' + namespace ) ) {
|
|
66
|
+
ImplementationClass = globalThis.WebQitObserverNamespaceRegistry.get( type + '-' + namespace );
|
|
67
|
+
type += '-' + namespace
|
|
68
|
+
}
|
|
69
|
+
if ( !_internals( target, 'observerApi' ).has( type ) && createIfNotExists ) {
|
|
70
|
+
_internals( target, 'observerApi' ).set( type, new ImplementationClass( target ) );
|
|
71
|
+
}
|
|
72
|
+
return _internals( target, 'observerApi' ).get( type );
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Extend a Fireable Class with a namespace.
|
|
77
|
+
*
|
|
78
|
+
* @param string namespace
|
|
79
|
+
* @param class ImplementationClass
|
|
80
|
+
*
|
|
81
|
+
* @return void|class
|
|
82
|
+
*/
|
|
83
|
+
static _namespace( type, namespace, ImplementationClass = null ) {
|
|
84
|
+
type += '-' + namespace;
|
|
85
|
+
if ( arguments.length === 2 ) return globalThis.WebQitObserverNamespaceRegistry.get( type );
|
|
86
|
+
if ( !( ImplementationClass.prototype instanceof this ) ) {
|
|
87
|
+
throw new Error( `The implementation of the namespace ${ this.name }.${ namespace } must be a subclass of ${ this.name }.` );
|
|
88
|
+
}
|
|
89
|
+
globalThis.WebQitObserverNamespaceRegistry.set( type, ImplementationClass );
|
|
90
|
+
ImplementationClass.__namespace = namespace;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if ( !globalThis.WebQitObserverNamespaceRegistry ) {
|
|
95
|
+
globalThis.WebQitObserverNamespaceRegistry = new Map;
|
|
96
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
|
|
2
|
+
/**
|
|
3
|
+
* @imports
|
|
4
|
+
*/
|
|
5
|
+
import Registration from './Registration.js';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* ---------------------------
|
|
9
|
+
* The TrapsRegistration class
|
|
10
|
+
* ---------------------------
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
export default class TrapsRegistration extends Registration {
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Calls the observer's handler function
|
|
17
|
+
* on matching with the descriptor's fields.
|
|
18
|
+
*
|
|
19
|
+
* @param Descriptor descriptor
|
|
20
|
+
* @param function next
|
|
21
|
+
* @param mixed recieved
|
|
22
|
+
*
|
|
23
|
+
* @return void
|
|
24
|
+
*/
|
|
25
|
+
exec( descriptor, next, recieved ) {
|
|
26
|
+
if ( this.running || !this.traps[ descriptor.type ] ) {
|
|
27
|
+
return next( ...Array.prototype.slice.call( arguments, 2 ) );
|
|
28
|
+
}
|
|
29
|
+
this.running = true;
|
|
30
|
+
return this.traps[ descriptor.type ]( descriptor, recieved, ( ...args ) => {
|
|
31
|
+
this.running = false;
|
|
32
|
+
return next( ...args );
|
|
33
|
+
} );
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
|
|
2
|
+
/**
|
|
3
|
+
* @imports
|
|
4
|
+
*/
|
|
5
|
+
import TrapsRegistration from './TrapsRegistration.js';
|
|
6
|
+
import Registry from './Registry.js';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* ---------------------------
|
|
10
|
+
* The TrapsRegistry class
|
|
11
|
+
* ---------------------------
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
export default class TrapsRegistry extends Registry {
|
|
15
|
+
|
|
16
|
+
static getInstance( target, createIfNotExists = true, namespace = null ) {
|
|
17
|
+
return super._getInstance( 'traps', ...arguments );
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
static namespace( namespace, ImplementationClass = null ) {
|
|
21
|
+
return super._namespace( 'traps', ...arguments );
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* @inheritdoc
|
|
26
|
+
*/
|
|
27
|
+
addRegistration( dfn ) {
|
|
28
|
+
return super.addRegistration( new TrapsRegistration( this, dfn ) );
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Fires all interceptors with the given action.
|
|
33
|
+
*
|
|
34
|
+
* @param Descriptor descriptor
|
|
35
|
+
* @param function defaultHandler
|
|
36
|
+
*
|
|
37
|
+
* @return mixed
|
|
38
|
+
*/
|
|
39
|
+
emit( descriptor, defaultHandler = null ) {
|
|
40
|
+
const $this = this;
|
|
41
|
+
return ( function next( index, ..._args ) {
|
|
42
|
+
const registration = $this.entries[ index ];
|
|
43
|
+
if ( registration ) {
|
|
44
|
+
return registration.exec( descriptor, ( ...args ) => {
|
|
45
|
+
return next( index + 1, ...args );
|
|
46
|
+
}/*next*/, ..._args );
|
|
47
|
+
}
|
|
48
|
+
return defaultHandler ? defaultHandler( descriptor, ..._args ) : _args[ 0 ];
|
|
49
|
+
} )( 0 );
|
|
50
|
+
}
|
|
51
|
+
}
|