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.
Files changed (147) hide show
  1. package/build-metadata.json +3 -3
  2. package/dist/ember-template-compiler.js +48 -42
  3. package/dist/ember-testing.js +1 -1
  4. package/dist/ember.debug.js +23102 -23100
  5. package/dist/ember.prod.js +31562 -31563
  6. package/dist/packages/@ember/-internals/container/index.js +1 -1
  7. package/dist/packages/@ember/-internals/deprecations/index.js +2 -1
  8. package/dist/packages/@ember/-internals/glimmer/index.js +2 -1
  9. package/dist/packages/@ember/-internals/meta/lib/meta.js +4 -3
  10. package/dist/packages/@ember/-internals/metal/index.js +9 -8
  11. package/dist/packages/@ember/-internals/routing/index.js +6 -5
  12. package/dist/packages/@ember/-internals/runtime/lib/ext/rsvp.js +3 -2
  13. package/dist/packages/@ember/-internals/runtime/lib/mixins/-proxy.js +6 -5
  14. package/dist/packages/@ember/-internals/runtime/lib/mixins/action_handler.js +4 -3
  15. package/dist/packages/@ember/-internals/runtime/lib/mixins/container_proxy.js +1 -1
  16. package/dist/packages/@ember/-internals/runtime/lib/mixins/registry_proxy.js +2 -1
  17. package/dist/packages/@ember/-internals/runtime/lib/mixins/target_action_support.js +4 -3
  18. package/dist/packages/@ember/-internals/string/index.js +1 -1
  19. package/dist/packages/@ember/-internals/utils/index.js +4 -4
  20. package/dist/packages/@ember/-internals/views/index.js +1 -1
  21. package/dist/packages/@ember/-internals/views/lib/compat/fallback-view-registry.js +1 -1
  22. package/dist/packages/@ember/-internals/views/lib/component_lookup.js +1 -1
  23. package/dist/packages/@ember/-internals/views/lib/mixins/action_support.js +5 -3
  24. package/dist/packages/@ember/-internals/views/lib/mixins/child_views_support.js +3 -3
  25. package/dist/packages/@ember/-internals/views/lib/mixins/class_names_support.js +4 -3
  26. package/dist/packages/@ember/-internals/views/lib/mixins/view_support.js +4 -3
  27. package/dist/packages/@ember/-internals/views/lib/system/event_dispatcher.js +6 -12
  28. package/dist/packages/@ember/-internals/views/lib/system/utils.js +3 -2
  29. package/dist/packages/@ember/-internals/views/lib/views/core_view.js +76 -8
  30. package/dist/packages/@ember/-internals/views/lib/views/states.js +4 -3
  31. package/dist/packages/@ember/application/index.js +16 -7
  32. package/dist/packages/@ember/application/instance.js +13 -9
  33. package/dist/packages/@ember/application/namespace.js +7 -6
  34. package/dist/packages/@ember/array/index.js +617 -11
  35. package/dist/packages/@ember/array/make.js +1 -0
  36. package/dist/packages/@ember/array/mutable.js +1 -1
  37. package/dist/packages/@ember/array/proxy.js +8 -5
  38. package/dist/packages/@ember/component/helper.js +4 -4
  39. package/dist/packages/@ember/component/index.js +4 -4
  40. package/dist/packages/@ember/controller/index.js +6 -6
  41. package/dist/packages/@ember/debug/container-debug-adapter.js +5 -4
  42. package/dist/packages/@ember/debug/data-adapter.js +7 -4
  43. package/dist/packages/@ember/debug/index.js +213 -4
  44. package/dist/packages/@ember/debug/lib/assert.js +47 -0
  45. package/dist/packages/@ember/debug/lib/deprecate.js +194 -4
  46. package/dist/packages/@ember/debug/lib/inspect.js +120 -2
  47. package/dist/packages/@ember/debug/lib/warn.js +94 -3
  48. package/dist/packages/@ember/engine/index.js +440 -17
  49. package/dist/packages/@ember/engine/instance.js +175 -11
  50. package/dist/packages/@ember/engine/parent.js +1 -0
  51. package/dist/packages/@ember/helper/index.js +4 -4
  52. package/dist/packages/@ember/instrumentation/index.js +2 -1
  53. package/dist/packages/@ember/modifier/index.js +13 -5
  54. package/dist/packages/@ember/modifier/on.js +15 -0
  55. package/dist/packages/@ember/object/-internals.js +6 -5
  56. package/dist/packages/@ember/object/compat.js +4 -3
  57. package/dist/packages/@ember/object/computed.js +4 -4
  58. package/dist/packages/@ember/object/core.js +861 -14
  59. package/dist/packages/@ember/object/evented.js +4 -4
  60. package/dist/packages/@ember/object/events.js +3 -3
  61. package/dist/packages/@ember/object/index.js +260 -9
  62. package/dist/packages/@ember/object/internals.js +1 -1
  63. package/dist/packages/@ember/object/lib/computed/computed_macros.js +8 -6
  64. package/dist/packages/@ember/object/lib/computed/reduce_computed_macros.js +8 -4
  65. package/dist/packages/@ember/object/mixin.js +6 -5
  66. package/dist/packages/@ember/object/observable.js +103 -9
  67. package/dist/packages/@ember/object/observers.js +3 -3
  68. package/dist/packages/@ember/object/promise-proxy-mixin.js +5 -5
  69. package/dist/packages/@ember/renderer/index.js +4 -4
  70. package/dist/packages/@ember/routing/-internals.js +3 -1
  71. package/dist/packages/@ember/routing/hash-location.js +2 -2
  72. package/dist/packages/@ember/routing/history-location.js +3 -2
  73. package/dist/packages/@ember/routing/index.js +4 -4
  74. package/dist/packages/@ember/routing/lib/dsl.js +2 -1
  75. package/dist/packages/@ember/routing/lib/generate_controller.js +4 -3
  76. package/dist/packages/@ember/routing/lib/router_state.js +26 -1
  77. package/dist/packages/@ember/routing/lib/routing-service.js +107 -9
  78. package/dist/packages/@ember/routing/lib/utils.js +238 -7
  79. package/dist/packages/@ember/routing/none-location.js +3 -2
  80. package/dist/packages/@ember/routing/route.js +1618 -22
  81. package/dist/packages/@ember/routing/router-service.js +638 -12
  82. package/dist/packages/@ember/routing/router.js +1449 -14
  83. package/dist/packages/@ember/runloop/index.js +760 -6
  84. package/dist/packages/@ember/service/index.js +3 -3
  85. package/dist/packages/@ember/template/index.js +4 -4
  86. package/dist/packages/@ember/utils/index.js +2 -1
  87. package/dist/packages/@ember/utils/lib/compare.js +159 -4
  88. package/dist/packages/@ember/utils/lib/is_empty.js +4 -4
  89. package/dist/packages/@ember/utils/lib/type-of.js +110 -1
  90. package/dist/packages/@glimmer/tracking/index.js +3 -3
  91. package/dist/packages/@glimmer/tracking/primitives/cache.js +3 -3
  92. package/dist/packages/ember/barrel.js +28 -13
  93. package/dist/packages/ember/version.js +1 -1
  94. package/dist/packages/ember-testing/lib/adapters/adapter.js +1 -1
  95. package/dist/packages/ember-testing/lib/adapters/qunit.js +2 -1
  96. package/dist/packages/ember-testing/lib/ext/application.js +2 -1
  97. package/dist/packages/ember-testing/lib/ext/rsvp.js +1 -1
  98. package/dist/packages/ember-testing/lib/helpers/and_then.js +2 -1
  99. package/dist/packages/ember-testing/lib/helpers/current_path.js +8 -6
  100. package/dist/packages/ember-testing/lib/helpers/current_route_name.js +8 -6
  101. package/dist/packages/ember-testing/lib/helpers/current_url.js +6 -5
  102. package/dist/packages/ember-testing/lib/helpers/pause_test.js +2 -1
  103. package/dist/packages/ember-testing/lib/helpers/visit.js +4 -3
  104. package/dist/packages/ember-testing/lib/helpers/wait.js +4 -3
  105. package/dist/packages/ember-testing/lib/initializers.js +15 -8
  106. package/dist/packages/ember-testing/lib/setup_for_testing.js +1 -1
  107. package/dist/packages/ember-testing/lib/test/run.js +1 -1
  108. package/dist/packages/router_js/index.js +2 -1
  109. package/dist/packages/shared-chunks/{alias-By_2yu5c.js → alias-Dri0koi2.js} +5 -3
  110. package/dist/packages/shared-chunks/array-3xbmc_4J.js +119 -0
  111. package/dist/packages/shared-chunks/{cache-gDE3bkXq.js → cache-BESCGvbE.js} +667 -1529
  112. package/dist/packages/shared-chunks/{core_view-Cxne2_wu.js → chunk-3SQBS3Y5-Cj4eryg1.js} +1 -88
  113. package/dist/packages/shared-chunks/{index-BXPoca1S.js → index-Llq6dmgX.js} +40 -4660
  114. package/dist/packages/shared-chunks/{is_proxy-Dmis-70B.js → is_proxy-DjvCKvd5.js} +1 -1
  115. package/dist/packages/shared-chunks/{mandatory-setter-1UQhiJOb.js → mandatory-setter-BiXq-dpN.js} +2 -1
  116. package/dist/packages/shared-chunks/{name-z9D9Yibn.js → name-Dx2bGFVv.js} +1 -1
  117. package/dist/packages/shared-chunks/{namespace_search-CBgHTkDh.js → namespace_search-btMaPM-_.js} +2 -2
  118. package/dist/packages/shared-chunks/{property_set-CW4q-uo4.js → property_set-BapAkp3X.js} +5 -4
  119. package/dist/packages/shared-chunks/{registry-DzfcDwii.js → registry-B8WARvkP.js} +3 -2
  120. package/dist/packages/shared-chunks/{router-B-Q1aYBn.js → router-DrLZsJeE.js} +2 -482
  121. package/dist/packages/shared-chunks/{set_properties-DvalyQdu.js → set_properties-BScfxzvI.js} +2 -2
  122. package/dist/packages/shared-chunks/setup-registry-du4pSGZi.js +48 -0
  123. package/dist/packages/shared-chunks/{to-string-D8i3mjEU.js → to-string-B1BmwUkt.js} +1 -1
  124. package/dist/packages/shared-chunks/unrecognized-url-error-zpz-JEoG.js +484 -0
  125. package/docs/data.json +152 -142
  126. package/package.json +4 -7
  127. package/types/stable/@ember/-internals/metal/lib/array.d.ts +1 -2
  128. package/types/stable/@ember/-internals/metal/lib/object-at.d.ts +4 -0
  129. package/types/stable/@ember/-internals/metal/lib/observer.d.ts +2 -1
  130. package/types/stable/@ember/array/index.d.ts +1 -1
  131. package/types/stable/@ember/array/make.d.ts +3 -0
  132. package/types/stable/@ember/debug/index.d.ts +3 -7
  133. package/types/stable/@ember/debug/lib/assert.d.ts +8 -0
  134. package/types/stable/@ember/engine/index.d.ts +1 -1
  135. package/types/stable/@ember/engine/instance.d.ts +2 -2
  136. package/types/stable/@ember/engine/parent.d.ts +3 -0
  137. package/types/stable/@ember/modifier/index.d.ts +1 -3
  138. package/types/stable/@ember/modifier/on.d.ts +5 -0
  139. package/types/stable/@ember/routing/lib/routing-service.d.ts +1 -1
  140. package/types/stable/@ember/routing/route.d.ts +2 -3
  141. package/types/stable/@ember/routing/router-service.d.ts +1 -1
  142. package/types/stable/@ember/routing/router.d.ts +4 -4
  143. package/types/stable/ember/barrel.d.ts +1 -1
  144. package/types/stable/ember/index.d.ts +1 -1
  145. package/types/stable/index.d.ts +5 -0
  146. package/dist/packages/shared-chunks/index-DTxy4Zgx.js +0 -641
  147. package/dist/packages/shared-chunks/index-PYiGj1jp.js +0 -2071
