ember-source 5.11.0-beta.2 → 5.11.1
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/build-metadata.json +3 -3
- package/dist/ember-template-compiler.js +48 -42
- package/dist/ember-testing.js +1 -1
- package/dist/ember.debug.js +23102 -23100
- package/dist/ember.prod.js +31562 -31563
- package/dist/packages/@ember/-internals/container/index.js +1 -1
- package/dist/packages/@ember/-internals/deprecations/index.js +2 -1
- package/dist/packages/@ember/-internals/glimmer/index.js +2 -1
- package/dist/packages/@ember/-internals/meta/lib/meta.js +4 -3
- package/dist/packages/@ember/-internals/metal/index.js +9 -8
- package/dist/packages/@ember/-internals/routing/index.js +6 -5
- package/dist/packages/@ember/-internals/runtime/lib/ext/rsvp.js +3 -2
- package/dist/packages/@ember/-internals/runtime/lib/mixins/-proxy.js +6 -5
- package/dist/packages/@ember/-internals/runtime/lib/mixins/action_handler.js +4 -3
- package/dist/packages/@ember/-internals/runtime/lib/mixins/container_proxy.js +1 -1
- package/dist/packages/@ember/-internals/runtime/lib/mixins/registry_proxy.js +2 -1
- package/dist/packages/@ember/-internals/runtime/lib/mixins/target_action_support.js +4 -3
- package/dist/packages/@ember/-internals/string/index.js +1 -1
- package/dist/packages/@ember/-internals/utils/index.js +4 -4
- package/dist/packages/@ember/-internals/views/index.js +1 -1
- package/dist/packages/@ember/-internals/views/lib/compat/fallback-view-registry.js +1 -1
- package/dist/packages/@ember/-internals/views/lib/component_lookup.js +1 -1
- package/dist/packages/@ember/-internals/views/lib/mixins/action_support.js +5 -3
- package/dist/packages/@ember/-internals/views/lib/mixins/child_views_support.js +3 -3
- package/dist/packages/@ember/-internals/views/lib/mixins/class_names_support.js +4 -3
- package/dist/packages/@ember/-internals/views/lib/mixins/view_support.js +4 -3
- package/dist/packages/@ember/-internals/views/lib/system/event_dispatcher.js +6 -12
- package/dist/packages/@ember/-internals/views/lib/system/utils.js +3 -2
- package/dist/packages/@ember/-internals/views/lib/views/core_view.js +76 -8
- package/dist/packages/@ember/-internals/views/lib/views/states.js +4 -3
- package/dist/packages/@ember/application/index.js +16 -7
- package/dist/packages/@ember/application/instance.js +13 -9
- package/dist/packages/@ember/application/namespace.js +7 -6
- package/dist/packages/@ember/array/index.js +617 -11
- package/dist/packages/@ember/array/make.js +1 -0
- package/dist/packages/@ember/array/mutable.js +1 -1
- package/dist/packages/@ember/array/proxy.js +8 -5
- package/dist/packages/@ember/component/helper.js +4 -4
- package/dist/packages/@ember/component/index.js +4 -4
- package/dist/packages/@ember/controller/index.js +6 -6
- package/dist/packages/@ember/debug/container-debug-adapter.js +5 -4
- package/dist/packages/@ember/debug/data-adapter.js +7 -4
- package/dist/packages/@ember/debug/index.js +213 -4
- package/dist/packages/@ember/debug/lib/assert.js +47 -0
- package/dist/packages/@ember/debug/lib/deprecate.js +194 -4
- package/dist/packages/@ember/debug/lib/inspect.js +120 -2
- package/dist/packages/@ember/debug/lib/warn.js +94 -3
- package/dist/packages/@ember/engine/index.js +440 -17
- package/dist/packages/@ember/engine/instance.js +175 -11
- package/dist/packages/@ember/engine/parent.js +1 -0
- package/dist/packages/@ember/helper/index.js +4 -4
- package/dist/packages/@ember/instrumentation/index.js +2 -1
- package/dist/packages/@ember/modifier/index.js +13 -5
- package/dist/packages/@ember/modifier/on.js +15 -0
- package/dist/packages/@ember/object/-internals.js +6 -5
- package/dist/packages/@ember/object/compat.js +4 -3
- package/dist/packages/@ember/object/computed.js +4 -4
- package/dist/packages/@ember/object/core.js +861 -14
- package/dist/packages/@ember/object/evented.js +4 -4
- package/dist/packages/@ember/object/events.js +3 -3
- package/dist/packages/@ember/object/index.js +260 -9
- package/dist/packages/@ember/object/internals.js +1 -1
- package/dist/packages/@ember/object/lib/computed/computed_macros.js +8 -6
- package/dist/packages/@ember/object/lib/computed/reduce_computed_macros.js +8 -4
- package/dist/packages/@ember/object/mixin.js +6 -5
- package/dist/packages/@ember/object/observable.js +103 -9
- package/dist/packages/@ember/object/observers.js +3 -3
- package/dist/packages/@ember/object/promise-proxy-mixin.js +5 -5
- package/dist/packages/@ember/renderer/index.js +4 -4
- package/dist/packages/@ember/routing/-internals.js +3 -1
- package/dist/packages/@ember/routing/hash-location.js +2 -2
- package/dist/packages/@ember/routing/history-location.js +3 -2
- package/dist/packages/@ember/routing/index.js +4 -4
- package/dist/packages/@ember/routing/lib/dsl.js +2 -1
- package/dist/packages/@ember/routing/lib/generate_controller.js +4 -3
- package/dist/packages/@ember/routing/lib/router_state.js +26 -1
- package/dist/packages/@ember/routing/lib/routing-service.js +107 -9
- package/dist/packages/@ember/routing/lib/utils.js +238 -7
- package/dist/packages/@ember/routing/none-location.js +3 -2
- package/dist/packages/@ember/routing/route.js +1618 -22
- package/dist/packages/@ember/routing/router-service.js +638 -12
- package/dist/packages/@ember/routing/router.js +1449 -14
- package/dist/packages/@ember/runloop/index.js +760 -6
- package/dist/packages/@ember/service/index.js +3 -3
- package/dist/packages/@ember/template/index.js +4 -4
- package/dist/packages/@ember/utils/index.js +2 -1
- package/dist/packages/@ember/utils/lib/compare.js +159 -4
- package/dist/packages/@ember/utils/lib/is_empty.js +4 -4
- package/dist/packages/@ember/utils/lib/type-of.js +110 -1
- package/dist/packages/@glimmer/tracking/index.js +3 -3
- package/dist/packages/@glimmer/tracking/primitives/cache.js +3 -3
- package/dist/packages/ember/barrel.js +28 -13
- package/dist/packages/ember/version.js +1 -1
- package/dist/packages/ember-testing/lib/adapters/adapter.js +1 -1
- package/dist/packages/ember-testing/lib/adapters/qunit.js +2 -1
- package/dist/packages/ember-testing/lib/ext/application.js +2 -1
- package/dist/packages/ember-testing/lib/ext/rsvp.js +1 -1
- package/dist/packages/ember-testing/lib/helpers/and_then.js +2 -1
- package/dist/packages/ember-testing/lib/helpers/current_path.js +8 -6
- package/dist/packages/ember-testing/lib/helpers/current_route_name.js +8 -6
- package/dist/packages/ember-testing/lib/helpers/current_url.js +6 -5
- package/dist/packages/ember-testing/lib/helpers/pause_test.js +2 -1
- package/dist/packages/ember-testing/lib/helpers/visit.js +4 -3
- package/dist/packages/ember-testing/lib/helpers/wait.js +4 -3
- package/dist/packages/ember-testing/lib/initializers.js +15 -8
- package/dist/packages/ember-testing/lib/setup_for_testing.js +1 -1
- package/dist/packages/ember-testing/lib/test/run.js +1 -1
- package/dist/packages/router_js/index.js +2 -1
- package/dist/packages/shared-chunks/{alias-By_2yu5c.js → alias-Dri0koi2.js} +5 -3
- package/dist/packages/shared-chunks/array-3xbmc_4J.js +119 -0
- package/dist/packages/shared-chunks/{cache-gDE3bkXq.js → cache-BESCGvbE.js} +667 -1529
- package/dist/packages/shared-chunks/{core_view-Cxne2_wu.js → chunk-3SQBS3Y5-Cj4eryg1.js} +1 -88
- package/dist/packages/shared-chunks/{index-BXPoca1S.js → index-Llq6dmgX.js} +40 -4660
- package/dist/packages/shared-chunks/{is_proxy-Dmis-70B.js → is_proxy-DjvCKvd5.js} +1 -1
- package/dist/packages/shared-chunks/{mandatory-setter-1UQhiJOb.js → mandatory-setter-BiXq-dpN.js} +2 -1
- package/dist/packages/shared-chunks/{name-z9D9Yibn.js → name-Dx2bGFVv.js} +1 -1
- package/dist/packages/shared-chunks/{namespace_search-CBgHTkDh.js → namespace_search-btMaPM-_.js} +2 -2
- package/dist/packages/shared-chunks/{property_set-CW4q-uo4.js → property_set-BapAkp3X.js} +5 -4
- package/dist/packages/shared-chunks/{registry-DzfcDwii.js → registry-B8WARvkP.js} +3 -2
- package/dist/packages/shared-chunks/{router-B-Q1aYBn.js → router-DrLZsJeE.js} +2 -482
- package/dist/packages/shared-chunks/{set_properties-DvalyQdu.js → set_properties-BScfxzvI.js} +2 -2
- package/dist/packages/shared-chunks/setup-registry-du4pSGZi.js +48 -0
- package/dist/packages/shared-chunks/{to-string-D8i3mjEU.js → to-string-B1BmwUkt.js} +1 -1
- package/dist/packages/shared-chunks/unrecognized-url-error-zpz-JEoG.js +484 -0
- package/docs/data.json +152 -142
- package/package.json +4 -7
- package/types/stable/@ember/-internals/metal/lib/array.d.ts +1 -2
- package/types/stable/@ember/-internals/metal/lib/object-at.d.ts +4 -0
- package/types/stable/@ember/-internals/metal/lib/observer.d.ts +2 -1
- package/types/stable/@ember/array/index.d.ts +1 -1
- package/types/stable/@ember/array/make.d.ts +3 -0
- package/types/stable/@ember/debug/index.d.ts +3 -7
- package/types/stable/@ember/debug/lib/assert.d.ts +8 -0
- package/types/stable/@ember/engine/index.d.ts +1 -1
- package/types/stable/@ember/engine/instance.d.ts +2 -2
- package/types/stable/@ember/engine/parent.d.ts +3 -0
- package/types/stable/@ember/modifier/index.d.ts +1 -3
- package/types/stable/@ember/modifier/on.d.ts +5 -0
- package/types/stable/@ember/routing/lib/routing-service.d.ts +1 -1
- package/types/stable/@ember/routing/route.d.ts +2 -3
- package/types/stable/@ember/routing/router-service.d.ts +1 -1
- package/types/stable/@ember/routing/router.d.ts +4 -4
- package/types/stable/ember/barrel.d.ts +1 -1
- package/types/stable/ember/index.d.ts +1 -1
- package/types/stable/index.d.ts +5 -0
- package/dist/packages/shared-chunks/index-DTxy4Zgx.js +0 -641
- package/dist/packages/shared-chunks/index-PYiGj1jp.js +0 -2071
|
@@ -1,1669 +1,807 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { peekMeta, meta } from '../@ember/-internals/meta/lib/meta.js';
|
|
2
|
+
import { f as setupMandatorySetter, e as isObject, d as setListeners, h as setWithMandatorySetter } from './mandatory-setter-BiXq-dpN.js';
|
|
3
3
|
import { isDevelopingApp } from '@embroider/macros';
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
4
|
+
import { warn, debug } from '../@ember/debug/index.js';
|
|
5
|
+
import { isDestroyed, registerDestructor } from '../@glimmer/destroyable/index.js';
|
|
6
|
+
import { tagFor, CONSTANT_TAG, dirtyTagFor, updateTag as UPDATE_TAG, tagMetaFor, combine, validateTag, createUpdatableTag, valueForTag, CURRENT_TAG, untrack, ALLOW_CYCLES, consumeTag, track, isTracking, trackedData } from '../@glimmer/validator/index.js';
|
|
7
7
|
import { getCustomTagFor } from '../@glimmer/manager/index.js';
|
|
8
8
|
import { E as ENV } from './env-BJLX2Arx.js';
|
|
9
|
-
import {
|
|
10
|
-
import { t as toString, s as symbol } from './to-string-
|
|
11
|
-
import { s as setProxy } from './is_proxy-
|
|
9
|
+
import { assert } from '../@ember/debug/lib/assert.js';
|
|
10
|
+
import { t as toString, s as symbol } from './to-string-B1BmwUkt.js';
|
|
11
|
+
import { s as setProxy } from './is_proxy-DjvCKvd5.js';
|
|
12
12
|
import { isEmberArray } from '../@ember/array/-internals.js';
|
|
13
13
|
import { C as Cache } from './cache-qDyqAcpg.js';
|
|
14
14
|
import Version from '../ember/version.js';
|
|
15
15
|
import { getOwner } from '../@ember/-internals/owner/index.js';
|
|
16
|
-
import
|
|
16
|
+
import inspect from '../@ember/debug/lib/inspect.js';
|
|
17
17
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
function addListener(obj, eventName, target, method, once, sync = true) {
|
|
23
|
-
(isDevelopingApp() && !(Boolean(obj) && Boolean(eventName)) && assert('You must pass at least an object and event name to addListener', Boolean(obj) && Boolean(eventName)));
|
|
24
|
-
if (!method && 'function' === typeof target) {
|
|
25
|
-
method = target;
|
|
26
|
-
target = null;
|
|
27
|
-
}
|
|
28
|
-
meta(obj).addToListeners(eventName, target, method, once === true, sync);
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
Remove an event listener
|
|
33
|
-
|
|
34
|
-
Arguments should match those passed to `addListener`.
|
|
35
|
-
|
|
36
|
-
@method removeListener
|
|
37
|
-
@static
|
|
38
|
-
@for @ember/object/events
|
|
39
|
-
@param obj
|
|
40
|
-
@param {String} eventName
|
|
41
|
-
@param {Object|Function} target A target object or a function
|
|
42
|
-
@param {Function|String} method A function or the name of a function to be called on `target`
|
|
43
|
-
@public
|
|
44
|
-
*/
|
|
45
|
-
function removeListener(obj, eventName, targetOrFunction, functionOrName) {
|
|
46
|
-
(isDevelopingApp() && !(Boolean(obj) && Boolean(eventName) && (typeof targetOrFunction === 'function' || typeof targetOrFunction === 'object' && Boolean(functionOrName))) && assert('You must pass at least an object, event name, and method or target and method/method name to removeListener', Boolean(obj) && Boolean(eventName) && (typeof targetOrFunction === 'function' || typeof targetOrFunction === 'object' && Boolean(functionOrName))));
|
|
47
|
-
let target, method;
|
|
48
|
-
if (typeof targetOrFunction === 'object') {
|
|
49
|
-
target = targetOrFunction;
|
|
50
|
-
method = functionOrName;
|
|
18
|
+
function objectAt(array, index) {
|
|
19
|
+
if (Array.isArray(array)) {
|
|
20
|
+
return array[index];
|
|
51
21
|
} else {
|
|
52
|
-
|
|
53
|
-
method = targetOrFunction;
|
|
22
|
+
return array.objectAt(index);
|
|
54
23
|
}
|
|
55
|
-
let m = meta(obj);
|
|
56
|
-
m.removeFromListeners(eventName, target, method);
|
|
57
24
|
}
|
|
58
25
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
@method sendEvent
|
|
66
|
-
@static
|
|
67
|
-
@for @ember/object/events
|
|
68
|
-
@param obj
|
|
69
|
-
@param {String} eventName
|
|
70
|
-
@param {Array} params Optional parameters for each listener.
|
|
71
|
-
@return {Boolean} if the event was delivered to one or more actions
|
|
72
|
-
@public
|
|
73
|
-
*/
|
|
74
|
-
function sendEvent(obj, eventName, params, actions, _meta) {
|
|
75
|
-
if (actions === undefined) {
|
|
76
|
-
let meta = _meta === undefined ? peekMeta(obj) : _meta;
|
|
77
|
-
actions = meta !== null ? meta.matchingListeners(eventName) : undefined;
|
|
78
|
-
}
|
|
79
|
-
if (actions === undefined || actions.length === 0) {
|
|
80
|
-
return false;
|
|
26
|
+
// This is exported for `@tracked`, but should otherwise be avoided. Use `tagForObject`.
|
|
27
|
+
const SELF_TAG = symbol('SELF_TAG');
|
|
28
|
+
function tagForProperty(obj, propertyKey, addMandatorySetter = false, meta) {
|
|
29
|
+
let customTagFor = getCustomTagFor(obj);
|
|
30
|
+
if (customTagFor !== undefined) {
|
|
31
|
+
return customTagFor(obj, propertyKey, addMandatorySetter);
|
|
81
32
|
}
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
let method = actions[i + 1];
|
|
86
|
-
let once = actions[i + 2];
|
|
87
|
-
if (!method) {
|
|
88
|
-
continue;
|
|
89
|
-
}
|
|
90
|
-
if (once) {
|
|
91
|
-
removeListener(obj, eventName, target, method);
|
|
92
|
-
}
|
|
93
|
-
if (!target) {
|
|
94
|
-
target = obj;
|
|
95
|
-
}
|
|
96
|
-
let type = typeof method;
|
|
97
|
-
if (type === 'string' || type === 'symbol') {
|
|
98
|
-
method = target[method];
|
|
99
|
-
}
|
|
100
|
-
method.apply(target, params);
|
|
33
|
+
let tag = tagFor(obj, propertyKey, meta);
|
|
34
|
+
if (isDevelopingApp() && addMandatorySetter) {
|
|
35
|
+
setupMandatorySetter(tag, obj, propertyKey);
|
|
101
36
|
}
|
|
102
|
-
return
|
|
37
|
+
return tag;
|
|
103
38
|
}
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
@param obj
|
|
111
|
-
@param {String} eventName
|
|
112
|
-
@return {Boolean} if `obj` has listeners for event `eventName`
|
|
113
|
-
*/
|
|
114
|
-
function hasListeners(obj, eventName) {
|
|
115
|
-
let meta = peekMeta(obj);
|
|
116
|
-
if (meta === null) {
|
|
117
|
-
return false;
|
|
39
|
+
function tagForObject(obj) {
|
|
40
|
+
if (isObject(obj)) {
|
|
41
|
+
if (isDevelopingApp()) {
|
|
42
|
+
(isDevelopingApp() && !(!isDestroyed(obj)) && assert(isDestroyed(obj) ? `Cannot create a new tag for \`${toString(obj)}\` after it has been destroyed.` : '', !isDestroyed(obj)));
|
|
43
|
+
}
|
|
44
|
+
return tagFor(obj, SELF_TAG);
|
|
118
45
|
}
|
|
119
|
-
|
|
120
|
-
return matched !== undefined && matched.length > 0;
|
|
46
|
+
return CONSTANT_TAG;
|
|
121
47
|
}
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
a specified event or events are triggered.
|
|
126
|
-
|
|
127
|
-
``` javascript
|
|
128
|
-
import EmberObject from '@ember/object';
|
|
129
|
-
import { on } from '@ember/object/evented';
|
|
130
|
-
import { sendEvent } from '@ember/object/events';
|
|
131
|
-
|
|
132
|
-
let Job = EmberObject.extend({
|
|
133
|
-
logCompleted: on('completed', function() {
|
|
134
|
-
console.log('Job completed!');
|
|
135
|
-
})
|
|
136
|
-
});
|
|
137
|
-
|
|
138
|
-
let job = Job.create();
|
|
139
|
-
|
|
140
|
-
sendEvent(job, 'completed'); // Logs 'Job completed!'
|
|
141
|
-
```
|
|
142
|
-
|
|
143
|
-
@method on
|
|
144
|
-
@static
|
|
145
|
-
@for @ember/object/evented
|
|
146
|
-
@param {String} eventNames*
|
|
147
|
-
@param {Function} func
|
|
148
|
-
@return {Function} the listener function, passed as last argument to on(...)
|
|
149
|
-
@public
|
|
150
|
-
*/
|
|
151
|
-
function on(...args) {
|
|
152
|
-
let func = args.pop();
|
|
153
|
-
let events = args;
|
|
154
|
-
(isDevelopingApp() && !(typeof func === 'function') && assert('on expects function as last argument', typeof func === 'function'));
|
|
155
|
-
(isDevelopingApp() && !(events.length > 0 && events.every(p => typeof p === 'string' && p.length > 0)) && assert('on called without valid event names', events.length > 0 && events.every(p => typeof p === 'string' && p.length > 0)));
|
|
156
|
-
setListeners(func, events);
|
|
157
|
-
return func;
|
|
48
|
+
function markObjectAsDirty(obj, propertyKey) {
|
|
49
|
+
dirtyTagFor(obj, propertyKey);
|
|
50
|
+
dirtyTagFor(obj, SELF_TAG);
|
|
158
51
|
}
|
|
159
52
|
|
|
160
|
-
|
|
161
|
-
function
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
53
|
+
const CHAIN_PASS_THROUGH = new WeakSet();
|
|
54
|
+
function finishLazyChains(meta, key, value) {
|
|
55
|
+
let lazyTags = meta.readableLazyChainsFor(key);
|
|
56
|
+
if (lazyTags === undefined) {
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
if (isObject(value)) {
|
|
60
|
+
for (let [tag, deps] of lazyTags) {
|
|
61
|
+
UPDATE_TAG(tag, getChainTagsForKey(value, deps, tagMetaFor(value), peekMeta(value)));
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
lazyTags.length = 0;
|
|
170
65
|
}
|
|
171
|
-
function
|
|
172
|
-
|
|
173
|
-
|
|
66
|
+
function getChainTagsForKeys(obj, keys, tagMeta, meta) {
|
|
67
|
+
let tags = [];
|
|
68
|
+
for (let key of keys) {
|
|
69
|
+
getChainTags(tags, obj, key, tagMeta, meta);
|
|
174
70
|
}
|
|
175
|
-
|
|
71
|
+
return combine(tags);
|
|
176
72
|
}
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
/**
|
|
180
|
-
Array of named queues. This array determines the order in which queues
|
|
181
|
-
are flushed at the end of the RunLoop. You can define your own queues by
|
|
182
|
-
simply adding the queue name to this array. Normally you should not need
|
|
183
|
-
to inspect or modify this property.
|
|
184
|
-
|
|
185
|
-
@property queues
|
|
186
|
-
@type Array
|
|
187
|
-
@default ['actions', 'destroy']
|
|
188
|
-
@private
|
|
189
|
-
*/
|
|
190
|
-
const _queues = ['actions',
|
|
191
|
-
// used in router transitions to prevent unnecessary loading state entry
|
|
192
|
-
// if all context promises resolve on the 'actions' queue first
|
|
193
|
-
'routerTransitions', 'render', 'afterRender', 'destroy',
|
|
194
|
-
// used to re-throw unhandled RSVP rejection errors specifically in this
|
|
195
|
-
// position to avoid breaking anything rendered in the other sections
|
|
196
|
-
_rsvpErrorQueue];
|
|
197
|
-
|
|
198
|
-
/**
|
|
199
|
-
* @internal
|
|
200
|
-
* @private
|
|
201
|
-
*/
|
|
202
|
-
const _backburner = new Backburner(_queues, {
|
|
203
|
-
defaultQueue: 'actions',
|
|
204
|
-
onBegin,
|
|
205
|
-
onEnd,
|
|
206
|
-
onErrorTarget,
|
|
207
|
-
onErrorMethod: 'onerror',
|
|
208
|
-
flush
|
|
209
|
-
});
|
|
210
|
-
|
|
211
|
-
/**
|
|
212
|
-
@module @ember/runloop
|
|
213
|
-
*/
|
|
214
|
-
// ..........................................................
|
|
215
|
-
// run - this is ideally the only public API the dev sees
|
|
216
|
-
//
|
|
217
|
-
|
|
218
|
-
/**
|
|
219
|
-
Runs the passed target and method inside of a RunLoop, ensuring any
|
|
220
|
-
deferred actions including bindings and views updates are flushed at the
|
|
221
|
-
end.
|
|
222
|
-
|
|
223
|
-
Normally you should not need to invoke this method yourself. However if
|
|
224
|
-
you are implementing raw event handlers when interfacing with other
|
|
225
|
-
libraries or plugins, you should probably wrap all of your code inside this
|
|
226
|
-
call.
|
|
227
|
-
|
|
228
|
-
```javascript
|
|
229
|
-
import { run } from '@ember/runloop';
|
|
230
|
-
|
|
231
|
-
run(function() {
|
|
232
|
-
// code to be executed within a RunLoop
|
|
233
|
-
});
|
|
234
|
-
```
|
|
235
|
-
@method run
|
|
236
|
-
@for @ember/runloop
|
|
237
|
-
@static
|
|
238
|
-
@param {Object} [target] target of method to call
|
|
239
|
-
@param {Function|String} method Method to invoke.
|
|
240
|
-
May be a function or a string. If you pass a string
|
|
241
|
-
then it will be looked up on the passed target.
|
|
242
|
-
@param {Object} [args*] Any additional arguments you wish to pass to the method.
|
|
243
|
-
@return {Object} return value from invoking the passed function.
|
|
244
|
-
@public
|
|
245
|
-
*/
|
|
246
|
-
|
|
247
|
-
function run(...args) {
|
|
248
|
-
// @ts-expect-error TS doesn't like our spread args
|
|
249
|
-
return _backburner.run(...args);
|
|
73
|
+
function getChainTagsForKey(obj, key, tagMeta, meta) {
|
|
74
|
+
return combine(getChainTags([], obj, key, tagMeta, meta));
|
|
250
75
|
}
|
|
76
|
+
function getChainTags(chainTags, obj, path, tagMeta, meta$1) {
|
|
77
|
+
let current = obj;
|
|
78
|
+
let currentTagMeta = tagMeta;
|
|
79
|
+
let currentMeta = meta$1;
|
|
80
|
+
let pathLength = path.length;
|
|
81
|
+
let segmentEnd = -1;
|
|
82
|
+
// prevent closures
|
|
83
|
+
let segment, descriptor;
|
|
251
84
|
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
85
|
+
// eslint-disable-next-line no-constant-condition
|
|
86
|
+
while (true) {
|
|
87
|
+
let lastSegmentEnd = segmentEnd + 1;
|
|
88
|
+
segmentEnd = path.indexOf('.', lastSegmentEnd);
|
|
89
|
+
if (segmentEnd === -1) {
|
|
90
|
+
segmentEnd = pathLength;
|
|
91
|
+
}
|
|
92
|
+
segment = path.slice(lastSegmentEnd, segmentEnd);
|
|
256
93
|
|
|
257
|
-
|
|
94
|
+
// If the segment is an @each, we can process it and then break
|
|
95
|
+
if (segment === '@each' && segmentEnd !== pathLength) {
|
|
96
|
+
lastSegmentEnd = segmentEnd + 1;
|
|
97
|
+
segmentEnd = path.indexOf('.', lastSegmentEnd);
|
|
98
|
+
let arrLength = current.length;
|
|
99
|
+
if (typeof arrLength !== 'number' ||
|
|
100
|
+
// TODO: should the second test be `isEmberArray` instead?
|
|
101
|
+
!(Array.isArray(current) || 'objectAt' in current)) {
|
|
102
|
+
// If the current object isn't an array, there's nothing else to do,
|
|
103
|
+
// we don't watch individual properties. Break out of the loop.
|
|
104
|
+
break;
|
|
105
|
+
} else if (arrLength === 0) {
|
|
106
|
+
// Fast path for empty arrays
|
|
107
|
+
chainTags.push(tagForProperty(current, '[]'));
|
|
108
|
+
break;
|
|
109
|
+
}
|
|
110
|
+
if (segmentEnd === -1) {
|
|
111
|
+
segment = path.slice(lastSegmentEnd);
|
|
112
|
+
} else {
|
|
113
|
+
// Deprecated, remove once we turn the deprecation into an assertion
|
|
114
|
+
segment = path.slice(lastSegmentEnd, segmentEnd);
|
|
115
|
+
}
|
|
258
116
|
|
|
259
|
-
|
|
117
|
+
// Push the tags for each item's property
|
|
118
|
+
for (let i = 0; i < arrLength; i++) {
|
|
119
|
+
let item = objectAt(current, i);
|
|
120
|
+
if (item) {
|
|
121
|
+
(isDevelopingApp() && !(typeof item === 'object') && assert(`When using @each to observe the array \`${current.toString()}\`, the items in the array must be objects`, typeof item === 'object'));
|
|
122
|
+
chainTags.push(tagForProperty(item, segment, true));
|
|
123
|
+
currentMeta = peekMeta(item);
|
|
124
|
+
descriptor = currentMeta !== null ? currentMeta.peekDescriptors(segment) : undefined;
|
|
260
125
|
|
|
261
|
-
|
|
262
|
-
|
|
126
|
+
// If the key is an alias, we need to bootstrap it
|
|
127
|
+
if (descriptor !== undefined && typeof descriptor.altKey === 'string') {
|
|
128
|
+
item[segment];
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
263
132
|
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
```
|
|
268
|
-
|
|
269
|
-
Alternatively, if called within an existing run loop:
|
|
270
|
-
|
|
271
|
-
```javascript
|
|
272
|
-
import { run, join } from '@ember/runloop';
|
|
273
|
-
|
|
274
|
-
run(function() {
|
|
275
|
-
// creates a new run-loop
|
|
276
|
-
|
|
277
|
-
join(function() {
|
|
278
|
-
// joins with the existing run-loop, and queues for invocation on
|
|
279
|
-
// the existing run-loops action queue.
|
|
280
|
-
});
|
|
281
|
-
});
|
|
282
|
-
```
|
|
283
|
-
|
|
284
|
-
@method join
|
|
285
|
-
@static
|
|
286
|
-
@for @ember/runloop
|
|
287
|
-
@param {Object} [target] target of method to call
|
|
288
|
-
@param {Function|String} method Method to invoke.
|
|
289
|
-
May be a function or a string. If you pass a string
|
|
290
|
-
then it will be looked up on the passed target.
|
|
291
|
-
@param {Object} [args*] Any additional arguments you wish to pass to the method.
|
|
292
|
-
@return {Object} Return value from invoking the passed function. Please note,
|
|
293
|
-
when called within an existing loop, no return value is possible.
|
|
294
|
-
@public
|
|
295
|
-
*/
|
|
296
|
-
|
|
297
|
-
function join(methodOrTarget, methodOrArg, ...additionalArgs) {
|
|
298
|
-
return _backburner.join(methodOrTarget, methodOrArg, ...additionalArgs);
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
/**
|
|
302
|
-
Allows you to specify which context to call the specified function in while
|
|
303
|
-
adding the execution of that function to the Ember run loop. This ability
|
|
304
|
-
makes this method a great way to asynchronously integrate third-party libraries
|
|
305
|
-
into your Ember application.
|
|
306
|
-
|
|
307
|
-
`bind` takes two main arguments, the desired context and the function to
|
|
308
|
-
invoke in that context. Any additional arguments will be supplied as arguments
|
|
309
|
-
to the function that is passed in.
|
|
310
|
-
|
|
311
|
-
Let's use the creation of a TinyMCE component as an example. Currently,
|
|
312
|
-
TinyMCE provides a setup configuration option we can use to do some processing
|
|
313
|
-
after the TinyMCE instance is initialized but before it is actually rendered.
|
|
314
|
-
We can use that setup option to do some additional setup for our component.
|
|
315
|
-
The component itself could look something like the following:
|
|
316
|
-
|
|
317
|
-
```app/components/rich-text-editor.js
|
|
318
|
-
import Component from '@ember/component';
|
|
319
|
-
import { on } from '@ember/object/evented';
|
|
320
|
-
import { bind } from '@ember/runloop';
|
|
321
|
-
|
|
322
|
-
export default Component.extend({
|
|
323
|
-
initializeTinyMCE: on('didInsertElement', function() {
|
|
324
|
-
tinymce.init({
|
|
325
|
-
selector: '#' + this.$().prop('id'),
|
|
326
|
-
setup: bind(this, this.setupEditor)
|
|
327
|
-
});
|
|
328
|
-
}),
|
|
329
|
-
|
|
330
|
-
didInsertElement() {
|
|
331
|
-
tinymce.init({
|
|
332
|
-
selector: '#' + this.$().prop('id'),
|
|
333
|
-
setup: bind(this, this.setupEditor)
|
|
334
|
-
});
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
setupEditor(editor) {
|
|
338
|
-
this.set('editor', editor);
|
|
339
|
-
|
|
340
|
-
editor.on('change', function() {
|
|
341
|
-
console.log('content changed!');
|
|
342
|
-
});
|
|
343
|
-
}
|
|
344
|
-
});
|
|
345
|
-
```
|
|
346
|
-
|
|
347
|
-
In this example, we use `bind` to bind the setupEditor method to the
|
|
348
|
-
context of the RichTextEditor component and to have the invocation of that
|
|
349
|
-
method be safely handled and executed by the Ember run loop.
|
|
350
|
-
|
|
351
|
-
@method bind
|
|
352
|
-
@static
|
|
353
|
-
@for @ember/runloop
|
|
354
|
-
@param {Object} [target] target of method to call
|
|
355
|
-
@param {Function|String} method Method to invoke.
|
|
356
|
-
May be a function or a string. If you pass a string
|
|
357
|
-
then it will be looked up on the passed target.
|
|
358
|
-
@param {Object} [args*] Any additional arguments you wish to pass to the method.
|
|
359
|
-
@return {Function} returns a new function that will always have a particular context
|
|
360
|
-
@since 1.4.0
|
|
361
|
-
@public
|
|
362
|
-
*/
|
|
363
|
-
|
|
364
|
-
// This final fallback is the equivalent of the (quite unsafe!) type for `bind`
|
|
365
|
-
// from TS' defs for `Function.prototype.bind`. In general, it means we have a
|
|
366
|
-
// loss of safety if we do not
|
|
367
|
-
|
|
368
|
-
function bind(...curried) {
|
|
369
|
-
(isDevelopingApp() && !(function (methodOrTarget, methodOrArg) {
|
|
370
|
-
// Applies the same logic as backburner parseArgs for detecting if a method
|
|
371
|
-
// is actually being passed.
|
|
372
|
-
let length = arguments.length;
|
|
373
|
-
if (length === 0) {
|
|
374
|
-
return false;
|
|
375
|
-
} else if (length === 1) {
|
|
376
|
-
return typeof methodOrTarget === 'function';
|
|
377
|
-
} else {
|
|
378
|
-
return typeof methodOrArg === 'function' ||
|
|
379
|
-
// second argument is a function
|
|
380
|
-
methodOrTarget !== null && typeof methodOrArg === 'string' && methodOrArg in methodOrTarget ||
|
|
381
|
-
// second argument is the name of a method in first argument
|
|
382
|
-
typeof methodOrTarget === 'function' //first argument is a function
|
|
383
|
-
;
|
|
384
|
-
}
|
|
385
|
-
// @ts-expect-error TS doesn't like our spread args
|
|
386
|
-
}(...curried)) && assert('could not find a suitable method to bind', function (methodOrTarget, methodOrArg) {
|
|
387
|
-
let length = arguments.length;
|
|
388
|
-
if (length === 0) {
|
|
389
|
-
return false;
|
|
390
|
-
} else if (length === 1) {
|
|
391
|
-
return typeof methodOrTarget === 'function';
|
|
392
|
-
} else {
|
|
393
|
-
return typeof methodOrArg === 'function' || methodOrTarget !== null && typeof methodOrArg === 'string' && methodOrArg in methodOrTarget || typeof methodOrTarget === 'function';
|
|
394
|
-
}
|
|
395
|
-
}(...curried))); // @ts-expect-error TS doesn't like our spread args
|
|
396
|
-
return (...args) => join(...curried.concat(args));
|
|
397
|
-
}
|
|
398
|
-
|
|
399
|
-
/**
|
|
400
|
-
Begins a new RunLoop. Any deferred actions invoked after the begin will
|
|
401
|
-
be buffered until you invoke a matching call to `end()`. This is
|
|
402
|
-
a lower-level way to use a RunLoop instead of using `run()`.
|
|
403
|
-
|
|
404
|
-
```javascript
|
|
405
|
-
import { begin, end } from '@ember/runloop';
|
|
406
|
-
|
|
407
|
-
begin();
|
|
408
|
-
// code to be executed within a RunLoop
|
|
409
|
-
end();
|
|
410
|
-
```
|
|
411
|
-
|
|
412
|
-
@method begin
|
|
413
|
-
@static
|
|
414
|
-
@for @ember/runloop
|
|
415
|
-
@return {void}
|
|
416
|
-
@public
|
|
417
|
-
*/
|
|
418
|
-
function begin() {
|
|
419
|
-
_backburner.begin();
|
|
420
|
-
}
|
|
421
|
-
|
|
422
|
-
/**
|
|
423
|
-
Ends a RunLoop. This must be called sometime after you call
|
|
424
|
-
`begin()` to flush any deferred actions. This is a lower-level way
|
|
425
|
-
to use a RunLoop instead of using `run()`.
|
|
426
|
-
|
|
427
|
-
```javascript
|
|
428
|
-
import { begin, end } from '@ember/runloop';
|
|
429
|
-
|
|
430
|
-
begin();
|
|
431
|
-
// code to be executed within a RunLoop
|
|
432
|
-
end();
|
|
433
|
-
```
|
|
434
|
-
|
|
435
|
-
@method end
|
|
436
|
-
@static
|
|
437
|
-
@for @ember/runloop
|
|
438
|
-
@return {void}
|
|
439
|
-
@public
|
|
440
|
-
*/
|
|
441
|
-
function end() {
|
|
442
|
-
_backburner.end();
|
|
443
|
-
}
|
|
444
|
-
|
|
445
|
-
/**
|
|
446
|
-
Adds the passed target/method and any optional arguments to the named
|
|
447
|
-
queue to be executed at the end of the RunLoop. If you have not already
|
|
448
|
-
started a RunLoop when calling this method one will be started for you
|
|
449
|
-
automatically.
|
|
450
|
-
|
|
451
|
-
At the end of a RunLoop, any methods scheduled in this way will be invoked.
|
|
452
|
-
Methods will be invoked in an order matching the named queues defined in
|
|
453
|
-
the `queues` property.
|
|
454
|
-
|
|
455
|
-
```javascript
|
|
456
|
-
import { schedule } from '@ember/runloop';
|
|
457
|
-
|
|
458
|
-
schedule('afterRender', this, function() {
|
|
459
|
-
// this will be executed in the 'afterRender' queue
|
|
460
|
-
console.log('scheduled on afterRender queue');
|
|
461
|
-
});
|
|
462
|
-
|
|
463
|
-
schedule('actions', this, function() {
|
|
464
|
-
// this will be executed in the 'actions' queue
|
|
465
|
-
console.log('scheduled on actions queue');
|
|
466
|
-
});
|
|
467
|
-
|
|
468
|
-
// Note the functions will be run in order based on the run queues order.
|
|
469
|
-
// Output would be:
|
|
470
|
-
// scheduled on actions queue
|
|
471
|
-
// scheduled on afterRender queue
|
|
472
|
-
```
|
|
473
|
-
|
|
474
|
-
@method schedule
|
|
475
|
-
@static
|
|
476
|
-
@for @ember/runloop
|
|
477
|
-
@param {String} queue The name of the queue to schedule against. Default queues is 'actions'
|
|
478
|
-
@param {Object} [target] target object to use as the context when invoking a method.
|
|
479
|
-
@param {String|Function} method The method to invoke. If you pass a string it
|
|
480
|
-
will be resolved on the target object at the time the scheduled item is
|
|
481
|
-
invoked allowing you to change the target function.
|
|
482
|
-
@param {Object} [arguments*] Optional arguments to be passed to the queued method.
|
|
483
|
-
@return {*} Timer information for use in canceling, see `cancel`.
|
|
484
|
-
@public
|
|
485
|
-
*/
|
|
486
|
-
|
|
487
|
-
function schedule(...args) {
|
|
488
|
-
// @ts-expect-error TS doesn't like the rest args here
|
|
489
|
-
return _backburner.schedule(...args);
|
|
490
|
-
}
|
|
491
|
-
|
|
492
|
-
// Used by global test teardown
|
|
493
|
-
function _hasScheduledTimers() {
|
|
494
|
-
return _backburner.hasTimers();
|
|
495
|
-
}
|
|
496
|
-
|
|
497
|
-
// Used by global test teardown
|
|
498
|
-
function _cancelTimers() {
|
|
499
|
-
_backburner.cancelTimers();
|
|
500
|
-
}
|
|
501
|
-
|
|
502
|
-
/**
|
|
503
|
-
Invokes the passed target/method and optional arguments after a specified
|
|
504
|
-
period of time. The last parameter of this method must always be a number
|
|
505
|
-
of milliseconds.
|
|
506
|
-
|
|
507
|
-
You should use this method whenever you need to run some action after a
|
|
508
|
-
period of time instead of using `setTimeout()`. This method will ensure that
|
|
509
|
-
items that expire during the same script execution cycle all execute
|
|
510
|
-
together, which is often more efficient than using a real setTimeout.
|
|
511
|
-
|
|
512
|
-
```javascript
|
|
513
|
-
import { later } from '@ember/runloop';
|
|
514
|
-
|
|
515
|
-
later(myContext, function() {
|
|
516
|
-
// code here will execute within a RunLoop in about 500ms with this == myContext
|
|
517
|
-
}, 500);
|
|
518
|
-
```
|
|
519
|
-
|
|
520
|
-
@method later
|
|
521
|
-
@static
|
|
522
|
-
@for @ember/runloop
|
|
523
|
-
@param {Object} [target] target of method to invoke
|
|
524
|
-
@param {Function|String} method The method to invoke.
|
|
525
|
-
If you pass a string it will be resolved on the
|
|
526
|
-
target at the time the method is invoked.
|
|
527
|
-
@param {Object} [args*] Optional arguments to pass to the timeout.
|
|
528
|
-
@param {Number} wait Number of milliseconds to wait.
|
|
529
|
-
@return {*} Timer information for use in canceling, see `cancel`.
|
|
530
|
-
@public
|
|
531
|
-
*/
|
|
532
|
-
|
|
533
|
-
function later(...args) {
|
|
534
|
-
return _backburner.later(...args);
|
|
535
|
-
}
|
|
536
|
-
|
|
537
|
-
/**
|
|
538
|
-
Schedule a function to run one time during the current RunLoop. This is equivalent
|
|
539
|
-
to calling `scheduleOnce` with the "actions" queue.
|
|
540
|
-
|
|
541
|
-
@method once
|
|
542
|
-
@static
|
|
543
|
-
@for @ember/runloop
|
|
544
|
-
@param {Object} [target] The target of the method to invoke.
|
|
545
|
-
@param {Function|String} method The method to invoke.
|
|
546
|
-
If you pass a string it will be resolved on the
|
|
547
|
-
target at the time the method is invoked.
|
|
548
|
-
@param {Object} [args*] Optional arguments to pass to the timeout.
|
|
549
|
-
@return {Object} Timer information for use in canceling, see `cancel`.
|
|
550
|
-
@public
|
|
551
|
-
*/
|
|
552
|
-
|
|
553
|
-
function once(...args) {
|
|
554
|
-
// @ts-expect-error TS doesn't like the rest args here
|
|
555
|
-
return _backburner.scheduleOnce('actions', ...args);
|
|
556
|
-
}
|
|
557
|
-
|
|
558
|
-
/**
|
|
559
|
-
Schedules a function to run one time in a given queue of the current RunLoop.
|
|
560
|
-
Calling this method with the same queue/target/method combination will have
|
|
561
|
-
no effect (past the initial call).
|
|
562
|
-
|
|
563
|
-
Note that although you can pass optional arguments these will not be
|
|
564
|
-
considered when looking for duplicates. New arguments will replace previous
|
|
565
|
-
calls.
|
|
566
|
-
|
|
567
|
-
```javascript
|
|
568
|
-
import { run, scheduleOnce } from '@ember/runloop';
|
|
569
|
-
|
|
570
|
-
function sayHi() {
|
|
571
|
-
console.log('hi');
|
|
572
|
-
}
|
|
573
|
-
|
|
574
|
-
run(function() {
|
|
575
|
-
scheduleOnce('afterRender', myContext, sayHi);
|
|
576
|
-
scheduleOnce('afterRender', myContext, sayHi);
|
|
577
|
-
// sayHi will only be executed once, in the afterRender queue of the RunLoop
|
|
578
|
-
});
|
|
579
|
-
```
|
|
580
|
-
|
|
581
|
-
Also note that for `scheduleOnce` to prevent additional calls, you need to
|
|
582
|
-
pass the same function instance. The following case works as expected:
|
|
583
|
-
|
|
584
|
-
```javascript
|
|
585
|
-
function log() {
|
|
586
|
-
console.log('Logging only once');
|
|
587
|
-
}
|
|
588
|
-
|
|
589
|
-
function scheduleIt() {
|
|
590
|
-
scheduleOnce('actions', myContext, log);
|
|
591
|
-
}
|
|
592
|
-
|
|
593
|
-
scheduleIt();
|
|
594
|
-
scheduleIt();
|
|
595
|
-
```
|
|
596
|
-
|
|
597
|
-
But this other case will schedule the function multiple times:
|
|
598
|
-
|
|
599
|
-
```javascript
|
|
600
|
-
import { scheduleOnce } from '@ember/runloop';
|
|
601
|
-
|
|
602
|
-
function scheduleIt() {
|
|
603
|
-
scheduleOnce('actions', myContext, function() {
|
|
604
|
-
console.log('Closure');
|
|
605
|
-
});
|
|
606
|
-
}
|
|
607
|
-
|
|
608
|
-
scheduleIt();
|
|
609
|
-
scheduleIt();
|
|
610
|
-
|
|
611
|
-
// "Closure" will print twice, even though we're using `scheduleOnce`,
|
|
612
|
-
// because the function we pass to it won't match the
|
|
613
|
-
// previously scheduled operation.
|
|
614
|
-
```
|
|
615
|
-
|
|
616
|
-
Available queues, and their order, can be found at `queues`
|
|
617
|
-
|
|
618
|
-
@method scheduleOnce
|
|
619
|
-
@static
|
|
620
|
-
@for @ember/runloop
|
|
621
|
-
@param {String} [queue] The name of the queue to schedule against. Default queues is 'actions'.
|
|
622
|
-
@param {Object} [target] The target of the method to invoke.
|
|
623
|
-
@param {Function|String} method The method to invoke.
|
|
624
|
-
If you pass a string it will be resolved on the
|
|
625
|
-
target at the time the method is invoked.
|
|
626
|
-
@param {Object} [args*] Optional arguments to pass to the timeout.
|
|
627
|
-
@return {Object} Timer information for use in canceling, see `cancel`.
|
|
628
|
-
@public
|
|
629
|
-
*/
|
|
630
|
-
|
|
631
|
-
function scheduleOnce(...args) {
|
|
632
|
-
// @ts-expect-error TS doesn't like the rest args here
|
|
633
|
-
return _backburner.scheduleOnce(...args);
|
|
634
|
-
}
|
|
635
|
-
|
|
636
|
-
/**
|
|
637
|
-
Schedules an item to run from within a separate run loop, after
|
|
638
|
-
control has been returned to the system. This is equivalent to calling
|
|
639
|
-
`later` with a wait time of 1ms.
|
|
640
|
-
|
|
641
|
-
```javascript
|
|
642
|
-
import { next } from '@ember/runloop';
|
|
643
|
-
|
|
644
|
-
next(myContext, function() {
|
|
645
|
-
// code to be executed in the next run loop,
|
|
646
|
-
// which will be scheduled after the current one
|
|
647
|
-
});
|
|
648
|
-
```
|
|
649
|
-
|
|
650
|
-
Multiple operations scheduled with `next` will coalesce
|
|
651
|
-
into the same later run loop, along with any other operations
|
|
652
|
-
scheduled by `later` that expire right around the same
|
|
653
|
-
time that `next` operations will fire.
|
|
654
|
-
|
|
655
|
-
Note that there are often alternatives to using `next`.
|
|
656
|
-
For instance, if you'd like to schedule an operation to happen
|
|
657
|
-
after all DOM element operations have completed within the current
|
|
658
|
-
run loop, you can make use of the `afterRender` run loop queue (added
|
|
659
|
-
by the `ember-views` package, along with the preceding `render` queue
|
|
660
|
-
where all the DOM element operations happen).
|
|
661
|
-
|
|
662
|
-
Example:
|
|
663
|
-
|
|
664
|
-
```app/components/my-component.js
|
|
665
|
-
import Component from '@ember/component';
|
|
666
|
-
import { scheduleOnce } from '@ember/runloop';
|
|
667
|
-
|
|
668
|
-
export Component.extend({
|
|
669
|
-
didInsertElement() {
|
|
670
|
-
this._super(...arguments);
|
|
671
|
-
scheduleOnce('afterRender', this, 'processChildElements');
|
|
672
|
-
},
|
|
673
|
-
|
|
674
|
-
processChildElements() {
|
|
675
|
-
// ... do something with component's child component
|
|
676
|
-
// elements after they've finished rendering, which
|
|
677
|
-
// can't be done within this component's
|
|
678
|
-
// `didInsertElement` hook because that gets run
|
|
679
|
-
// before the child elements have been added to the DOM.
|
|
133
|
+
// Push the tag for the array length itself
|
|
134
|
+
chainTags.push(tagForProperty(current, '[]', true, currentTagMeta));
|
|
135
|
+
break;
|
|
680
136
|
}
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
One benefit of the above approach compared to using `next` is
|
|
685
|
-
that you will be able to perform DOM/CSS operations before unprocessed
|
|
686
|
-
elements are rendered to the screen, which may prevent flickering or
|
|
687
|
-
other artifacts caused by delaying processing until after rendering.
|
|
688
|
-
|
|
689
|
-
The other major benefit to the above approach is that `next`
|
|
690
|
-
introduces an element of non-determinism, which can make things much
|
|
691
|
-
harder to test, due to its reliance on `setTimeout`; it's much harder
|
|
692
|
-
to guarantee the order of scheduled operations when they are scheduled
|
|
693
|
-
outside of the current run loop, i.e. with `next`.
|
|
694
|
-
|
|
695
|
-
@method next
|
|
696
|
-
@static
|
|
697
|
-
@for @ember/runloop
|
|
698
|
-
@param {Object} [target] target of method to invoke
|
|
699
|
-
@param {Function|String} method The method to invoke.
|
|
700
|
-
If you pass a string it will be resolved on the
|
|
701
|
-
target at the time the method is invoked.
|
|
702
|
-
@param {Object} [args*] Optional arguments to pass to the timeout.
|
|
703
|
-
@return {Object} Timer information for use in canceling, see `cancel`.
|
|
704
|
-
@public
|
|
705
|
-
*/
|
|
706
|
-
|
|
707
|
-
function next(...args) {
|
|
708
|
-
return _backburner.later(...args, 1);
|
|
709
|
-
}
|
|
710
|
-
|
|
711
|
-
/**
|
|
712
|
-
Cancels a scheduled item. Must be a value returned by `later()`,
|
|
713
|
-
`once()`, `scheduleOnce()`, `next()`, `debounce()`, or
|
|
714
|
-
`throttle()`.
|
|
715
|
-
|
|
716
|
-
```javascript
|
|
717
|
-
import {
|
|
718
|
-
next,
|
|
719
|
-
cancel,
|
|
720
|
-
later,
|
|
721
|
-
scheduleOnce,
|
|
722
|
-
once,
|
|
723
|
-
throttle,
|
|
724
|
-
debounce
|
|
725
|
-
} from '@ember/runloop';
|
|
726
|
-
|
|
727
|
-
let runNext = next(myContext, function() {
|
|
728
|
-
// will not be executed
|
|
729
|
-
});
|
|
730
|
-
|
|
731
|
-
cancel(runNext);
|
|
732
|
-
|
|
733
|
-
let runLater = later(myContext, function() {
|
|
734
|
-
// will not be executed
|
|
735
|
-
}, 500);
|
|
736
|
-
|
|
737
|
-
cancel(runLater);
|
|
738
|
-
|
|
739
|
-
let runScheduleOnce = scheduleOnce('afterRender', myContext, function() {
|
|
740
|
-
// will not be executed
|
|
741
|
-
});
|
|
742
|
-
|
|
743
|
-
cancel(runScheduleOnce);
|
|
744
|
-
|
|
745
|
-
let runOnce = once(myContext, function() {
|
|
746
|
-
// will not be executed
|
|
747
|
-
});
|
|
748
|
-
|
|
749
|
-
cancel(runOnce);
|
|
750
|
-
|
|
751
|
-
let throttle = throttle(myContext, function() {
|
|
752
|
-
// will not be executed
|
|
753
|
-
}, 1, false);
|
|
754
|
-
|
|
755
|
-
cancel(throttle);
|
|
756
|
-
|
|
757
|
-
let debounce = debounce(myContext, function() {
|
|
758
|
-
// will not be executed
|
|
759
|
-
}, 1);
|
|
760
|
-
|
|
761
|
-
cancel(debounce);
|
|
762
|
-
|
|
763
|
-
let debounceImmediate = debounce(myContext, function() {
|
|
764
|
-
// will be executed since we passed in true (immediate)
|
|
765
|
-
}, 100, true);
|
|
766
|
-
|
|
767
|
-
// the 100ms delay until this method can be called again will be canceled
|
|
768
|
-
cancel(debounceImmediate);
|
|
769
|
-
```
|
|
770
|
-
|
|
771
|
-
@method cancel
|
|
772
|
-
@static
|
|
773
|
-
@for @ember/runloop
|
|
774
|
-
@param {Object} [timer] Timer object to cancel
|
|
775
|
-
@return {Boolean} true if canceled or false/undefined if it wasn't found
|
|
776
|
-
@public
|
|
777
|
-
*/
|
|
778
|
-
function cancel(timer) {
|
|
779
|
-
return _backburner.cancel(timer);
|
|
780
|
-
}
|
|
781
|
-
|
|
782
|
-
/**
|
|
783
|
-
Delay calling the target method until the debounce period has elapsed
|
|
784
|
-
with no additional debounce calls. If `debounce` is called again before
|
|
785
|
-
the specified time has elapsed, the timer is reset and the entire period
|
|
786
|
-
must pass again before the target method is called.
|
|
787
|
-
|
|
788
|
-
This method should be used when an event may be called multiple times
|
|
789
|
-
but the action should only be called once when the event is done firing.
|
|
790
|
-
A common example is for scroll events where you only want updates to
|
|
791
|
-
happen once scrolling has ceased.
|
|
792
|
-
|
|
793
|
-
```javascript
|
|
794
|
-
import { debounce } from '@ember/runloop';
|
|
795
|
-
|
|
796
|
-
function whoRan() {
|
|
797
|
-
console.log(this.name + ' ran.');
|
|
798
|
-
}
|
|
799
|
-
|
|
800
|
-
let myContext = { name: 'debounce' };
|
|
801
|
-
|
|
802
|
-
debounce(myContext, whoRan, 150);
|
|
803
|
-
|
|
804
|
-
// less than 150ms passes
|
|
805
|
-
debounce(myContext, whoRan, 150);
|
|
806
|
-
|
|
807
|
-
// 150ms passes
|
|
808
|
-
// whoRan is invoked with context myContext
|
|
809
|
-
// console logs 'debounce ran.' one time.
|
|
810
|
-
```
|
|
811
|
-
|
|
812
|
-
Immediate allows you to run the function immediately, but debounce
|
|
813
|
-
other calls for this function until the wait time has elapsed. If
|
|
814
|
-
`debounce` is called again before the specified time has elapsed,
|
|
815
|
-
the timer is reset and the entire period must pass again before
|
|
816
|
-
the method can be called again.
|
|
817
|
-
|
|
818
|
-
```javascript
|
|
819
|
-
import { debounce } from '@ember/runloop';
|
|
820
|
-
|
|
821
|
-
function whoRan() {
|
|
822
|
-
console.log(this.name + ' ran.');
|
|
823
|
-
}
|
|
824
|
-
|
|
825
|
-
let myContext = { name: 'debounce' };
|
|
826
|
-
|
|
827
|
-
debounce(myContext, whoRan, 150, true);
|
|
828
|
-
|
|
829
|
-
// console logs 'debounce ran.' one time immediately.
|
|
830
|
-
// 100ms passes
|
|
831
|
-
debounce(myContext, whoRan, 150, true);
|
|
832
|
-
|
|
833
|
-
// 150ms passes and nothing else is logged to the console and
|
|
834
|
-
// the debouncee is no longer being watched
|
|
835
|
-
debounce(myContext, whoRan, 150, true);
|
|
836
|
-
|
|
837
|
-
// console logs 'debounce ran.' one time immediately.
|
|
838
|
-
// 150ms passes and nothing else is logged to the console and
|
|
839
|
-
// the debouncee is no longer being watched
|
|
840
|
-
```
|
|
841
|
-
|
|
842
|
-
@method debounce
|
|
843
|
-
@static
|
|
844
|
-
@for @ember/runloop
|
|
845
|
-
@param {Object} [target] target of method to invoke
|
|
846
|
-
@param {Function|String} method The method to invoke.
|
|
847
|
-
May be a function or a string. If you pass a string
|
|
848
|
-
then it will be looked up on the passed target.
|
|
849
|
-
@param {Object} [args*] Optional arguments to pass to the timeout.
|
|
850
|
-
@param {Number} wait Number of milliseconds to wait.
|
|
851
|
-
@param {Boolean} immediate Trigger the function on the leading instead
|
|
852
|
-
of the trailing edge of the wait interval. Defaults to false.
|
|
853
|
-
@return {Array} Timer information for use in canceling, see `cancel`.
|
|
854
|
-
@public
|
|
855
|
-
*/
|
|
856
|
-
|
|
857
|
-
function debounce(...args) {
|
|
858
|
-
// @ts-expect-error TS doesn't like the rest args here
|
|
859
|
-
return _backburner.debounce(...args);
|
|
860
|
-
}
|
|
861
|
-
|
|
862
|
-
/**
|
|
863
|
-
Ensure that the target method is never called more frequently than
|
|
864
|
-
the specified spacing period. The target method is called immediately.
|
|
865
|
-
|
|
866
|
-
```javascript
|
|
867
|
-
import { throttle } from '@ember/runloop';
|
|
868
|
-
|
|
869
|
-
function whoRan() {
|
|
870
|
-
console.log(this.name + ' ran.');
|
|
871
|
-
}
|
|
872
|
-
|
|
873
|
-
let myContext = { name: 'throttle' };
|
|
874
|
-
|
|
875
|
-
throttle(myContext, whoRan, 150);
|
|
876
|
-
// whoRan is invoked with context myContext
|
|
877
|
-
// console logs 'throttle ran.'
|
|
878
|
-
|
|
879
|
-
// 50ms passes
|
|
880
|
-
throttle(myContext, whoRan, 150);
|
|
881
|
-
|
|
882
|
-
// 50ms passes
|
|
883
|
-
throttle(myContext, whoRan, 150);
|
|
884
|
-
|
|
885
|
-
// 150ms passes
|
|
886
|
-
throttle(myContext, whoRan, 150);
|
|
887
|
-
// whoRan is invoked with context myContext
|
|
888
|
-
// console logs 'throttle ran.'
|
|
889
|
-
```
|
|
890
|
-
|
|
891
|
-
@method throttle
|
|
892
|
-
@static
|
|
893
|
-
@for @ember/runloop
|
|
894
|
-
@param {Object} [target] target of method to invoke
|
|
895
|
-
@param {Function|String} method The method to invoke.
|
|
896
|
-
May be a function or a string. If you pass a string
|
|
897
|
-
then it will be looked up on the passed target.
|
|
898
|
-
@param {Object} [args*] Optional arguments to pass to the timeout.
|
|
899
|
-
@param {Number} spacing Number of milliseconds to space out requests.
|
|
900
|
-
@param {Boolean} immediate Trigger the function on the leading instead
|
|
901
|
-
of the trailing edge of the wait interval. Defaults to true.
|
|
902
|
-
@return {Array} Timer information for use in canceling, see `cancel`.
|
|
903
|
-
@public
|
|
904
|
-
*/
|
|
905
|
-
|
|
906
|
-
function throttle(...args) {
|
|
907
|
-
// @ts-expect-error TS doesn't like the rest args here
|
|
908
|
-
return _backburner.throttle(...args);
|
|
909
|
-
}
|
|
910
|
-
|
|
911
|
-
const AFTER_OBSERVERS = ':change';
|
|
912
|
-
function changeEvent(keyName) {
|
|
913
|
-
return keyName + AFTER_OBSERVERS;
|
|
914
|
-
}
|
|
915
|
-
|
|
916
|
-
const SYNC_DEFAULT = !ENV._DEFAULT_ASYNC_OBSERVERS;
|
|
917
|
-
const SYNC_OBSERVERS = new Map();
|
|
918
|
-
const ASYNC_OBSERVERS = new Map();
|
|
919
|
-
|
|
920
|
-
/**
|
|
921
|
-
@module @ember/object
|
|
922
|
-
*/
|
|
923
|
-
|
|
924
|
-
/**
|
|
925
|
-
@method addObserver
|
|
926
|
-
@static
|
|
927
|
-
@for @ember/object/observers
|
|
928
|
-
@param obj
|
|
929
|
-
@param {String} path
|
|
930
|
-
@param {Object|Function} target
|
|
931
|
-
@param {Function|String} [method]
|
|
932
|
-
@public
|
|
933
|
-
*/
|
|
934
|
-
function addObserver(obj, path, target, method, sync = SYNC_DEFAULT) {
|
|
935
|
-
let eventName = changeEvent(path);
|
|
936
|
-
addListener(obj, eventName, target, method, false, sync);
|
|
937
|
-
let meta = peekMeta(obj);
|
|
938
|
-
if (meta === null || !(meta.isPrototypeMeta(obj) || meta.isInitializing())) {
|
|
939
|
-
activateObserver(obj, eventName, sync);
|
|
940
|
-
}
|
|
941
|
-
}
|
|
137
|
+
let propertyTag = tagForProperty(current, segment, true, currentTagMeta);
|
|
138
|
+
descriptor = currentMeta !== null ? currentMeta.peekDescriptors(segment) : undefined;
|
|
139
|
+
chainTags.push(propertyTag);
|
|
942
140
|
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
}
|
|
985
|
-
let DEACTIVATE_SUSPENDED = false;
|
|
986
|
-
let SCHEDULED_DEACTIVATE = [];
|
|
987
|
-
function deactivateObserver(target, eventName, sync = false) {
|
|
988
|
-
if (DEACTIVATE_SUSPENDED === true) {
|
|
989
|
-
SCHEDULED_DEACTIVATE.push([target, eventName, sync]);
|
|
990
|
-
return;
|
|
991
|
-
}
|
|
992
|
-
let observerMap = sync === true ? SYNC_OBSERVERS : ASYNC_OBSERVERS;
|
|
993
|
-
let activeObservers = observerMap.get(target);
|
|
994
|
-
if (activeObservers !== undefined) {
|
|
995
|
-
let observer = activeObservers.get(eventName);
|
|
996
|
-
observer.count--;
|
|
997
|
-
if (observer.count === 0) {
|
|
998
|
-
activeObservers.delete(eventName);
|
|
999
|
-
if (activeObservers.size === 0) {
|
|
1000
|
-
observerMap.delete(target);
|
|
141
|
+
// If we're at the end of the path, processing the last segment, and it's
|
|
142
|
+
// not an alias, we should _not_ get the last value, since we already have
|
|
143
|
+
// its tag. There's no reason to access it and do more work.
|
|
144
|
+
if (segmentEnd === pathLength) {
|
|
145
|
+
// If the key was an alias, we should always get the next value in order to
|
|
146
|
+
// bootstrap the alias. This is because aliases, unlike other CPs, should
|
|
147
|
+
// always be in sync with the aliased value.
|
|
148
|
+
if (CHAIN_PASS_THROUGH.has(descriptor)) {
|
|
149
|
+
current[segment];
|
|
150
|
+
}
|
|
151
|
+
break;
|
|
152
|
+
}
|
|
153
|
+
if (descriptor === undefined) {
|
|
154
|
+
// If the descriptor is undefined, then its a normal property, so we should
|
|
155
|
+
// lookup the value to chain off of like normal.
|
|
156
|
+
|
|
157
|
+
if (!(segment in current) && typeof current.unknownProperty === 'function') {
|
|
158
|
+
current = current.unknownProperty(segment);
|
|
159
|
+
} else {
|
|
160
|
+
current = current[segment];
|
|
161
|
+
}
|
|
162
|
+
} else if (CHAIN_PASS_THROUGH.has(descriptor)) {
|
|
163
|
+
current = current[segment];
|
|
164
|
+
} else {
|
|
165
|
+
// If the descriptor is defined, then its a normal CP (not an alias, which
|
|
166
|
+
// would have been handled earlier). We get the last revision to check if
|
|
167
|
+
// the CP is still valid, and if so we use the cached value. If not, then
|
|
168
|
+
// we create a lazy chain lookup, and the next time the CP is calculated,
|
|
169
|
+
// it will update that lazy chain.
|
|
170
|
+
let instanceMeta = currentMeta.source === current ? currentMeta : meta(current);
|
|
171
|
+
let lastRevision = instanceMeta.revisionFor(segment);
|
|
172
|
+
if (lastRevision !== undefined && validateTag(propertyTag, lastRevision)) {
|
|
173
|
+
current = instanceMeta.valueFor(segment);
|
|
174
|
+
} else {
|
|
175
|
+
// use metaFor here to ensure we have the meta for the instance
|
|
176
|
+
let lazyChains = instanceMeta.writableLazyChainsFor(segment);
|
|
177
|
+
let rest = path.substring(segmentEnd + 1);
|
|
178
|
+
let placeholderTag = createUpdatableTag();
|
|
179
|
+
lazyChains.push([placeholderTag, rest]);
|
|
180
|
+
chainTags.push(placeholderTag);
|
|
181
|
+
break;
|
|
1001
182
|
}
|
|
1002
183
|
}
|
|
184
|
+
if (!isObject(current)) {
|
|
185
|
+
// we've hit the end of the chain for now, break out
|
|
186
|
+
break;
|
|
187
|
+
}
|
|
188
|
+
currentTagMeta = tagMetaFor(current);
|
|
189
|
+
currentMeta = peekMeta(current);
|
|
1003
190
|
}
|
|
191
|
+
return chainTags;
|
|
1004
192
|
}
|
|
1005
|
-
|
|
1006
|
-
|
|
193
|
+
|
|
194
|
+
function isElementDescriptor(args) {
|
|
195
|
+
let [maybeTarget, maybeKey, maybeDesc] = args;
|
|
196
|
+
return (
|
|
197
|
+
// Ensure we have the right number of args
|
|
198
|
+
args.length === 3 && (
|
|
199
|
+
// Make sure the target is a class or object (prototype)
|
|
200
|
+
typeof maybeTarget === 'function' || typeof maybeTarget === 'object' && maybeTarget !== null) &&
|
|
201
|
+
// Make sure the key is a string
|
|
202
|
+
typeof maybeKey === 'string' && (
|
|
203
|
+
// Make sure the descriptor is the right shape
|
|
204
|
+
typeof maybeDesc === 'object' && maybeDesc !== null || maybeDesc === undefined)
|
|
205
|
+
);
|
|
1007
206
|
}
|
|
1008
|
-
function
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
207
|
+
function nativeDescDecorator(propertyDesc) {
|
|
208
|
+
let decorator = function () {
|
|
209
|
+
return propertyDesc;
|
|
210
|
+
};
|
|
211
|
+
setClassicDecorator(decorator);
|
|
212
|
+
return decorator;
|
|
1014
213
|
}
|
|
1015
214
|
|
|
1016
215
|
/**
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
if (SYNC_OBSERVERS.has(target)) {
|
|
1031
|
-
SYNC_OBSERVERS.get(target).forEach(observer => {
|
|
1032
|
-
observer.tag = getChainTagsForKey(target, observer.path, tagMetaFor(target), peekMeta(target));
|
|
1033
|
-
observer.lastRevision = valueForTag(observer.tag);
|
|
1034
|
-
});
|
|
216
|
+
Objects of this type can implement an interface to respond to requests to
|
|
217
|
+
get and set. The default implementation handles simple properties.
|
|
218
|
+
|
|
219
|
+
@class Descriptor
|
|
220
|
+
@private
|
|
221
|
+
*/
|
|
222
|
+
class ComputedDescriptor {
|
|
223
|
+
enumerable = true;
|
|
224
|
+
configurable = true;
|
|
225
|
+
_dependentKeys = undefined;
|
|
226
|
+
_meta = undefined;
|
|
227
|
+
setup(_obj, keyName, _propertyDesc, meta) {
|
|
228
|
+
meta.writeDescriptors(keyName, this);
|
|
1035
229
|
}
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
function flushAsyncObservers(shouldSchedule = true) {
|
|
1039
|
-
let currentRevision = valueForTag(CURRENT_TAG);
|
|
1040
|
-
if (lastKnownRevision === currentRevision) {
|
|
1041
|
-
return;
|
|
230
|
+
teardown(_obj, keyName, meta) {
|
|
231
|
+
meta.removeDescriptors(keyName);
|
|
1042
232
|
}
|
|
1043
|
-
lastKnownRevision = currentRevision;
|
|
1044
|
-
ASYNC_OBSERVERS.forEach((activeObservers, target) => {
|
|
1045
|
-
let meta = peekMeta(target);
|
|
1046
|
-
activeObservers.forEach((observer, eventName) => {
|
|
1047
|
-
if (!validateTag(observer.tag, observer.lastRevision)) {
|
|
1048
|
-
let sendObserver = () => {
|
|
1049
|
-
try {
|
|
1050
|
-
sendEvent(target, eventName, [target, observer.path], undefined, meta);
|
|
1051
|
-
} finally {
|
|
1052
|
-
observer.tag = getChainTagsForKey(target, observer.path, tagMetaFor(target), peekMeta(target));
|
|
1053
|
-
observer.lastRevision = valueForTag(observer.tag);
|
|
1054
|
-
}
|
|
1055
|
-
};
|
|
1056
|
-
if (shouldSchedule) {
|
|
1057
|
-
schedule('actions', sendObserver);
|
|
1058
|
-
} else {
|
|
1059
|
-
sendObserver();
|
|
1060
|
-
}
|
|
1061
|
-
}
|
|
1062
|
-
});
|
|
1063
|
-
});
|
|
1064
233
|
}
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
// a global revision.
|
|
1069
|
-
|
|
1070
|
-
SYNC_OBSERVERS.forEach((activeObservers, target) => {
|
|
1071
|
-
let meta = peekMeta(target);
|
|
1072
|
-
activeObservers.forEach((observer, eventName) => {
|
|
1073
|
-
if (!observer.suspended && !validateTag(observer.tag, observer.lastRevision)) {
|
|
1074
|
-
try {
|
|
1075
|
-
observer.suspended = true;
|
|
1076
|
-
sendEvent(target, eventName, [target, observer.path], undefined, meta);
|
|
1077
|
-
} finally {
|
|
1078
|
-
observer.tag = getChainTagsForKey(target, observer.path, tagMetaFor(target), peekMeta(target));
|
|
1079
|
-
observer.lastRevision = valueForTag(observer.tag);
|
|
1080
|
-
observer.suspended = false;
|
|
1081
|
-
}
|
|
1082
|
-
}
|
|
1083
|
-
});
|
|
1084
|
-
});
|
|
234
|
+
let COMPUTED_GETTERS;
|
|
235
|
+
if (isDevelopingApp()) {
|
|
236
|
+
COMPUTED_GETTERS = new WeakSet();
|
|
1085
237
|
}
|
|
1086
|
-
function
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
return;
|
|
238
|
+
function DESCRIPTOR_GETTER_FUNCTION(name, descriptor) {
|
|
239
|
+
function getter() {
|
|
240
|
+
return descriptor.get(this, name);
|
|
1090
241
|
}
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
observer.suspended = suspended;
|
|
242
|
+
if (isDevelopingApp()) {
|
|
243
|
+
COMPUTED_GETTERS.add(getter);
|
|
1094
244
|
}
|
|
245
|
+
return getter;
|
|
1095
246
|
}
|
|
1096
|
-
function
|
|
1097
|
-
|
|
1098
|
-
|
|
247
|
+
function DESCRIPTOR_SETTER_FUNCTION(name, descriptor) {
|
|
248
|
+
let set = function CPSETTER_FUNCTION(value) {
|
|
249
|
+
return descriptor.set(this, name, value);
|
|
250
|
+
};
|
|
251
|
+
COMPUTED_SETTERS.add(set);
|
|
252
|
+
return set;
|
|
253
|
+
}
|
|
254
|
+
const COMPUTED_SETTERS = new WeakSet();
|
|
255
|
+
function makeComputedDecorator(desc, DecoratorClass) {
|
|
256
|
+
let decorator = function COMPUTED_DECORATOR(target, key, propertyDesc, maybeMeta, isClassicDecorator) {
|
|
257
|
+
(isDevelopingApp() && !(isClassicDecorator || !propertyDesc || !propertyDesc.get || !COMPUTED_GETTERS.has(propertyDesc.get)) && assert(`Only one computed property decorator can be applied to a class field or accessor, but '${key}' was decorated twice. You may have added the decorator to both a getter and setter, which is unnecessary.`, isClassicDecorator || !propertyDesc || !propertyDesc.get || !COMPUTED_GETTERS.has(propertyDesc.get)));
|
|
258
|
+
let meta$1 = arguments.length === 3 ? meta(target) : maybeMeta;
|
|
259
|
+
desc.setup(target, key, propertyDesc, meta$1);
|
|
260
|
+
let computedDesc = {
|
|
261
|
+
enumerable: desc.enumerable,
|
|
262
|
+
configurable: desc.configurable,
|
|
263
|
+
get: DESCRIPTOR_GETTER_FUNCTION(key, desc),
|
|
264
|
+
set: DESCRIPTOR_SETTER_FUNCTION(key, desc)
|
|
265
|
+
};
|
|
266
|
+
return computedDesc;
|
|
267
|
+
};
|
|
268
|
+
setClassicDecorator(decorator, desc);
|
|
269
|
+
Object.setPrototypeOf(decorator, DecoratorClass.prototype);
|
|
270
|
+
return decorator;
|
|
1099
271
|
}
|
|
1100
272
|
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
273
|
+
/////////////
|
|
274
|
+
|
|
275
|
+
const DECORATOR_DESCRIPTOR_MAP = new WeakMap();
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
Returns the CP descriptor associated with `obj` and `keyName`, if any.
|
|
279
|
+
|
|
280
|
+
@method descriptorForProperty
|
|
281
|
+
@param {Object} obj the object to check
|
|
282
|
+
@param {String} keyName the key to check
|
|
283
|
+
@return {Descriptor}
|
|
284
|
+
@private
|
|
285
|
+
*/
|
|
286
|
+
function descriptorForProperty(obj, keyName, _meta) {
|
|
287
|
+
(isDevelopingApp() && !(obj !== null) && assert('Cannot call `descriptorForProperty` on null', obj !== null));
|
|
288
|
+
(isDevelopingApp() && !(obj !== undefined) && assert('Cannot call `descriptorForProperty` on undefined', obj !== undefined));
|
|
289
|
+
(isDevelopingApp() && !(typeof obj === 'object' || typeof obj === 'function') && assert(`Cannot call \`descriptorForProperty\` on ${typeof obj}`, typeof obj === 'object' || typeof obj === 'function'));
|
|
290
|
+
let meta = _meta === undefined ? peekMeta(obj) : _meta;
|
|
291
|
+
if (meta !== null) {
|
|
292
|
+
return meta.peekDescriptors(keyName);
|
|
1111
293
|
}
|
|
1112
|
-
return tag;
|
|
1113
294
|
}
|
|
1114
|
-
function
|
|
1115
|
-
|
|
1116
|
-
if (isDevelopingApp()) {
|
|
1117
|
-
(isDevelopingApp() && !(!isDestroyed(obj)) && assert(isDestroyed(obj) ? `Cannot create a new tag for \`${toString(obj)}\` after it has been destroyed.` : '', !isDestroyed(obj)));
|
|
1118
|
-
}
|
|
1119
|
-
return tagFor(obj, SELF_TAG);
|
|
1120
|
-
}
|
|
1121
|
-
return CONSTANT_TAG;
|
|
295
|
+
function descriptorForDecorator(dec) {
|
|
296
|
+
return DECORATOR_DESCRIPTOR_MAP.get(dec);
|
|
1122
297
|
}
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
298
|
+
|
|
299
|
+
/**
|
|
300
|
+
Check whether a value is a decorator
|
|
301
|
+
|
|
302
|
+
@method isClassicDecorator
|
|
303
|
+
@param {any} possibleDesc the value to check
|
|
304
|
+
@return {boolean}
|
|
305
|
+
@private
|
|
306
|
+
*/
|
|
307
|
+
function isClassicDecorator(dec) {
|
|
308
|
+
return typeof dec === 'function' && DECORATOR_DESCRIPTOR_MAP.has(dec);
|
|
1126
309
|
}
|
|
1127
310
|
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
311
|
+
/**
|
|
312
|
+
Set a value as a decorator
|
|
313
|
+
|
|
314
|
+
@method setClassicDecorator
|
|
315
|
+
@param {function} decorator the value to mark as a decorator
|
|
316
|
+
@private
|
|
317
|
+
*/
|
|
318
|
+
function setClassicDecorator(dec, value = true) {
|
|
319
|
+
DECORATOR_DESCRIPTOR_MAP.set(dec, value);
|
|
1131
320
|
}
|
|
1132
|
-
|
|
321
|
+
|
|
322
|
+
const END_WITH_EACH_REGEX = /\.@each$/;
|
|
1133
323
|
|
|
1134
324
|
/**
|
|
1135
|
-
|
|
1136
|
-
It will notify any observers and clear caches among other things.
|
|
325
|
+
Expands `pattern`, invoking `callback` for each expansion.
|
|
1137
326
|
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
manually.
|
|
327
|
+
The only pattern supported is brace-expansion, anything else will be passed
|
|
328
|
+
once to `callback` directly.
|
|
1141
329
|
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
330
|
+
Example
|
|
331
|
+
|
|
332
|
+
```js
|
|
333
|
+
import { expandProperties } from '@ember/object/computed';
|
|
334
|
+
|
|
335
|
+
function echo(arg){ console.log(arg); }
|
|
336
|
+
|
|
337
|
+
expandProperties('foo.bar', echo); //=> 'foo.bar'
|
|
338
|
+
expandProperties('{foo,bar}', echo); //=> 'foo', 'bar'
|
|
339
|
+
expandProperties('foo.{bar,baz}', echo); //=> 'foo.bar', 'foo.baz'
|
|
340
|
+
expandProperties('{foo,bar}.baz', echo); //=> 'foo.baz', 'bar.baz'
|
|
341
|
+
expandProperties('foo.{bar,baz}.[]', echo) //=> 'foo.bar.[]', 'foo.baz.[]'
|
|
342
|
+
expandProperties('{foo,bar}.{spam,eggs}', echo) //=> 'foo.spam', 'foo.eggs', 'bar.spam', 'bar.eggs'
|
|
343
|
+
expandProperties('{foo}.bar.{baz}') //=> 'foo.bar.baz'
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
@method expandProperties
|
|
347
|
+
@static
|
|
348
|
+
@for @ember/object/computed
|
|
1150
349
|
@public
|
|
350
|
+
@param {String} pattern The property pattern to expand.
|
|
351
|
+
@param {Function} callback The callback to invoke. It is invoked once per
|
|
352
|
+
expansion, and is passed the expansion.
|
|
1151
353
|
*/
|
|
1152
|
-
function
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
354
|
+
function expandProperties(pattern, callback) {
|
|
355
|
+
(isDevelopingApp() && !(typeof pattern === 'string') && assert(`A computed property key must be a string, you passed ${typeof pattern} ${pattern}`, typeof pattern === 'string'));
|
|
356
|
+
(isDevelopingApp() && !(pattern.indexOf(' ') === -1) && assert('Brace expanded properties cannot contain spaces, e.g. "user.{firstName, lastName}" should be "user.{firstName,lastName}"', pattern.indexOf(' ') === -1)); // regex to look for double open, double close, or unclosed braces
|
|
357
|
+
(isDevelopingApp() && !(pattern.match(/\{[^}{]*\{|\}[^}{]*\}|\{[^}]*$/g) === null) && assert(`Brace expanded properties have to be balanced and cannot be nested, pattern: ${pattern}`, pattern.match(/\{[^}{]*\{|\}[^}{]*\}|\{[^}]*$/g) === null));
|
|
358
|
+
let start = pattern.indexOf('{');
|
|
359
|
+
if (start < 0) {
|
|
360
|
+
callback(pattern.replace(END_WITH_EACH_REGEX, '.[]'));
|
|
361
|
+
} else {
|
|
362
|
+
dive('', pattern, start, callback);
|
|
1160
363
|
}
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
364
|
+
}
|
|
365
|
+
function dive(prefix, pattern, start, callback) {
|
|
366
|
+
let end = pattern.indexOf('}'),
|
|
367
|
+
i = 0,
|
|
368
|
+
newStart,
|
|
369
|
+
arrayLength;
|
|
370
|
+
let tempArr = pattern.substring(start + 1, end).split(',');
|
|
371
|
+
let after = pattern.substring(end + 1);
|
|
372
|
+
prefix = prefix + pattern.substring(0, start);
|
|
373
|
+
arrayLength = tempArr.length;
|
|
374
|
+
while (i < arrayLength) {
|
|
375
|
+
newStart = after.indexOf('{');
|
|
376
|
+
if (newStart < 0) {
|
|
377
|
+
callback((prefix + tempArr[i++] + after).replace(END_WITH_EACH_REGEX, '.[]'));
|
|
1168
378
|
} else {
|
|
1169
|
-
|
|
379
|
+
dive(prefix + tempArr[i++], after, newStart, callback);
|
|
1170
380
|
}
|
|
1171
381
|
}
|
|
1172
382
|
}
|
|
1173
383
|
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
@private
|
|
1178
|
-
*/
|
|
1179
|
-
function beginPropertyChanges() {
|
|
1180
|
-
deferred++;
|
|
1181
|
-
suspendedObserverDeactivation();
|
|
384
|
+
const AFTER_OBSERVERS = ':change';
|
|
385
|
+
function changeEvent(keyName) {
|
|
386
|
+
return keyName + AFTER_OBSERVERS;
|
|
1182
387
|
}
|
|
1183
388
|
|
|
1184
389
|
/**
|
|
1185
|
-
|
|
1186
|
-
@private
|
|
390
|
+
@module @ember/object
|
|
1187
391
|
*/
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
392
|
+
|
|
393
|
+
function addListener(obj, eventName, target, method, once, sync = true) {
|
|
394
|
+
(isDevelopingApp() && !(Boolean(obj) && Boolean(eventName)) && assert('You must pass at least an object and event name to addListener', Boolean(obj) && Boolean(eventName)));
|
|
395
|
+
if (!method && 'function' === typeof target) {
|
|
396
|
+
method = target;
|
|
397
|
+
target = null;
|
|
1193
398
|
}
|
|
399
|
+
meta(obj).addToListeners(eventName, target, method, once === true, sync);
|
|
1194
400
|
}
|
|
1195
401
|
|
|
1196
402
|
/**
|
|
1197
|
-
|
|
1198
|
-
exception-safe way.
|
|
403
|
+
Remove an event listener
|
|
1199
404
|
|
|
1200
|
-
|
|
1201
|
-
Ember.changeProperties(function() {
|
|
1202
|
-
obj1.set('foo', mayBlowUpWhenSet);
|
|
1203
|
-
obj2.set('bar', baz);
|
|
1204
|
-
});
|
|
1205
|
-
```
|
|
405
|
+
Arguments should match those passed to `addListener`.
|
|
1206
406
|
|
|
1207
|
-
@method
|
|
1208
|
-
@
|
|
1209
|
-
@
|
|
407
|
+
@method removeListener
|
|
408
|
+
@static
|
|
409
|
+
@for @ember/object/events
|
|
410
|
+
@param obj
|
|
411
|
+
@param {String} eventName
|
|
412
|
+
@param {Object|Function} target A target object or a function
|
|
413
|
+
@param {Function|String} method A function or the name of a function to be called on `target`
|
|
414
|
+
@public
|
|
1210
415
|
*/
|
|
1211
|
-
function
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
416
|
+
function removeListener(obj, eventName, targetOrFunction, functionOrName) {
|
|
417
|
+
(isDevelopingApp() && !(Boolean(obj) && Boolean(eventName) && (typeof targetOrFunction === 'function' || typeof targetOrFunction === 'object' && Boolean(functionOrName))) && assert('You must pass at least an object, event name, and method or target and method/method name to removeListener', Boolean(obj) && Boolean(eventName) && (typeof targetOrFunction === 'function' || typeof targetOrFunction === 'object' && Boolean(functionOrName))));
|
|
418
|
+
let target, method;
|
|
419
|
+
if (typeof targetOrFunction === 'object') {
|
|
420
|
+
target = targetOrFunction;
|
|
421
|
+
method = functionOrName;
|
|
422
|
+
} else {
|
|
423
|
+
target = null;
|
|
424
|
+
method = targetOrFunction;
|
|
1217
425
|
}
|
|
426
|
+
let m = meta(obj);
|
|
427
|
+
m.removeFromListeners(eventName, target, method);
|
|
1218
428
|
}
|
|
1219
429
|
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
430
|
+
/**
|
|
431
|
+
Send an event. The execution of suspended listeners
|
|
432
|
+
is skipped, and once listeners are removed. A listener without
|
|
433
|
+
a target is executed on the passed object. If an array of actions
|
|
434
|
+
is not passed, the actions stored on the passed object are invoked.
|
|
435
|
+
|
|
436
|
+
@method sendEvent
|
|
437
|
+
@static
|
|
438
|
+
@for @ember/object/events
|
|
439
|
+
@param obj
|
|
440
|
+
@param {String} eventName
|
|
441
|
+
@param {Array} params Optional parameters for each listener.
|
|
442
|
+
@return {Boolean} if the event was delivered to one or more actions
|
|
443
|
+
@public
|
|
444
|
+
*/
|
|
445
|
+
function sendEvent(obj, eventName, params, actions, _meta) {
|
|
446
|
+
if (actions === undefined) {
|
|
447
|
+
let meta = _meta === undefined ? peekMeta(obj) : _meta;
|
|
448
|
+
actions = meta !== null ? meta.matchingListeners(eventName) : undefined;
|
|
1232
449
|
}
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
}
|
|
1236
|
-
function arrayContentDidChange(array, startIdx, removeAmt, addAmt, notify = true) {
|
|
1237
|
-
// if no args are passed assume everything changes
|
|
1238
|
-
if (startIdx === undefined) {
|
|
1239
|
-
startIdx = 0;
|
|
1240
|
-
removeAmt = addAmt = -1;
|
|
1241
|
-
} else {
|
|
1242
|
-
if (removeAmt === undefined) {
|
|
1243
|
-
removeAmt = -1;
|
|
1244
|
-
}
|
|
1245
|
-
if (addAmt === undefined) {
|
|
1246
|
-
addAmt = -1;
|
|
1247
|
-
}
|
|
450
|
+
if (actions === undefined || actions.length === 0) {
|
|
451
|
+
return false;
|
|
1248
452
|
}
|
|
1249
|
-
let
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
453
|
+
for (let i = actions.length - 3; i >= 0; i -= 3) {
|
|
454
|
+
// looping in reverse for once listeners
|
|
455
|
+
let target = actions[i];
|
|
456
|
+
let method = actions[i + 1];
|
|
457
|
+
let once = actions[i + 2];
|
|
458
|
+
if (!method) {
|
|
459
|
+
continue;
|
|
1253
460
|
}
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
sendEvent(array, '@array:change', [array, startIdx, removeAmt, addAmt]);
|
|
1257
|
-
if (meta !== null) {
|
|
1258
|
-
let length = array.length;
|
|
1259
|
-
let addedAmount = addAmt === -1 ? 0 : addAmt;
|
|
1260
|
-
let removedAmount = removeAmt === -1 ? 0 : removeAmt;
|
|
1261
|
-
let delta = addedAmount - removedAmount;
|
|
1262
|
-
let previousLength = length - delta;
|
|
1263
|
-
let normalStartIdx = startIdx < 0 ? previousLength + startIdx : startIdx;
|
|
1264
|
-
if (meta.revisionFor('firstObject') !== undefined && normalStartIdx === 0) {
|
|
1265
|
-
notifyPropertyChange(array, 'firstObject', meta);
|
|
461
|
+
if (once) {
|
|
462
|
+
removeListener(obj, eventName, target, method);
|
|
1266
463
|
}
|
|
1267
|
-
if (
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
464
|
+
if (!target) {
|
|
465
|
+
target = obj;
|
|
466
|
+
}
|
|
467
|
+
let type = typeof method;
|
|
468
|
+
if (type === 'string' || type === 'symbol') {
|
|
469
|
+
method = target[method];
|
|
1273
470
|
}
|
|
471
|
+
method.apply(target, params);
|
|
1274
472
|
}
|
|
1275
|
-
return
|
|
473
|
+
return true;
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
/**
|
|
477
|
+
@public
|
|
478
|
+
@method hasListeners
|
|
479
|
+
@static
|
|
480
|
+
@for @ember/object/events
|
|
481
|
+
@param obj
|
|
482
|
+
@param {String} eventName
|
|
483
|
+
@return {Boolean} if `obj` has listeners for event `eventName`
|
|
484
|
+
*/
|
|
485
|
+
function hasListeners(obj, eventName) {
|
|
486
|
+
let meta = peekMeta(obj);
|
|
487
|
+
if (meta === null) {
|
|
488
|
+
return false;
|
|
489
|
+
}
|
|
490
|
+
let matched = meta.matchingListeners(eventName);
|
|
491
|
+
return matched !== undefined && matched.length > 0;
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
/**
|
|
495
|
+
Define a property as a function that should be executed when
|
|
496
|
+
a specified event or events are triggered.
|
|
497
|
+
|
|
498
|
+
``` javascript
|
|
499
|
+
import EmberObject from '@ember/object';
|
|
500
|
+
import { on } from '@ember/object/evented';
|
|
501
|
+
import { sendEvent } from '@ember/object/events';
|
|
502
|
+
|
|
503
|
+
let Job = EmberObject.extend({
|
|
504
|
+
logCompleted: on('completed', function() {
|
|
505
|
+
console.log('Job completed!');
|
|
506
|
+
})
|
|
507
|
+
});
|
|
508
|
+
|
|
509
|
+
let job = Job.create();
|
|
510
|
+
|
|
511
|
+
sendEvent(job, 'completed'); // Logs 'Job completed!'
|
|
512
|
+
```
|
|
513
|
+
|
|
514
|
+
@method on
|
|
515
|
+
@static
|
|
516
|
+
@for @ember/object/evented
|
|
517
|
+
@param {String} eventNames*
|
|
518
|
+
@param {Function} func
|
|
519
|
+
@return {Function} the listener function, passed as last argument to on(...)
|
|
520
|
+
@public
|
|
521
|
+
*/
|
|
522
|
+
function on(...args) {
|
|
523
|
+
let func = args.pop();
|
|
524
|
+
let events = args;
|
|
525
|
+
(isDevelopingApp() && !(typeof func === 'function') && assert('on expects function as last argument', typeof func === 'function'));
|
|
526
|
+
(isDevelopingApp() && !(events.length > 0 && events.every(p => typeof p === 'string' && p.length > 0)) && assert('on called without valid event names', events.length > 0 && events.every(p => typeof p === 'string' && p.length > 0)));
|
|
527
|
+
setListeners(func, events);
|
|
528
|
+
return func;
|
|
1276
529
|
}
|
|
1277
530
|
|
|
1278
|
-
const
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
531
|
+
const SYNC_DEFAULT = !ENV._DEFAULT_ASYNC_OBSERVERS;
|
|
532
|
+
const SYNC_OBSERVERS = new Map();
|
|
533
|
+
const ASYNC_OBSERVERS = new Map();
|
|
534
|
+
|
|
535
|
+
/**
|
|
536
|
+
@module @ember/object
|
|
537
|
+
*/
|
|
538
|
+
|
|
539
|
+
/**
|
|
540
|
+
@method addObserver
|
|
541
|
+
@static
|
|
542
|
+
@for @ember/object/observers
|
|
543
|
+
@param obj
|
|
544
|
+
@param {String} path
|
|
545
|
+
@param {Object|Function} target
|
|
546
|
+
@param {Function|String} [method]
|
|
547
|
+
@public
|
|
548
|
+
*/
|
|
549
|
+
function addObserver(obj, path, target, method, sync = SYNC_DEFAULT) {
|
|
550
|
+
let eventName = changeEvent(path);
|
|
551
|
+
addListener(obj, eventName, target, method, false, sync);
|
|
552
|
+
let meta = peekMeta(obj);
|
|
553
|
+
if (meta === null || !(meta.isPrototypeMeta(obj) || meta.isInitializing())) {
|
|
554
|
+
activateObserver(obj, eventName, sync);
|
|
1284
555
|
}
|
|
1285
556
|
}
|
|
1286
557
|
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
558
|
+
/**
|
|
559
|
+
@method removeObserver
|
|
560
|
+
@static
|
|
561
|
+
@for @ember/object/observers
|
|
562
|
+
@param obj
|
|
563
|
+
@param {String} path
|
|
564
|
+
@param {Object|Function} target
|
|
565
|
+
@param {Function|String} [method]
|
|
566
|
+
@public
|
|
567
|
+
*/
|
|
568
|
+
function removeObserver(obj, path, target, method, sync = SYNC_DEFAULT) {
|
|
569
|
+
let eventName = changeEvent(path);
|
|
570
|
+
let meta = peekMeta(obj);
|
|
571
|
+
if (meta === null || !(meta.isPrototypeMeta(obj) || meta.isInitializing())) {
|
|
572
|
+
deactivateObserver(obj, eventName, sync);
|
|
573
|
+
}
|
|
574
|
+
removeListener(obj, eventName, target, method);
|
|
1291
575
|
}
|
|
1292
|
-
function
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
(
|
|
1297
|
-
replaceInNativeArray(array, start, deleteCount, items);
|
|
576
|
+
function getOrCreateActiveObserversFor(target, sync) {
|
|
577
|
+
let observerMap = sync === true ? SYNC_OBSERVERS : ASYNC_OBSERVERS;
|
|
578
|
+
if (!observerMap.has(target)) {
|
|
579
|
+
observerMap.set(target, new Map());
|
|
580
|
+
registerDestructor(target, () => destroyObservers(target), true);
|
|
1298
581
|
}
|
|
582
|
+
return observerMap.get(target);
|
|
1299
583
|
}
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
function replaceInNativeArray(array, start, deleteCount, items) {
|
|
1305
|
-
arrayContentWillChange(array, start, deleteCount, items.length);
|
|
1306
|
-
if (items.length <= CHUNK_SIZE) {
|
|
1307
|
-
array.splice(start, deleteCount, ...items);
|
|
584
|
+
function activateObserver(target, eventName, sync = false) {
|
|
585
|
+
let activeObservers = getOrCreateActiveObserversFor(target, sync);
|
|
586
|
+
if (activeObservers.has(eventName)) {
|
|
587
|
+
activeObservers.get(eventName).count++;
|
|
1308
588
|
} else {
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
589
|
+
let path = eventName.substring(0, eventName.lastIndexOf(':'));
|
|
590
|
+
let tag = getChainTagsForKey(target, path, tagMetaFor(target), peekMeta(target));
|
|
591
|
+
activeObservers.set(eventName, {
|
|
592
|
+
count: 1,
|
|
593
|
+
path,
|
|
594
|
+
tag,
|
|
595
|
+
lastRevision: valueForTag(tag),
|
|
596
|
+
suspended: false
|
|
597
|
+
});
|
|
1314
598
|
}
|
|
1315
|
-
arrayContentDidChange(array, start, deleteCount, items.length);
|
|
1316
|
-
}
|
|
1317
|
-
function arrayObserversHelper(obj, target, opts, operation) {
|
|
1318
|
-
let {
|
|
1319
|
-
willChange,
|
|
1320
|
-
didChange
|
|
1321
|
-
} = opts;
|
|
1322
|
-
operation(obj, '@array:before', target, willChange);
|
|
1323
|
-
operation(obj, '@array:change', target, didChange);
|
|
1324
|
-
|
|
1325
|
-
/*
|
|
1326
|
-
* Array proxies have a `_revalidate` method which must be called to set
|
|
1327
|
-
* up their internal array observation systems.
|
|
1328
|
-
*/
|
|
1329
|
-
obj._revalidate?.();
|
|
1330
|
-
return obj;
|
|
1331
|
-
}
|
|
1332
|
-
function addArrayObserver(array, target, opts) {
|
|
1333
|
-
return arrayObserversHelper(array, target, opts, addListener);
|
|
1334
599
|
}
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
function finishLazyChains(meta, key, value) {
|
|
1341
|
-
let lazyTags = meta.readableLazyChainsFor(key);
|
|
1342
|
-
if (lazyTags === undefined) {
|
|
600
|
+
let DEACTIVATE_SUSPENDED = false;
|
|
601
|
+
let SCHEDULED_DEACTIVATE = [];
|
|
602
|
+
function deactivateObserver(target, eventName, sync = false) {
|
|
603
|
+
if (DEACTIVATE_SUSPENDED === true) {
|
|
604
|
+
SCHEDULED_DEACTIVATE.push([target, eventName, sync]);
|
|
1343
605
|
return;
|
|
1344
606
|
}
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
for (let key of keys) {
|
|
1355
|
-
getChainTags(tags, obj, key, tagMeta, meta);
|
|
1356
|
-
}
|
|
1357
|
-
return combine(tags);
|
|
1358
|
-
}
|
|
1359
|
-
function getChainTagsForKey(obj, key, tagMeta, meta) {
|
|
1360
|
-
return combine(getChainTags([], obj, key, tagMeta, meta));
|
|
1361
|
-
}
|
|
1362
|
-
function getChainTags(chainTags, obj, path, tagMeta, meta$1) {
|
|
1363
|
-
let current = obj;
|
|
1364
|
-
let currentTagMeta = tagMeta;
|
|
1365
|
-
let currentMeta = meta$1;
|
|
1366
|
-
let pathLength = path.length;
|
|
1367
|
-
let segmentEnd = -1;
|
|
1368
|
-
// prevent closures
|
|
1369
|
-
let segment, descriptor;
|
|
1370
|
-
|
|
1371
|
-
// eslint-disable-next-line no-constant-condition
|
|
1372
|
-
while (true) {
|
|
1373
|
-
let lastSegmentEnd = segmentEnd + 1;
|
|
1374
|
-
segmentEnd = path.indexOf('.', lastSegmentEnd);
|
|
1375
|
-
if (segmentEnd === -1) {
|
|
1376
|
-
segmentEnd = pathLength;
|
|
1377
|
-
}
|
|
1378
|
-
segment = path.slice(lastSegmentEnd, segmentEnd);
|
|
1379
|
-
|
|
1380
|
-
// If the segment is an @each, we can process it and then break
|
|
1381
|
-
if (segment === '@each' && segmentEnd !== pathLength) {
|
|
1382
|
-
lastSegmentEnd = segmentEnd + 1;
|
|
1383
|
-
segmentEnd = path.indexOf('.', lastSegmentEnd);
|
|
1384
|
-
let arrLength = current.length;
|
|
1385
|
-
if (typeof arrLength !== 'number' ||
|
|
1386
|
-
// TODO: should the second test be `isEmberArray` instead?
|
|
1387
|
-
!(Array.isArray(current) || 'objectAt' in current)) {
|
|
1388
|
-
// If the current object isn't an array, there's nothing else to do,
|
|
1389
|
-
// we don't watch individual properties. Break out of the loop.
|
|
1390
|
-
break;
|
|
1391
|
-
} else if (arrLength === 0) {
|
|
1392
|
-
// Fast path for empty arrays
|
|
1393
|
-
chainTags.push(tagForProperty(current, '[]'));
|
|
1394
|
-
break;
|
|
1395
|
-
}
|
|
1396
|
-
if (segmentEnd === -1) {
|
|
1397
|
-
segment = path.slice(lastSegmentEnd);
|
|
1398
|
-
} else {
|
|
1399
|
-
// Deprecated, remove once we turn the deprecation into an assertion
|
|
1400
|
-
segment = path.slice(lastSegmentEnd, segmentEnd);
|
|
1401
|
-
}
|
|
1402
|
-
|
|
1403
|
-
// Push the tags for each item's property
|
|
1404
|
-
for (let i = 0; i < arrLength; i++) {
|
|
1405
|
-
let item = objectAt(current, i);
|
|
1406
|
-
if (item) {
|
|
1407
|
-
(isDevelopingApp() && !(typeof item === 'object') && assert(`When using @each to observe the array \`${current.toString()}\`, the items in the array must be objects`, typeof item === 'object'));
|
|
1408
|
-
chainTags.push(tagForProperty(item, segment, true));
|
|
1409
|
-
currentMeta = peekMeta(item);
|
|
1410
|
-
descriptor = currentMeta !== null ? currentMeta.peekDescriptors(segment) : undefined;
|
|
1411
|
-
|
|
1412
|
-
// If the key is an alias, we need to bootstrap it
|
|
1413
|
-
if (descriptor !== undefined && typeof descriptor.altKey === 'string') {
|
|
1414
|
-
item[segment];
|
|
1415
|
-
}
|
|
1416
|
-
}
|
|
1417
|
-
}
|
|
1418
|
-
|
|
1419
|
-
// Push the tag for the array length itself
|
|
1420
|
-
chainTags.push(tagForProperty(current, '[]', true, currentTagMeta));
|
|
1421
|
-
break;
|
|
1422
|
-
}
|
|
1423
|
-
let propertyTag = tagForProperty(current, segment, true, currentTagMeta);
|
|
1424
|
-
descriptor = currentMeta !== null ? currentMeta.peekDescriptors(segment) : undefined;
|
|
1425
|
-
chainTags.push(propertyTag);
|
|
1426
|
-
|
|
1427
|
-
// If we're at the end of the path, processing the last segment, and it's
|
|
1428
|
-
// not an alias, we should _not_ get the last value, since we already have
|
|
1429
|
-
// its tag. There's no reason to access it and do more work.
|
|
1430
|
-
if (segmentEnd === pathLength) {
|
|
1431
|
-
// If the key was an alias, we should always get the next value in order to
|
|
1432
|
-
// bootstrap the alias. This is because aliases, unlike other CPs, should
|
|
1433
|
-
// always be in sync with the aliased value.
|
|
1434
|
-
if (CHAIN_PASS_THROUGH.has(descriptor)) {
|
|
1435
|
-
current[segment];
|
|
1436
|
-
}
|
|
1437
|
-
break;
|
|
1438
|
-
}
|
|
1439
|
-
if (descriptor === undefined) {
|
|
1440
|
-
// If the descriptor is undefined, then its a normal property, so we should
|
|
1441
|
-
// lookup the value to chain off of like normal.
|
|
1442
|
-
|
|
1443
|
-
if (!(segment in current) && typeof current.unknownProperty === 'function') {
|
|
1444
|
-
current = current.unknownProperty(segment);
|
|
1445
|
-
} else {
|
|
1446
|
-
current = current[segment];
|
|
1447
|
-
}
|
|
1448
|
-
} else if (CHAIN_PASS_THROUGH.has(descriptor)) {
|
|
1449
|
-
current = current[segment];
|
|
1450
|
-
} else {
|
|
1451
|
-
// If the descriptor is defined, then its a normal CP (not an alias, which
|
|
1452
|
-
// would have been handled earlier). We get the last revision to check if
|
|
1453
|
-
// the CP is still valid, and if so we use the cached value. If not, then
|
|
1454
|
-
// we create a lazy chain lookup, and the next time the CP is calculated,
|
|
1455
|
-
// it will update that lazy chain.
|
|
1456
|
-
let instanceMeta = currentMeta.source === current ? currentMeta : meta(current);
|
|
1457
|
-
let lastRevision = instanceMeta.revisionFor(segment);
|
|
1458
|
-
if (lastRevision !== undefined && validateTag(propertyTag, lastRevision)) {
|
|
1459
|
-
current = instanceMeta.valueFor(segment);
|
|
1460
|
-
} else {
|
|
1461
|
-
// use metaFor here to ensure we have the meta for the instance
|
|
1462
|
-
let lazyChains = instanceMeta.writableLazyChainsFor(segment);
|
|
1463
|
-
let rest = path.substring(segmentEnd + 1);
|
|
1464
|
-
let placeholderTag = createUpdatableTag();
|
|
1465
|
-
lazyChains.push([placeholderTag, rest]);
|
|
1466
|
-
chainTags.push(placeholderTag);
|
|
1467
|
-
break;
|
|
607
|
+
let observerMap = sync === true ? SYNC_OBSERVERS : ASYNC_OBSERVERS;
|
|
608
|
+
let activeObservers = observerMap.get(target);
|
|
609
|
+
if (activeObservers !== undefined) {
|
|
610
|
+
let observer = activeObservers.get(eventName);
|
|
611
|
+
observer.count--;
|
|
612
|
+
if (observer.count === 0) {
|
|
613
|
+
activeObservers.delete(eventName);
|
|
614
|
+
if (activeObservers.size === 0) {
|
|
615
|
+
observerMap.delete(target);
|
|
1468
616
|
}
|
|
1469
617
|
}
|
|
1470
|
-
if (!isObject(current)) {
|
|
1471
|
-
// we've hit the end of the chain for now, break out
|
|
1472
|
-
break;
|
|
1473
|
-
}
|
|
1474
|
-
currentTagMeta = tagMetaFor(current);
|
|
1475
|
-
currentMeta = peekMeta(current);
|
|
1476
618
|
}
|
|
1477
|
-
return chainTags;
|
|
1478
619
|
}
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
let [maybeTarget, maybeKey, maybeDesc] = args;
|
|
1482
|
-
return (
|
|
1483
|
-
// Ensure we have the right number of args
|
|
1484
|
-
args.length === 3 && (
|
|
1485
|
-
// Make sure the target is a class or object (prototype)
|
|
1486
|
-
typeof maybeTarget === 'function' || typeof maybeTarget === 'object' && maybeTarget !== null) &&
|
|
1487
|
-
// Make sure the key is a string
|
|
1488
|
-
typeof maybeKey === 'string' && (
|
|
1489
|
-
// Make sure the descriptor is the right shape
|
|
1490
|
-
typeof maybeDesc === 'object' && maybeDesc !== null || maybeDesc === undefined)
|
|
1491
|
-
);
|
|
620
|
+
function suspendedObserverDeactivation() {
|
|
621
|
+
DEACTIVATE_SUSPENDED = true;
|
|
1492
622
|
}
|
|
1493
|
-
function
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
623
|
+
function resumeObserverDeactivation() {
|
|
624
|
+
DEACTIVATE_SUSPENDED = false;
|
|
625
|
+
for (let [target, eventName, sync] of SCHEDULED_DEACTIVATE) {
|
|
626
|
+
deactivateObserver(target, eventName, sync);
|
|
627
|
+
}
|
|
628
|
+
SCHEDULED_DEACTIVATE = [];
|
|
1499
629
|
}
|
|
1500
630
|
|
|
1501
631
|
/**
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
*/
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
meta.writeDescriptors(keyName, this);
|
|
632
|
+
* Primarily used for cases where we are redefining a class, e.g. mixins/reopen
|
|
633
|
+
* being applied later. Revalidates all the observers, resetting their tags.
|
|
634
|
+
*
|
|
635
|
+
* @private
|
|
636
|
+
* @param target
|
|
637
|
+
*/
|
|
638
|
+
function revalidateObservers(target) {
|
|
639
|
+
if (ASYNC_OBSERVERS.has(target)) {
|
|
640
|
+
ASYNC_OBSERVERS.get(target).forEach(observer => {
|
|
641
|
+
observer.tag = getChainTagsForKey(target, observer.path, tagMetaFor(target), peekMeta(target));
|
|
642
|
+
observer.lastRevision = valueForTag(observer.tag);
|
|
643
|
+
});
|
|
1515
644
|
}
|
|
1516
|
-
|
|
1517
|
-
|
|
645
|
+
if (SYNC_OBSERVERS.has(target)) {
|
|
646
|
+
SYNC_OBSERVERS.get(target).forEach(observer => {
|
|
647
|
+
observer.tag = getChainTagsForKey(target, observer.path, tagMetaFor(target), peekMeta(target));
|
|
648
|
+
observer.lastRevision = valueForTag(observer.tag);
|
|
649
|
+
});
|
|
1518
650
|
}
|
|
1519
651
|
}
|
|
1520
|
-
let
|
|
1521
|
-
|
|
1522
|
-
|
|
652
|
+
let lastKnownRevision = 0;
|
|
653
|
+
function flushAsyncObservers(_schedule) {
|
|
654
|
+
let currentRevision = valueForTag(CURRENT_TAG);
|
|
655
|
+
if (lastKnownRevision === currentRevision) {
|
|
656
|
+
return;
|
|
657
|
+
}
|
|
658
|
+
lastKnownRevision = currentRevision;
|
|
659
|
+
ASYNC_OBSERVERS.forEach((activeObservers, target) => {
|
|
660
|
+
let meta = peekMeta(target);
|
|
661
|
+
activeObservers.forEach((observer, eventName) => {
|
|
662
|
+
if (!validateTag(observer.tag, observer.lastRevision)) {
|
|
663
|
+
let sendObserver = () => {
|
|
664
|
+
try {
|
|
665
|
+
sendEvent(target, eventName, [target, observer.path], undefined, meta);
|
|
666
|
+
} finally {
|
|
667
|
+
observer.tag = getChainTagsForKey(target, observer.path, tagMetaFor(target), peekMeta(target));
|
|
668
|
+
observer.lastRevision = valueForTag(observer.tag);
|
|
669
|
+
}
|
|
670
|
+
};
|
|
671
|
+
if (_schedule) {
|
|
672
|
+
_schedule('actions', sendObserver);
|
|
673
|
+
} else {
|
|
674
|
+
sendObserver();
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
});
|
|
678
|
+
});
|
|
1523
679
|
}
|
|
1524
|
-
function
|
|
1525
|
-
|
|
1526
|
-
|
|
680
|
+
function flushSyncObservers() {
|
|
681
|
+
// When flushing synchronous observers, we know that something has changed (we
|
|
682
|
+
// only do this during a notifyPropertyChange), so there's no reason to check
|
|
683
|
+
// a global revision.
|
|
684
|
+
|
|
685
|
+
SYNC_OBSERVERS.forEach((activeObservers, target) => {
|
|
686
|
+
let meta = peekMeta(target);
|
|
687
|
+
activeObservers.forEach((observer, eventName) => {
|
|
688
|
+
if (!observer.suspended && !validateTag(observer.tag, observer.lastRevision)) {
|
|
689
|
+
try {
|
|
690
|
+
observer.suspended = true;
|
|
691
|
+
sendEvent(target, eventName, [target, observer.path], undefined, meta);
|
|
692
|
+
} finally {
|
|
693
|
+
observer.tag = getChainTagsForKey(target, observer.path, tagMetaFor(target), peekMeta(target));
|
|
694
|
+
observer.lastRevision = valueForTag(observer.tag);
|
|
695
|
+
observer.suspended = false;
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
});
|
|
699
|
+
});
|
|
700
|
+
}
|
|
701
|
+
function setObserverSuspended(target, property, suspended) {
|
|
702
|
+
let activeObservers = SYNC_OBSERVERS.get(target);
|
|
703
|
+
if (!activeObservers) {
|
|
704
|
+
return;
|
|
1527
705
|
}
|
|
1528
|
-
|
|
1529
|
-
|
|
706
|
+
let observer = activeObservers.get(changeEvent(property));
|
|
707
|
+
if (observer) {
|
|
708
|
+
observer.suspended = suspended;
|
|
1530
709
|
}
|
|
1531
|
-
return getter;
|
|
1532
|
-
}
|
|
1533
|
-
function DESCRIPTOR_SETTER_FUNCTION(name, descriptor) {
|
|
1534
|
-
let set = function CPSETTER_FUNCTION(value) {
|
|
1535
|
-
return descriptor.set(this, name, value);
|
|
1536
|
-
};
|
|
1537
|
-
COMPUTED_SETTERS.add(set);
|
|
1538
|
-
return set;
|
|
1539
710
|
}
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
(isDevelopingApp() && !(isClassicDecorator || !propertyDesc || !propertyDesc.get || !COMPUTED_GETTERS.has(propertyDesc.get)) && assert(`Only one computed property decorator can be applied to a class field or accessor, but '${key}' was decorated twice. You may have added the decorator to both a getter and setter, which is unnecessary.`, isClassicDecorator || !propertyDesc || !propertyDesc.get || !COMPUTED_GETTERS.has(propertyDesc.get)));
|
|
1544
|
-
let meta$1 = arguments.length === 3 ? meta(target) : maybeMeta;
|
|
1545
|
-
desc.setup(target, key, propertyDesc, meta$1);
|
|
1546
|
-
let computedDesc = {
|
|
1547
|
-
enumerable: desc.enumerable,
|
|
1548
|
-
configurable: desc.configurable,
|
|
1549
|
-
get: DESCRIPTOR_GETTER_FUNCTION(key, desc),
|
|
1550
|
-
set: DESCRIPTOR_SETTER_FUNCTION(key, desc)
|
|
1551
|
-
};
|
|
1552
|
-
return computedDesc;
|
|
1553
|
-
};
|
|
1554
|
-
setClassicDecorator(decorator, desc);
|
|
1555
|
-
Object.setPrototypeOf(decorator, DecoratorClass.prototype);
|
|
1556
|
-
return decorator;
|
|
711
|
+
function destroyObservers(target) {
|
|
712
|
+
if (SYNC_OBSERVERS.size > 0) SYNC_OBSERVERS.delete(target);
|
|
713
|
+
if (ASYNC_OBSERVERS.size > 0) ASYNC_OBSERVERS.delete(target);
|
|
1557
714
|
}
|
|
1558
715
|
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
716
|
+
const PROPERTY_DID_CHANGE = Symbol('PROPERTY_DID_CHANGE');
|
|
717
|
+
function hasPropertyDidChange(obj) {
|
|
718
|
+
return obj != null && typeof obj === 'object' && typeof obj[PROPERTY_DID_CHANGE] === 'function';
|
|
719
|
+
}
|
|
720
|
+
let deferred = 0;
|
|
1562
721
|
|
|
1563
722
|
/**
|
|
1564
|
-
|
|
723
|
+
This function is called just after an object property has changed.
|
|
724
|
+
It will notify any observers and clear caches among other things.
|
|
1565
725
|
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
@
|
|
726
|
+
Normally you will not need to call this method directly but if for some
|
|
727
|
+
reason you can't directly watch a property you can invoke this method
|
|
728
|
+
manually.
|
|
729
|
+
|
|
730
|
+
@method notifyPropertyChange
|
|
731
|
+
@for @ember/object
|
|
732
|
+
@param {Object} obj The object with the property that will change
|
|
733
|
+
@param {String} keyName The property key (or path) that will change.
|
|
734
|
+
@param {Meta} [_meta] The objects meta.
|
|
735
|
+
@param {unknown} [value] The new value to set for the property
|
|
736
|
+
@return {void}
|
|
737
|
+
@since 3.1.0
|
|
738
|
+
@public
|
|
1571
739
|
*/
|
|
1572
|
-
function
|
|
1573
|
-
(isDevelopingApp() && !(obj !== null) && assert('Cannot call `descriptorForProperty` on null', obj !== null));
|
|
1574
|
-
(isDevelopingApp() && !(obj !== undefined) && assert('Cannot call `descriptorForProperty` on undefined', obj !== undefined));
|
|
1575
|
-
(isDevelopingApp() && !(typeof obj === 'object' || typeof obj === 'function') && assert(`Cannot call \`descriptorForProperty\` on ${typeof obj}`, typeof obj === 'object' || typeof obj === 'function'));
|
|
740
|
+
function notifyPropertyChange(obj, keyName, _meta, value) {
|
|
1576
741
|
let meta = _meta === undefined ? peekMeta(obj) : _meta;
|
|
1577
|
-
if (meta !== null) {
|
|
1578
|
-
return
|
|
742
|
+
if (meta !== null && (meta.isInitializing() || meta.isPrototypeMeta(obj))) {
|
|
743
|
+
return;
|
|
744
|
+
}
|
|
745
|
+
markObjectAsDirty(obj, keyName);
|
|
746
|
+
if (deferred <= 0) {
|
|
747
|
+
flushSyncObservers();
|
|
748
|
+
}
|
|
749
|
+
if (PROPERTY_DID_CHANGE in obj) {
|
|
750
|
+
// It's redundant to do this here, but we don't want to check above so we can avoid an extra function call in prod.
|
|
751
|
+
(isDevelopingApp() && !(hasPropertyDidChange(obj)) && assert('property did change hook is invalid', hasPropertyDidChange(obj))); // we need to check the arguments length here; there's a check in Component's `PROPERTY_DID_CHANGE`
|
|
752
|
+
// that checks its arguments length, so we have to explicitly not call this with `value`
|
|
753
|
+
// if it is not passed to `notifyPropertyChange`
|
|
754
|
+
if (arguments.length === 4) {
|
|
755
|
+
obj[PROPERTY_DID_CHANGE](keyName, value);
|
|
756
|
+
} else {
|
|
757
|
+
obj[PROPERTY_DID_CHANGE](keyName);
|
|
758
|
+
}
|
|
1579
759
|
}
|
|
1580
|
-
}
|
|
1581
|
-
function descriptorForDecorator(dec) {
|
|
1582
|
-
return DECORATOR_DESCRIPTOR_MAP.get(dec);
|
|
1583
760
|
}
|
|
1584
761
|
|
|
1585
762
|
/**
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
@method isClassicDecorator
|
|
1589
|
-
@param {any} possibleDesc the value to check
|
|
1590
|
-
@return {boolean}
|
|
763
|
+
@method beginPropertyChanges
|
|
764
|
+
@chainable
|
|
1591
765
|
@private
|
|
1592
766
|
*/
|
|
1593
|
-
function
|
|
1594
|
-
|
|
767
|
+
function beginPropertyChanges() {
|
|
768
|
+
deferred++;
|
|
769
|
+
suspendedObserverDeactivation();
|
|
1595
770
|
}
|
|
1596
771
|
|
|
1597
772
|
/**
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
@method setClassicDecorator
|
|
1601
|
-
@param {function} decorator the value to mark as a decorator
|
|
773
|
+
@method endPropertyChanges
|
|
1602
774
|
@private
|
|
1603
775
|
*/
|
|
1604
|
-
function
|
|
1605
|
-
|
|
776
|
+
function endPropertyChanges() {
|
|
777
|
+
deferred--;
|
|
778
|
+
if (deferred <= 0) {
|
|
779
|
+
flushSyncObservers();
|
|
780
|
+
resumeObserverDeactivation();
|
|
781
|
+
}
|
|
1606
782
|
}
|
|
1607
783
|
|
|
1608
|
-
const END_WITH_EACH_REGEX = /\.@each$/;
|
|
1609
|
-
|
|
1610
784
|
/**
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
The only pattern supported is brace-expansion, anything else will be passed
|
|
1614
|
-
once to `callback` directly.
|
|
1615
|
-
|
|
1616
|
-
Example
|
|
1617
|
-
|
|
1618
|
-
```js
|
|
1619
|
-
import { expandProperties } from '@ember/object/computed';
|
|
1620
|
-
|
|
1621
|
-
function echo(arg){ console.log(arg); }
|
|
785
|
+
Make a series of property changes together in an
|
|
786
|
+
exception-safe way.
|
|
1622
787
|
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
expandProperties('{foo,bar}.{spam,eggs}', echo) //=> 'foo.spam', 'foo.eggs', 'bar.spam', 'bar.eggs'
|
|
1629
|
-
expandProperties('{foo}.bar.{baz}') //=> 'foo.bar.baz'
|
|
788
|
+
```javascript
|
|
789
|
+
Ember.changeProperties(function() {
|
|
790
|
+
obj1.set('foo', mayBlowUpWhenSet);
|
|
791
|
+
obj2.set('bar', baz);
|
|
792
|
+
});
|
|
1630
793
|
```
|
|
1631
794
|
|
|
1632
|
-
@method
|
|
1633
|
-
@
|
|
1634
|
-
@
|
|
1635
|
-
@public
|
|
1636
|
-
@param {String} pattern The property pattern to expand.
|
|
1637
|
-
@param {Function} callback The callback to invoke. It is invoked once per
|
|
1638
|
-
expansion, and is passed the expansion.
|
|
795
|
+
@method changeProperties
|
|
796
|
+
@param {Function} callback
|
|
797
|
+
@private
|
|
1639
798
|
*/
|
|
1640
|
-
function
|
|
1641
|
-
(
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
callback(pattern.replace(END_WITH_EACH_REGEX, '.[]'));
|
|
1647
|
-
} else {
|
|
1648
|
-
dive('', pattern, start, callback);
|
|
1649
|
-
}
|
|
1650
|
-
}
|
|
1651
|
-
function dive(prefix, pattern, start, callback) {
|
|
1652
|
-
let end = pattern.indexOf('}'),
|
|
1653
|
-
i = 0,
|
|
1654
|
-
newStart,
|
|
1655
|
-
arrayLength;
|
|
1656
|
-
let tempArr = pattern.substring(start + 1, end).split(',');
|
|
1657
|
-
let after = pattern.substring(end + 1);
|
|
1658
|
-
prefix = prefix + pattern.substring(0, start);
|
|
1659
|
-
arrayLength = tempArr.length;
|
|
1660
|
-
while (i < arrayLength) {
|
|
1661
|
-
newStart = after.indexOf('{');
|
|
1662
|
-
if (newStart < 0) {
|
|
1663
|
-
callback((prefix + tempArr[i++] + after).replace(END_WITH_EACH_REGEX, '.[]'));
|
|
1664
|
-
} else {
|
|
1665
|
-
dive(prefix + tempArr[i++], after, newStart, callback);
|
|
1666
|
-
}
|
|
799
|
+
function changeProperties(callback) {
|
|
800
|
+
beginPropertyChanges();
|
|
801
|
+
try {
|
|
802
|
+
callback();
|
|
803
|
+
} finally {
|
|
804
|
+
endPropertyChanges();
|
|
1667
805
|
}
|
|
1668
806
|
}
|
|
1669
807
|
|
|
@@ -2775,4 +1913,4 @@ class TrackedDescriptor {
|
|
|
2775
1913
|
}
|
|
2776
1914
|
}
|
|
2777
1915
|
|
|
2778
|
-
export {
|
|
1916
|
+
export { TrackedDescriptor as $, revalidateObservers as A, nativeDescDecorator as B, ComputedProperty as C, DEBUG_INJECTION_FUNCTIONS as D, descriptorForDecorator as E, makeComputedDecorator as F, addListener as G, removeListener as H, flushAsyncObservers as I, ComputedDescriptor as J, CHAIN_PASS_THROUGH as K, getChainTagsForKey as L, finishLazyChains as M, on as N, LIBRARIES as O, PROXY_CONTENT as P, PROPERTY_DID_CHANGE as Q, autoComputed as R, tracked as S, changeProperties as T, isComputed as U, _getProp as V, Libraries as W, ASYNC_OBSERVERS as X, SYNC_OBSERVERS as Y, markObjectAsDirty as Z, _getPath as _, tagForProperty as a, descriptorForProperty as b, computed as c, defineProperty as d, expandProperties as e, isClassicDecorator as f, get as g, hasUnknownProperty as h, isElementDescriptor as i, activateObserver as j, sendEvent as k, endPropertyChanges as l, beginPropertyChanges as m, notifyPropertyChange as n, objectAt as o, inject as p, isPath as q, COMPUTED_SETTERS as r, setClassicDecorator as s, tagForObject as t, getPossibleMandatoryProxyValue as u, addObserver as v, removeObserver as w, hasListeners as x, defineValue as y, defineDecorator as z };
|