@@ -1,1669 +1,807 @@
1
- import { meta, peekMeta } from '../@ember/-internals/meta/lib/meta.js';
2
- import { d as setListeners, f as setupMandatorySetter, e as isObject, h as setWithMandatorySetter } from './mandatory-setter-1UQhiJOb.js';
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 { a as assert, b as inspect, w as warn, c as debug } from './index-DTxy4Zgx.js';
5
- import { registerDestructor, isDestroyed } from '../@glimmer/destroyable/index.js';
6
- import { tagMetaFor, valueForTag, CURRENT_TAG, validateTag, tagFor, CONSTANT_TAG, dirtyTagFor, combine, updateTag as UPDATE_TAG, createUpdatableTag, untrack, ALLOW_CYCLES, consumeTag, track, isTracking, trackedData } from '../@glimmer/validator/index.js';
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 { onErrorTarget } from '../@ember/-internals/error-handling/index.js';
10
- import { t as toString, s as symbol } from './to-string-D8i3mjEU.js';
11
- import { s as setProxy } from './is_proxy-Dmis-70B.js';
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 Backburner from '../backburner.js/index.js';
16
+ import inspect from '../@ember/debug/lib/inspect.js';
17
17
 
18
- /**
19
- @module @ember/object
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
- target = null;
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
- Send an event. The execution of suspended listeners
61
- is skipped, and once listeners are removed. A listener without
62
- a target is executed on the passed object. If an array of actions
63
- is not passed, the actions stored on the passed object are invoked.
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
- for (let i = actions.length - 3; i >= 0; i -= 3) {
83
- // looping in reverse for once listeners
84
- let target = actions[i];
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 true;
37
+ return tag;
103
38
  }
104
-
105
- /**
106
- @public
107
- @method hasListeners
108
- @static
109
- @for @ember/object/events
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
- let matched = meta.matchingListeners(eventName);
120
- return matched !== undefined && matched.length > 0;
46
+ return CONSTANT_TAG;
121
47
  }
122
-
123
- /**
124
- Define a property as a function that should be executed when
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
- let currentRunLoop = null;
161
- function _getCurrentRunLoop() {
162
- return currentRunLoop;
163
- }
164
- function onBegin(current) {
165
- currentRunLoop = current;
166
- }
167
- function onEnd(_current, next) {
168
- currentRunLoop = next;
169
- flushAsyncObservers();
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 flush(queueName, next) {
172
- if (queueName === 'render' || queueName === _rsvpErrorQueue) {
173
- flushAsyncObservers();
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
- next();
71
+ return combine(tags);
176
72
  }
177
- const _rsvpErrorQueue = `${Math.random()}${Date.now()}`.replace('.', '');
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
- If no run-loop is present, it creates a new one. If a run loop is
254
- present it will queue itself to run on the existing run-loops action
255
- queue.
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
- Please note: This is not for normal usage, and should be used sparingly.
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
- If invoked when not within a run loop:
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
- ```javascript
262
- import { join } from '@ember/runloop';
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
- join(function() {
265
- // creates a new run-loop
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
- @method removeObserver
945
- @static
946
- @for @ember/object/observers
947
- @param obj
948
- @param {String} path
949
- @param {Object|Function} target
950
- @param {Function|String} [method]
951
- @public
952
- */
953
- function removeObserver(obj, path, target, method, sync = SYNC_DEFAULT) {
954
- let eventName = changeEvent(path);
955
- let meta = peekMeta(obj);
956
- if (meta === null || !(meta.isPrototypeMeta(obj) || meta.isInitializing())) {
957
- deactivateObserver(obj, eventName, sync);
958
- }
959
- removeListener(obj, eventName, target, method);
960
- }
961
- function getOrCreateActiveObserversFor(target, sync) {
962
- let observerMap = sync === true ? SYNC_OBSERVERS : ASYNC_OBSERVERS;
963
- if (!observerMap.has(target)) {
964
- observerMap.set(target, new Map());
965
- registerDestructor(target, () => destroyObservers(target), true);
966
- }
967
- return observerMap.get(target);
968
- }
969
- function activateObserver(target, eventName, sync = false) {
970
- let activeObservers = getOrCreateActiveObserversFor(target, sync);
971
- if (activeObservers.has(eventName)) {
972
- activeObservers.get(eventName).count++;
973
- } else {
974
- let path = eventName.substring(0, eventName.lastIndexOf(':'));
975
- let tag = getChainTagsForKey(target, path, tagMetaFor(target), peekMeta(target));
976
- activeObservers.set(eventName, {
977
- count: 1,
978
- path,
979
- tag,
980
- lastRevision: valueForTag(tag),
981
- suspended: false
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
- function suspendedObserverDeactivation() {
1006
- DEACTIVATE_SUSPENDED = true;
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 resumeObserverDeactivation() {
1009
- DEACTIVATE_SUSPENDED = false;
1010
- for (let [target, eventName, sync] of SCHEDULED_DEACTIVATE) {
1011
- deactivateObserver(target, eventName, sync);
1012
- }
1013
- SCHEDULED_DEACTIVATE = [];
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
- * Primarily used for cases where we are redefining a class, e.g. mixins/reopen
1018
- * being applied later. Revalidates all the observers, resetting their tags.
1019
- *
1020
- * @private
1021
- * @param target
1022
- */
1023
- function revalidateObservers(target) {
1024
- if (ASYNC_OBSERVERS.has(target)) {
1025
- ASYNC_OBSERVERS.get(target).forEach(observer => {
1026
- observer.tag = getChainTagsForKey(target, observer.path, tagMetaFor(target), peekMeta(target));
1027
- observer.lastRevision = valueForTag(observer.tag);
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
- let lastKnownRevision = 0;
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
- function flushSyncObservers() {
1066
- // When flushing synchronous observers, we know that something has changed (we
1067
- // only do this during a notifyPropertyChange), so there's no reason to check
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 setObserverSuspended(target, property, suspended) {
1087
- let activeObservers = SYNC_OBSERVERS.get(target);
1088
- if (!activeObservers) {
1089
- return;
238
+ function DESCRIPTOR_GETTER_FUNCTION(name, descriptor) {
239
+ function getter() {
240
+ return descriptor.get(this, name);
1090
241
  }
1091
- let observer = activeObservers.get(changeEvent(property));
1092
- if (observer) {
1093
- observer.suspended = suspended;
242
+ if (isDevelopingApp()) {
243
+ COMPUTED_GETTERS.add(getter);
1094
244
  }
245
+ return getter;
1095
246
  }
1096
- function destroyObservers(target) {
1097
- if (SYNC_OBSERVERS.size > 0) SYNC_OBSERVERS.delete(target);
1098
- if (ASYNC_OBSERVERS.size > 0) ASYNC_OBSERVERS.delete(target);
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
- // This is exported for `@tracked`, but should otherwise be avoided. Use `tagForObject`.
1102
- const SELF_TAG = symbol('SELF_TAG');
1103
- function tagForProperty(obj, propertyKey, addMandatorySetter = false, meta) {
1104
- let customTagFor = getCustomTagFor(obj);
1105
- if (customTagFor !== undefined) {
1106
- return customTagFor(obj, propertyKey, addMandatorySetter);
1107
- }
1108
- let tag = tagFor(obj, propertyKey, meta);
1109
- if (isDevelopingApp() && addMandatorySetter) {
1110
- setupMandatorySetter(tag, obj, propertyKey);
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 tagForObject(obj) {
1115
- if (isObject(obj)) {
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
- function markObjectAsDirty(obj, propertyKey) {
1124
- dirtyTagFor(obj, propertyKey);
1125
- dirtyTagFor(obj, SELF_TAG);
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
- const PROPERTY_DID_CHANGE = Symbol('PROPERTY_DID_CHANGE');
1129
- function hasPropertyDidChange(obj) {
1130
- return obj != null && typeof obj === 'object' && typeof obj[PROPERTY_DID_CHANGE] === 'function';
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
- let deferred = 0;
321
+
322
+ const END_WITH_EACH_REGEX = /\.@each$/;
1133
323
 
1134
324
  /**
1135
- This function is called just after an object property has changed.
1136
- It will notify any observers and clear caches among other things.
325
+ Expands `pattern`, invoking `callback` for each expansion.
1137
326
 
1138
- Normally you will not need to call this method directly but if for some
1139
- reason you can't directly watch a property you can invoke this method
1140
- manually.
327
+ The only pattern supported is brace-expansion, anything else will be passed
328
+ once to `callback` directly.
1141
329
 
1142
- @method notifyPropertyChange
1143
- @for @ember/object
1144
- @param {Object} obj The object with the property that will change
1145
- @param {String} keyName The property key (or path) that will change.
1146
- @param {Meta} [_meta] The objects meta.
1147
- @param {unknown} [value] The new value to set for the property
1148
- @return {void}
1149
- @since 3.1.0
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 notifyPropertyChange(obj, keyName, _meta, value) {
1153
- let meta = _meta === undefined ? peekMeta(obj) : _meta;
1154
- if (meta !== null && (meta.isInitializing() || meta.isPrototypeMeta(obj))) {
1155
- return;
1156
- }
1157
- markObjectAsDirty(obj, keyName);
1158
- if (deferred <= 0) {
1159
- flushSyncObservers();
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
- if (PROPERTY_DID_CHANGE in obj) {
1162
- // 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.
1163
- (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`
1164
- // that checks its arguments length, so we have to explicitly not call this with `value`
1165
- // if it is not passed to `notifyPropertyChange`
1166
- if (arguments.length === 4) {
1167
- obj[PROPERTY_DID_CHANGE](keyName, value);
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
- obj[PROPERTY_DID_CHANGE](keyName);
379
+ dive(prefix + tempArr[i++], after, newStart, callback);
1170
380
  }
1171
381
  }
1172
382
  }
1173
383
 
1174
- /**
1175
- @method beginPropertyChanges
1176
- @chainable
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
- @method endPropertyChanges
1186
- @private
390
+ @module @ember/object
1187
391
  */
1188
- function endPropertyChanges() {
1189
- deferred--;
1190
- if (deferred <= 0) {
1191
- flushSyncObservers();
1192
- resumeObserverDeactivation();
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
- Make a series of property changes together in an
1198
- exception-safe way.
403
+ Remove an event listener
1199
404
 
1200
- ```javascript
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 changeProperties
1208
- @param {Function} callback
1209
- @private
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 changeProperties(callback) {
1212
- beginPropertyChanges();
1213
- try {
1214
- callback();
1215
- } finally {
1216
- endPropertyChanges();
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
- function arrayContentWillChange(array, startIdx, removeAmt, addAmt) {
1221
- // if no args are passed assume everything changes
1222
- if (startIdx === undefined) {
1223
- startIdx = 0;
1224
- removeAmt = addAmt = -1;
1225
- } else {
1226
- if (removeAmt === undefined) {
1227
- removeAmt = -1;
1228
- }
1229
- if (addAmt === undefined) {
1230
- addAmt = -1;
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
- sendEvent(array, '@array:before', [array, startIdx, removeAmt, addAmt]);
1234
- return array;
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 meta = peekMeta(array);
1250
- if (notify) {
1251
- if (addAmt < 0 || removeAmt < 0 || addAmt - removeAmt !== 0) {
1252
- notifyPropertyChange(array, 'length', meta);
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
- notifyPropertyChange(array, '[]', meta);
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 (meta.revisionFor('lastObject') !== undefined) {
1268
- let previousLastIndex = previousLength - 1;
1269
- let lastAffectedIndex = normalStartIdx + removedAmount;
1270
- if (previousLastIndex < lastAffectedIndex) {
1271
- notifyPropertyChange(array, 'lastObject', meta);
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 array;
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 EMPTY_ARRAY = Object.freeze([]);
1279
- function objectAt(array, index) {
1280
- if (Array.isArray(array)) {
1281
- return array[index];
1282
- } else {
1283
- return array.objectAt(index);
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
- // Ideally, we'd use MutableArray.detect but for unknown reasons this causes
1288
- // the node tests to fail strangely.
1289
- function isMutableArray(obj) {
1290
- return obj != null && typeof obj.replace === 'function';
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 replace(array, start, deleteCount, items = EMPTY_ARRAY) {
1293
- if (isMutableArray(array)) {
1294
- array.replace(start, deleteCount, items);
1295
- } else {
1296
- (isDevelopingApp() && !(Array.isArray(array)) && assert('Can only replace content of a native array or MutableArray', Array.isArray(array)));
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
- const CHUNK_SIZE = 60000;
1301
-
1302
- // To avoid overflowing the stack, we splice up to CHUNK_SIZE items at a time.
1303
- // See https://code.google.com/p/chromium/issues/detail?id=56588 for more details.
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
- array.splice(start, deleteCount);
1310
- for (let i = 0; i < items.length; i += CHUNK_SIZE) {
1311
- let chunk = items.slice(i, i + CHUNK_SIZE);
1312
- array.splice(start + i, 0, ...chunk);
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
- function removeArrayObserver(array, target, opts) {
1336
- return arrayObserversHelper(array, target, opts, removeListener);
1337
- }
1338
-
1339
- const CHAIN_PASS_THROUGH = new WeakSet();
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
- if (isObject(value)) {
1346
- for (let [tag, deps] of lazyTags) {
1347
- UPDATE_TAG(tag, getChainTagsForKey(value, deps, tagMetaFor(value), peekMeta(value)));
1348
- }
1349
- }
1350
- lazyTags.length = 0;
1351
- }
1352
- function getChainTagsForKeys(obj, keys, tagMeta, meta) {
1353
- let tags = [];
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
- function isElementDescriptor(args) {
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 nativeDescDecorator(propertyDesc) {
1494
- let decorator = function () {
1495
- return propertyDesc;
1496
- };
1497
- setClassicDecorator(decorator);
1498
- return decorator;
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
- Objects of this type can implement an interface to respond to requests to
1503
- get and set. The default implementation handles simple properties.
1504
-
1505
- @class Descriptor
1506
- @private
1507
- */
1508
- class ComputedDescriptor {
1509
- enumerable = true;
1510
- configurable = true;
1511
- _dependentKeys = undefined;
1512
- _meta = undefined;
1513
- setup(_obj, keyName, _propertyDesc, meta) {
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
- teardown(_obj, keyName, meta) {
1517
- meta.removeDescriptors(keyName);
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 COMPUTED_GETTERS;
1521
- if (isDevelopingApp()) {
1522
- COMPUTED_GETTERS = new WeakSet();
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 DESCRIPTOR_GETTER_FUNCTION(name, descriptor) {
1525
- function getter() {
1526
- return descriptor.get(this, name);
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
- if (isDevelopingApp()) {
1529
- COMPUTED_GETTERS.add(getter);
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
- const COMPUTED_SETTERS = new WeakSet();
1541
- function makeComputedDecorator(desc, DecoratorClass) {
1542
- let decorator = function COMPUTED_DECORATOR(target, key, propertyDesc, maybeMeta, isClassicDecorator) {
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
- const DECORATOR_DESCRIPTOR_MAP = new WeakMap();
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
- Returns the CP descriptor associated with `obj` and `keyName`, if any.
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
- @method descriptorForProperty
1567
- @param {Object} obj the object to check
1568
- @param {String} keyName the key to check
1569
- @return {Descriptor}
1570
- @private
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 descriptorForProperty(obj, keyName, _meta) {
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 meta.peekDescriptors(keyName);
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
- Check whether a value is a decorator
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 isClassicDecorator(dec) {
1594
- return typeof dec === 'function' && DECORATOR_DESCRIPTOR_MAP.has(dec);
767
+ function beginPropertyChanges() {
768
+ deferred++;
769
+ suspendedObserverDeactivation();
1595
770
  }
1596
771
 
1597
772
  /**
1598
- Set a value as a decorator
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 setClassicDecorator(dec, value = true) {
1605
- DECORATOR_DESCRIPTOR_MAP.set(dec, value);
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
- Expands `pattern`, invoking `callback` for each expansion.
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
- expandProperties('foo.bar', echo); //=> 'foo.bar'
1624
- expandProperties('{foo,bar}', echo); //=> 'foo', 'bar'
1625
- expandProperties('foo.{bar,baz}', echo); //=> 'foo.bar', 'foo.baz'
1626
- expandProperties('{foo,bar}.baz', echo); //=> 'foo.baz', 'bar.baz'
1627
- expandProperties('foo.{bar,baz}.[]', echo) //=> 'foo.bar.[]', 'foo.baz.[]'
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 expandProperties
1633
- @static
1634
- @for @ember/object/computed
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 expandProperties(pattern, callback) {
1641
- (isDevelopingApp() && !(typeof pattern === 'string') && assert(`A computed property key must be a string, you passed ${typeof pattern} ${pattern}`, typeof pattern === 'string'));
1642
- (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
1643
- (isDevelopingApp() && !(pattern.match(/\{[^}{]*\{|\}[^}{]*\}|\{[^}]*$/g) === null) && assert(`Brace expanded properties have to be balanced and cannot be nested, pattern: ${pattern}`, pattern.match(/\{[^}{]*\{|\}[^}{]*\}|\{[^}]*$/g) === null));
1644
- let start = pattern.indexOf('{');
1645
- if (start < 0) {
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 { _hasScheduledTimers as $, removeListener as A, descriptorForProperty as B, ComputedProperty as C, ComputedDescriptor as D, CHAIN_PASS_THROUGH as E, getChainTagsForKey as F, finishLazyChains as G, hasUnknownProperty as H, sendEvent as I, hasListeners as J, on as K, setClassicDecorator as L, LIBRARIES as M, once as N, run as O, bind as P, PROPERTY_DID_CHANGE as Q, objectAt as R, replace as S, arrayContentWillChange as T, arrayContentDidChange as U, addArrayObserver as V, removeArrayObserver as W, next as X, autoComputed as Y, _getCurrentRunLoop as Z, _backburner as _, tagForProperty as a, tracked as a0, beginPropertyChanges as a1, changeProperties as a2, endPropertyChanges as a3, isComputed as a4, PROXY_CONTENT as a5, _getProp as a6, replaceInNativeArray as a7, Libraries as a8, ASYNC_OBSERVERS as a9, SYNC_OBSERVERS as aa, activateObserver as ab, flushAsyncObservers as ac, DEBUG_INJECTION_FUNCTIONS as ad, markObjectAsDirty as ae, TrackedDescriptor as af, scheduleOnce as ag, cancel as ah, _queues as ai, begin as aj, end as ak, _cancelTimers as al, later as am, debounce as an, throttle as ao, _rsvpErrorQueue as b, computed as c, defineProperty as d, expandProperties as e, inject as f, get as g, isPath as h, isElementDescriptor as i, join as j, COMPUTED_SETTERS as k, getPossibleMandatoryProxyValue as l, _getPath as m, notifyPropertyChange as n, defineValue as o, defineDecorator as p, nativeDescDecorator as q, revalidateObservers as r, schedule as s, tagForObject as t, isClassicDecorator as u, descriptorForDecorator as v, makeComputedDecorator as w, addObserver as x, removeObserver as y, addListener as z };
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 };