1/* 2 * Copyright (c) 2023 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15class stateMgmtProfiler { 16 static begin(blockName) { 17 var _a; 18 (_a = stateMgmtProfiler.instance) === null || _a === void 0 ? void 0 : _a.begin(blockName); 19 } 20 static end() { 21 var _a; 22 (_a = stateMgmtProfiler.instance) === null || _a === void 0 ? void 0 : _a.end(); 23 } 24 static report() { 25 var _a; 26 (_a = stateMgmtProfiler.instance) === null || _a === void 0 ? void 0 : _a.report(); 27 } 28 static clear() { 29 var _a; 30 (_a = stateMgmtProfiler.instance) === null || _a === void 0 ? void 0 : _a.clear(); 31 } 32 static init(instance) { 33 stateMgmtProfiler.instance = instance; 34 } 35} 36stateMgmtProfiler.instance = undefined; 37/* 38 * Copyright (c) 2021 Huawei Device Co., Ltd. 39 * Licensed under the Apache License, Version 2.0 (the "License"); 40 * you may not use this file except in compliance with the License. 41 * You may obtain a copy of the License at 42 * 43 * http://www.apache.org/licenses/LICENSE-2.0 44 * 45 * Unless required by applicable law or agreed to in writing, software 46 * distributed under the License is distributed on an "AS IS" BASIS, 47 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 48 * See the License for the specific language governing permissions and 49 * limitations under the License. 50 */ 51/* 52 * Copyright (c) 2021 Huawei Device Co., Ltd. 53 * Licensed under the Apache License, Version 2.0 (the "License"); 54 * you may not use this file except in compliance with the License. 55 * You may obtain a copy of the License at 56 * 57 * http://www.apache.org/licenses/LICENSE-2.0 58 * 59 * Unless required by applicable law or agreed to in writing, software 60 * distributed under the License is distributed on an "AS IS" BASIS, 61 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 62 * See the License for the specific language governing permissions and 63 * limitations under the License. 64 */ 65/* 66 * Copyright (c) 2023 Huawei Device Co., Ltd. 67 * Licensed under the Apache License, Version 2.0 (the "License"); 68 * you may not use this file except in compliance with the License. 69 * You may obtain a copy of the License at 70 * 71 * http://www.apache.org/licenses/LICENSE-2.0 72 * 73 * Unless required by applicable law or agreed to in writing, software 74 * distributed under the License is distributed on an "AS IS" BASIS, 75 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 76 * See the License for the specific language governing permissions and 77 * limitations under the License. 78 */ 79/* 80 * Copyright (c) 2021 Huawei Device Co., Ltd. 81 * Licensed under the Apache License, Version 2.0 (the "License"); 82 * you may not use this file except in compliance with the License. 83 * You may obtain a copy of the License at 84 * 85 * http://www.apache.org/licenses/LICENSE-2.0 86 * 87 * Unless required by applicable law or agreed to in writing, software 88 * distributed under the License is distributed on an "AS IS" BASIS, 89 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 90 * See the License for the specific language governing permissions and 91 * limitations under the License. 92 */ 93/* 94 * Copyright (c) 2021 Huawei Device Co., Ltd. 95 * Licensed under the Apache License, Version 2.0 (the "License"); 96 * you may not use this file except in compliance with the License. 97 * You may obtain a copy of the License at 98 * 99 * http://www.apache.org/licenses/LICENSE-2.0 100 * 101 * Unless required by applicable law or agreed to in writing, software 102 * distributed under the License is distributed on an "AS IS" BASIS, 103 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 104 * See the License for the specific language governing permissions and 105 * limitations under the License. 106 */ 107/** 108 * 109 * LocalStorage 110 * 111 * Class implements a Map of ObservableObjectBase UI state variables. 112 * Instances can be created to manage UI state within a limited "local" 113 * access, and life cycle as defined by the app. 114 * AppStorage singleton is sub-class of LocalStorage for 115 * UI state of app-wide access and same life cycle as the app. 116 * 117 * @since 9 118 */ 119class LocalStorage extends NativeLocalStorage { 120 /** 121 * Construct new instance of LocalStorage 122 * initialzie with all properties and their values that Object.keys(params) returns 123 * Property values must not be undefined. 124 * @param initializingProperties Object containing keys and values. @see set() for valid values 125 * 126 * @since 9 127 */ 128 constructor(initializingProperties = {}) { 129 // This is edited for the statibility issue that "construtor is false", which meaning that the super() is not callable 130 // It is just the debug log using ArkTools print. 131 try { 132 super(); 133 } 134 catch (error) { 135 stateMgmtConsole.error(`An error occurred in the constructor of LocalStorage ${error.message}`); 136 ArkTools.print("NativeLocalStorage", NativeLocalStorage); 137 throw error; 138 } 139 140 this.storage_ = new Map(); 141 if (Object.keys(initializingProperties).length) { 142 this.initializeProps(initializingProperties); 143 } 144 } 145 /* 146 get access to provded LocalStorage instance thru Stake model 147 @StageModelOnly 148 @form 149 @since 10 150 */ 151 static getShared() { 152 return LocalStorage.GetShared(); 153 } 154 /** 155 * clear storage and init with given properties 156 * @param initializingProperties 157 * 158 * not a public / sdk function 159 */ 160 initializeProps(initializingProperties = {}) { 161 162 this.storage_.clear(); 163 Object.keys(initializingProperties).filter((propName) => initializingProperties[propName] != undefined).forEach((propName) => this.addNewPropertyInternal(propName, initializingProperties[propName])); 164 } 165 /** 166 * Use before deleting owning Ability, window, or service UI 167 * (letting it go out of scope). 168 * 169 * This method orderly closes down a LocalStorage instance by calling @see clear(). 170 * This requires that no property is left with one or more subscribers. 171 * @see clear() and @see delete() 172 * @returns true if all properties could be removed from storage 173 */ 174 aboutToBeDeleted() { 175 return this.clear(); 176 } 177 /** 178 * Check if LocalStorage has a property with given name 179 * return true if prooperty with given name exists 180 * same as ES6 Map.prototype.has() 181 * @param propName searched property 182 * @returns true if property with such name exists in LocalStorage 183 * 184 * @since 9 185 */ 186 has(propName) { 187 return this.storage_.has(propName); 188 } 189 /** 190 * Provide names of all properties in LocalStorage 191 * same as ES6 Map.prototype.keys() 192 * @returns return a Map Iterator 193 * 194 * @since 9 195 */ 196 keys() { 197 return this.storage_.keys(); 198 } 199 /** 200 * Returns number of properties in LocalStorage 201 * same as Map.prototype.size() 202 * @param propName 203 * @returns return number of properties 204 * 205 * @since 9 206 */ 207 size() { 208 return this.storage_.size; 209 } 210 /** 211 * Returns value of given property 212 * return undefined if no property with this name 213 * @param propName 214 * @returns property value if found or undefined 215 * 216 * @since 9 217 */ 218 get(propName) { 219 var p = this.storage_.get(propName); 220 return (p) ? p.get() : undefined; 221 } 222 /** 223 * Set value of given property in LocalStorage 224 * Methosd sets nothing and returns false if property with this name does not exist 225 * or if newValue is `undefined` or `null` (`undefined`, `null` value are not allowed for state variables). 226 * @param propName 227 * @param newValue must be of type T and must not be undefined or null 228 * @returns true on success, i.e. when above conditions are satisfied, otherwise false 229 * 230 * @since 9 231 */ 232 set(propName, newValue) { 233 234 if (newValue == undefined) { 235 stateMgmtConsole.warn(`${this.constructor.name}: set('${propName}') with newValue == undefined not allowed.`); 236 237 return false; 238 } 239 var p = this.storage_.get(propName); 240 if (p == undefined) { 241 stateMgmtConsole.warn(`${this.constructor.name}: set: no property ${propName} error.`); 242 243 return false; 244 } 245 p.set(newValue); 246 247 return true; 248 } 249 /** 250 * Set value of given property, if it exists, @see set() . 251 * Add property if no property with given name and initialize with given value. 252 * Do nothing and return false if newValuue is undefined or null 253 * (undefined, null value is not allowed for state variables) 254 * @param propName 255 * @param newValue must be of type T and must not be undefined or null 256 * @returns true on success, i.e. when above conditions are satisfied, otherwise false 257 * 258 * @since 9 259 */ 260 setOrCreate(propName, newValue) { 261 262 if (newValue == undefined) { 263 stateMgmtConsole.warn(`${this.constructor.name}: setOrCreate('${propName}') with newValue == undefined not allowed.`); 264 265 return false; 266 } 267 var p = this.storage_.get(propName); 268 if (p) { 269 270 p.set(newValue); 271 } 272 else { 273 274 this.addNewPropertyInternal(propName, newValue); 275 } 276 277 return true; 278 } 279 /** 280 * Internal use helper function to create and initialize a new property. 281 * caller needs to be all the checking beforehand 282 * @param propName 283 * @param value 284 * 285 * Not a public / sdk method. 286 */ 287 addNewPropertyInternal(propName, value) { 288 let newProp; 289 if (ViewStackProcessor.UsesNewPipeline()) { 290 newProp = new ObservedPropertyPU(value, undefined, propName); 291 } 292 else { 293 newProp = (typeof value === "object") ? 294 new ObservedPropertyObject(value, undefined, propName) 295 : new ObservedPropertySimple(value, undefined, propName); 296 } 297 this.storage_.set(propName, newProp); 298 return newProp; 299 } 300 /** 301 * create and return a two-way sync "(link") to named property 302 * @param propName name of source property in LocalStorage 303 * @param linkUser IPropertySubscriber to be notified when source changes, 304 * @param subscribersName optional, the linkUser (subscriber) uses this name for the property 305 * this name will be used in propertyChange(propName) callback of IMultiPropertiesChangeSubscriber 306 * @returns SynchedPropertyTwoWay{Simple|Object| object with given LocalStoage prop as its source. 307 * Apps can use SDK functions of base class SubscribedAbstractProperty<S> 308 * return undefiend if named property does not already exist in LocalStorage 309 * Apps can use SDK functions of base class SubscribedPropertyAbstract<S> 310 * return undefiend if named property does not already exist in LocalStorage 311 * 312 * @since 9 313 */ 314 link(propName, linkUser, subscribersName) { 315 316 var p = this.storage_.get(propName); 317 if (p == undefined) { 318 stateMgmtConsole.warn(`${this.constructor.name}: link: no property ${propName} error.`); 319 320 return undefined; 321 } 322 let linkResult; 323 if (ViewStackProcessor.UsesNewPipeline()) { 324 linkResult = new SynchedPropertyTwoWayPU(p, linkUser, propName); 325 } 326 else { 327 linkResult = p.createLink(linkUser, propName); 328 } 329 linkResult.setInfo(subscribersName); 330 331 return linkResult; 332 } 333 /** 334 * Like @see link(), but will create and initialize a new source property in LocalStorge if missing 335 * @param propName name of source property in LocalStorage 336 * @param defaultValue value to be used for initializing if new creating new property in LocalStorage 337 * default value must be of type S, must not be undefined or null. 338 * @param linkUser IPropertySubscriber to be notified when return 'link' changes, 339 * @param subscribersName the linkUser (subscriber) uses this name for the property 340 * this name will be used in propertyChange(propName) callback of IMultiPropertiesChangeSubscriber 341 * @returns SynchedPropertyTwoWay{Simple|Object| object with given LocalStoage prop as its source. 342 * Apps can use SDK functions of base class SubscribedAbstractProperty<S> 343 * 344 * @since 9 345 */ 346 setAndLink(propName, defaultValue, linkUser, subscribersName) { 347 348 var p = this.storage_.get(propName); 349 if (!p) { 350 this.setOrCreate(propName, defaultValue); 351 } 352 const link = this.link(propName, linkUser, subscribersName); 353 354 return link; 355 } 356 /** 357 * create and return a one-way sync ('prop') to named property 358 * @param propName name of source property in LocalStorage 359 * @param propUser IPropertySubscriber to be notified when source changes, 360 * @param subscribersName the linkUser (subscriber) uses this name for the property 361 * this name will be used in propertyChange(propName) callback of IMultiPropertiesChangeSubscriber 362 * @returns SynchedPropertyOneWay{Simple|Object| object with given LocalStoage prop as its source. 363 * Apps can use SDK functions of base class SubscribedAbstractProperty<S> 364 * return undefiend if named property does not already exist in LocalStorage. 365 * Apps can use SDK functions of base class SubscribedPropertyAbstract<S> 366 * return undefiend if named property does not already exist in LocalStorage. 367 * @since 9 368 */ 369 prop(propName, propUser, subscribersName) { 370 371 var p = this.storage_.get(propName); 372 if (p == undefined) { 373 stateMgmtConsole.warn(`${this.constructor.name}: prop: no property ${propName} error.`); 374 375 return undefined; 376 } 377 let propResult; 378 if (ViewStackProcessor.UsesNewPipeline()) { 379 propResult = new SynchedPropertyOneWayPU(p, propUser, propName); 380 } 381 else { 382 propResult = p.createProp(propUser, propName); 383 } 384 propResult.setInfo(subscribersName); 385 386 return propResult; 387 } 388 /** 389 * Like @see prop(), will create and initialize a new source property in LocalStorage if missing 390 * @param propName name of source property in LocalStorage 391 * @param defaultValue value to be used for initializing if new creating new property in LocalStorage. 392 * default value must be of type S, must not be undefined or null. 393 * @param propUser IPropertySubscriber to be notified when returned 'prop' changes, 394 * @param subscribersName the propUser (subscriber) uses this name for the property 395 * this name will be used in propertyChange(propName) callback of IMultiPropertiesChangeSubscriber 396 * @returns SynchedPropertyOneWay{Simple|Object| object with given LocalStoage prop as its source. 397 * Apps can use SDK functions of base class SubscribedAbstractProperty<S> 398 * @since 9 399 */ 400 setAndProp(propName, defaultValue, propUser, subscribersName) { 401 402 var p = this.storage_.get(propName); 403 if (!p) { 404 this.setOrCreate(propName, defaultValue); 405 } 406 const prop = this.prop(propName, propUser, subscribersName); 407 408 return prop; 409 } 410 /** 411 * Delete property from StorageBase 412 * Use with caution: 413 * Before deleting a prop from LocalStorage all its subscribers need to 414 * unsubscribe from the property. 415 * This method fails and returns false if given property still has subscribers 416 * Another reason for failing is unkmown property. 417 * 418 * Developer advise: 419 * Subscribers are created with @see link(), @see prop() 420 * and also via @LocalStorageLink and @LocalStorageProp state variable decorators. 421 * That means as long as their is a @Component instance that uses such decorated variable 422 * or a sync relationship with a SubscribedAbstractProperty variable the property can nit 423 * (and also should not!) be deleted from LocalStorage. 424 * 425 * @param propName 426 * @returns false if method failed 427 * 428 * @since 9 429 */ 430 delete(propName) { 431 432 var p = this.storage_.get(propName); 433 if (p) { 434 if (p.numberOfSubscrbers()) { 435 stateMgmtConsole.error(`${this.constructor.name}: Attempt to delete property ${propName} that has \ 436 ${p.numberOfSubscrbers()} subscribers. Subscribers need to unsubscribe before prop deletion.`); 437 438 return false; 439 } 440 p.aboutToBeDeleted(); 441 this.storage_.delete(propName); 442 443 return true; 444 } 445 else { 446 stateMgmtConsole.warn(`${this.constructor.name}: Attempt to delete unknown property ${propName}.`); 447 448 return false; 449 } 450 } 451 /** 452 * delete all properties from the LocalStorage instance 453 * @see delete(). 454 * precondition is that there are no subscribers. 455 * method returns false and deletes no poperties if there is any property 456 * that still has subscribers 457 * 458 * @since 9 459 */ 460 clear() { 461 462 for (let propName of this.keys()) { 463 var p = this.storage_.get(propName); 464 if (p.numberOfSubscrbers()) { 465 stateMgmtConsole.error(`${this.constructor.name}.deleteAll: Attempt to delete property ${propName} that \ 466 has ${p.numberOfSubscrbers()} subscribers. Subscribers need to unsubscribe before prop deletion. 467 Any @Component instance with a @StorageLink/Prop or @LocalStorageLink/Prop is a subscriber.`); 468 469 return false; 470 } 471 } 472 for (let propName of this.keys()) { 473 var p = this.storage_.get(propName); 474 p.aboutToBeDeleted(); 475 } 476 this.storage_.clear(); 477 478 479 return true; 480 } 481 /** 482 * Subscribe to value change notifications of named property 483 * Any object implementing ISinglePropertyChangeSubscriber interface 484 * and registerign itself to SubscriberManager can register 485 * Caution: do remember to unregister, otherwise the property will block 486 * cleanup, @see delete() and @see clear() 487 * 488 * @param propName property in LocalStorage to subscribe to 489 * @param subscriber object that implements ISinglePropertyChangeSubscriber interface 490 * @returns false if named property does not exist 491 * 492 * @since 9 493 */ 494 subscribeToChangesOf(propName, subscriber) { 495 var p = this.storage_.get(propName); 496 if (p) { 497 p.addSubscriber(subscriber); 498 return true; 499 } 500 return false; 501 } 502 /** 503 * inverse of @see subscribeToChangesOf 504 * @param propName property in LocalStorage to subscribe to 505 * @param subscriberId id of the subscrber passed to @see subscribeToChangesOf 506 * @returns false if named property does not exist 507 * 508 * @since 9 509 */ 510 unsubscribeFromChangesOf(propName, subscriberId) { 511 var p = this.storage_.get(propName); 512 if (p) { 513 p.removeSubscriber(null, subscriberId); 514 return true; 515 } 516 return false; 517 } 518 __createSync(storagePropName, defaultValue, factoryFunc) { 519 let p = this.storage_.get(storagePropName); 520 if (p == undefined) { 521 // property named 'storagePropName' not yet in storage 522 // add new property to storage 523 if (defaultValue === undefined) { 524 stateMgmtConsole.error(`${this.constructor.name}.__createSync(${storagePropName}, non-existing property and undefined default value. ERROR.`); 525 return undefined; 526 } 527 p = this.addNewPropertyInternal(storagePropName, defaultValue); 528 } 529 return factoryFunc(p); 530 } 531} 532/* 533 * Copyright (c) 2021-2023 Huawei Device Co., Ltd. 534 * Licensed under the Apache License, Version 2.0 (the "License"); 535 * you may not use this file except in compliance with the License. 536 * You may obtain a copy of the License at 537 * 538 * http://www.apache.org/licenses/LICENSE-2.0 539 * 540 * Unless required by applicable law or agreed to in writing, software 541 * distributed under the License is distributed on an "AS IS" BASIS, 542 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 543 * See the License for the specific language governing permissions and 544 * limitations under the License. 545 */ 546/** 547 * 548 * AppStorage 549 * 550 * Class implements a Map of ObservableObjectBase UI state variables. 551 * AppStorage singleton is sub-class of @see LocalStorage for 552 * UI state of app-wide access and same life cycle as the app. 553 * 554 * @since 7 555 */ 556class AppStorage extends LocalStorage { 557 /** singleton class, app can not create instances 558 * 559 * not a public / sdk function 560 */ 561 constructor(initializingProperties) { 562 super(initializingProperties); 563 } 564 /** 565 * create and initialize singleton 566 * initialzie with all properties and their values that Object.keys(params) returns 567 * Property values must not be undefined. 568 * 569 * not a public / sdk function 570 */ 571 static createSingleton(initializingPropersties) { 572 if (!AppStorage.instance_) { 573 574 AppStorage.instance_ = new AppStorage(initializingPropersties); 575 } 576 else { 577 stateMgmtConsole.error("AppStorage.createNewInstance(..): instance exists already, internal error!"); 578 } 579 } 580 /** 581 * create and return a two-way sync "(link") to named property 582 * 583 * Same as @see LocalStorage.link() 584 * 585 * @param propName name of source property in AppStorage 586 * @param linkUser IPropertySubscriber to be notified when source changes, 587 * @param subscribersName the linkUser (subscriber) uses this name for the property 588 * this name will be used in propertyChange(propName) callback of IMultiPropertiesChangeSubscriber 589 * @returns SynchedPropertyTwoWay{Simple|Object| object with given LocalStoage prop as its source. 590 * Apps can use SDK functions of base class SubscribedAbstractProperty<S> 591 * return undefiend if named property does not already exist in AppStorage 592 * 593 * @since 10 594 */ 595 static link(key, linkUser, subscribersName) { 596 return AppStorage.getOrCreate().link(key, linkUser, subscribersName); 597 } 598 /** 599 * @see link 600 * @since 7 601 * @deprecated 602 */ 603 static Link(key, linkUser, subscribersName) { 604 return AppStorage.getOrCreate().link(key, linkUser, subscribersName); 605 } 606 /** 607 * Like @see link(), but will create and initialize a new source property in LocalStorage if missing 608 * 609 * Same as @see LocalStorage.setAndLink() 610 * 611 * @param propName name of source property in AppStorage 612 * @param defaultValue value to be used for initializing if new creating new property in AppStorage 613 * default value must be of type S, must not be undefined or null. 614 * @param linkUser IPropertySubscriber to be notified when return 'link' changes, 615 * @param subscribersName the linkUser (subscriber) uses this name for the property 616 * this name will be used in propertyChange(propName) callback of IMultiPropertiesChangeSubscriber 617 * @returns SynchedPropertyTwoWay{Simple|Object| object with given LocalStoage prop as its source. 618 * Apps can use SDK functions of base class SubscribedAbstractProperty<S> 619 * 620 * @since 10 621 */ 622 static setAndLink(key, defaultValue, linkUser, subscribersName) { 623 return AppStorage.getOrCreate().setAndLink(key, defaultValue, linkUser, subscribersName); 624 } 625 /** 626 * @see setAndLink 627 * @since 7 628 * @deprecated 629 */ 630 static SetAndLink(key, defaultValue, linkUser, subscribersName) { 631 return AppStorage.getOrCreate().setAndLink(key, defaultValue, linkUser, subscribersName); 632 } 633 /** 634 * create and return a one-way sync ('prop') to named property 635 * 636 * Same as @see LocalStorage.prop() 637 * 638 * @param propName name of source property in AppStorage 639 * @param propUser IPropertySubscriber to be notified when source changes, 640 * @param subscribersName the linkUser (subscriber) uses this name for the property 641 * this name will be used in propertyChange(propName) callback of IMultiPropertiesChangeSubscriber 642 * @returns SynchedPropertyOneWay{Simple|Object| object with given LocalStoage prop as its source. 643 * Apps can use SDK functions of base class SubscribedAbstractProperty<S> 644 * return undefiend if named property does not already exist in AppStorage. 645 * @since 10 646 */ 647 static prop(propName, propUser, subscribersName) { 648 return AppStorage.getOrCreate().prop(propName, propUser, subscribersName); 649 } 650 /** 651 * @see prop 652 * @since 7 653 * @deprecated 654 */ 655 static Prop(propName, propUser, subscribersName) { 656 return AppStorage.getOrCreate().prop(propName, propUser, subscribersName); 657 } 658 /** 659 * Like @see prop(), will create and initialize a new source property in AppStorage if missing 660 * 661 * Same as @see LocalStorage.setAndProp() 662 * 663 * @param propName name of source property in AppStorage 664 * @param defaultValue value to be used for initializing if new creating new property in AppStorage. 665 * default value must be of type S, must not be undefined or null. 666 * @param propUser IPropertySubscriber to be notified when returned 'prop' changes, 667 * @param subscribersName the propUser (subscriber) uses this name for the property 668 * this name will be used in propertyChange(propName) callback of IMultiPropertiesChangeSubscriber 669 * @returns SynchedPropertyOneWay{Simple|Object| object with given LocalStoage prop as its source. 670 * Apps can use SDK functions of base class SubscribedAbstractProperty<S> 671 * 672 * @since 10 673 */ 674 static setAndProp(key, defaultValue, propUser, subscribersName) { 675 return AppStorage.getOrCreate().setAndProp(key, defaultValue, propUser, subscribersName); 676 } 677 /** 678 * @see setAndProp 679 * @since 7 680 * @deprecated 681 */ 682 static SetAndProp(key, defaultValue, propUser, subscribersName) { 683 return AppStorage.getOrCreate().setAndProp(key, defaultValue, propUser, subscribersName); 684 } 685 /** 686 * Check if AppStorage has a property with given name 687 * return true if property with given name exists 688 * same as ES6 Map.prototype.has() 689 * 690 * Same as @see LocalStorage.has() 691 * 692 * @param propName searched property 693 * @returns true if property with such name exists in AppStorage 694 * 695 * @since 10 696 */ 697 static has(key) { 698 return AppStorage.getOrCreate().has(key); 699 } 700 /** 701 * @see has() 702 * @since 7 703 * @deprecated 704 */ 705 static Has(key) { 706 return AppStorage.getOrCreate().has(key); 707 } 708 /** 709 * Returns value of given property 710 * return undefined if no property with this name 711 * 712 * @Same as see LocalStorage.get() 713 * 714 * @param propName 715 * @returns property value if found or undefined 716 * 717 * @since 10 718 * 719 */ 720 static get(key) { 721 return AppStorage.getOrCreate().get(key); 722 } 723 /** 724 * @see get 725 * @since 7 726 * @deprecated 727 * 728 */ 729 static Get(key) { 730 return AppStorage.getOrCreate().get(key); 731 } 732 /** 733 * Set value of given property in AppStorage 734 * Method sets nothing and returns false if property with this name does not exist 735 * or if newValue is `undefined` or `null` (`undefined`, `null` value are not allowed for state variables). 736 * 737 * Same as @see LocalStorage.set 738 * 739 * @param propName 740 * @param newValue must be of type T and must not be undefined or null 741 * @returns true on success, i.e. when above conditions are satisfied, otherwise false 742 * 743 * @since 10 744 */ 745 static set(key, newValue) { 746 return AppStorage.getOrCreate().set(key, newValue); 747 } 748 /** 749 * @see set 750 * @since 7 751 * @deprecated 752 */ 753 static Set(key, newValue) { 754 return AppStorage.getOrCreate().set(key, newValue); 755 } 756 /** 757 * Set value of given property, if it exists, @see set() . 758 * Add property if no property with given name and initialize with given value. 759 * Do nothing and return false if newValuue is undefined or null 760 * (undefined, null value is not allowed for state variables) 761 * 762 * @see LocalStorage.setOrCreate() 763 * 764 * @param propName 765 * @param newValue must be of type T and must not be undefined or null 766 * @returns true on success, i.e. when above conditions are satisfied, otherwise false 767 * 768 * @since 10 769 */ 770 static setOrCreate(key, newValue) { 771 AppStorage.getOrCreate().setOrCreate(key, newValue); 772 } 773 /** 774 * @see setOrCreate 775 * @since 7 776 * @deprecated 777 */ 778 static SetOrCreate(key, newValue) { 779 AppStorage.getOrCreate().setOrCreate(key, newValue); 780 } 781 /** 782 * Delete property from StorageBase 783 * Use with caution: 784 * Before deleting a prop from AppStorage all its subscribers need to 785 * unsubscribe from the property. 786 * This method fails and returns false if given property still has subscribers 787 * Another reason for failing is unkmown property. 788 * 789 * Developer advise: 790 * Subscribers are created with @see link(), @see prop() 791 * and also via @LocalStorageLink and @LocalStorageProp state variable decorators. 792 * That means as long as their is a @Component instance that uses such decorated variable 793 * or a sync relationship with a SubscribedAbstractProperty variable the property can nit 794 * (and also should not!) be deleted from AppStorage. 795 * 796 * Same as @see LocalStorage.delete() 797 * 798 * @param propName 799 * @returns false if method failed 800 * 801 * @since 10 802 */ 803 static delete(key) { 804 return AppStorage.getOrCreate().delete(key); 805 } 806 /** 807 * @see delete 808 * @since 7 809 * @deprecated 810 */ 811 static Delete(key) { 812 return AppStorage.getOrCreate().delete(key); 813 } 814 /** 815 * Provide names of all properties in AppStorage 816 * same as ES6 Map.prototype.keys() 817 * 818 * Same as @see LocalStorage.keys() 819 * 820 * @returns return a Map Iterator 821 * 822 * @since 10 823 */ 824 static keys() { 825 return AppStorage.getOrCreate().keys(); 826 } 827 /** 828 * @see keys 829 * @since 7 830 * @deprecated 831 */ 832 static Keys() { 833 return AppStorage.getOrCreate().keys(); 834 } 835 /** 836 * Returns number of properties in AppStorage 837 * same as Map.prototype.size() 838 * 839 * Same as @see LocalStorage.size() 840 * 841 * @param propName 842 * @returns return number of properties 843 * 844 * @since 10 845 */ 846 static size() { 847 return AppStorage.getOrCreate().size(); 848 } 849 /** 850 * @see size 851 * @since 7 852 * @deprecated 853 */ 854 static Size() { 855 return AppStorage.getOrCreate().size(); 856 } 857 /** 858 * delete all properties from the AppStorage 859 * 860 * @see delete(), same as @see LocalStorage.clear() 861 * 862 * precondition is that there are no subscribers. 863 * method returns false and deletes no poperties if there is any property 864 * that still has subscribers 865 * 866 * @since 10 867 */ 868 static clear() { 869 return AppStorage.getOrCreate().clear(); 870 } 871 /** 872 * @see clear 873 * @since 7 874 * @deprecated 875 */ 876 static Clear() { 877 return AppStorage.getOrCreate().clear(); 878 } 879 /** 880 * Same as @see clear(). 881 * 882 * @since 7, deprecated, used clear() instead! 883 * 884 */ 885 static StaticClear() { 886 return AppStorage.clear(); 887 } 888 /** 889 * not a public / sdk function 890 */ 891 static aboutToBeDeleted() { 892 AppStorage.getOrCreate().aboutToBeDeleted(); 893 } 894 /** 895 * Subscribe to value change notifications of named property 896 * Any object implementing ISinglePropertyChangeSubscriber interface 897 * and registerign itself to SubscriberManager can register 898 * Caution: do remember to unregister, otherwise the property will block 899 * cleanup, @see delete() and @see clear() 900 * 901 * Same as @see LocalStorage.subscribeToChangesOf() 902 * 903 * @param propName property in AppStorage to subscribe to 904 * @param subscriber object that implements ISinglePropertyChangeSubscriber interface 905 * @returns false if named property does not exist 906 * 907 * @since 10 908 */ 909 static subscribeToChangesOf(propName, subscriber) { 910 return AppStorage.getOrCreate().subscribeToChangesOf(propName, subscriber); 911 } 912 /** 913 * @see subscribeToChangesOf 914 * @since 7 915 * @deprecated 916 */ 917 static SubscribeToChangesOf(propName, subscriber) { 918 return AppStorage.getOrCreate().subscribeToChangesOf(propName, subscriber); 919 } 920 /** 921 * inverse of @see SubscribeToChangesOf, 922 * same as @see LocalStorage.subscribeToChangesOf() 923 * 924 * @param propName property in AppStorage to subscribe to 925 * @param subscriberId id of the subscrber passed to @see subscribeToChangesOf 926 * @returns false if named property does not exist 927 * 928 * @since 10 929 */ 930 static unsubscribeFromChangesOf(propName, subscriberId) { 931 return AppStorage.getOrCreate().unsubscribeFromChangesOf(propName, subscriberId); 932 } 933 /** 934 * @see unsubscribeFromChangesOf 935 * @since 7 936 * @deprecated 937 */ 938 static UnsubscribeFromChangesOf(propName, subscriberId) { 939 return AppStorage.getOrCreate().unsubscribeFromChangesOf(propName, subscriberId); 940 } 941 /** 942 * Unimplemented, currently all properties of AppStorage are mutable. 943 * 944 * @since 7, deprecated 945 */ 946 static IsMutable(key) { 947 return true; 948 } 949 /** 950 * not a public / sdk function 951 */ 952 static __createSync(storagePropName, defaultValue, factoryFunc) { 953 return AppStorage.getOrCreate().__createSync(storagePropName, defaultValue, factoryFunc); 954 } 955 /** 956 * not a public / sdk function 957 */ 958 static getOrCreate() { 959 if (!AppStorage.instance_) { 960 stateMgmtConsole.warn("AppStorage instance missing. Use AppStorage.createInstance(initObj). Creating instance without any initialization."); 961 AppStorage.instance_ = new AppStorage({}); 962 } 963 return AppStorage.instance_; 964 } 965} 966// instance functions below: 967// Should all be protected, but TS lang does not allow access from static member to protected member 968AppStorage.instance_ = undefined; 969/* 970 * Copyright (c) 2022 Huawei Device Co., Ltd. 971 * Licensed under the Apache License, Version 2.0 (the "License"); 972 * you may not use this file except in compliance with the License. 973 * You may obtain a copy of the License at 974 * 975 * http://www.apache.org/licenses/LICENSE-2.0 976 * 977 * Unless required by applicable law or agreed to in writing, software 978 * distributed under the License is distributed on an "AS IS" BASIS, 979 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 980 * See the License for the specific language governing permissions and 981 * limitations under the License. 982 */ 983/** 984 * Singleton class SubscriberManager implements IPropertySubscriberLookup 985 * public API to manage IPropertySubscriber 986 */ 987class SubscriberManager { 988 /** 989 * SubscriberManager is a singleton created by the framework 990 * do not use 991 * 992 * internal method 993 */ 994 constructor() { 995 this.subscriberById_ = new Map(); 996 997 } 998 /** 999 * check subscriber is known 1000 * same as ES6 Map.prototype.has() 1001 * 1002 * @since 9 1003 */ 1004 static Has(id) { 1005 return SubscriberManager.GetInstance().has(id); 1006 } 1007 /** 1008 * 1009 * retrieve subscriber by id 1010 * same as ES6 Map.prototype.get() 1011 * 1012 * @since 9 1013 */ 1014 static Find(id) { 1015 return SubscriberManager.GetInstance().get(id); 1016 } 1017 /** 1018 * unregister a subscriber 1019 * same as ES6 Map.prototype.delete() 1020 * @return boolean success or failure to delete 1021 * 1022 * @since 9 1023 */ 1024 static Delete(id) { 1025 return SubscriberManager.GetInstance().delete(id); 1026 } 1027 /** 1028 * add a new subscriber. 1029 * The subscriber must have a new (unused) id (@see MakeId() ) 1030 * for add() to succeed. 1031 * same as Map.prototype.set() 1032 * 1033 * @since 9 1034 */ 1035 static Add(newSubsriber) { 1036 return SubscriberManager.GetInstance().add(newSubsriber); 1037 } 1038 /** 1039 * Update recycle custom node element id. 1040 */ 1041 static UpdateRecycleElmtId(oldId, newId) { 1042 return SubscriberManager.GetInstance().updateRecycleElmtId(oldId, newId); 1043 } 1044 /** 1045 * 1046 * @returns a globally unique id to be assigned to a IPropertySubscriber objet 1047 * Use MakeId() to assign a IPropertySubscriber object an id before calling @see add() . 1048 * 1049 * @since 9 1050 */ 1051 static MakeId() { 1052 return SubscriberManager.GetInstance().makeId(); 1053 } 1054 /** 1055 * Check number of registered Subscriber / registered IDs. 1056 * @returns number of registered unique ids. 1057 * 1058 * @since 9 1059 */ 1060 static NumberOfSubscribers() { 1061 return SubscriberManager.GetInstance().numberOfSubscribers(); 1062 } 1063 /** 1064 * 1065 * internal (non-SDK) methods below 1066 * 1067 */ 1068 /** 1069 * Get singleton, create it on first call 1070 * @returns SubscriberManager singleton 1071 * 1072 * internal function 1073 * This function will be removed soon, use static functions instead! 1074 * Note: Fnction gets used by transpiler output for both full update and partial update 1075 */ 1076 static Get() { 1077 if (!SubscriberManager.instance_) { 1078 SubscriberManager.instance_ = new SubscriberManager(); 1079 } 1080 return SubscriberManager.instance_; 1081 } 1082 /** 1083 * Get singleton, create it on first call 1084 * @returns SubscriberManager singleton 1085 * 1086 * internal function 1087 */ 1088 static GetInstance() { 1089 if (!SubscriberManager.instance_) { 1090 SubscriberManager.instance_ = new SubscriberManager(); 1091 } 1092 return SubscriberManager.instance_; 1093 } 1094 /** 1095 * for debug purposes dump all known subscriber's info to comsole 1096 * 1097 * not a public / sdk function 1098 */ 1099 static DumpSubscriberInfo() { 1100 SubscriberManager.GetInstance().dumpSubscriberInfo(); 1101 } 1102 /** 1103 * not a public / sdk function 1104 * @see Has 1105 */ 1106 has(id) { 1107 return this.subscriberById_.has(id); 1108 } 1109 /** 1110 * not a public / sdk function 1111 * @see Get 1112 */ 1113 get(id) { 1114 return this.subscriberById_.get(id); 1115 } 1116 /** 1117 * not a public / sdk function 1118 * @see Delete 1119 */ 1120 delete(id) { 1121 if (!this.has(id)) { 1122 stateMgmtConsole.warn(`SubscriberManager.delete unknown id ${id} `); 1123 return false; 1124 } 1125 return this.subscriberById_.delete(id); 1126 } 1127 /** 1128 * not a public / sdk function 1129 * @see Add 1130 */ 1131 add(newSubsriber) { 1132 if (this.has(newSubsriber.id__())) { 1133 return false; 1134 } 1135 this.subscriberById_.set(newSubsriber.id__(), newSubsriber); 1136 return true; 1137 } 1138 updateRecycleElmtId(oldId, newId) { 1139 if (!this.has(oldId)) { 1140 return false; 1141 } 1142 const subscriber = this.get(oldId); 1143 this.subscriberById_.delete(oldId); 1144 this.subscriberById_.set(newId, subscriber); 1145 return true; 1146 } 1147 /** 1148 * Method for testing purposes 1149 * @returns number of subscribers 1150 * 1151 * not a public / sdk function 1152 */ 1153 numberOfSubscribers() { 1154 return this.subscriberById_.size; 1155 } 1156 /** 1157 * for debug purposes dump all known subscriber's info to comsole 1158 * 1159 * not a public / sdk function 1160 */ 1161 dumpSubscriberInfo() { 1162 1163 for (let [id, subscriber] of this.subscriberById_) { 1164 1165 } 1166 1167 } 1168 /** 1169 * 1170 * @returns a globally unique id to be assigned to a Subscriber 1171 */ 1172 makeId() { 1173 return ViewStackProcessor.MakeUniqueId(); 1174 } 1175} 1176/* 1177 * Copyright (c) 2022 Huawei Device Co., Ltd. 1178 * Licensed under the Apache License, Version 2.0 (the "License"); 1179 * you may not use this file except in compliance with the License. 1180 * You may obtain a copy of the License at 1181 * 1182 * http://www.apache.org/licenses/LICENSE-2.0 1183 * 1184 * Unless required by applicable law or agreed to in writing, software 1185 * distributed under the License is distributed on an "AS IS" BASIS, 1186 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1187 * See the License for the specific language governing permissions and 1188 * limitations under the License. 1189 */ 1190/** 1191 * 1192 * SubscribedAbstractProperty is base class of ObservedPropertyAbstract 1193 * and includes these 3 functions that are part of the SDK. 1194 * 1195 * SubscribedAbstractProperty<T> is the return type of 1196 * - AppStorage static functions Link(), Prop(), SetAndLink(), and SetAndProp() 1197 * - LocalStorage methods link(), prop(), setAndLink(), and setAndProp() 1198 * 1199 * 'T' can be boolean, string, number or custom class. 1200 * 1201 * Main functions 1202 * @see get() reads the linked AppStorage/LocalStorage property value, 1203 * @see set(newValue) write a new value to the synched AppStorage/LocalStorage property value 1204 * @see aboutToBeDeleted() ends the sync relationship with the AppStorage/LocalStorage property 1205 * The app must call this function before the SubscribedAbstractProperty<T> object 1206 * goes out of scope. 1207 * 1208 * @since 7 1209*/ 1210class SubscribedAbstractProperty { 1211} 1212/* 1213 * Copyright (c) 2021-2022 Huawei Device Co., Ltd. 1214 * Licensed under the Apache License, Version 2.0 (the "License"); 1215 * you may not use this file except in compliance with the License. 1216 * You may obtain a copy of the License at 1217 * 1218 * http://www.apache.org/licenses/LICENSE-2.0 1219 * 1220 * Unless required by applicable law or agreed to in writing, software 1221 * distributed under the License is distributed on an "AS IS" BASIS, 1222 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1223 * See the License for the specific language governing permissions and 1224 * limitations under the License. 1225 */ 1226/** 1227 * 1228 * SubscribableAbstract 1229 * 1230 * This class is part of the SDK. 1231 * @since 10 1232 * 1233 * SubscribableAbstract is an abstract class that manages subscribers 1234 * to value changes. These subscribers are the implementation of 1235 * @State, @Link, @Provide, @Consume decorated variables inside the 1236 * framework. Each using @State, @Link, etc., decorated variable in 1237 * a @Component will make its own subscription. When the component 1238 * is created the subscription is added, and before the component 1239 * is deleted it unsubscribes 1240 * 1241 * An application may extend SubscribableAbstract for a custom class 1242 * that manages state data. @State, @Link, @Provide, @Consume 1243 * decorated variables can hold an Object that is instance of 1244 * SubscribableAbstract. 1245 * 1246 * About lifecycle: It is legal use for two @Components with two @State 1247 * decorated variables to share the same SubscribableAbstract object. 1248 * Each such decorated variable implementation makes its own 1249 * subscription to the SubscribableAbstract object. Hence, when both variables 1250 * have unsubscribed the SubscribableAbstract custom class may do its own 1251 * de-initialization, e.g. release held external resources. 1252 * 1253 * How to extend: 1254 * A subclass manages the get and set to one or several properties on its own. 1255 * The subclass needs to notify all relevant value changes to the framework for the 1256 * UI to be updated. Notification should only be given for class properties that 1257 * are used to generate the UI. 1258 * 1259 * A subclass must call super() in its constructor to let this base class 1260 * initialize itself. 1261 * 1262 * A subclass must call 'notifyPropertyHasChanged*(' after the relevant property 1263 * has changes. The framework will notify all dependent components to re-render. 1264 * 1265 * A sub-class may overwrite the 'addOwningProperty' function to add own 1266 * functionality, but it must call super.addowningOwningProperty(..). E.g. 1267 * the sub-class could connect to external resources upon the first subscriber. 1268 * 1269 * A sub-class may also overwrite the 'removeOwningProperty' function or 1270 * 'removeOwningPropertyById' function to add own functionality, 1271 * but it must call super.removeOwningProperty(..). 1272 * E.g. the sub-class could release held external resources upon loosing the 1273 * last subscriber. 1274 * 1275 */ 1276class SubscribableAbstract { 1277 /** 1278 * make sure to call super() from subclass constructor! 1279 * 1280 * @since 10 1281 */ 1282 constructor() { 1283 this.owningProperties_ = new Set(); 1284 1285 } 1286 /** 1287 * A subsclass must call this function whenever one of its properties has 1288 * changed that is used to construct the UI. 1289 * @param propName name of the change property 1290 * @param newValue the property value after the change 1291 * 1292 * @since 10 1293 */ 1294 notifyPropertyHasChanged(propName, newValue) { 1295 1296 this.owningProperties_.forEach((subscribedId) => { 1297 var owningProperty = SubscriberManager.Find(subscribedId); 1298 if (!owningProperty) { 1299 stateMgmtConsole.error(`SubscribableAbstract: notifyHasChanged: unknown subscriber.'${subscribedId}' error!.`); 1300 return; 1301 } 1302 // PU code path 1303 if ('onTrackedObjectPropertyCompatModeHasChangedPU' in owningProperty) { 1304 owningProperty.onTrackedObjectPropertyCompatModeHasChangedPU(this, propName); 1305 } 1306 // FU code path 1307 if ('hasChanged' in owningProperty) { 1308 owningProperty.hasChanged(newValue); 1309 } 1310 if ('propertyHasChanged' in owningProperty) { 1311 owningProperty.propertyHasChanged(propName); 1312 } 1313 }); 1314 } 1315 /** 1316 * Provides the current number of subscribers. 1317 * Application may use this function to determine a shared object has no more remaining subscribers and can be deleted. 1318 * @returns number of current subscribers 1319 * 1320 * @since 10 1321 */ 1322 numberOfSubscribers() { 1323 return this.owningProperties_.size; 1324 } 1325 /** 1326 * Method used by the framework to add subscribing decorated variables 1327 * Subclass may overwrite this function but must call the function of the base 1328 * class from its own implementation. 1329 * @param subscriber new subscriber that implements ISinglePropertyChangeSubscriber 1330 * and/or IMultiPropertiesChangeSubscriber interfaces 1331 * 1332 * @since 10 1333 */ 1334 addOwningProperty(subscriber) { 1335 1336 this.owningProperties_.add(subscriber.id__()); 1337 } 1338 /** 1339 * Method used by the framework to unsubscribing decorated variables 1340 * Subclass may overwrite this function but must call the function of the base 1341 * class from its own implementation. 1342 * @param subscriber subscriber that implements ISinglePropertyChangeSubscriber 1343 * and/or IMultiPropertiesChangeSubscriber interfaces 1344 * 1345 * @since 10 1346 */ 1347 removeOwningProperty(property) { 1348 return this.removeOwningPropertyById(property.id__()); 1349 } 1350 /** 1351 * Same as @see removeOwningProperty() but by Subscriber id. 1352 * @param subscriberId 1353 * 1354 * framework internal function, not to be used by applications. 1355 */ 1356 removeOwningPropertyById(subscriberId) { 1357 1358 this.owningProperties_.delete(subscriberId); 1359 } 1360 /** 1361 * flush all subscribers / owning properties 1362 * This is needed when copying a SubscribableAbstract object to the localObject or @prop / SynchedPropertyObjectOneWay 1363 * - shallowCopy: copies the _reference to original_ Set. Hence, we must not modify this Set but assign a new Set 1364 * - deepCopy also (deep-) copies this class' owningProperties_ Set, incl. the numbers it includes. Assigning a new Set fixes. 1365 * 1366 */ 1367 clearOwningProperties() { 1368 this.owningProperties_ = new Set(); 1369 } 1370} 1371/** 1372 * SubscribaleAbstract class with typo in its nam,e 1373 * 1374 * @depreciated, use SubscribableAbstract 1375 */ 1376class SubscribaleAbstract extends SubscribableAbstract { 1377} 1378/* 1379 * Copyright (c) 2021-2023 Huawei Device Co., Ltd. 1380 * Licensed under the Apache License, Version 2.0 (the "License"); 1381 * you may not use this file except in compliance with the License. 1382 * You may obtain a copy of the License at 1383 * 1384 * http://www.apache.org/licenses/LICENSE-2.0 1385 * 1386 * Unless required by applicable law or agreed to in writing, software 1387 * distributed under the License is distributed on an "AS IS" BASIS, 1388 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1389 * See the License for the specific language governing permissions and 1390 * limitations under the License. 1391 */ 1392/** 1393 * PersistentStorage 1394 * 1395 * Keeps current values of select AppStorage property properties persisted to file. 1396 * 1397 * since 9 1398 */ 1399class PersistentStorage { 1400 /** 1401 * all following methods are framework internal 1402 */ 1403 constructor() { 1404 this.links_ = new Map(); 1405 this.id_ = SubscriberManager.MakeId(); 1406 SubscriberManager.Add(this); 1407 } 1408 /** 1409 * 1410 * @param storage method to be used by the framework to set the backend 1411 * this is to be done during startup 1412 * 1413 * internal function, not part of the SDK 1414 * 1415 */ 1416 static configureBackend(storage) { 1417 PersistentStorage.storage_ = storage; 1418 } 1419 /** 1420 * private, use static functions! 1421 */ 1422 static getOrCreate() { 1423 if (PersistentStorage.instance_) { 1424 // already initialized 1425 return PersistentStorage.instance_; 1426 } 1427 PersistentStorage.instance_ = new PersistentStorage(); 1428 return PersistentStorage.instance_; 1429 } 1430 /** 1431 * 1432 * internal function, not part of the SDK 1433 */ 1434 static aboutToBeDeleted() { 1435 if (!PersistentStorage.instance_) { 1436 return; 1437 } 1438 PersistentStorage.getOrCreate().aboutToBeDeleted(); 1439 PersistentStorage.instance_ = undefined; 1440 } 1441 /** 1442 * Add property 'key' to AppStorage properties whose current value will be 1443 * persistent. 1444 * If AppStorage does not include this property it will be added and initializes 1445 * with given value 1446 * 1447 * @since 10 1448 * 1449 * @param key property name 1450 * @param defaultValue If AppStorage does not include this property it will be initialized with this value 1451 * 1452 */ 1453 static persistProp(key, defaultValue) { 1454 PersistentStorage.getOrCreate().persistProp(key, defaultValue); 1455 } 1456 /** 1457 * @see persistProp 1458 * @deprecated 1459 */ 1460 static PersistProp(key, defaultValue) { 1461 PersistentStorage.getOrCreate().persistProp(key, defaultValue); 1462 } 1463 /** 1464 * Reverse of @see persistProp 1465 * @param key no longer persist the property named key 1466 * 1467 * @since 10 1468 */ 1469 static deleteProp(key) { 1470 PersistentStorage.getOrCreate().deleteProp(key); 1471 } 1472 /** 1473 * @see deleteProp 1474 * @deprecated 1475 */ 1476 static DeleteProp(key) { 1477 PersistentStorage.getOrCreate().deleteProp(key); 1478 } 1479 /** 1480 * Persist given AppStorage properties with given names. 1481 * If a property does not exist in AppStorage, add it and initialize it with given value 1482 * works as @see persistProp for multiple properties. 1483 * 1484 * @param properties 1485 * 1486 * @since 10 1487 * 1488 */ 1489 static persistProps(properties) { 1490 PersistentStorage.getOrCreate().persistProps(properties); 1491 } 1492 /** 1493 * @see persistProps 1494 * @deprecated 1495 */ 1496 static PersistProps(properties) { 1497 PersistentStorage.getOrCreate().persistProps(properties); 1498 } 1499 /** 1500 * Inform persisted AppStorage property names 1501 * @returns array of AppStorage keys 1502 * 1503 * @since 10 1504 */ 1505 static keys() { 1506 let result = []; 1507 const it = PersistentStorage.getOrCreate().keys(); 1508 let val = it.next(); 1509 while (!val.done) { 1510 result.push(val.value); 1511 val = it.next(); 1512 } 1513 return result; 1514 } 1515 /** 1516 * @see keys 1517 * @deprecated 1518 */ 1519 static Keys() { 1520 return PersistentStorage.keys(); 1521 } 1522 /** 1523 * This methid offers a way to force writing the property value with given 1524 * key to persistent storage. 1525 * In the general case this is unnecessary as the framework observed changes 1526 * and triggers writing to disk by itself. For nested objects (e.g. array of 1527 * objects) however changes of a property of a property as not observed. This 1528 * is the case where the application needs to signal to the framework. 1529 * 1530 * @param key property that has changed 1531 * 1532 * @since 10 1533 * 1534 */ 1535 static notifyHasChanged(propName) { 1536 1537 PersistentStorage.storage_.set(propName, PersistentStorage.getOrCreate().links_.get(propName).get()); 1538 } 1539 /** 1540 * @see notifyHasChanged 1541 * @deprecated 1542 */ 1543 static NotifyHasChanged(propName) { 1544 1545 PersistentStorage.storage_.set(propName, PersistentStorage.getOrCreate().links_.get(propName).get()); 1546 } 1547 keys() { 1548 return this.links_.keys(); 1549 } 1550 persistProp(propName, defaultValue) { 1551 if (this.persistProp1(propName, defaultValue)) { 1552 // persist new prop 1553 1554 PersistentStorage.storage_.set(propName, this.links_.get(propName).get()); 1555 } 1556 } 1557 // helper function to persist a property 1558 // does everything except writing prop to disk 1559 persistProp1(propName, defaultValue) { 1560 1561 if (defaultValue == null || defaultValue == undefined) { 1562 stateMgmtConsole.error(`PersistentStorage: persistProp for ${propName} called with 'null' or 'undefined' default value!`); 1563 return false; 1564 } 1565 if (this.links_.get(propName)) { 1566 stateMgmtConsole.warn(`PersistentStorage: persistProp: ${propName} is already persisted`); 1567 return false; 1568 } 1569 let link = AppStorage.link(propName, this); 1570 if (link) { 1571 1572 this.links_.set(propName, link); 1573 } 1574 else { 1575 let newValue = PersistentStorage.storage_.get(propName); 1576 let returnValue; 1577 if (newValue == undefined || newValue == null) { 1578 1579 returnValue = defaultValue; 1580 } 1581 else { 1582 returnValue = newValue; 1583 } 1584 link = AppStorage.setAndLink(propName, returnValue, this); 1585 this.links_.set(propName, link); 1586 1587 } 1588 return true; 1589 } 1590 persistProps(properties) { 1591 properties.forEach(property => this.persistProp1(property.key, property.defaultValue)); 1592 this.write(); 1593 } 1594 deleteProp(propName) { 1595 let link = this.links_.get(propName); 1596 if (link) { 1597 link.aboutToBeDeleted(); 1598 this.links_.delete(propName); 1599 PersistentStorage.storage_.delete(propName); 1600 1601 } 1602 else { 1603 stateMgmtConsole.warn(`PersistentStorage: '${propName}' is not a persisted property warning.`); 1604 } 1605 } 1606 write() { 1607 this.links_.forEach((link, propName, map) => { 1608 1609 PersistentStorage.storage_.set(propName, link.get()); 1610 }); 1611 } 1612 // FU code path method 1613 propertyHasChanged(info) { 1614 1615 this.write(); 1616 } 1617 // PU code path method 1618 syncPeerHasChanged(eventSource) { 1619 1620 this.write(); 1621 } 1622 // public required by the interface, use the static method instead! 1623 aboutToBeDeleted() { 1624 1625 this.links_.forEach((val, key, map) => { 1626 1627 val.aboutToBeDeleted(); 1628 }); 1629 this.links_.clear(); 1630 SubscriberManager.Delete(this.id__()); 1631 PersistentStorage.storage_.clear(); 1632 } 1633 id__() { 1634 return this.id_; 1635 } 1636} 1637PersistentStorage.instance_ = undefined; 1638; 1639/* 1640 * Copyright (c) 2021-2023 Huawei Device Co., Ltd. 1641 * Licensed under the Apache License, Version 2.0 (the "License"); 1642 * you may not use this file except in compliance with the License. 1643 * You may obtain a copy of the License at 1644 * 1645 * http://www.apache.org/licenses/LICENSE-2.0 1646 * 1647 * Unless required by applicable law or agreed to in writing, software 1648 * distributed under the License is distributed on an "AS IS" BASIS, 1649 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1650 * See the License for the specific language governing permissions and 1651 * limitations under the License. 1652 */ 1653/** 1654 * Environment 1655 * 1656 * Injects device properties ("environment") into AppStorage 1657 * 1658 */ 1659class Environment { 1660 constructor() { 1661 this.props_ = new Map(); 1662 Environment.envBackend_.onValueChanged(this.onValueChanged.bind(this)); 1663 } 1664 static getOrCreate() { 1665 if (Environment.instance_) { 1666 // already initialized 1667 return Environment.instance_; 1668 } 1669 Environment.instance_ = new Environment(); 1670 return Environment.instance_; 1671 } 1672 static configureBackend(envBackend) { 1673 Environment.envBackend_ = envBackend; 1674 } 1675 /** 1676 * @see configureBackend 1677 * @deprecated 1678 */ 1679 static ConfigureBackend(envBackend) { 1680 Environment.envBackend_ = envBackend; 1681 } 1682 static aboutToBeDeleted() { 1683 if (!Environment.instance_) { 1684 return; 1685 } 1686 Environment.getOrCreate().aboutToBeDeleted(); 1687 Environment.instance_ = undefined; 1688 } 1689 /** 1690 * @see aboutToBeDeleted 1691 * @deprecated 1692 */ 1693 static AboutToBeDeleted() { 1694 Environment.aboutToBeDeleted(); 1695 } 1696 static envProp(key, value) { 1697 return Environment.getOrCreate().envProp(key, value); 1698 } 1699 /** 1700 * @see envProp 1701 * @deprecated 1702 */ 1703 static EnvProp(key, value) { 1704 return Environment.getOrCreate().envProp(key, value); 1705 } 1706 static envProps(props) { 1707 Environment.getOrCreate().envProps(props); 1708 } 1709 /** 1710 * @see envProps 1711 * @deprecated 1712 */ 1713 static EnvProps(props) { 1714 Environment.getOrCreate().envProps(props); 1715 } 1716 static keys() { 1717 return Environment.getOrCreate().keys(); 1718 } 1719 /** 1720 * @see keys 1721 * @deprecated 1722 */ 1723 static Keys() { 1724 return Environment.getOrCreate().keys(); 1725 } 1726 envProp(key, value) { 1727 let prop = AppStorage.prop(key); 1728 if (prop) { 1729 stateMgmtConsole.warn(`Environment: envProp '${key}': Property already exists in AppStorage. Not using environment property.`); 1730 return false; 1731 } 1732 let tmp; 1733 switch (key) { 1734 case "accessibilityEnabled": 1735 tmp = Environment.envBackend_.getAccessibilityEnabled(); 1736 break; 1737 case "colorMode": 1738 tmp = Environment.envBackend_.getColorMode(); 1739 break; 1740 case "fontScale": 1741 tmp = Environment.envBackend_.getFontScale(); 1742 break; 1743 case "fontWeightScale": 1744 tmp = Environment.envBackend_.getFontWeightScale().toFixed(2); 1745 break; 1746 case "layoutDirection": 1747 tmp = Environment.envBackend_.getLayoutDirection(); 1748 break; 1749 case "languageCode": 1750 tmp = Environment.envBackend_.getLanguageCode(); 1751 break; 1752 default: 1753 tmp = value; 1754 } 1755 if (!tmp && tmp !== 0) { 1756 tmp = value; 1757 } 1758 prop = AppStorage.setAndProp(key, tmp); 1759 if (!prop) { 1760 stateMgmtConsole.warn(`Environment: envProp '${key}': AppStorage setAndProp failed.`); 1761 return false; 1762 } 1763 this.props_.set(key, prop); 1764 1765 return true; 1766 } 1767 envProps(properties) { 1768 properties.forEach(property => { 1769 this.envProp(property.key, property.defaultValue); 1770 1771 }); 1772 } 1773 keys() { 1774 let result = []; 1775 const it = this.props_.keys(); 1776 let val = it.next(); 1777 while (!val.done) { 1778 result.push(val.value); 1779 val = it.next(); 1780 } 1781 return result; 1782 } 1783 onValueChanged(key, value) { 1784 let ok = AppStorage.set(key, value); 1785 if (ok) { 1786 1787 } 1788 else { 1789 stateMgmtConsole.warn(`Environment: onValueChanged: error changing ${key}! See results above.`); 1790 } 1791 } 1792 aboutToBeDeleted() { 1793 this.props_.forEach((val, key, map) => { 1794 val.aboutToBeDeleted(); 1795 AppStorage.delete(key); 1796 }); 1797 } 1798} 1799Environment.instance_ = undefined; 1800/* 1801 * Copyright (c) 2023 Huawei Device Co., Ltd. 1802 * Licensed under the Apache License, Version 2.0 (the "License"); 1803 * you may not use this file except in compliance with the License. 1804 * You may obtain a copy of the License at 1805 * 1806 * http://www.apache.org/licenses/LICENSE-2.0 1807 * 1808 * Unless required by applicable law or agreed to in writing, software 1809 * distributed under the License is distributed on an "AS IS" BASIS, 1810 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1811 * See the License for the specific language governing permissions and 1812 * limitations under the License. 1813 */ 1814/** 1815 * state mgmt library uses its own class for logging 1816* allows to remap separately from other use of aceConsole 1817* 1818* everything in this file is framework internal 1819*/ 1820class stateMgmtConsole { 1821 static log(...args) { 1822 aceConsole.log(...args); 1823 } 1824 static debug(...args) { 1825 aceConsole.debug(...args); 1826 } 1827 static info(...args) { 1828 aceConsole.info(...args); 1829 } 1830 static warn(...args) { 1831 aceConsole.warn(...args); 1832 } 1833 static error(...args) { 1834 aceConsole.error(...args); 1835 } 1836 static propertyAccess(...args) { 1837 // enable for fine grain debugging variable observation 1838 // aceConsole debug (...args) 1839 } 1840 static applicationError(...args) { 1841 aceConsole.error(`FIX THIS APPLICATION ERROR \n`, ...args); 1842 } 1843} 1844class stateMgmtTrace { 1845 static scopedTrace(codeBlock, arg1, ...args) { 1846 aceTrace.begin(arg1, ...args); 1847 let result = codeBlock(); 1848 aceTrace.end(); 1849 return result; 1850 } 1851} 1852class errorReport { 1853 static varValueCheckFailed(params) { 1854 let msg = `@Component '${params.customComponent}': Illegal variable value error with decorated variable ${params.variableDeco} '${params.variableName}': `; 1855 msg += `failed validation: '${params.expectedType}`; 1856 try { 1857 msg += `, attempt to assign value type: '${typeof params.value}'`; 1858 msg += `, value: '${JSON.stringify(params.value, null, 4)}'`; 1859 } 1860 catch (e) { } 1861 msg += "!"; 1862 throw new TypeError(msg); 1863 } 1864 static varObservationFailed(params) { 1865 let msg = `@Component '${params.customComponent}': decorated variable ${params.variableDeco} '${params.variableName}': `; 1866 msg += `its class is neither decorated with '@Observed' nor it is an instance of 'SubscribableAbstract'`; 1867 try { 1868 msg += `, attempt to assign value type: '${typeof params.value}'`; 1869 msg += `, value: '${JSON.stringify(params.value, null, 4)}'`; 1870 } 1871 catch (e) { } 1872 msg += "!"; 1873 throw new TypeError(msg); 1874 } 1875} 1876/* 1877 * Copyright (c) 2021-2023 Huawei Device Co., Ltd. 1878 * Licensed under the Apache License, Version 2.0 (the "License"); 1879 * you may not use this file except in compliance with the License. 1880 * You may obtain a copy of the License at 1881 * 1882 * http://www.apache.org/licenses/LICENSE-2.0 1883 * 1884 * Unless required by applicable law or agreed to in writing, software 1885 * distributed under the License is distributed on an "AS IS" BASIS, 1886 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1887 * See the License for the specific language governing permissions and 1888 * limitations under the License. 1889 */ 1890var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { 1891 var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; 1892 if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); 1893 else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; 1894 return c > 3 && r && Object.defineProperty(target, key, r), r; 1895}; 1896/** 1897* @Observed class decorator 1898* 1899* usage: 1900* @Observed class ClassA { ... } 1901* 1902* Causes every instance of decorated clss to be automatically wrapped inside an ObservedObject. 1903* 1904* Implemented by extending the decroaetd class by class named 'ObservableObjectClass'. 1905* 1906* It is permisstable to decorate the base and the extended class like thisNote: I 1907* @Observed class ClassA { ...} 1908* @Observed class ClassB extends ClassA { ... } 1909* and use 1910* a = new ClassA(); 1911* b = new ClassB(); 1912* Only one ES6 Proxy is added. 1913* 1914* 1915* Take note the decorator implementation extends the prototype chain. 1916* 1917* The prototype chain of a in above example is 1918* - ObservableObjectClass prototype 1919* - ClassA prototype 1920* - Object prototype 1921* 1922* Snd the prototype chain of b is 1923* - ObservableObjectClass prototype 1924* - ClassB prototype 1925* - ObservableObjectClass prototype 1926* - ClassA prototype 1927* - Object prototype 1928* 1929* The @Observed decorator is public, part of the SDK, starting from API 9. 1930* 1931*/ 1932// define just once to get just one Symbol 1933const __IS_OBSERVED_PROXIED = Symbol("_____is_observed_proxied__"); 1934function Observed(constructor_, _) { 1935 1936 let ObservedClass = class extends constructor_ { 1937 constructor(...args) { 1938 super(...args); 1939 1940 let isProxied = Reflect.has(this, __IS_OBSERVED_PROXIED); 1941 Object.defineProperty(this, __IS_OBSERVED_PROXIED, { 1942 value: true, 1943 enumerable: false, 1944 configurable: false, 1945 writable: false 1946 }); 1947 if (isProxied) { 1948 1949 return this; 1950 } 1951 else { 1952 1953 return ObservedObject.createNewInternal(this, undefined); 1954 } 1955 } 1956 }; 1957 return ObservedClass; 1958} 1959// force tsc to generate the __decorate data structure needed for @Observed 1960// tsc will not generate unless the @Observed class decorator is used at least once 1961let __IGNORE_FORCE_decode_GENERATION__ = class __IGNORE_FORCE_decode_GENERATION__ { 1962}; 1963__IGNORE_FORCE_decode_GENERATION__ = __decorate([ 1964 Observed 1965], __IGNORE_FORCE_decode_GENERATION__); 1966class SubscribableHandler { 1967 constructor(owningProperty) { 1968 this.owningProperties_ = new Set(); 1969 if (owningProperty) { 1970 this.addOwningProperty(owningProperty); 1971 } 1972 1973 } 1974 isPropertyTracked(obj, property) { 1975 return Reflect.has(obj, `___TRACKED_${property}`) || 1976 property === TrackedObject.___TRACKED_OPTI_ASSIGNMENT_FAKE_PROP_PROPERTY || 1977 property === TrackedObject.___TRACKED_OPTI_ASSIGNMENT_FAKE_OBJLINK_PROPERTY; 1978 } 1979 addOwningProperty(subscriber) { 1980 if (subscriber) { 1981 1982 this.owningProperties_.add(subscriber.id__()); 1983 } 1984 else { 1985 stateMgmtConsole.warn(`SubscribableHandler: addOwningProperty: undefined subscriber. - Internal error?`); 1986 } 1987 } 1988 /* 1989 the inverse function of createOneWaySync or createTwoWaySync 1990 */ 1991 removeOwningProperty(property) { 1992 return this.removeOwningPropertyById(property.id__()); 1993 } 1994 removeOwningPropertyById(subscriberId) { 1995 1996 this.owningProperties_.delete(subscriberId); 1997 } 1998 notifyObjectPropertyHasChanged(propName, newValue) { 1999 2000 this.owningProperties_.forEach((subscribedId) => { 2001 var owningProperty = SubscriberManager.Find(subscribedId); 2002 if (!owningProperty) { 2003 stateMgmtConsole.warn(`SubscribableHandler: notifyObjectPropertyHasChanged: unknown subscriber.'${subscribedId}' error!.`); 2004 return; 2005 } 2006 // PU code path 2007 if ('onTrackedObjectPropertyCompatModeHasChangedPU' in owningProperty) { 2008 owningProperty.onTrackedObjectPropertyCompatModeHasChangedPU(this, propName); 2009 } 2010 // FU code path 2011 if ('hasChanged' in owningProperty) { 2012 owningProperty.hasChanged(newValue); 2013 } 2014 if ('propertyHasChanged' in owningProperty) { 2015 owningProperty.propertyHasChanged(propName); 2016 } 2017 }); 2018 } 2019 notifyTrackedObjectPropertyHasChanged(propName) { 2020 2021 this.owningProperties_.forEach((subscribedId) => { 2022 var owningProperty = SubscriberManager.Find(subscribedId); 2023 if (owningProperty && 'onTrackedObjectPropertyHasChangedPU' in owningProperty) { 2024 // PU code path with observed object property change tracking optimization 2025 owningProperty.onTrackedObjectPropertyHasChangedPU(this, propName); 2026 } 2027 else { 2028 stateMgmtConsole.warn(`SubscribableHandler: notifyTrackedObjectPropertyHasChanged: subscriber.'${subscribedId}' lacks method 'trackedObjectPropertyHasChangedPU' internal error!.`); 2029 } 2030 }); 2031 // no need to support FU code path when app uses @Track 2032 } 2033 has(target, property) { 2034 2035 return (property === ObservedObject.__IS_OBSERVED_OBJECT) ? true : Reflect.has(target, property); 2036 } 2037 get(target, property, receiver) { 2038 switch (property) { 2039 case ObservedObject.__OBSERVED_OBJECT_RAW_OBJECT: 2040 return target; 2041 break; 2042 case SubscribableHandler.COUNT_SUBSCRIBERS: 2043 return this.owningProperties_.size; 2044 break; 2045 default: 2046 const result = Reflect.get(target, property, receiver); 2047 let propertyStr = String(property); 2048 if (this.readCbFunc_ && typeof result !== 'function') { 2049 let isTracked = this.isPropertyTracked(target, propertyStr); 2050 2051 this.readCbFunc_(receiver, propertyStr, isTracked); 2052 } 2053 else { 2054 // result is function or in compatibility mode (in compat mode cbFunc will never be set) 2055 2056 } 2057 return result; 2058 break; 2059 } 2060 } 2061 set(target, property, newValue) { 2062 switch (property) { 2063 case SubscribableHandler.SUBSCRIBE: 2064 // assignment obsObj[SubscribableHandler.SUBSCRIBE] = subscriber 2065 this.addOwningProperty(newValue); 2066 return true; 2067 break; 2068 case SubscribableHandler.UNSUBSCRIBE: 2069 // assignment obsObj[SubscribableHandler.UNSUBSCRIBE] = subscriber 2070 this.removeOwningProperty(newValue); 2071 return true; 2072 break; 2073 case SubscribableHandler.SET_ONREAD_CB: 2074 // assignment obsObj[SubscribableHandler.SET_ONREAD_CB] = readCallbackFunc 2075 2076 this.readCbFunc_ = TrackedObject.isCompatibilityMode(target) ? undefined : newValue; 2077 return true; 2078 break; 2079 default: 2080 // this is added for stability test: Reflect.get target is not object 2081 try { 2082 if (Reflect.get(target, property) == newValue) { 2083 return true; 2084 } 2085 } 2086 catch (error) { 2087 ArkTools.print("SubscribableHandler: set", target); 2088 stateMgmtConsole.error(`An error occurred in SubscribableHandler set, target type is: ${typeof target}, ${error.message}`); 2089 throw error; 2090 } 2091 Reflect.set(target, property, newValue); 2092 const propString = String(property); 2093 if (TrackedObject.isCompatibilityMode(target)) { 2094 2095 this.notifyObjectPropertyHasChanged(propString, newValue); 2096 } 2097 else { 2098 if (this.isPropertyTracked(target, propString)) { 2099 2100 this.notifyTrackedObjectPropertyHasChanged(propString); 2101 } 2102 else { 2103 2104 } 2105 } 2106 return true; 2107 break; 2108 } 2109 // unreachable 2110 return false; 2111 } 2112} 2113SubscribableHandler.SUBSCRIBE = Symbol("_____subscribe__"); 2114SubscribableHandler.UNSUBSCRIBE = Symbol("_____unsubscribe__"); 2115SubscribableHandler.COUNT_SUBSCRIBERS = Symbol("____count_subscribers__"); 2116SubscribableHandler.SET_ONREAD_CB = Symbol("_____set_onread_cb__"); 2117class SubscribableMapSetHandler extends SubscribableHandler { 2118 constructor(owningProperty) { 2119 super(owningProperty); 2120 // In-place Map/Set modification functions 2121 this.mutatingFunctions = new Set([ 2122 /*Map functions*/ 2123 "set", "clear", "delete", 2124 /*Set functions*/ 2125 "add", "clear", "delete", 2126 ]); 2127 this.proxiedFunctions = new Set([ 2128 /*Map functions*/ 2129 "set", 2130 /*Set functions*/ 2131 "add" 2132 ]); 2133 } 2134 /** 2135 * Get trap for Map/Set type proxy 2136 * Functions that modify Map/Set in-place are intercepted and replaced with a function 2137 * that executes the original function and notifies the handler of a change. 2138 * @param target Original Map/Set object 2139 * @param property 2140 * @param receiver Proxied Map/Set object 2141 * @returns 2142 */ 2143 get(target, property, receiver) { 2144 if (property === ObservedObject.__OBSERVED_OBJECT_RAW_OBJECT) { 2145 return target; 2146 } 2147 //receiver will fail for internal slot methods of Set and Map 2148 //So assign the target as receiver in this case. 2149 if (property === Symbol.iterator || property === 'size') { 2150 receiver = target; 2151 } 2152 let ret = super.get(target, property, receiver); 2153 if (ret && typeof ret === 'function') { 2154 const self = this; 2155 return function () { 2156 // execute original function with given arguments 2157 const result = ret.apply(target, arguments); 2158 if (self.mutatingFunctions.has(property)) { 2159 self.notifyObjectPropertyHasChanged(property, target); 2160 } 2161 // Only calls to inserting items can be chained, so returning the 'proxiedObject' 2162 // ensures that when chain calls also 2nd function call operates on the proxied object. 2163 // Otherwise return the original result of the function. 2164 return self.proxiedFunctions.has(property) ? receiver : result; 2165 }.bind(receiver); 2166 } 2167 return ret; 2168 } 2169} 2170class SubscribableDateHandler extends SubscribableHandler { 2171 constructor(owningProperty) { 2172 super(owningProperty); 2173 this.dateSetFunctions = new Set(["setFullYear", "setMonth", "setDate", "setHours", "setMinutes", "setSeconds", 2174 "setMilliseconds", "setTime", "setUTCFullYear", "setUTCMonth", "setUTCDate", "setUTCHours", "setUTCMinutes", 2175 "setUTCSeconds", "setUTCMilliseconds"]); 2176 } 2177 /** 2178 * Get trap for Date type proxy 2179 * Functions that modify Date in-place are intercepted and replaced with a function 2180 * that executes the original function and notifies the handler of a change. 2181 * @param target Original Date object 2182 * @param property 2183 * @returns 2184 */ 2185 get(target, property) { 2186 let ret = super.get(target, property); 2187 if (typeof ret === "function") { 2188 if (this.dateSetFunctions.has(property)) { 2189 const self = this; 2190 return function () { 2191 // execute original function with given arguments 2192 let result = ret.apply(this, arguments); 2193 self.notifyObjectPropertyHasChanged(property.toString(), this); 2194 return result; 2195 // bind "this" to target inside the function 2196 }.bind(target); 2197 } 2198 return ret.bind(target); 2199 } 2200 return ret; 2201 } 2202} 2203class SubscribableArrayHandler extends SubscribableHandler { 2204 constructor(owningProperty) { 2205 super(owningProperty); 2206 // In-place array modification functions 2207 this.mutatingFunctions = new Set(["splice", "copyWithin", "fill", "reverse", "sort"]); 2208 // 'splice' and 'pop' self modifies the array, returns deleted array items 2209 // means, alike other self-modifying functions, splice does not return the array itself. 2210 this.specialFunctions = new Set(["splice", "pop"]); 2211 } 2212 /** 2213 * Get trap for Array type proxy 2214 * Functions that modify Array in-place are intercepted and replaced with a function 2215 * that executes the original function and notifies the handler of a change. 2216 * @param target Original Array object 2217 * @param property 2218 * @param receiver Proxied Array object 2219 * @returns 2220 */ 2221 get(target, property, receiver) { 2222 if (property === ObservedObject.__OBSERVED_OBJECT_RAW_OBJECT) { 2223 return target; 2224 } 2225 let ret = super.get(target, property, receiver); 2226 if (ret && typeof ret === "function") { 2227 const self = this; 2228 const prop = property.toString(); 2229 if (self.mutatingFunctions.has(prop)) { 2230 return function () { 2231 const result = ret.apply(target, arguments); 2232 // prop is the function name here 2233 // and result is the function return value 2234 // function modifies none or more properties 2235 self.notifyObjectPropertyHasChanged(prop, self.specialFunctions.has(prop) ? target : result); 2236 // returning the 'receiver(proxied object)' ensures that when chain calls also 2nd function call 2237 // operates on the proxied object. 2238 return self.specialFunctions.has(prop) ? result : receiver; 2239 }.bind(receiver); 2240 } 2241 // binding the proxiedObject ensures that modifying functions like push() operate on the 2242 // proxied array and each array change is notified. 2243 return ret.bind(receiver); 2244 } 2245 return ret; 2246 } 2247} 2248class ExtendableProxy { 2249 constructor(obj, handler) { 2250 return new Proxy(obj, handler); 2251 } 2252} 2253class ObservedObject extends ExtendableProxy { 2254 /** 2255 * To create a new ObservableObject use CreateNew function 2256 * 2257 * constructor create a new ObservableObject and subscribe its owner to propertyHasChanged 2258 * notifications 2259 * @param obj raw Object, if obj is a ObservableOject throws an error 2260 * @param objectOwner 2261 */ 2262 constructor(obj, handler, objectOwningProperty) { 2263 super(obj, handler); 2264 if (ObservedObject.IsObservedObject(obj)) { 2265 stateMgmtConsole.error("ObservableOject constructor: INTERNAL ERROR: after jsObj is observedObject already"); 2266 } 2267 if (objectOwningProperty != undefined) { 2268 this[SubscribableHandler.SUBSCRIBE] = objectOwningProperty; 2269 } 2270 } // end of constructor 2271 /** 2272 * Factory function for ObservedObjects / 2273 * wrapping of objects for proxying 2274 * 2275 * @param rawObject unproxied Object or ObservedObject 2276 * @param objOwner owner of this Object to sign uop for propertyChange 2277 * notifications 2278 * @returns the rawObject if object is already an ObservedObject, 2279 * otherwise the newly created ObservedObject 2280 */ 2281 static createNew(rawObject, owningProperty) { 2282 if (rawObject === null || rawObject === undefined) { 2283 stateMgmtConsole.error(`ObservedObject.CreateNew, input object must not be null or undefined.`); 2284 return rawObject; 2285 } 2286 if (ObservedObject.IsObservedObject(rawObject)) { 2287 ObservedObject.addOwningProperty(rawObject, owningProperty); 2288 return rawObject; 2289 } 2290 return ObservedObject.createNewInternal(rawObject, owningProperty); 2291 } 2292 static createNewInternal(rawObject, owningProperty) { 2293 let proxiedObject; 2294 if (rawObject instanceof Map || rawObject instanceof Set) { 2295 proxiedObject = new ObservedObject(rawObject, new SubscribableMapSetHandler(owningProperty), owningProperty); 2296 } 2297 else if (rawObject instanceof Date) { 2298 proxiedObject = new ObservedObject(rawObject, new SubscribableDateHandler(owningProperty), owningProperty); 2299 } 2300 else if (Array.isArray(rawObject)) { 2301 proxiedObject = new ObservedObject(rawObject, new SubscribableArrayHandler(owningProperty), owningProperty); 2302 } 2303 else { 2304 proxiedObject = new ObservedObject(rawObject, new SubscribableHandler(owningProperty), owningProperty); 2305 } 2306 return proxiedObject; 2307 } 2308 /* 2309 Return the unproxied object 'inside' the ObservedObject / the ES6 Proxy 2310 no set observation, no notification of changes! 2311 Use with caution, do not store any references 2312 */ 2313 static GetRawObject(obj) { 2314 return !ObservedObject.IsObservedObject(obj) ? obj : obj[ObservedObject.__OBSERVED_OBJECT_RAW_OBJECT]; 2315 } 2316 /** 2317 * 2318 * @param obj anything 2319 * @returns true if the parameter is an Object wrpped with a ObservedObject 2320 * Note: Since ES6 Proying is transparent, 'instance of' will not work. Use 2321 * this static function instead. 2322 */ 2323 static IsObservedObject(obj) { 2324 return (obj && (typeof obj === "object") && Reflect.has(obj, ObservedObject.__IS_OBSERVED_OBJECT)); 2325 } 2326 /** 2327 * add a subscriber to given ObservedObject 2328 * due to the proxy nature this static method approach needs to be used instead of a member 2329 * function 2330 * @param obj 2331 * @param subscriber 2332 * @returns false if given object is not an ObservedObject 2333 */ 2334 static addOwningProperty(obj, subscriber) { 2335 if (!ObservedObject.IsObservedObject(obj) || subscriber == undefined) { 2336 return false; 2337 } 2338 obj[SubscribableHandler.SUBSCRIBE] = subscriber; 2339 return true; 2340 } 2341 /** 2342 * remove a subscriber to given ObservedObject 2343 * due to the proxy nature this static method approach needs to be used instead of a member 2344 * function 2345 * @param obj 2346 * @param subscriber 2347 * @returns false if given object is not an ObservedObject 2348 */ 2349 static removeOwningProperty(obj, subscriber) { 2350 if (!ObservedObject.IsObservedObject(obj)) { 2351 return false; 2352 } 2353 obj[SubscribableHandler.UNSUBSCRIBE] = subscriber; 2354 return true; 2355 } 2356 /** 2357 * 2358 * @param obj any Object 2359 * @returns return number of subscribers to the given ObservedObject 2360 * or false if given object is not an ObservedObject 2361 */ 2362 static countSubscribers(obj) { 2363 return ObservedObject.IsObservedObject(obj) ? obj[SubscribableHandler.COUNT_SUBSCRIBERS] : false; 2364 } 2365 /* 2366 set or unset callback function to be called when a property has been called 2367 */ 2368 static registerPropertyReadCb(obj, readPropCb) { 2369 if (!ObservedObject.IsObservedObject(obj)) { 2370 return false; 2371 } 2372 obj[SubscribableHandler.SET_ONREAD_CB] = readPropCb; 2373 return true; 2374 } 2375 static unregisterPropertyReadCb(obj) { 2376 if (!ObservedObject.IsObservedObject(obj)) { 2377 return false; 2378 } 2379 obj[SubscribableHandler.SET_ONREAD_CB] = undefined; 2380 return true; 2381 } 2382 /** 2383 * Utility function for debugging the prototype chain of given Object 2384 * The given object can be any Object, it is not required to be an ObservedObject 2385 * @param object 2386 * @returns multi-line string containing info about the prototype chain 2387 * on class in class hiararchy per line 2388 */ 2389 static tracePrototypeChainOfObject(object) { 2390 let proto = Object.getPrototypeOf(object); 2391 let result = ""; 2392 let sepa = ""; 2393 while (proto) { 2394 result += `${sepa}${ObservedObject.tracePrototype(proto)}`; 2395 proto = Object.getPrototypeOf(proto); 2396 sepa = ",\n"; 2397 } 2398 return result; 2399 } 2400 /** 2401 * Utility function for debugging all functions of given Prototype. 2402 * @returns string containing containing names of all functions and members of given Prototype 2403 */ 2404 static tracePrototype(proto) { 2405 if (!proto) { 2406 return ""; 2407 } 2408 let result = `${proto.constructor && proto.constructor.name ? proto.constructor.name : '<no class>'}: `; 2409 let sepa = ""; 2410 for (let name of Object.getOwnPropertyNames(proto)) { 2411 result += `${sepa}${name}`; 2412 sepa = ", "; 2413 } 2414 ; 2415 return result; 2416 } 2417 /** 2418 * @Observed decorator extends the decorated class. This function returns the prototype of the decorated class 2419 * @param proto 2420 * @returns prototype of the @Observed decorated class or 'proto' parameter if not @Observed decorated 2421 */ 2422 static getPrototypeOfObservedClass(proto) { 2423 return (proto.constructor && proto.constructor.name == "ObservedClass") 2424 ? Object.getPrototypeOf(proto.constructor.prototype) 2425 : proto; 2426 } 2427} 2428ObservedObject.__IS_OBSERVED_OBJECT = Symbol("_____is_observed_object__"); 2429ObservedObject.__OBSERVED_OBJECT_RAW_OBJECT = Symbol("_____raw_object__"); 2430/* 2431 * Copyright (c) 2021 Huawei Device Co., Ltd. 2432 * Licensed under the Apache License, Version 2.0 (the "License"); 2433 * you may not use this file except in compliance with the License. 2434 * You may obtain a copy of the License at 2435 * 2436 * http://www.apache.org/licenses/LICENSE-2.0 2437 * 2438 * Unless required by applicable law or agreed to in writing, software 2439 * distributed under the License is distributed on an "AS IS" BASIS, 2440 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 2441 * See the License for the specific language governing permissions and 2442 * limitations under the License. 2443 */ 2444/* 2445 manage subscriptions to a property 2446 managing the property is left to sub 2447 classes 2448 Extended by ObservedProperty, SyncedPropertyOneWay 2449 and SyncedPropertyTwoWay 2450*/ 2451class ObservedPropertyAbstract extends SubscribedAbstractProperty { 2452 constructor(subscribeMe, info) { 2453 super(); 2454 this.subscribers_ = new Set(); 2455 this.id_ = SubscriberManager.MakeId(); 2456 SubscriberManager.Add(this); 2457 if (subscribeMe) { 2458 this.subscribers_.add(subscribeMe.id__()); 2459 } 2460 if (info) { 2461 this.info_ = info; 2462 } 2463 } 2464 aboutToBeDeleted() { 2465 SubscriberManager.Delete(this.id__()); 2466 } 2467 id__() { 2468 return this.id_; 2469 } 2470 info() { 2471 return this.info_; 2472 } 2473 setInfo(propName) { 2474 if (propName && propName != "") { 2475 this.info_ = propName; 2476 } 2477 } 2478 // Partial Update "*PU" classes will overwrite 2479 getUnmonitored() { 2480 return this.get(); 2481 } 2482 // update the element id for recycle custom component 2483 updateElmtId(oldElmtId, newElmtId) { 2484 if (this.subscribers_.has(oldElmtId)) { 2485 this.subscribers_.delete(oldElmtId); 2486 this.subscribers_.add(newElmtId); 2487 } 2488 } 2489 // Method name is used to check object is of type ObservedPropertyAbstract 2490 // Do NOT override in derived classed, use addSubscriber 2491 subscribeMe(subscriber) { 2492 2493 this.subscribers_.add(subscriber.id__()); 2494 } 2495 /* 2496 the inverse function of createOneWaySync or createTwoWaySync 2497 Do NOT override in derived classed, use removeSubscriber 2498 */ 2499 unlinkSuscriber(subscriberId) { 2500 this.subscribers_.delete(subscriberId); 2501 } 2502 /* 2503 Virtualized version of the subscription mechanism - add subscriber 2504 */ 2505 addSubscriber(subscriber) { 2506 if (subscriber) { 2507 this.subscribeMe(subscriber); 2508 } 2509 } 2510 /* 2511 Virtualized version of the subscription mechanism - remove subscriber 2512 */ 2513 removeSubscriber(subscriber, id) { 2514 if (id) { 2515 this.unlinkSuscriber(id); 2516 } 2517 else if (subscriber) { 2518 this.unlinkSuscriber(subscriber.id__()); 2519 } 2520 } 2521 // FU code path callback 2522 notifyHasChanged(newValue) { 2523 2524 2525 this.subscribers_.forEach((subscribedId) => { 2526 var subscriber = SubscriberManager.Find(subscribedId); 2527 if (subscriber) { 2528 // FU code path 2529 if ('hasChanged' in subscriber) { 2530 subscriber.hasChanged(newValue); 2531 } 2532 if ('propertyHasChanged' in subscriber) { 2533 subscriber.propertyHasChanged(this.info_); 2534 } 2535 } 2536 else { 2537 stateMgmtConsole.warn(`ObservedPropertyAbstract[${this.id__()}, '${this.info() || "unknown"}']: notifyHasChanged: unknown subscriber ID '${subscribedId}' error!`); 2538 } 2539 }); 2540 2541 } 2542 notifyPropertyRead() { 2543 2544 2545 this.subscribers_.forEach((subscribedId) => { 2546 var subscriber = SubscriberManager.Find(subscribedId); 2547 if (subscriber) { 2548 if ('propertyRead' in subscriber) { 2549 subscriber.propertyRead(this.info_); 2550 } 2551 } 2552 }); 2553 2554 } 2555 /* 2556 return numebr of subscribers to this property 2557 mostly useful for unit testin 2558 */ 2559 numberOfSubscrbers() { 2560 return this.subscribers_.size; 2561 } 2562 /** 2563 * provide a factory function that creates a SynchedPropertyXXXX of choice 2564 * that uses 'this' as source 2565 * @param factoryFunc 2566 * @returns 2567 */ 2568 createSync(factoryFunc) { 2569 return factoryFunc(this); 2570 } 2571 /** 2572 * depreciated SDK function, not used anywhere by the framework 2573 */ 2574 createTwoWaySync(subscribeMe, info) { 2575 stateMgmtConsole.warn("Using depreciated method 'createTwoWaySync'!"); 2576 return this.createLink(subscribeMe, info); 2577 } 2578 /** 2579 * depreciated SDK function, not used anywhere by the framework 2580 */ 2581 createOneWaySync(subscribeMe, info) { 2582 stateMgmtConsole.warn("Using depreciated method 'createOneWaySync' !"); 2583 return this.createProp(subscribeMe, info); 2584 } 2585 /** 2586 * factory function for concrete 'object' or 'simple' ObservedProperty object 2587 * depending if value is Class object 2588 * or simple type (boolean | number | string) 2589 * @param value 2590 * @param owningView 2591 * @param thisPropertyName 2592 * @returns either 2593 */ 2594 static CreateObservedObject(value, owningView, thisPropertyName) { 2595 return (typeof value === "object") ? 2596 new ObservedPropertyObject(value, owningView, thisPropertyName) 2597 : new ObservedPropertySimple(value, owningView, thisPropertyName); 2598 } 2599} 2600/* 2601 * Copyright (c) 2024 Huawei Device Co., Ltd. 2602 * Licensed under the Apache License, Version 2.0 (the "License"); 2603 * you may not use this file except in compliance with the License. 2604 * You may obtain a copy of the License at 2605 * 2606 * http://www.apache.org/licenses/LICENSE-2.0 2607 * 2608 * Unless required by applicable law or agreed to in writing, software 2609 * distributed under the License is distributed on an "AS IS" BASIS, 2610 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 2611 * See the License for the specific language governing permissions and 2612 * limitations under the License. 2613 */ 2614class CustomDialogController extends NativeCustomDialogController { 2615 constructor(arg, view) { 2616 super(arg, view); 2617 this.arg_ = arg; 2618 this.view_ = view; 2619 } 2620} 2621/* 2622 * Copyright (c) 2021 Huawei Device Co., Ltd. 2623 * Licensed under the Apache License, Version 2.0 (the "License"); 2624 * you may not use this file except in compliance with the License. 2625 * You may obtain a copy of the License at 2626 * 2627 * http://www.apache.org/licenses/LICENSE-2.0 2628 * 2629 * Unless required by applicable law or agreed to in writing, software 2630 * distributed under the License is distributed on an "AS IS" BASIS, 2631 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 2632 * See the License for the specific language governing permissions and 2633 * limitations under the License. 2634 */ 2635/** 2636 * ObservedPropertyObjectAbstract 2637 * 2638 * all definitions in this file are framework internal 2639 * 2640 * common base class of ObservedPropertyObject and 2641 * SyncedObjectPropertyTwoWay 2642 * adds the createObjectLink to the ObservedPropertyAbstract base 2643 */ 2644class ObservedPropertyObjectAbstract extends ObservedPropertyAbstract { 2645 constructor(owningView, thisPropertyName) { 2646 super(owningView, thisPropertyName); 2647 } 2648} 2649/* 2650 * Copyright (c) 2021 Huawei Device Co., Ltd. 2651 * Licensed under the Apache License, Version 2.0 (the "License"); 2652 * you may not use this file except in compliance with the License. 2653 * You may obtain a copy of the License at 2654 * 2655 * http://www.apache.org/licenses/LICENSE-2.0 2656 * 2657 * Unless required by applicable law or agreed to in writing, software 2658 * distributed under the License is distributed on an "AS IS" BASIS, 2659 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 2660 * See the License for the specific language governing permissions and 2661 * limitations under the License. 2662 */ 2663/** 2664 * 2665 * ObservedPropertySimpleAbstract 2666 * 2667 * all definitions in this file are framework internal 2668 */ 2669class ObservedPropertySimpleAbstract extends ObservedPropertyAbstract { 2670 constructor(owningView, propertyName) { 2671 super(owningView, propertyName); 2672 } 2673} 2674/* 2675 * Copyright (c) 2021-2022 Huawei Device Co., Ltd. 2676 * Licensed under the Apache License, Version 2.0 (the "License"); 2677 * you may not use this file except in compliance with the License. 2678 * You may obtain a copy of the License at 2679 * 2680 * http://www.apache.org/licenses/LICENSE-2.0 2681 * 2682 * Unless required by applicable law or agreed to in writing, software 2683 * distributed under the License is distributed on an "AS IS" BASIS, 2684 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 2685 * See the License for the specific language governing permissions and 2686 * limitations under the License. 2687 */ 2688/** 2689 * ObservedPropertyObject 2690 * 2691 * all definitions in this file are framework internal 2692 * 2693 * class that holds an actual property value of type T 2694 * uses its base class to manage subscribers to this 2695 * property. 2696*/ 2697class ObservedPropertyObject extends ObservedPropertyObjectAbstract { 2698 constructor(value, owningView, propertyName) { 2699 super(owningView, propertyName); 2700 this.setValueInternal(value); 2701 } 2702 aboutToBeDeleted(unsubscribeMe) { 2703 this.unsubscribeFromOwningProperty(); 2704 if (unsubscribeMe) { 2705 this.unlinkSuscriber(unsubscribeMe.id__()); 2706 } 2707 super.aboutToBeDeleted(); 2708 } 2709 // notification from ObservedObject value one of its 2710 // props has chnaged. Implies the ObservedProperty has changed 2711 // Note: this function gets called when in this case: 2712 // thisProp.aObsObj.aProp = 47 a object prop gets changed 2713 // It is NOT called when 2714 // thisProp.aObsObj = new ClassA 2715 hasChanged(newValue) { 2716 2717 this.notifyHasChanged(this.wrappedValue_); 2718 } 2719 unsubscribeFromOwningProperty() { 2720 if (this.wrappedValue_) { 2721 if (this.wrappedValue_ instanceof SubscribaleAbstract) { 2722 this.wrappedValue_.removeOwningProperty(this); 2723 } 2724 else { 2725 ObservedObject.removeOwningProperty(this.wrappedValue_, this); 2726 } 2727 } 2728 } 2729 /* 2730 actually update this.wrappedValue_ 2731 called needs to do value change check 2732 and also notify with this.aboutToChange(); 2733 */ 2734 setValueInternal(newValue) { 2735 if (typeof newValue !== 'object') { 2736 2737 return false; 2738 } 2739 this.unsubscribeFromOwningProperty(); 2740 if (ObservedObject.IsObservedObject(newValue)) { 2741 2742 ObservedObject.addOwningProperty(newValue, this); 2743 this.wrappedValue_ = newValue; 2744 } 2745 else if (newValue instanceof SubscribaleAbstract) { 2746 2747 this.wrappedValue_ = newValue; 2748 this.wrappedValue_.addOwningProperty(this); 2749 } 2750 else { 2751 2752 this.wrappedValue_ = ObservedObject.createNew(newValue, this); 2753 } 2754 return true; 2755 } 2756 get() { 2757 2758 this.notifyPropertyRead(); 2759 return this.wrappedValue_; 2760 } 2761 set(newValue) { 2762 if (this.wrappedValue_ == newValue) { 2763 2764 return; 2765 } 2766 2767 this.setValueInternal(newValue); 2768 this.notifyHasChanged(newValue); 2769 } 2770 /** 2771 * These functions are used 2772 * LocalStorage.link (also in partial update config) 2773 * (FU)View.initializeConsumeinitializeConsume 2774 */ 2775 createLink(subscribeOwner, linkPropName) { 2776 return new SynchedPropertyObjectTwoWay(this, subscribeOwner, linkPropName); 2777 } 2778 createProp(subscribeOwner, linkPropName) { 2779 throw new Error("Creating a 'Prop' property is unsupported for Object type property value."); 2780 } 2781} 2782/* 2783 * Copyright (c) 2021-2022 Huawei Device Co., Ltd. 2784 * Licensed under the Apache License, Version 2.0 (the "License"); 2785 * you may not use this file except in compliance with the License. 2786 * You may obtain a copy of the License at 2787 * 2788 * http://www.apache.org/licenses/LICENSE-2.0 2789 * 2790 * Unless required by applicable law or agreed to in writing, software 2791 * distributed under the License is distributed on an "AS IS" BASIS, 2792 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 2793 * See the License for the specific language governing permissions and 2794 * limitations under the License. 2795 */ 2796/** 2797 * ObservedPropertySimple 2798 * 2799 * all definitions in this file are framework internal 2800 */ 2801class ObservedPropertySimple extends ObservedPropertySimpleAbstract { 2802 constructor(value, owningView, propertyName) { 2803 super(owningView, propertyName); 2804 if (typeof value === "object") { 2805 throw new SyntaxError("ObservedPropertySimple value must not be an object"); 2806 } 2807 this.setValueInternal(value); 2808 } 2809 aboutToBeDeleted(unsubscribeMe) { 2810 if (unsubscribeMe) { 2811 this.unlinkSuscriber(unsubscribeMe.id__()); 2812 } 2813 super.aboutToBeDeleted(); 2814 } 2815 hasChanged(newValue) { 2816 2817 this.notifyHasChanged(this.wrappedValue_); 2818 } 2819 /* 2820 actually update this.wrappedValue_ 2821 called needs to do value change check 2822 and also notify with this.aboutToChange(); 2823 */ 2824 setValueInternal(newValue) { 2825 2826 this.wrappedValue_ = newValue; 2827 } 2828 get() { 2829 2830 this.notifyPropertyRead(); 2831 return this.wrappedValue_; 2832 } 2833 set(newValue) { 2834 if (this.wrappedValue_ == newValue) { 2835 2836 return; 2837 } 2838 2839 this.setValueInternal(newValue); 2840 this.notifyHasChanged(newValue); 2841 } 2842 /** 2843 * These functions are meant for use in connection with the App Stoage and 2844 * business logic implementation. 2845 * the created Link and Prop will update when 'this' property value 2846 * changes. 2847 */ 2848 createLink(subscribeOwner, linkPropName) { 2849 return new SynchedPropertySimpleTwoWay(this, subscribeOwner, linkPropName); 2850 } 2851 createProp(subscribeOwner, linkPropName) { 2852 return new SynchedPropertySimpleOneWaySubscribing(this, subscribeOwner, linkPropName); 2853 } 2854} 2855/* 2856 * Copyright (c) 2021 Huawei Device Co., Ltd. 2857 * Licensed under the Apache License, Version 2.0 (the "License"); 2858 * you may not use this file except in compliance with the License. 2859 * You may obtain a copy of the License at 2860 * 2861 * http://www.apache.org/licenses/LICENSE-2.0 2862 * 2863 * Unless required by applicable law or agreed to in writing, software 2864 * distributed under the License is distributed on an "AS IS" BASIS, 2865 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 2866 * See the License for the specific language governing permissions and 2867 * limitations under the License. 2868 */ 2869/** 2870 * SynchedPropertyObjectTwoWay 2871 * 2872 * all definitions in this file are framework internal 2873 */ 2874class SynchedPropertyObjectTwoWay extends ObservedPropertyObjectAbstract { 2875 constructor(linkSource, owningChildView, thisPropertyName) { 2876 super(owningChildView, thisPropertyName); 2877 this.changeNotificationIsOngoing_ = false; 2878 this.linkedParentProperty_ = linkSource; 2879 if (this.linkedParentProperty_) { 2880 // register to the parent property 2881 this.linkedParentProperty_.subscribeMe(this); 2882 } 2883 // register to the ObservedObject 2884 ObservedObject.addOwningProperty(this.getObject(), this); 2885 } 2886 /* 2887 like a destructor, need to call this before deleting 2888 the property. 2889 */ 2890 aboutToBeDeleted() { 2891 if (this.linkedParentProperty_) { 2892 // unregister from parent of this link 2893 this.linkedParentProperty_.unlinkSuscriber(this.id__()); 2894 // unregister from the ObservedObject 2895 ObservedObject.removeOwningProperty(this.getObject(), this); 2896 } 2897 super.aboutToBeDeleted(); 2898 } 2899 getObject() { 2900 this.notifyPropertyRead(); 2901 return (this.linkedParentProperty_ ? this.linkedParentProperty_.get() : undefined); 2902 } 2903 setObject(newValue) { 2904 if (this.linkedParentProperty_) { 2905 this.linkedParentProperty_.set(newValue); 2906 } 2907 } 2908 // this object is subscriber to ObservedObject 2909 // will call this cb function when property has changed 2910 hasChanged(newValue) { 2911 if (!this.changeNotificationIsOngoing_) { 2912 2913 this.notifyHasChanged(this.getObject()); 2914 } 2915 } 2916 // get 'read through` from the ObservedProperty 2917 get() { 2918 2919 return this.getObject(); 2920 } 2921 // set 'writes through` to the ObservedProperty 2922 set(newValue) { 2923 if (this.getObject() == newValue) { 2924 2925 return; 2926 } 2927 2928 ObservedObject.removeOwningProperty(this.getObject(), this); 2929 // the purpose of the changeNotificationIsOngoing_ is to avoid 2930 // circular notifications @Link -> source @State -> other but alos same @Link 2931 this.changeNotificationIsOngoing_ = true; 2932 this.setObject(newValue); 2933 ObservedObject.addOwningProperty(this.getObject(), this); 2934 this.notifyHasChanged(newValue); 2935 this.changeNotificationIsOngoing_ = false; 2936 } 2937 /** 2938 * These functions are meant for use in connection with the App Stoage and 2939 * business logic implementation. 2940 * the created Link and Prop will update when 'this' property value 2941 * changes. 2942 */ 2943 createLink(subscribeOwner, linkPropName) { 2944 return new SynchedPropertyObjectTwoWay(this, subscribeOwner, linkPropName); 2945 } 2946 createProp(subscribeOwner, linkPropName) { 2947 throw new Error("Creating a 'Prop' property is unsupported for Object type property value."); 2948 } 2949} 2950/* 2951 * Copyright (c) 2021-2022 Huawei Device Co., Ltd. 2952 * Licensed under the Apache License, Version 2.0 (the "License"); 2953 * you may not use this file except in compliance with the License. 2954 * You may obtain a copy of the License at 2955 * 2956 * http://www.apache.org/licenses/LICENSE-2.0 2957 * 2958 * Unless required by applicable law or agreed to in writing, software 2959 * distributed under the License is distributed on an "AS IS" BASIS, 2960 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 2961 * See the License for the specific language governing permissions and 2962 * limitations under the License. 2963 */ 2964/** 2965 * SynchedPropertySimpleOneWay 2966 * 2967 * all definitions in this file are framework internal 2968 */ 2969class SynchedPropertySimpleOneWay extends ObservedPropertySimpleAbstract { 2970 constructor(value, subscribeMe, info) { 2971 super(subscribeMe, info); 2972 // add a test here that T is a simple type 2973 this.wrappedValue_ = value; 2974 } 2975 /* 2976 like a destructor, need to call this before deleting 2977 the property. 2978 */ 2979 aboutToBeDeleted() { 2980 super.aboutToBeDeleted(); 2981 } 2982 // get 'read through` from the ObservedProperty 2983 get() { 2984 2985 this.notifyPropertyRead(); 2986 return this.wrappedValue_; 2987 } 2988 set(newValue) { 2989 if (this.wrappedValue_ == newValue) { 2990 2991 return; 2992 } 2993 2994 this.wrappedValue_ = newValue; 2995 this.notifyHasChanged(newValue); 2996 } 2997 /** 2998 * These functions are meant for use in connection with the App Stoage and 2999 * business logic implementation. 3000 * the created Link and Prop will update when 'this' property value 3001 * changes. 3002 */ 3003 createLink(subscribeOwner, linkPropName) { 3004 throw new Error("Can not create a 'Link' from a 'Prop' property. "); 3005 } 3006 createProp(subscribeOwner, linkPropName) { 3007 throw new Error("Method not supported, create a SynchedPropertySimpleOneWaySubscribing from, where to create a Prop."); 3008 } 3009} 3010/* 3011 This exrension of SynchedPropertySimpleOneWay needs to be used for AppStorage 3012 because it needs to be notified about the source property changing 3013 ( there is no re-render process as in Views to update the wrappedValue ) 3014*/ 3015class SynchedPropertySimpleOneWaySubscribing extends SynchedPropertySimpleOneWay { 3016 constructor(linkedProperty, subscribeMe, info) { 3017 super(linkedProperty.get(), subscribeMe, info); 3018 this.linkedParentProperty_ = linkedProperty; 3019 this.linkedParentProperty_.subscribeMe(this); 3020 } 3021 aboutToBeDeleted() { 3022 // unregister from parent of this prop 3023 this.linkedParentProperty_.unlinkSuscriber(this.id__()); 3024 super.aboutToBeDeleted(); 3025 } 3026 hasChanged(newValue) { 3027 3028 this.set(newValue); 3029 } 3030 /** 3031 * These functions are meant for use in connection with the App Stoage and 3032 * business logic implementation. 3033 * the created Link and Prop will update when 'this' property value 3034 * changes. 3035 */ 3036 createLink(subscribeOwner, linkPropName) { 3037 throw new Error("Can not create a 'Link' from a 'Prop' property. "); 3038 } 3039 createProp(subscribeOwner, propPropName) { 3040 return new SynchedPropertySimpleOneWaySubscribing(this, subscribeOwner, propPropName); 3041 } 3042} 3043/* 3044 * Copyright (c) 2021-2022 Huawei Device Co., Ltd. 3045 * Licensed under the Apache License, Version 2.0 (the "License"); 3046 * you may not use this file except in compliance with the License. 3047 * You may obtain a copy of the License at 3048 * 3049 * http://www.apache.org/licenses/LICENSE-2.0 3050 * 3051 * Unless required by applicable law or agreed to in writing, software 3052 * distributed under the License is distributed on an "AS IS" BASIS, 3053 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 3054 * See the License for the specific language governing permissions and 3055 * limitations under the License. 3056 */ 3057/** 3058 * SynchedPropertySimpleTwoWay 3059 * 3060 * all definitions in this file are framework internal 3061 */ 3062class SynchedPropertySimpleTwoWay extends ObservedPropertySimpleAbstract { 3063 constructor(source, owningView, owningViewPropNme) { 3064 super(owningView, owningViewPropNme); 3065 this.changeNotificationIsOngoing_ = false; 3066 this.source_ = source; 3067 this.source_.subscribeMe(this); 3068 } 3069 /* 3070 like a destructor, need to call this before deleting 3071 the property. 3072 */ 3073 aboutToBeDeleted() { 3074 if (this.source_) { 3075 this.source_.unlinkSuscriber(this.id__()); 3076 this.source_ = undefined; 3077 } 3078 super.aboutToBeDeleted(); 3079 } 3080 // this object is subscriber to SynchedPropertySimpleTwoWay 3081 // will call this cb function when property has changed 3082 // a set (newValue) is not done because get reads through for the source_ 3083 hasChanged(newValue) { 3084 if (!this.changeNotificationIsOngoing_) { 3085 3086 this.notifyHasChanged(newValue); 3087 } 3088 } 3089 // get 'read through` from the ObservedProperty 3090 get() { 3091 3092 if (!this.source_) { 3093 stateMgmtConsole.error(`SynchedPropertySimpleTwoWay[${this.id__()}IP, '${this.info() || "unknown"}'] source_ is undefined: get value is undefined.`); 3094 return undefined; 3095 } 3096 this.notifyPropertyRead(); 3097 return this.source_.get(); 3098 } 3099 // set 'writes through` to the ObservedProperty 3100 set(newValue) { 3101 if (!this.source_) { 3102 stateMgmtConsole.error(`SynchedPropertySimpleTwoWay[${this.id__()}IP, '${this.info() || "unknown"}'] source_ is undefined: set '${newValue}' ignoring.`); 3103 return; 3104 } 3105 if (this.source_.get() == newValue) { 3106 3107 return; 3108 } 3109 3110 // the source_ ObservedProeprty will call: this.hasChanged(newValue); 3111 // the purpose of the changeNotificationIsOngoing_ is to avoid 3112 // circular notifications @Link -> source @State -> other but alos same @Link 3113 this.changeNotificationIsOngoing_ = true; 3114 this.source_.set(newValue); 3115 this.notifyHasChanged(newValue); 3116 this.changeNotificationIsOngoing_ = false; 3117 } 3118 /** 3119 * These functions are meant for use in connection with the App Stoage and 3120 * business logic implementation. 3121 * the created Link and Prop will update when 'this' property value 3122 * changes. 3123 */ 3124 createLink(subscribeOwner, linkPropName) { 3125 return new SynchedPropertySimpleTwoWay(this, subscribeOwner, linkPropName); 3126 } 3127 createProp(subscribeOwner, propPropName) { 3128 return new SynchedPropertySimpleOneWaySubscribing(this, subscribeOwner, propPropName); 3129 } 3130} 3131/* 3132 * Copyright (c) 2021-2022 Huawei Device Co., Ltd. 3133 * Licensed under the Apache License, Version 2.0 (the "License"); 3134 * you may not use this file except in compliance with the License. 3135 * You may obtain a copy of the License at 3136 * 3137 * http://www.apache.org/licenses/LICENSE-2.0 3138 * 3139 * Unless required by applicable law or agreed to in writing, software 3140 * distributed under the License is distributed on an "AS IS" BASIS, 3141 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 3142 * See the License for the specific language governing permissions and 3143 * limitations under the License. 3144 */ 3145/** 3146 * SynchedPropertyNesedObject 3147 * 3148 * all definitions in this file are framework internal 3149 */ 3150class SynchedPropertyNesedObject extends ObservedPropertyObjectAbstract { 3151 /** 3152 * Construct a Property of a su component that links to a variable of parent view that holds an ObservedObject 3153 * example 3154 * this.b.$a with b of type PC and a of type C, or 3155 * this.$b[5] with this.b of type PC and array item b[5] of type C; 3156 * 3157 * @param subscribeMe 3158 * @param propName 3159 */ 3160 constructor(obsObject, owningChildView, propertyName) { 3161 super(owningChildView, propertyName); 3162 this.obsObject_ = obsObject; 3163 // register to the ObservedObject 3164 ObservedObject.addOwningProperty(this.obsObject_, this); 3165 } 3166 /* 3167 like a destructor, need to call this before deleting 3168 the property. 3169 */ 3170 aboutToBeDeleted() { 3171 // unregister from the ObservedObject 3172 ObservedObject.removeOwningProperty(this.obsObject_, this); 3173 super.aboutToBeDeleted(); 3174 } 3175 // this object is subscriber to ObservedObject 3176 // will call this cb function when property has changed 3177 hasChanged(newValue) { 3178 3179 this.notifyHasChanged(this.obsObject_); 3180 } 3181 // get 'read through` from the ObservedProperty 3182 get() { 3183 3184 this.notifyPropertyRead(); 3185 return this.obsObject_; 3186 } 3187 // set 'writes through` to the ObservedProperty 3188 set(newValue) { 3189 if (this.obsObject_ == newValue) { 3190 3191 return; 3192 } 3193 3194 // unsubscribe from the old value ObservedObject 3195 ObservedObject.removeOwningProperty(this.obsObject_, this); 3196 this.obsObject_ = newValue; 3197 // subscribe to the new value ObservedObject 3198 ObservedObject.addOwningProperty(this.obsObject_, this); 3199 // notify value change to subscribing View 3200 this.notifyHasChanged(this.obsObject_); 3201 } 3202 /** 3203 * These functions are meant for use in connection with the App Stoage and 3204 * business logic implementation. 3205 * the created Link and Prop will update when 'this' property value 3206 * changes. 3207 */ 3208 createLink(subscribeOwner, linkPropName) { 3209 throw new Error("Method not supported for property linking to a nested objects."); 3210 } 3211 createProp(subscribeOwner, linkPropName) { 3212 throw new Error("Creating a 'Prop' proerty is unsuppoeted for Object type prperty value."); 3213 } 3214} 3215/* 3216 * Copyright (c) 2021-2022 Huawei Device Co., Ltd. 3217 * Licensed under the Apache License, Version 2.0 (the "License"); 3218 * you may not use this file except in compliance with the License. 3219 * You may obtain a copy of the License at 3220 * 3221 * http://www.apache.org/licenses/LICENSE-2.0 3222 * 3223 * Unless required by applicable law or agreed to in writing, software 3224 * distributed under the License is distributed on an "AS IS" BASIS, 3225 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 3226 * See the License for the specific language governing permissions and 3227 * limitations under the License. 3228 */ 3229// Nativeview 3230// implemented in C++ for release 3231// and in utest/view_native_mock.ts for testing 3232class View extends NativeViewFullUpdate { 3233 /** 3234 * Create a View 3235 * 3236 * 1. option: top level View, specify 3237 * - compilerAssignedUniqueChildId must specify 3238 * - parent=undefined 3239 * - localStorage must provide if @LocalSTorageLink/Prop variables are used 3240 * in this View or descendant Views. 3241 * 3242 * 2. option: not a top level View 3243 * - compilerAssignedUniqueChildId must specify 3244 * - parent must specify 3245 * - localStorage do not specify, will inherit from parent View. 3246 * 3247 * @param compilerAssignedUniqueChildId Tw 3248 * @param parent 3249 * @param localStorage 3250 */ 3251 constructor(compilerAssignedUniqueChildId, parent, localStorage) { 3252 super(compilerAssignedUniqueChildId, parent); 3253 this.propsUsedForRender = new Set(); 3254 this.isRenderingInProgress = false; 3255 this.watchedProps = new Map(); 3256 // my LocalStorge instance, shared with ancestor Views. 3257 // create a default instance on demand if none is initialized 3258 this.localStoragebackStore_ = undefined; 3259 this.id_ = SubscriberManager.MakeId(); 3260 this.providedVars_ = parent ? new Map(parent.providedVars_) 3261 : new Map(); 3262 this.localStoragebackStore_ = undefined; 3263 if (parent) { 3264 // this View is not a top-level View 3265 3266 this.setCardId(parent.getCardId()); 3267 this.localStorage_ = parent.localStorage_; 3268 } 3269 else if (localStorage) { 3270 this.localStorage_ = localStorage; 3271 3272 } 3273 SubscriberManager.Add(this); 3274 3275 } 3276 get localStorage_() { 3277 if (!this.localStoragebackStore_) { 3278 3279 this.localStoragebackStore_ = new LocalStorage({ /* emty */}); 3280 } 3281 return this.localStoragebackStore_; 3282 } 3283 set localStorage_(instance) { 3284 if (!instance) { 3285 // setting to undefined not allowed 3286 return; 3287 } 3288 if (this.localStoragebackStore_) { 3289 stateMgmtConsole.error(`${this.constructor.name} is setting LocalStorage instance twice`); 3290 } 3291 this.localStoragebackStore_ = instance; 3292 } 3293 // globally unique id, this is different from compilerAssignedUniqueChildId! 3294 id__() { 3295 return this.id_; 3296 } 3297 // temporary function, do not use, it will be removed soon! 3298 // prupsoe is to allow eDSL transpiler to fix a bug that 3299 // relies on this method 3300 id() { 3301 return this.id__(); 3302 } 3303 propertyHasChanged(info) { 3304 if (info) { 3305 // need to sync container instanceId to switch instanceId in C++ side. 3306 this.syncInstanceId(); 3307 if (this.propsUsedForRender.has(info)) { 3308 3309 this.markNeedUpdate(); 3310 } 3311 else { 3312 3313 } 3314 let cb = this.watchedProps.get(info); 3315 if (cb) { 3316 3317 cb.call(this, info); 3318 } 3319 this.restoreInstanceId(); 3320 } // if info avail. 3321 } 3322 propertyRead(info) { 3323 3324 if (info && (info != "unknown") && this.isRenderingInProgress) { 3325 this.propsUsedForRender.add(info); 3326 } 3327 } 3328 // for test purposes 3329 propertiesNeededToRender() { 3330 return this.propsUsedForRender; 3331 } 3332 aboutToRender() { 3333 3334 // reset 3335 this.propsUsedForRender = new Set(); 3336 this.isRenderingInProgress = true; 3337 } 3338 aboutToContinueRender() { 3339 // do not reset 3340 this.isRenderingInProgress = true; 3341 } 3342 onRenderDone() { 3343 this.isRenderingInProgress = false; 3344 3345 } 3346 /** 3347 * Function to be called from the constructor of the sub component 3348 * to register a @Watch varibale 3349 * @param propStr name of the variable. Note from @Provide and @Consume this is 3350 * the variable name and not the alias! 3351 * @param callback application defined member function of sub-class 3352 */ 3353 declareWatch(propStr, callback) { 3354 this.watchedProps.set(propStr, callback); 3355 } 3356 /** 3357 * This View @Provide's a variable under given name 3358 * Call this function from the constructor of the sub class 3359 * @param providedPropName either the variable name or the alias defined as 3360 * decorator param 3361 * @param store the backing store object for this variable (not the get/set variable!) 3362 */ 3363 addProvidedVar(providedPropName, store) { 3364 if (this.providedVars_.has(providedPropName)) { 3365 throw new ReferenceError(`${this.constructor.name}: duplicate @Provide property with name ${providedPropName}. 3366 Property with this name is provided by one of the ancestor Views already.`); 3367 } 3368 this.providedVars_.set(providedPropName, store); 3369 } 3370 /** 3371 * Method for the sub-class to call from its constructor for resolving 3372 * a @Consume variable and initializing its backing store 3373 * with the yncedPropertyTwoWay<T> object created from the 3374 * @Provide variable's backing store. 3375 * @param providedPropName the name of the @Provide'd variable. 3376 * This is either the @Consume decortor parameter, or variable name. 3377 * @param consumeVarName the @Consume variable name (not the 3378 * @Consume decortor parameter) 3379 * @returns initiaizing value of the @Consume backing store 3380 */ 3381 initializeConsume(providedPropName, consumeVarName) { 3382 let providedVarStore = this.providedVars_.get(providedPropName); 3383 if (providedVarStore === undefined) { 3384 throw new ReferenceError(`${this.constructor.name}: missing @Provide property with name ${providedPropName}. 3385 Fail to resolve @Consume(${providedPropName}).`); 3386 } 3387 return providedVarStore.createLink(this, consumeVarName); 3388 } 3389} 3390/* 3391 * Copyright (c) 2022 Huawei Device Co., Ltd. 3392 * Licensed under the Apache License, Version 2.0 (the "License"); 3393 * you may not use this file except in compliance with the License. 3394 * You may obtain a copy of the License at 3395 * 3396 * http://www.apache.org/licenses/LICENSE-2.0 3397 * 3398 * Unless required by applicable law or agreed to in writing, software 3399 * distributed under the License is distributed on an "AS IS" BASIS, 3400 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 3401 * See the License for the specific language governing permissions and 3402 * limitations under the License. 3403 */ 3404/* 3405 * Copyright (c) 2023 Huawei Device Co., Ltd. 3406 * Licensed under the Apache License, Version 2.0 (the "License"); 3407 * you may not use this file except in compliance with the License. 3408 * You may obtain a copy of the License at 3409 * 3410 * http://www.apache.org/licenses/LICENSE-2.0 3411 * 3412 * Unless required by applicable law or agreed to in writing, software 3413 * distributed under the License is distributed on an "AS IS" BASIS, 3414 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 3415 * See the License for the specific language governing permissions and 3416 * limitations under the License. 3417 */ 3418// @Track class property decorator 3419// indicates to framework to track individual object property value changes 3420function Track(target, property) { 3421 var _a; 3422 Reflect.set(target, `${TrackedObject.___TRACKED_PREFIX}${property}`, true); 3423 Reflect.set(target, TrackedObject.___IS_TRACKED_OPTIMISED, true); 3424 3425} 3426class TrackedObject { 3427 static isCompatibilityMode(obj) { 3428 return !obj || (typeof obj !== "object") || !Reflect.has(obj, TrackedObject.___IS_TRACKED_OPTIMISED); 3429 } 3430 static needsPropertyReadCb(obj) { 3431 return obj && (typeof obj === 'object') && Reflect.has(obj, TrackedObject.___IS_TRACKED_OPTIMISED); 3432 } 3433 /** 3434 * @Track new object assignment optimization 3435 * can apply if old and new value are object, instance of same class, do not use compat mode. 3436 * in this case function returns true and calls supplied notifyTrackedPropertyChanged cb function 3437 * for each @Tracked'ed property whose value actually changed. 3438 * if optimisation can not be applied calls notifyPropertyChanged and returns false 3439 */ 3440 static notifyObjectValueAssignment(obj1, obj2, notifyPropertyChanged, // notify as assignment (none-optimised) 3441 notifyTrackedPropertyChange) { 3442 if (!obj1 || !obj2 || (typeof obj1 !== 'object') || (typeof obj2 !== 'object') || 3443 (obj1.constructor !== obj2.constructor) || 3444 TrackedObject.isCompatibilityMode(obj1)) { 3445 3446 notifyPropertyChanged(); 3447 return false; 3448 } 3449 3450 const obj1Raw = ObservedObject.GetRawObject(obj1); 3451 const obj2Raw = ObservedObject.GetRawObject(obj2); 3452 let shouldFakePropPropertyBeNotified = false; 3453 Object.keys(obj2Raw) 3454 .forEach(propName => { 3455 // Collect only @Track'ed changed properties 3456 if (Reflect.has(obj1Raw, `${TrackedObject.___TRACKED_PREFIX}${propName}`) && 3457 (Reflect.get(obj1Raw, propName) !== Reflect.get(obj2Raw, propName))) { 3458 3459 notifyTrackedPropertyChange(propName); 3460 shouldFakePropPropertyBeNotified = true; 3461 } 3462 else { 3463 3464 } 3465 }); 3466 // notify this non-existing object property has changed only if some of the tracked properties changed. 3467 // SynchedPropertyOneWay.reset() report a 'read' on this property, thereby creating a dependency 3468 // reporting the property as changed causes @Prop sync from source 3469 if (shouldFakePropPropertyBeNotified) { 3470 3471 notifyTrackedPropertyChange(TrackedObject.___TRACKED_OPTI_ASSIGNMENT_FAKE_PROP_PROPERTY); 3472 } 3473 // always notify this non-existing object property has changed for SynchedPropertyNestedObject as 3474 // the object has changed in assigment. 3475 // SynchedPropertyNestedObject.set() reports a 'read' on this property, thereby creating a dependency 3476 // reporting the property as changed causes @ObjectLink sync from source 3477 3478 notifyTrackedPropertyChange(TrackedObject.___TRACKED_OPTI_ASSIGNMENT_FAKE_OBJLINK_PROPERTY); 3479 return true; 3480 } 3481} 3482TrackedObject.___IS_TRACKED_OPTIMISED = `___IS_TRACKED_OPTIMISED`; 3483TrackedObject.___TRACKED_OPTI_ASSIGNMENT_FAKE_PROP_PROPERTY = `___OPTI_TRACKED_ASSIGNMENT_FAKE_PROP_PROPERTY`; 3484TrackedObject.___TRACKED_OPTI_ASSIGNMENT_FAKE_OBJLINK_PROPERTY = `___OPTI_TRACKED_ASSIGNMENT_FAKE_OBJLINK_PROPERTY`; 3485TrackedObject.___TRACKED_PREFIX = `___TRACKED_`; 3486TrackedObject.___TRACKED_PREFIX_LEN = TrackedObject.___TRACKED_PREFIX.length; 3487/* 3488 * Copyright (c) 2022 Huawei Device Co., Ltd. 3489 * Licensed under the Apache License, Version 2.0 (the "License"); 3490 * you may not use this file except in compliance with the License. 3491 * You may obtain a copy of the License at 3492 * 3493 * http://www.apache.org/licenses/LICENSE-2.0 3494 * 3495 * Unless required by applicable law or agreed to in writing, software 3496 * distributed under the License is distributed on an "AS IS" BASIS, 3497 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 3498 * See the License for the specific language governing permissions and 3499 * limitations under the License. 3500 */ 3501var _a; 3502/** 3503 * ObservedPropertyAbstractPU aka ObservedPropertyAbstract for partial update 3504 * 3505 * all definitions in this file are framework internal 3506 */ 3507class ObservedPropertyAbstractPU extends ObservedPropertyAbstract { 3508 constructor(subscriber, viewName) { 3509 super(subscriber, viewName); 3510 this.owningView_ = undefined; 3511 // when owning ViewPU is inActive, delay notifying changes 3512 this.delayedNotification_ = ObservedPropertyAbstractPU.DelayedNotifyChangesEnum.do_not_delay; 3513 // install when current value is ObservedObject and the value type is not using compatibility mode 3514 // note value may change for union type variables when switching an object from one class to another. 3515 this.shouldInstallTrackedObjectReadCb = false; 3516 this.dependentElmtIdsByProperty_ = new PropertyDependencies(); 3517 Object.defineProperty(this, 'owningView_', { writable: true, enumerable: false }); 3518 Object.defineProperty(this, 'subscriberRefs_', { writable: true, enumerable: false, value: new Set() }); 3519 if (subscriber) { 3520 if (subscriber instanceof ViewPU) { 3521 this.owningView_ = subscriber; 3522 } 3523 else { 3524 this.subscriberRefs_.add(subscriber); 3525 } 3526 } 3527 } 3528 aboutToBeDeleted() { 3529 super.aboutToBeDeleted(); 3530 this.subscriberRefs_.clear(); 3531 this.owningView_ = undefined; 3532 } 3533 // dump basic info about this variable to a string, non-recursive, no subscriber info 3534 debugInfo() { 3535 const propSource = this.isPropSourceObservedPropertyFakeName(); 3536 return (propSource) 3537 ? `internal source (ObservedPropertyPU) of @Prop ${propSource} [${this.id__()}]` 3538 : `${this.debugInfoDecorator()} '${this.info()}'[${this.id__()}] <${this.debugInfoOwningView()}>`; 3539 } 3540 debugInfoOwningView() { 3541 return `${this.owningView_ ? this.owningView_.debugInfo__() : "owning @Component UNKNOWN"}`; 3542 } 3543 // dump info about owning view and subscribers (PU ones only) 3544 // use function only for debug output and DFX. 3545 debugInfoSubscribers() { 3546 return (this.owningView_) 3547 ? `|--Owned by ${this.debugInfoOwningView()} ` 3548 : `|--Owned by: owning view not known`; 3549 } 3550 debugInfoSyncPeers() { 3551 if (!this.subscriberRefs_.size) { 3552 return "|--Sync peers: none"; 3553 } 3554 let result = `|--Sync peers: {`; 3555 let sepa = ""; 3556 this.subscriberRefs_.forEach((subscriber) => { 3557 if ("debugInfo" in subscriber) { 3558 result += `\n ${sepa}${subscriber.debugInfo()}`; 3559 sepa = ", "; 3560 } 3561 }); 3562 result += "\n }"; 3563 return result; 3564 } 3565 debugInfoDependentElmtIds() { 3566 return this.dependentElmtIdsByProperty_.dumpInfoDependencies(); 3567 } 3568 /* for @Prop value from source we need to generate a @State 3569 that observes when this value changes. This ObservedPropertyPU 3570 sits inside SynchedPropertyOneWayPU. 3571 below methods invent a fake variable name for it 3572 */ 3573 getPropSourceObservedPropertyFakeName() { 3574 return `${this.info()}_prop_fake_state_source___`; 3575 } 3576 isPropSourceObservedPropertyFakeName() { 3577 return this.info().endsWith("_prop_fake_state_source___") 3578 ? this.info().substring(0, this.info().length - "_prop_fake_state_source___".length) 3579 : false; 3580 } 3581 /* 3582 Virtualized version of the subscription mechanism - add subscriber 3583 Overrides implementation in ObservedPropertyAbstract<T> 3584 */ 3585 addSubscriber(subscriber) { 3586 if (subscriber) { 3587 // ObservedPropertyAbstract will also add subscriber to 3588 // SubscriberManager map and to its own Set of subscribers as well 3589 // Something to improve in the future for PU path. 3590 // subscribeMe should accept IPropertySubscriber interface 3591 super.subscribeMe(subscriber); 3592 this.subscriberRefs_.add(subscriber); 3593 } 3594 } 3595 /* 3596 Virtualized version of the subscription mechanism - remove subscriber 3597 Overrides implementation in ObservedPropertyAbstract<T> 3598 */ 3599 removeSubscriber(subscriber, id) { 3600 if (subscriber) { 3601 this.subscriberRefs_.delete(subscriber); 3602 if (!id) { 3603 id = subscriber.id__(); 3604 } 3605 } 3606 super.unlinkSuscriber(id); 3607 } 3608 /** 3609 * put the property to delayed notification mode 3610 * feature is only used for @StorageLink/Prop, @LocalStorageLink/Prop 3611 */ 3612 enableDelayedNotification() { 3613 if (this.delayedNotification_ != ObservedPropertyAbstractPU.DelayedNotifyChangesEnum.delay_notification_pending) { 3614 3615 this.delayedNotification_ = ObservedPropertyAbstractPU.DelayedNotifyChangesEnum.delay_none_pending; 3616 } 3617 } 3618 /* 3619 when moving from inActive to active state the owning ViewPU calls this function 3620 This solution is faster than ViewPU polling each variable to send back a viewPropertyHasChanged event 3621 with the elmtIds 3622 3623 returns undefined if variable has _not_ changed 3624 returns dependentElementIds_ Set if changed. This Set is empty if variable is not used to construct the UI 3625 */ 3626 moveElmtIdsForDelayedUpdate() { 3627 const result = (this.delayedNotification_ === ObservedPropertyAbstractPU.DelayedNotifyChangesEnum.delay_notification_pending) ? 3628 this.dependentElmtIdsByProperty_.getAllPropertyDependencies() : 3629 undefined; 3630 3631 this.delayedNotification_ = ObservedPropertyAbstractPU.DelayedNotifyChangesEnum.do_not_delay; 3632 return result; 3633 } 3634 notifyPropertyRead() { 3635 stateMgmtConsole.error(`${this.debugInfo()}: notifyPropertyRead, DO NOT USE with PU. Use notifyReadCb mechanism.`); 3636 } 3637 // notify owning ViewPU and peers of a variable assignment 3638 // also property/item changes to ObservedObjects of class object type, which use compat mode 3639 // Date and Array are notified as if there had been an assignment. 3640 notifyPropertyHasChangedPU() { 3641 3642 3643 if (this.owningView_) { 3644 if (this.delayedNotification_ == ObservedPropertyAbstractPU.DelayedNotifyChangesEnum.do_not_delay) { 3645 // send viewPropertyHasChanged right away 3646 this.owningView_.viewPropertyHasChanged(this.info_, this.dependentElmtIdsByProperty_.getAllPropertyDependencies()); 3647 } 3648 else { 3649 // mark this @StorageLink/Prop or @LocalStorageLink/Prop variable has having changed and notification of viewPropertyHasChanged delivery pending 3650 this.delayedNotification_ = ObservedPropertyAbstractPU.DelayedNotifyChangesEnum.delay_notification_pending; 3651 } 3652 } 3653 this.subscriberRefs_.forEach((subscriber) => { 3654 if (subscriber) { 3655 if ('syncPeerHasChanged' in subscriber) { 3656 subscriber.syncPeerHasChanged(this); 3657 } 3658 else { 3659 stateMgmtConsole.warn(`${this.debugInfo()}: notifyPropertyHasChangedPU: unknown subscriber ID 'subscribedId' error!`); 3660 } 3661 } 3662 }); 3663 3664 } 3665 // notify owning ViewPU and peers of a ObservedObject @Track property's assignment 3666 notifyTrackedObjectPropertyHasChanged(changedPropertyName) { 3667 3668 3669 if (this.owningView_) { 3670 if (this.delayedNotification_ == ObservedPropertyAbstractPU.DelayedNotifyChangesEnum.do_not_delay) { 3671 // send viewPropertyHasChanged right away 3672 this.owningView_.viewPropertyHasChanged(this.info_, this.dependentElmtIdsByProperty_.getTrackedObjectPropertyDependencies(changedPropertyName, "notifyTrackedObjectPropertyHasChanged")); 3673 } 3674 else { 3675 // mark this @StorageLink/Prop or @LocalStorageLink/Prop variable has having changed and notification of viewPropertyHasChanged delivery pending 3676 this.delayedNotification_ = ObservedPropertyAbstractPU.DelayedNotifyChangesEnum.delay_notification_pending; 3677 } 3678 } 3679 this.subscriberRefs_.forEach((subscriber) => { 3680 if (subscriber) { 3681 if ('syncPeerTrackedPropertyHasChanged' in subscriber) { 3682 subscriber.syncPeerTrackedPropertyHasChanged(this, changedPropertyName); 3683 } 3684 else { 3685 stateMgmtConsole.warn(`${this.debugInfo()}: notifyTrackedObjectPropertyHasChanged: unknown subscriber ID 'subscribedId' error!`); 3686 } 3687 } 3688 }); 3689 3690 } 3691 markDependentElementsDirty(view) { 3692 // TODO ace-ets2bundle, framework, complicated apps need to update together 3693 // this function will be removed after a short transition period. 3694 stateMgmtConsole.warn(`${this.debugInfo()}: markDependentElementsDirty no longer supported. App will work ok, but 3695 please update your ace-ets2bundle and recompile your application!`); 3696 } 3697 numberOfSubscrbers() { 3698 return this.subscriberRefs_.size + (this.owningView_ ? 1 : 0); 3699 } 3700 /* 3701 type checking for any supported type, as required for union type support 3702 see 1st parameter for explanation what is allowed 3703 3704 FIXME this expects the Map, Set patch to go in 3705 */ 3706 checkIsSupportedValue(value) { 3707 return this.checkNewValue(`undefined, null, number, boolean, string, or Object but not function`, value, () => ((typeof value == "object" && typeof value != "function") 3708 || typeof value == "number" || typeof value == "string" || typeof value == "boolean") 3709 || (value == undefined || value == null)); 3710 } 3711 /* 3712 type checking for allowed Object type value 3713 see 1st parameter for explanation what is allowed 3714 3715 FIXME this expects the Map, Set patch to go in 3716 */ 3717 checkIsObject(value) { 3718 return this.checkNewValue(`undefined, null, Object including Array and instance of SubscribableAbstract and excluding function, Set, and Map`, value, () => (value == undefined || value == null || (typeof value == "object"))); 3719 } 3720 /* 3721 type checking for allowed simple types value 3722 see 1st parameter for explanation what is allowed 3723 */ 3724 checkIsSimple(value) { 3725 return this.checkNewValue(`undefined, number, boolean, string`, value, () => (value == undefined || typeof value == "number" || typeof value == "string" || typeof value == "boolean")); 3726 } 3727 checkNewValue(isAllowedComment, newValue, validator) { 3728 if (validator(newValue)) { 3729 return true; 3730 } 3731 // report error 3732 // current implementation throws an Exception 3733 errorReport.varValueCheckFailed({ 3734 customComponent: this.debugInfoOwningView(), 3735 variableDeco: this.debugInfoDecorator(), 3736 variableName: this.info(), 3737 expectedType: isAllowedComment, 3738 value: newValue 3739 }); 3740 // never gets here if errorReport.varValueCheckFailed throws an exception 3741 // but should not depend on its implementation 3742 return false; 3743 } 3744 /** 3745 * factory function for concrete 'object' or 'simple' ObservedProperty object 3746 * depending if value is Class object 3747 * or simple type (boolean | number | string) 3748 * @param value 3749 * @param owningView 3750 * @param thisPropertyName 3751 * @returns either 3752 */ 3753 static CreateObservedObject(value, owningView, thisPropertyName) { 3754 return (typeof value === "object") ? 3755 new ObservedPropertyObject(value, owningView, thisPropertyName) 3756 : new ObservedPropertySimple(value, owningView, thisPropertyName); 3757 } 3758 /** 3759 * If owning viewPU is currently rendering or re-rendering a UINode, return its elmtId 3760 * return -1 otherwise 3761 * ViewPU caches the info, it does not request the info from C++ side (by calling 3762 * ViewStackProcessor.GetElmtIdToAccountFor(); as done in earlier implementation 3763 */ 3764 getRenderingElmtId() { 3765 return (this.owningView_) ? this.owningView_.getCurrentlyRenderedElmtId() : -1; 3766 } 3767 /** 3768 * during 'get' access recording take note of the created component and its elmtId 3769 * and add this component to the list of components who are dependent on this property 3770 */ 3771 recordPropertyDependentUpdate() { 3772 const elmtId = this.getRenderingElmtId(); 3773 if (elmtId < 0) { 3774 // not access recording 3775 return; 3776 } 3777 3778 this.dependentElmtIdsByProperty_.addPropertyDependency(elmtId); 3779 } 3780 /** record dependency ObservedObject + propertyName -> elmtId 3781 * caller ensures renderingElmtId >= 0 3782 */ 3783 recordTrackObjectPropertyDependencyForElmtId(renderingElmtId, readTrackedPropertyName) { 3784 3785 this.dependentElmtIdsByProperty_.addTrackedObjectPropertyDependency(readTrackedPropertyName, renderingElmtId); 3786 } 3787 purgeDependencyOnElmtId(rmElmtId) { 3788 var _a; 3789 (_a = this.dependentElmtIdsByProperty_) === null || _a === void 0 ? void 0 : _a.purgeDependenciesForElmtId(rmElmtId); 3790 } 3791 SetPropertyUnchanged() { 3792 // function to be removed 3793 // keep it here until transpiler is updated. 3794 } 3795 // unified Appstorage, what classes to use, and the API 3796 createLink(subscribeOwner, linkPropName) { 3797 throw new Error(`${this.debugInfo()}: createLink: Can not create a AppStorage 'Link' from this property.`); 3798 } 3799 createProp(subscribeOwner, linkPropName) { 3800 throw new Error(`${this.debugInfo()}: createProp: Can not create a AppStorage 'Prop' from a @State property. `); 3801 } 3802 /* 3803 Below empty functions required to keep as long as this class derives from FU version 3804 ObservedPropertyAbstract. Need to overwrite these functions to do nothing for PU 3805 */ 3806 notifyHasChanged(_) { 3807 stateMgmtConsole.error(`${this.debugInfo()}: notifyHasChanged, DO NOT USE with PU. Use syncPeerHasChanged() \ 3808 or onTrackedObjectProperty(CompatMode)HasChangedPU()`); 3809 } 3810 /** 3811 * event emitted by wrapped ObservedObject, when one of its property values changes 3812 * for class objects when in compatibility mode 3813 * for Array, Date instances always 3814 * @param souceObject 3815 * @param changedPropertyName 3816 */ 3817 onTrackedObjectPropertyHasChangedPU(sourceObject, changedPropertyName) { 3818 3819 this.notifyTrackedObjectPropertyHasChanged(changedPropertyName); 3820 } 3821 /** 3822 * event emitted by wrapped ObservedObject, when one of its property values changes 3823 * for class objects when in compatibility mode 3824 * for Array, Date instances always 3825 * @param souceObject 3826 * @param changedPropertyName 3827 */ 3828 onTrackedObjectPropertyCompatModeHasChangedPU(sourceObject, changedPropertyName) { 3829 3830 this.notifyPropertyHasChangedPU(); 3831 } 3832 hasChanged(_) { 3833 // unused for PU 3834 // need to overwrite impl of base class with empty function. 3835 } 3836 propertyHasChanged(_) { 3837 // unused for PU 3838 // need to overwrite impl of base class with empty function. 3839 } 3840 propertyRead(_) { 3841 // unused for PU 3842 // need to overwrite impl of base class with empty function. 3843 } 3844} 3845ObservedPropertyAbstractPU.DelayedNotifyChangesEnum = (_a = class { 3846 }, 3847 _a.do_not_delay = 0, 3848 _a.delay_none_pending = 1, 3849 _a.delay_notification_pending = 2, 3850 _a); 3851class PropertyDependencies { 3852 constructor() { 3853 // dependencies for property -> elmtId 3854 // variable read during render adds elmtId 3855 // variable assignment causes elmtId to need re-render. 3856 // UINode with elmtId deletion needs elmtId to be removed from all records, see purgeDependenciesForElmtId 3857 this.propertyDependencies_ = new Set(); 3858 // dependencies on individual object properties 3859 this.trackedObjectPropertyDependencies_ = new Map(); 3860 } 3861 getAllPropertyDependencies() { 3862 3863 return this.propertyDependencies_; 3864 } 3865 addPropertyDependency(elmtId) { 3866 this.propertyDependencies_.add(elmtId); 3867 3868 } 3869 purgeDependenciesForElmtId(rmElmtId) { 3870 3871 this.propertyDependencies_.delete(rmElmtId); 3872 3873 this.trackedObjectPropertyDependencies_.forEach((propertyElmtId, propertyName) => { 3874 propertyElmtId.delete(rmElmtId); 3875 3876 }); 3877 } 3878 addTrackedObjectPropertyDependency(readProperty, elmtId) { 3879 let dependentElmtIds = this.trackedObjectPropertyDependencies_.get(readProperty); 3880 if (!dependentElmtIds) { 3881 dependentElmtIds = new Set(); 3882 this.trackedObjectPropertyDependencies_.set(readProperty, dependentElmtIds); 3883 } 3884 dependentElmtIds.add(elmtId); 3885 3886 } 3887 getTrackedObjectPropertyDependencies(changedObjectProperty, debugInfo) { 3888 const dependentElmtIds = this.trackedObjectPropertyDependencies_.get(changedObjectProperty) || new Set(); 3889 3890 return dependentElmtIds; 3891 } 3892 dumpInfoDependencies() { 3893 let result = `dependencies: variable assignment (or object prop change in compat mode) affects elmtIds: ${JSON.stringify(Array.from(this.propertyDependencies_))} \n`; 3894 this.trackedObjectPropertyDependencies_.forEach((propertyElmtId, propertyName) => { 3895 result += ` property '@Track ${propertyName}' change affects elmtIds: ${JSON.stringify(Array.from(propertyElmtId))} \n`; 3896 }); 3897 return result; 3898 } 3899} 3900/* 3901 * Copyright (c) 2022-2023 Huawei Device Co., Ltd. 3902 * Licensed under the Apache License, Version 2.0 (the "License"); 3903 * you may not use this file except in compliance with the License. 3904 * You may obtain a copy of the License at 3905 * 3906 * http://www.apache.org/licenses/LICENSE-2.0 3907 * 3908 * Unless required by applicable law or agreed to in writing, software 3909 * distributed under the License is distributed on an "AS IS" BASIS, 3910 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 3911 * See the License for the specific language governing permissions and 3912 * limitations under the License. 3913 */ 3914/** 3915 * ObservedPropertyObjectPU 3916 * implementation of @State and @Provide decorated variables of type class object 3917 * 3918 * all definitions in this file are framework internal 3919 * 3920 * class that holds an actual property value of type T 3921 * uses its base class to manage subscribers to this 3922 * property. 3923*/ 3924class ObservedPropertyPU extends ObservedPropertyAbstractPU { 3925 constructor(localInitValue, owningView, propertyName) { 3926 super(owningView, propertyName); 3927 this.setValueInternal(localInitValue); 3928 } 3929 aboutToBeDeleted(unsubscribeMe) { 3930 this.unsubscribeWrappedObject(); 3931 this.removeSubscriber(unsubscribeMe); 3932 super.aboutToBeDeleted(); 3933 } 3934 debugInfoDecorator() { 3935 return `@State/@Provide (class ObservedPropertyPU)`; 3936 } 3937 /** 3938 * Called by a SynchedPropertyObjectTwoWayPU (@Link, @Consume) that uses this as sync peer when it has changed 3939 * @param eventSource 3940 */ 3941 syncPeerHasChanged(eventSource) { 3942 3943 this.notifyPropertyHasChangedPU(); 3944 } 3945 syncPeerTrackedPropertyHasChanged(eventSource, changedTrackedObjectPropertyName) { 3946 3947 this.notifyTrackedObjectPropertyHasChanged(changedTrackedObjectPropertyName); 3948 } 3949 /** 3950 * Wrapped ObservedObjectPU has changed 3951 * @param souceObject 3952 * @param changedPropertyName 3953 */ 3954 objectPropertyHasChangedPU(souceObject, changedPropertyName) { 3955 3956 this.notifyPropertyHasChangedPU(); 3957 } 3958 unsubscribeWrappedObject() { 3959 if (this.wrappedValue_) { 3960 if (this.wrappedValue_ instanceof SubscribableAbstract) { 3961 this.wrappedValue_.removeOwningProperty(this); 3962 } 3963 else { 3964 ObservedObject.removeOwningProperty(this.wrappedValue_, this); 3965 // make sure the ObservedObject no longer has a read callback function 3966 // assigned to it 3967 ObservedObject.unregisterPropertyReadCb(this.wrappedValue_); 3968 } 3969 } 3970 } 3971 /* 3972 actually update this.wrappedValue_ 3973 called needs to do value change check 3974 and also notify with this.aboutToChange(); 3975 */ 3976 setValueInternal(newValue) { 3977 3978 if (newValue === this.wrappedValue_) { 3979 3980 3981 return false; 3982 } 3983 if (!this.checkIsSupportedValue(newValue)) { 3984 3985 return false; 3986 } 3987 this.unsubscribeWrappedObject(); 3988 if (!newValue || typeof newValue !== 'object') { 3989 // undefined, null, simple type: 3990 // nothing to subscribe to in case of new value undefined || null || simple type 3991 this.wrappedValue_ = newValue; 3992 } 3993 else if (newValue instanceof SubscribableAbstract) { 3994 3995 this.wrappedValue_ = newValue; 3996 this.wrappedValue_.addOwningProperty(this); 3997 } 3998 else if (ObservedObject.IsObservedObject(newValue)) { 3999 4000 ObservedObject.addOwningProperty(newValue, this); 4001 this.shouldInstallTrackedObjectReadCb = TrackedObject.needsPropertyReadCb(newValue); 4002 this.wrappedValue_ = newValue; 4003 } 4004 else { 4005 4006 this.wrappedValue_ = ObservedObject.createNew(newValue, this); 4007 this.shouldInstallTrackedObjectReadCb = TrackedObject.needsPropertyReadCb(this.wrappedValue_); 4008 } 4009 4010 return true; 4011 } 4012 get() { 4013 4014 4015 this.recordPropertyDependentUpdate(); 4016 if (this.shouldInstallTrackedObjectReadCb) { 4017 4018 ObservedObject.registerPropertyReadCb(this.wrappedValue_, this.onOptimisedObjectPropertyRead.bind(this)); 4019 } 4020 else { 4021 4022 } 4023 4024 return this.wrappedValue_; 4025 } 4026 getUnmonitored() { 4027 4028 // unmonitored get access , no call to notifyPropertyRead ! 4029 return this.wrappedValue_; 4030 } 4031 set(newValue) { 4032 if (this.wrappedValue_ === newValue) { 4033 4034 return; 4035 } 4036 4037 const oldValue = this.wrappedValue_; 4038 if (this.setValueInternal(newValue)) { 4039 TrackedObject.notifyObjectValueAssignment(/* old value */ oldValue, /* new value */ this.wrappedValue_, this.notifyPropertyHasChangedPU.bind(this), this.notifyTrackedObjectPropertyHasChanged.bind(this)); 4040 } 4041 } 4042 onOptimisedObjectPropertyRead(readObservedObject, readPropertyName, isTracked) { 4043 4044 const renderingElmtId = this.getRenderingElmtId(); 4045 if (renderingElmtId >= 0) { 4046 if (!isTracked) { 4047 stateMgmtConsole.applicationError(`${this.debugInfo()}: onOptimisedObjectPropertyRead read NOT TRACKED property '${readPropertyName}' during rendering!`); 4048 throw new Error(`Illegal usage of not @Track'ed property '${readPropertyName}' on UI!`); 4049 } 4050 else { 4051 4052 // only record dependency when 4053 // 1 - currently rendering or re-rendering 4054 // TODO room for further optimization: if not an expression in updateFunc, only first time render needs to record 4055 // because there can be change to depended variables unless one of the bindings is a JS expression 4056 // 2 - the changed ObservedObject is the wrapped object. The situation where it can be different is after a value assignment. 4057 if (this.getUnmonitored() === readObservedObject) { 4058 this.recordTrackObjectPropertyDependencyForElmtId(renderingElmtId, readPropertyName); 4059 } 4060 } 4061 } 4062 4063 } 4064} 4065// class definitions for backward compatibility 4066class ObservedPropertyObjectPU extends ObservedPropertyPU { 4067} 4068class ObservedPropertySimplePU extends ObservedPropertyPU { 4069} 4070/* 4071 * Copyright (c) 2022 Huawei Device Co., Ltd. 4072 * Licensed under the Apache License, Version 2.0 (the "License"); 4073 * you may not use this file except in compliance with the License. 4074 * You may obtain a copy of the License at 4075 * 4076 * http://www.apache.org/licenses/LICENSE-2.0 4077 * 4078 * Unless required by applicable law or agreed to in writing, software 4079 * distributed under the License is distributed on an "AS IS" BASIS, 4080 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 4081 * See the License for the specific language governing permissions and 4082 * limitations under the License. 4083 */ 4084/** 4085 * SynchedPropertyObjectOneWayPU 4086 * implementation of @Prop decorated variables of type class object 4087 * 4088 * all definitions in this file are framework internal 4089 * 4090 */ 4091/** 4092 * Initialisation scenarios: 4093 * ------------------------- 4094 * 4095 * 1 - no local initialization, source provided (its ObservedObject value) 4096 * wrap the ObservedObject into an ObservedPropertyObjectPU 4097 * deep copy the ObservedObject into localCopyObservedObject_ 4098 * 4099 * 2 - local initialization, no source provided 4100 * app transpiled code calls set 4101 * leave source_ undefined 4102 * no deep copy needed, but provided local init might need wrapping inside an ObservedObject to set to 4103 * localCopyObservedObject_ 4104 * 4105 * 3 local initialization, source provided (its ObservedObject value) 4106 * current app transpiled code is not optional 4107 * sets source in constructor, as in case 1 4108 * calls set() to set the source value, but this will not deepcopy 4109 * 4110 * Update scenarios: 4111 * ----------------- 4112 * 4113 * 1- assignment of a new Object value: this.aProp = new ClassA() 4114 * rhs can be ObservedObject because of @Observed decoration or now 4115 * notifyPropertyHasChangedPU 4116 * 4117 * 2- local ObservedObject member property change 4118 * objectPropertyHasChangedPU called, eventSource is the ObservedObject stored in localCopyObservedObject_ 4119 * no need to copy, notifyPropertyHasChangedPU 4120 * 4121 * 3- Rerender of the custom component triggered from the parent 4122 * reset() is called (code generated by the transpiler), set the value of source_ , if that causes a change will call syncPeerHasChanged 4123 * syncPeerHasChanged need to deep copy the ObservedObject from source to localCopyObservedObject_ 4124 * notifyPropertyHasChangedPU 4125 * 4126 * 4- source_ ObservedObject member property change 4127 * objectPropertyHasChangedPU called, eventSource is the ObservedObject stored source_.getUnmonitored 4128 * notifyPropertyHasChangedPU 4129 */ 4130class SynchedPropertyOneWayPU extends ObservedPropertyAbstractPU { 4131 constructor(source, owningChildView, thisPropertyName) { 4132 super(owningChildView, thisPropertyName); 4133 if (source && (typeof (source) === "object") && ("subscribeMe" in source)) { 4134 // code path for @(Local)StorageProp, the source is a ObservedPropertyObject<C> in a LocalStorage) 4135 this.source_ = source; 4136 this.sourceIsOwnObject = false; 4137 // subscribe to receive value change updates from LocalStorage source property 4138 this.source_.addSubscriber(this); 4139 } 4140 else { 4141 const sourceValue = source; 4142 if (this.checkIsSupportedValue(sourceValue)) { 4143 // code path for 4144 // 1- source is of same type C in parent, source is its value, not the backing store ObservedPropertyObject 4145 // 2- nested Object/Array inside observed another object/array in parent, source is its value 4146 if (typeof sourceValue == "object" && !((sourceValue instanceof SubscribableAbstract) || ObservedObject.IsObservedObject(sourceValue))) { 4147 stateMgmtConsole.applicationError(`${this.debugInfo()}: Provided source object's class is not instance of SubscribableAbstract, 4148 it also lacks @Observed class decorator. Object property changes will not be observed. Application error!`); 4149 } 4150 4151 this.createSourceDependency(sourceValue); 4152 this.source_ = new ObservedPropertyObjectPU(sourceValue, this, this.getPropSourceObservedPropertyFakeName()); 4153 this.sourceIsOwnObject = true; 4154 } 4155 } 4156 if (this.source_ != undefined) { 4157 this.resetLocalValue(this.source_.get(), /* needCopyObject */ true); 4158 } 4159 4160 } 4161 /* 4162 like a destructor, need to call this before deleting 4163 the property. 4164 */ 4165 aboutToBeDeleted() { 4166 if (this.source_) { 4167 this.source_.removeSubscriber(this); 4168 if (this.sourceIsOwnObject == true && this.source_.numberOfSubscrbers() == 0) { 4169 4170 this.source_.aboutToBeDeleted(); 4171 } 4172 this.source_ = undefined; 4173 } 4174 super.aboutToBeDeleted(); 4175 } 4176 debugInfoDecorator() { 4177 return `@Prop (class SynchedPropertyOneWayPU)`; 4178 } 4179 // sync peer can be 4180 // 1. the embedded ObservedPropertyPU, followed by a reset when the owning ViewPU received a local update in parent 4181 // 2. a @Link or @Consume that uses this @Prop as a source. FIXME is this possible? - see the if (eventSource && this.source_ == eventSource) { 4182 syncPeerHasChanged(eventSource) { 4183 4184 if (this.source_ == undefined) { 4185 stateMgmtConsole.error(`${this.debugInfo()}: syncPeerHasChanged from peer ${eventSource && eventSource.debugInfo && eventSource.debugInfo()}. source_ undefined. Internal error.`); 4186 4187 return; 4188 } 4189 if (eventSource && this.source_ == eventSource) { 4190 // defensive programming: should always be the case! 4191 const newValue = this.source_.getUnmonitored(); 4192 if (this.checkIsSupportedValue(newValue)) { 4193 4194 if (this.resetLocalValue(newValue, /* needCopyObject */ true)) { 4195 this.notifyPropertyHasChangedPU(); 4196 } 4197 } 4198 } 4199 else { 4200 stateMgmtConsole.warn(`${this.debugInfo()}: syncPeerHasChanged: from peer '${eventSource === null || eventSource === void 0 ? void 0 : eventSource.debugInfo()}', Unexpected situation. syncPeerHasChanged from different sender than source_. Ignoring event.`); 4201 } 4202 4203 } 4204 syncPeerTrackedPropertyHasChanged(eventSource, changedPropertyName) { 4205 4206 if (this.source_ == undefined) { 4207 stateMgmtConsole.error(`${this.debugInfo()}: syncPeerTrackedPropertyHasChanged from peer ${eventSource && eventSource.debugInfo && eventSource.debugInfo()}. source_ undefined. Internal error.`); 4208 4209 return; 4210 } 4211 if (eventSource && this.source_ == eventSource) { 4212 // defensive programming: should always be the case! 4213 const newValue = this.source_.getUnmonitored(); 4214 if (this.checkIsSupportedValue(newValue)) { 4215 4216 if (this.resetLocalValue(newValue, /* needCopyObject */ true)) { 4217 this.notifyTrackedObjectPropertyHasChanged(changedPropertyName); 4218 } 4219 } 4220 } 4221 else { 4222 stateMgmtConsole.warn(`${this.debugInfo()}: syncPeerTrackedPropertyHasChanged: from peer '${eventSource === null || eventSource === void 0 ? void 0 : eventSource.debugInfo()}', Unexpected situation. syncPeerHasChanged from different sender than source_. Ignoring event.`); 4223 } 4224 4225 } 4226 getUnmonitored() { 4227 4228 // unmonitored get access , no call to notifyPropertyRead ! 4229 return this.localCopyObservedObject_; 4230 } 4231 get() { 4232 4233 4234 this.recordPropertyDependentUpdate(); 4235 if (this.shouldInstallTrackedObjectReadCb) { 4236 4237 ObservedObject.registerPropertyReadCb(this.localCopyObservedObject_, this.onOptimisedObjectPropertyRead.bind(this)); 4238 } 4239 else { 4240 4241 } 4242 4243 return this.localCopyObservedObject_; 4244 } 4245 // assignment to local variable in the form of this.aProp = <object value> 4246 set(newValue) { 4247 if (this.localCopyObservedObject_ === newValue) { 4248 4249 return; 4250 } 4251 4252 const oldValue = this.localCopyObservedObject_; 4253 if (this.resetLocalValue(newValue, /* needCopyObject */ false)) { 4254 TrackedObject.notifyObjectValueAssignment(/* old value */ oldValue, /* new value */ this.localCopyObservedObject_, this.notifyPropertyHasChangedPU.bind(this), this.notifyTrackedObjectPropertyHasChanged.bind(this)); 4255 } 4256 } 4257 onOptimisedObjectPropertyRead(readObservedObject, readPropertyName, isTracked) { 4258 4259 const renderingElmtId = this.getRenderingElmtId(); 4260 if (renderingElmtId >= 0) { 4261 if (!isTracked) { 4262 stateMgmtConsole.applicationError(`${this.debugInfo()}: onOptimisedObjectPropertyRead read NOT TRACKED property '${readPropertyName}' during rendering!`); 4263 throw new Error(`Illegal usage of not @Track'ed property '${readPropertyName}' on UI!`); 4264 } 4265 else { 4266 4267 if (this.getUnmonitored() === readObservedObject) { 4268 this.recordTrackObjectPropertyDependencyForElmtId(renderingElmtId, readPropertyName); 4269 } 4270 } 4271 } 4272 4273 } 4274 // called when updated from parent 4275 // during parent ViewPU rerender, calls update lambda of child ViewPU with @Prop variable 4276 // this lambda generated code calls ViewPU.updateStateVarsOfChildByElmtId, 4277 // calls inside app class updateStateVars() 4278 // calls reset() for each @Prop 4279 reset(sourceChangedValue) { 4280 4281 if (this.source_ !== undefined && this.checkIsSupportedValue(sourceChangedValue)) { 4282 // if this.source_.set causes an actual change, then, ObservedPropertyObject source_ will call syncPeerHasChanged method 4283 this.createSourceDependency(sourceChangedValue); 4284 this.source_.set(sourceChangedValue); 4285 } 4286 } 4287 createSourceDependency(sourceObject) { 4288 if (ObservedObject.IsObservedObject(sourceObject)) { 4289 4290 const fake = sourceObject[TrackedObject.___TRACKED_OPTI_ASSIGNMENT_FAKE_PROP_PROPERTY]; 4291 } 4292 } 4293 /* 4294 unsubscribe from previous wrapped ObjectObject 4295 take a shallow or (TODO) deep copy 4296 copied Object might already be an ObservedObject (e.g. becurse of @Observed decorator) or might be raw 4297 Therefore, conditionally wrap the object, then subscribe 4298 return value true only if localCopyObservedObject_ has been changed 4299 */ 4300 resetLocalValue(newObservedObjectValue, needCopyObject) { 4301 // note: We can not test for newObservedObjectValue == this.localCopyObservedObject_ 4302 // here because the object might still be the same, but some property of it has changed 4303 // this is added for stability test: Target of target is not Object/is not callable/ 4304 // InstanceOf error when target is not Callable/Can not get Prototype on non ECMA Object 4305 try { 4306 if (!this.checkIsSupportedValue(newObservedObjectValue)) { 4307 return; 4308 } 4309 // unsubscribe from old local copy 4310 if (this.localCopyObservedObject_ instanceof SubscribableAbstract) { 4311 this.localCopyObservedObject_.removeOwningProperty(this); 4312 } 4313 else { 4314 ObservedObject.removeOwningProperty(this.localCopyObservedObject_, this); 4315 // make sure the ObservedObject no longer has a read callback function 4316 // assigned to it 4317 ObservedObject.unregisterPropertyReadCb(this.localCopyObservedObject_); 4318 } 4319 } 4320 catch (error) { 4321 stateMgmtConsole.error(`${this.debugInfo()}, an error occurred in resetLocalValue: ${error.message}`); 4322 ArkTools.print("resetLocalValue SubscribableAbstract", SubscribableAbstract); 4323 ArkTools.print("resetLocalValue ObservedObject", ObservedObject); 4324 ArkTools.print("resetLocalValue this", this); 4325 let a = Reflect.getPrototypeOf(this); 4326 ArkTools.print("resetLocalVale getPrototypeOf", a); 4327 throw error; 4328 } 4329 // shallow/deep copy value 4330 // needed whenever newObservedObjectValue comes from source 4331 // not needed on a local set (aka when called from set() method) 4332 if (needCopyObject) { 4333 ViewPU.pauseRendering(); 4334 this.localCopyObservedObject_ = this.copyObject(newObservedObjectValue, this.info_); 4335 ViewPU.restoreRendering(); 4336 } 4337 else { 4338 this.localCopyObservedObject_ = newObservedObjectValue; 4339 } 4340 if (typeof this.localCopyObservedObject_ == "object") { 4341 if (this.localCopyObservedObject_ instanceof SubscribableAbstract) { 4342 // deep copy will copy Set of subscribers as well. But local copy only has its own subscribers 4343 // not those of its parent value. 4344 this.localCopyObservedObject_.clearOwningProperties(); 4345 this.localCopyObservedObject_.addOwningProperty(this); 4346 } 4347 else if (ObservedObject.IsObservedObject(this.localCopyObservedObject_)) { 4348 // case: new ObservedObject 4349 ObservedObject.addOwningProperty(this.localCopyObservedObject_, this); 4350 this.shouldInstallTrackedObjectReadCb = TrackedObject.needsPropertyReadCb(this.localCopyObservedObject_); 4351 } 4352 else { 4353 // wrap newObservedObjectValue raw object as ObservedObject and subscribe to it 4354 4355 this.localCopyObservedObject_ = ObservedObject.createNew(this.localCopyObservedObject_, this); 4356 this.shouldInstallTrackedObjectReadCb = TrackedObject.needsPropertyReadCb(this.localCopyObservedObject_); 4357 } 4358 4359 } 4360 return true; 4361 } 4362 copyObject(value, propName) { 4363 // ViewStackProcessor.getApiVersion function is not present in API9 4364 // therefore shallowCopyObject will always be used in API version 9 and before 4365 // but the code in this file is the same regardless of API version 4366 4367 return ((typeof ViewStackProcessor["getApiVersion"] == "function") && 4368 (ViewStackProcessor["getApiVersion"]() >= 10)) 4369 ? this.deepCopyObject(value, propName) 4370 : this.shallowCopyObject(value, propName); 4371 } 4372 // API 9 code path 4373 shallowCopyObject(value, propName) { 4374 let rawValue = ObservedObject.GetRawObject(value); 4375 let copy; 4376 if (!rawValue || typeof rawValue !== 'object') { 4377 copy = rawValue; 4378 } 4379 else if (typeof rawValue != "object") { 4380 // FIXME would it be better to throw Exception here? 4381 stateMgmtConsole.error(`${this.debugInfo()}: shallowCopyObject: request to copy non-object value, actual type is '${typeof rawValue}'. Internal error! Setting copy:=original value.`); 4382 copy = rawValue; 4383 } 4384 else if (rawValue instanceof Array) { 4385 // case Array inside ObservedObject 4386 copy = ObservedObject.createNew([...rawValue], this); 4387 Object.setPrototypeOf(copy, Object.getPrototypeOf(rawValue)); 4388 } 4389 else if (rawValue instanceof Date) { 4390 // case Date inside ObservedObject 4391 let d = new Date(); 4392 d.setTime(rawValue.getTime()); 4393 // subscribe, also Date gets wrapped / proxied by ObservedObject 4394 copy = ObservedObject.createNew(d, this); 4395 } 4396 else if (rawValue instanceof SubscribableAbstract) { 4397 // case SubscribableAbstract, no wrapping inside ObservedObject 4398 copy = Object.assign({}, rawValue); 4399 Object.setPrototypeOf(copy, Object.getPrototypeOf(rawValue)); 4400 if (copy instanceof SubscribableAbstract) { 4401 // subscribe 4402 copy.addOwningProperty(this); 4403 } 4404 } 4405 else if (typeof rawValue == "object") { 4406 // case Object that is not Array, not Date, not SubscribableAbstract 4407 copy = ObservedObject.createNew(Object.assign({}, rawValue), this); 4408 Object.setPrototypeOf(copy, Object.getPrototypeOf(rawValue)); 4409 } 4410 else { 4411 // TODO in PR "F": change to exception throwing: 4412 stateMgmtConsole.error(`${this.debugInfo()}: shallow failed. Attempt to copy unsupported value of type '${typeof rawValue}' .`); 4413 copy = rawValue; 4414 } 4415 return copy; 4416 } 4417 // API 10 code path 4418 deepCopyObject(obj, variable) { 4419 let copy = SynchedPropertyObjectOneWayPU.deepCopyObjectInternal(obj, variable); 4420 // this subscribe to the top level object/array of the copy 4421 // same as shallowCopy does 4422 if ((obj instanceof SubscribableAbstract) && 4423 (copy instanceof SubscribableAbstract)) { 4424 copy.addOwningProperty(this); 4425 } 4426 else if (ObservedObject.IsObservedObject(obj) && ObservedObject.IsObservedObject(copy)) { 4427 ObservedObject.addOwningProperty(copy, this); 4428 } 4429 return copy; 4430 ; 4431 } 4432 // do not use this function from outside unless it is for testing purposes. 4433 static deepCopyObjectInternal(obj, variable) { 4434 if (!obj || typeof obj !== 'object') { 4435 return obj; 4436 } 4437 let stack = new Array(); 4438 let copiedObjects = new Map(); 4439 return getDeepCopyOfObjectRecursive(obj); 4440 function getDeepCopyOfObjectRecursive(obj) { 4441 if (!obj || typeof obj !== 'object') { 4442 return obj; 4443 } 4444 const alreadyCopiedObject = copiedObjects.get(obj); 4445 if (alreadyCopiedObject) { 4446 let msg = `@Prop deepCopyObject: Found reference to already copied object: Path ${variable ? variable : 'unknown variable'}`; 4447 stack.forEach(stackItem => msg += ` - ${stackItem.name}`); 4448 4449 return alreadyCopiedObject; 4450 } 4451 let copy; 4452 if (obj instanceof Set) { 4453 copy = new Set(); 4454 Object.setPrototypeOf(copy, Object.getPrototypeOf(obj)); 4455 copiedObjects.set(obj, copy); 4456 for (const setKey of obj.keys()) { 4457 stack.push({ name: setKey }); 4458 copy.add(getDeepCopyOfObjectRecursive(setKey)); 4459 stack.pop(); 4460 } 4461 } 4462 else if (obj instanceof Map) { 4463 copy = new Map(); 4464 Object.setPrototypeOf(copy, Object.getPrototypeOf(obj)); 4465 copiedObjects.set(obj, copy); 4466 for (const mapKey of obj.keys()) { 4467 stack.push({ name: mapKey }); 4468 copy.set(mapKey, getDeepCopyOfObjectRecursive(obj.get(mapKey))); 4469 stack.pop(); 4470 } 4471 } 4472 else if (obj instanceof Date) { 4473 copy = new Date(); 4474 copy.setTime(obj.getTime()); 4475 Object.setPrototypeOf(copy, Object.getPrototypeOf(obj)); 4476 copiedObjects.set(obj, copy); 4477 } 4478 else if (obj instanceof Object) { 4479 copy = Array.isArray(obj) ? [] : {}; 4480 Object.setPrototypeOf(copy, Object.getPrototypeOf(obj)); 4481 copiedObjects.set(obj, copy); 4482 } 4483 for (const objKey of Object.keys(obj)) { 4484 stack.push({ name: objKey }); 4485 Reflect.set(copy, objKey, getDeepCopyOfObjectRecursive(obj[objKey])); 4486 stack.pop(); 4487 } 4488 return ObservedObject.IsObservedObject(obj) ? ObservedObject.createNew(copy, null) : copy; 4489 } 4490 } 4491} 4492// class definitions for backward compatibility 4493class SynchedPropertySimpleOneWayPU extends SynchedPropertyOneWayPU { 4494} 4495class SynchedPropertyObjectOneWayPU extends SynchedPropertyOneWayPU { 4496} 4497/* 4498 * Copyright (c) 2022 Huawei Device Co., Ltd. 4499 * Licensed under the Apache License, Version 2.0 (the "License"); 4500 * you may not use this file except in compliance with the License. 4501 * You may obtain a copy of the License at 4502 * 4503 * http://www.apache.org/licenses/LICENSE-2.0 4504 * 4505 * Unless required by applicable law or agreed to in writing, software 4506 * distributed under the License is distributed on an "AS IS" BASIS, 4507 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 4508 * See the License for the specific language governing permissions and 4509 * limitations under the License. 4510 */ 4511/** 4512 * SynchedPropertyObjectTwoWayPU 4513 * implementation of @Link and @Consume decorated variables of type class object 4514 * 4515 * all definitions in this file are framework internal 4516*/ 4517class SynchedPropertyTwoWayPU extends ObservedPropertyAbstractPU { 4518 constructor(source, owningChildView, thisPropertyName) { 4519 super(owningChildView, thisPropertyName); 4520 this.changeNotificationIsOngoing_ = false; 4521 this.source_ = source; 4522 if (this.source_) { 4523 // register to the parent property 4524 this.source_.addSubscriber(this); 4525 this.shouldInstallTrackedObjectReadCb = TrackedObject.needsPropertyReadCb(this.source_.getUnmonitored()); 4526 } 4527 else { 4528 throw new SyntaxError(`${this.debugInfo()}: constructor: source variable in parent/ancestor @Component must be defined. Application error!`); 4529 } 4530 } 4531 /* 4532 like a destructor, need to call this before deleting 4533 the property. 4534 */ 4535 aboutToBeDeleted() { 4536 // unregister from parent of this link 4537 if (this.source_) { 4538 this.source_.removeSubscriber(this); 4539 // unregister from the ObservedObject 4540 ObservedObject.removeOwningProperty(this.source_.getUnmonitored(), this); 4541 } 4542 super.aboutToBeDeleted(); 4543 } 4544 debugInfoDecorator() { 4545 return `@Link/@Consume (class SynchedPropertyTwoWayPU)`; 4546 } 4547 isStorageLinkProp() { 4548 return (this.source_ && this.source_ instanceof ObservedPropertyAbstract && (!(this.source_ instanceof ObservedPropertyAbstractPU))); 4549 } 4550 setObject(newValue) { 4551 if (!this.source_) { 4552 throw new SyntaxError(`${this.debugInfo()}: setObject (assign a new value), no source variable in parent/ancestor \ 4553 @Component. Application error.`); 4554 } 4555 if (this.getUnmonitored() === newValue) { 4556 4557 return; 4558 } 4559 4560 if (this.checkIsSupportedValue(newValue)) { 4561 // the source_ ObservedProperty will call: this.syncPeerHasChanged(newValue); 4562 this.source_.set(newValue); 4563 this.shouldInstallTrackedObjectReadCb = TrackedObject.needsPropertyReadCb(newValue); 4564 } 4565 } 4566 /** 4567 * Called when sync peer ObservedPropertyObject or SynchedPropertyObjectTwoWay has changed value 4568 * that peer can be in either parent or child component if 'this' is used for a @Link 4569 * that peer can be in either ancestor or descendant component if 'this' is used for a @Consume 4570 * @param eventSource 4571 */ 4572 syncPeerHasChanged(eventSource) { 4573 4574 if (!this.changeNotificationIsOngoing_) { 4575 4576 this.notifyPropertyHasChangedPU(); 4577 } 4578 4579 } 4580 syncPeerTrackedPropertyHasChanged(eventSource, changedTrackedObjectPropertyName) { 4581 4582 if (!this.changeNotificationIsOngoing_) { 4583 4584 this.notifyTrackedObjectPropertyHasChanged(changedTrackedObjectPropertyName); 4585 } 4586 4587 } 4588 getUnmonitored() { 4589 4590 return (this.source_ ? this.source_.getUnmonitored() : undefined); 4591 } 4592 // get 'read through` from the ObservedProperty 4593 get() { 4594 4595 4596 this.recordPropertyDependentUpdate(); 4597 const result = this.getUnmonitored(); 4598 if (this.shouldInstallTrackedObjectReadCb) { 4599 4600 ObservedObject.registerPropertyReadCb(result, this.onOptimisedObjectPropertyRead.bind(this)); 4601 } 4602 else { 4603 4604 } 4605 4606 return result; 4607 } 4608 // set 'writes through` to the ObservedProperty 4609 set(newValue) { 4610 4611 if (this.getUnmonitored() === newValue) { 4612 4613 4614 return; 4615 } 4616 4617 // avoid circular notifications @Link -> source @State -> other but also back to same @Link 4618 this.changeNotificationIsOngoing_ = true; 4619 let oldValue = this.getUnmonitored(); 4620 this.setObject(newValue); 4621 TrackedObject.notifyObjectValueAssignment(/* old value */ oldValue, /* new value */ newValue, this.notifyPropertyHasChangedPU.bind(this), this.notifyTrackedObjectPropertyHasChanged.bind(this)); 4622 this.changeNotificationIsOngoing_ = false; 4623 4624 } 4625 onOptimisedObjectPropertyRead(readObservedObject, readPropertyName, isTracked) { 4626 4627 const renderingElmtId = this.getRenderingElmtId(); 4628 if (renderingElmtId >= 0) { 4629 if (!isTracked) { 4630 stateMgmtConsole.applicationError(`${this.debugInfo()}: onOptimisedObjectPropertyRead read NOT TRACKED property '${readPropertyName}' during rendering!`); 4631 throw new Error(`Illegal usage of not @Track'ed property '${readPropertyName}' on UI!`); 4632 } 4633 else { 4634 4635 if (this.getUnmonitored() === readObservedObject) { 4636 this.recordTrackObjectPropertyDependencyForElmtId(renderingElmtId, readPropertyName); 4637 } 4638 } 4639 } 4640 4641 } 4642} 4643// class definitions for backward compatibility 4644class SynchedPropertyObjectTwoWayPU extends SynchedPropertyTwoWayPU { 4645} 4646class SynchedPropertySimpleTwoWayPU extends SynchedPropertyTwoWayPU { 4647} 4648/* 4649 * Copyright (c) 2022 Huawei Device Co., Ltd. 4650 * Licensed under the Apache License, Version 2.0 (the "License"); 4651 * you may not use this file except in compliance with the License. 4652 * You may obtain a copy of the License at 4653 * 4654 * http://www.apache.org/licenses/LICENSE-2.0 4655 * 4656 * Unless required by applicable law or agreed to in writing, software 4657 * distributed under the License is distributed on an "AS IS" BASIS, 4658 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 4659 * See the License for the specific language governing permissions and 4660 * limitations under the License. 4661 */ 4662/** 4663 * SynchedPropertyNestedObjectPU 4664 * implementation of @ObjectLink decorated variables 4665 * 4666 * all definitions in this file are framework internal 4667 * 4668 */ 4669class SynchedPropertyNestedObjectPU extends ObservedPropertyAbstractPU { 4670 /** 4671 * Construct a Property of a su component that links to a variable of parent view that holds an ObservedObject 4672 * example 4673 * this.b.$a with b of type PC and a of type C, or 4674 * this.$b[5] with this.b of type PC and array item b[5] of type C; 4675 * 4676 * @param subscribeMe 4677 * @param propName 4678 */ 4679 constructor(obsObject, owningChildView, propertyName) { 4680 super(owningChildView, propertyName); 4681 this.obsObject_ = obsObject; 4682 this.createSourceDependency(obsObject); 4683 this.setValueInternal(obsObject); 4684 } 4685 /* 4686 like a destructor, need to call this before deleting 4687 the property. 4688 */ 4689 aboutToBeDeleted() { 4690 // unregister from the ObservedObject 4691 ObservedObject.removeOwningProperty(this.obsObject_, this); 4692 super.aboutToBeDeleted(); 4693 } 4694 debugInfoDecorator() { 4695 return `@ObjectLink (class SynchedPropertyNestedObjectPU)`; 4696 } 4697 getUnmonitored() { 4698 4699 // unmonitored get access , no call to notifyPropertyRead ! 4700 return this.obsObject_; 4701 } 4702 // get 'read through` from the ObservedProperty 4703 get() { 4704 4705 4706 this.recordPropertyDependentUpdate(); 4707 if (this.shouldInstallTrackedObjectReadCb) { 4708 4709 ObservedObject.registerPropertyReadCb(this.obsObject_, this.onOptimisedObjectPropertyRead.bind(this)); 4710 } 4711 else { 4712 4713 } 4714 4715 return this.obsObject_; 4716 } 4717 // parent ViewPU rerender, runs update lambda with child ViewPU that contains a @ObjectLink 4718 // calls ViewPU.updateStateVarsByElmtId, calls updateStateVars in application class, calls this 'set' function 4719 set(newValue) { 4720 if (this.obsObject_ === newValue) { 4721 4722 return; 4723 } 4724 4725 const oldValue = this.obsObject_; 4726 if (this.setValueInternal(newValue)) { 4727 this.createSourceDependency(newValue); 4728 // notify value change to subscribing View 4729 TrackedObject.notifyObjectValueAssignment(/* old value */ oldValue, /* new value */ this.obsObject_, this.notifyPropertyHasChangedPU.bind(this), this.notifyTrackedObjectPropertyHasChanged.bind(this)); 4730 } 4731 } 4732 onOptimisedObjectPropertyRead(readObservedObject, readPropertyName, isTracked) { 4733 4734 const renderingElmtId = this.getRenderingElmtId(); 4735 if (renderingElmtId >= 0) { 4736 if (!isTracked) { 4737 stateMgmtConsole.applicationError(`${this.debugInfo()}: onOptimisedObjectPropertyRead read NOT TRACKED property '${readPropertyName}' during rendering!`); 4738 throw new Error(`Illegal usage of not @Track'ed property '${readPropertyName}' on UI!`); 4739 } 4740 else { 4741 4742 if (this.getUnmonitored() === readObservedObject) { 4743 this.recordTrackObjectPropertyDependencyForElmtId(renderingElmtId, readPropertyName); 4744 } 4745 } 4746 } 4747 4748 } 4749 createSourceDependency(sourceObject) { 4750 if (ObservedObject.IsObservedObject(sourceObject)) { 4751 4752 const fake = sourceObject[TrackedObject.___TRACKED_OPTI_ASSIGNMENT_FAKE_OBJLINK_PROPERTY]; 4753 } 4754 } 4755 setValueInternal(newValue) { 4756 if (!this.checkIsObject(newValue)) { 4757 return false; 4758 } 4759 if (this.obsObject_ != undefined) { 4760 if (this.obsObject_ instanceof SubscribableAbstract) { 4761 // unregister from SubscribableAbstract object 4762 this.obsObject_.removeOwningProperty(this); 4763 } 4764 else if (ObservedObject.IsObservedObject(this.obsObject_)) { 4765 // unregister from the ObservedObject 4766 ObservedObject.removeOwningProperty(this.obsObject_, this); 4767 // make sure the ObservedObject no longer has a read callback function 4768 // assigned to it 4769 ObservedObject.unregisterPropertyReadCb(this.obsObject_); 4770 } 4771 } 4772 this.obsObject_ = newValue; 4773 if (this.obsObject_ != undefined) { 4774 if (this.obsObject_ instanceof SubscribableAbstract) { 4775 // register to SubscribableAbstract object 4776 this.obsObject_.addOwningProperty(this); 4777 } 4778 else if (ObservedObject.IsObservedObject(this.obsObject_)) { 4779 // register to the ObservedObject 4780 ObservedObject.addOwningProperty(this.obsObject_, this); 4781 this.shouldInstallTrackedObjectReadCb = TrackedObject.needsPropertyReadCb(this.obsObject_); 4782 } 4783 else { 4784 stateMgmtConsole.applicationError(`${this.debugInfo()}: set/init (method setValueInternal): assigned value is neither ObservedObject nor SubscribableAbstract. \ 4785 value changes will bot be observed and UI will not update. forgot @Observed class decorator? Application error.`); 4786 } 4787 } 4788 return true; 4789 } 4790} 4791/** backward compatibility after typo in classname fix */ 4792class SynchedPropertyNesedObjectPU extends SynchedPropertyNestedObjectPU { 4793} 4794/* 4795 * Copyright (c) 2023 Huawei Device Co., Ltd. 4796 * Licensed under the Apache License, Version 2.0 (the "License"); 4797 * you may not use this file except in compliance with the License. 4798 * You may obtain a copy of the License at 4799 * 4800 * http://www.apache.org/licenses/LICENSE-2.0 4801 * 4802 * Unless required by applicable law or agreed to in writing, software 4803 * distributed under the License is distributed on an "AS IS" BASIS, 4804 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 4805 * See the License for the specific language governing permissions and 4806 * limitations under the License. 4807 */ 4808// defined a globle function to clean up the removeItems when idle 4809function uiNodeCleanUpIdleTask() { 4810 4811 UINodeRegisterProxy.obtainDeletedElmtIds(); 4812 UINodeRegisterProxy.unregisterElmtIdsFromViewPUs(); 4813} 4814class UINodeRegisterProxy { 4815 constructor() { 4816 this.removeElementsInfo_ = new Array(); 4817 } 4818 static obtainDeletedElmtIds() { 4819 4820 if ((!UINodeRegisterProxy.instance_.obtainDeletedElmtIds) || typeof UINodeRegisterProxy.instance_.obtainDeletedElmtIds != "function") { 4821 stateMgmtConsole.error(`UINodeRegisterProxy obtainDeletedElmtIds is not a function: ${UINodeRegisterProxy.instance_.obtainDeletedElmtIds}.`); 4822 } 4823 else { 4824 UINodeRegisterProxy.instance_.obtainDeletedElmtIds(); 4825 } 4826 } 4827 static unregisterElmtIdsFromViewPUs() { 4828 4829 UINodeRegisterProxy.instance_.unregisterElmtIdsFromViewPUs(); 4830 } 4831 // unregisters all the received removedElements in func parameter 4832 static unregisterRemovedElmtsFromViewPUs(removedElements) { 4833 4834 UINodeRegisterProxy.instance_.populateRemoveElementInfo(removedElements); 4835 UINodeRegisterProxy.instance_.unregisterElmtIdsFromViewPUs(); 4836 } 4837 populateRemoveElementInfo(removedElements) { 4838 for (const elmtId of removedElements) { 4839 const removedElementInfo = { elmtId, tag: "" }; 4840 this.removeElementsInfo_.push(removedElementInfo); 4841 } 4842 } 4843 /* just get the remove items from the native side 4844 */ 4845 obtainDeletedElmtIds() { 4846 4847 let removedElementsInfo = new Array(); 4848 ViewStackProcessor.moveDeletedElmtIds(removedElementsInfo); 4849 4850 this.removeElementsInfo_ = removedElementsInfo; 4851 } 4852 unregisterElmtIdsFromViewPUs() { 4853 4854 if (this.removeElementsInfo_.length == 0) { 4855 4856 return; 4857 } 4858 let owningView; 4859 this.removeElementsInfo_.forEach((rmElmtInfo) => { 4860 const owningViewPUWeak = UINodeRegisterProxy.ElementIdToOwningViewPU_.get(rmElmtInfo.elmtId); 4861 if (owningViewPUWeak != undefined) { 4862 owningView = owningViewPUWeak.deref(); 4863 if (owningView) { 4864 owningView.purgeDeleteElmtId(rmElmtInfo.elmtId); 4865 } 4866 else { 4867 4868 } 4869 } 4870 else { 4871 4872 } 4873 }); 4874 this.removeElementsInfo_.length = 0; 4875 } 4876} 4877UINodeRegisterProxy.instance_ = new UINodeRegisterProxy(); 4878UINodeRegisterProxy.ElementIdToOwningViewPU_ = new Map(); 4879/* 4880 * Copyright (c) 2022-2023 Huawei Device Co., Ltd. 4881 * Licensed under the Apache License, Version 2.0 (the "License"); 4882 * you may not use this file except in compliance with the License. 4883 * You may obtain a copy of the License at 4884 * 4885 * http://www.apache.org/licenses/LICENSE-2.0 4886 * 4887 * Unless required by applicable law or agreed to in writing, software 4888 * distributed under the License is distributed on an "AS IS" BASIS, 4889 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 4890 * See the License for the specific language governing permissions and 4891 * limitations under the License. 4892 * 4893 * * ViewPU - View for Partial Update 4894 * 4895* all definitions in this file are framework internal 4896*/ 4897// denotes a missing elemntId, this is the case during initial render 4898const UndefinedElmtId = -1; 4899// UpdateFuncRecord: misc framework-internal info related to updating of a UINode C++ object 4900// that TS side needs to know. 4901// updateFunc_ lambda function to update the UINode 4902// JS interface class reference (it only has static functions) 4903class UpdateFuncRecord { 4904 constructor(params) { 4905 this.updateFunc_ = params.updateFunc; 4906 this.classObject_ = params.classObject; 4907 this.node_ = params.node; 4908 } 4909 getUpdateFunc() { 4910 return this.updateFunc_; 4911 } 4912 getComponentClass() { 4913 return this.classObject_; 4914 } 4915 getComponentName() { 4916 return (this.classObject_ && ("name" in this.classObject_)) ? Reflect.get(this.classObject_, "name") : "unspecified UINode"; 4917 } 4918 getPopFunc() { 4919 return (this.classObject_ && "pop" in this.classObject_) ? this.classObject_.pop : () => { }; 4920 } 4921 getNode() { 4922 return this.node_; 4923 } 4924 setNode(node) { 4925 this.node_ = node; 4926 } 4927} 4928// NativeView 4929// implemented in C++ for release 4930// and in utest/view_native_mock.ts for testing 4931class ViewPU extends NativeViewPartialUpdate { 4932 /** 4933 * Create a View 4934 * 4935 * 1. option: top level View, specify 4936 * - compilerAssignedUniqueChildId must specify 4937 * - parent=undefined 4938 * - localStorage must provide if @LocalSTorageLink/Prop variables are used 4939 * in this View or descendant Views. 4940 * 4941 * 2. option: not a top level View 4942 * - compilerAssignedUniqueChildId must specify 4943 * - parent must specify 4944 * - localStorage do not specify, will inherit from parent View. 4945 * 4946 */ 4947 constructor(parent, localStorage, elmtId = -1, extraInfo = undefined) { 4948 super(); 4949 this.parent_ = undefined; 4950 this.childrenWeakrefMap_ = new Map(); 4951 // flag for initial rendering or re-render on-going. 4952 this.isRenderInProgress = false; 4953 // flag for initial rendering being done 4954 this.isInitialRenderDone = false; 4955 // indicates the currently rendered or rendered UINode's elmtIds 4956 // or -1 if none is currently rendering 4957 // isRenderInProgress == true always when currentlyRenderedElmtIdStack_.length >= 0 4958 this.currentlyRenderedElmtIdStack_ = new Array(); 4959 // flag if active of inActive 4960 // inActive means updates are delayed 4961 this.isActive_ = true; 4962 this.runReuse_ = false; 4963 this.hasBeenRecycled_ = false; 4964 // flag if {aboutToBeDeletedInternal} is called and the instance of ViewPU has not been GC. 4965 this.isDeleting_ = false; 4966 this.watchedProps = new Map(); 4967 this.recycleManager_ = undefined; 4968 this.isCompFreezeAllowed = false; 4969 this.extraInfo_ = undefined; 4970 // @Provide'd variables by this class and its ancestors 4971 this.providedVars_ = new Map(); 4972 // Set of dependent elmtIds that need partial update 4973 // during next re-render 4974 this.dirtDescendantElementIds_ = new Set(); 4975 // registry of update functions 4976 // the key is the elementId of the Component/Element that's the result of this function 4977 this.updateFuncByElmtId = new UpdateFuncsByElmtId(); 4978 // my LocalStorage instance, shared with ancestor Views. 4979 // create a default instance on demand if none is initialized 4980 this.localStoragebackStore_ = undefined; 4981 // if set use the elmtId also as the ViewPU object's subscribable id. 4982 // these matching is requirement for updateChildViewById(elmtId) being able to 4983 // find the child ViewPU object by given elmtId 4984 this.id_ = elmtId == -1 ? SubscriberManager.MakeId() : elmtId; 4985 this.localStoragebackStore_ = undefined; 4986 4987 if (extraInfo) { 4988 this.extraInfo_ = extraInfo; 4989 } 4990 if (parent) { 4991 // this View is not a top-level View 4992 this.setCardId(parent.getCardId()); 4993 // Call below will set this.parent_ to parent as well 4994 parent.addChild(this); 4995 } 4996 else if (localStorage) { 4997 this.localStorage_ = localStorage; 4998 4999 } 5000 this.isCompFreezeAllowed = this.isCompFreezeAllowed || (this.parent_ && this.parent_.isCompFreezeAllowed); 5001 SubscriberManager.Add(this); 5002 5003 } 5004 get ownObservedPropertiesStore_() { 5005 if (!this.ownObservedPropertiesStore__) { 5006 // lazy init 5007 this.ownObservedPropertiesStore__ = new Set(); 5008 this.obtainOwnObservedProperties(); 5009 } 5010 return this.ownObservedPropertiesStore__; 5011 } 5012 obtainOwnObservedProperties() { 5013 Object.getOwnPropertyNames(this) 5014 .filter((propName) => { 5015 return propName.startsWith("__"); 5016 }) 5017 .forEach((propName) => { 5018 const stateVar = Reflect.get(this, propName); 5019 if (stateVar && typeof stateVar === 'object' && "notifyPropertyHasChangedPU" in stateVar) { 5020 5021 this.ownObservedPropertiesStore_.add(stateVar); 5022 } 5023 else { 5024 5025 } 5026 }); 5027 } 5028 get localStorage_() { 5029 if (!this.localStoragebackStore_ && this.parent_) { 5030 5031 this.localStoragebackStore_ = this.parent_.localStorage_; 5032 } 5033 if (!this.localStoragebackStore_) { 5034 5035 this.localStoragebackStore_ = new LocalStorage({ /* empty */}); 5036 } 5037 return this.localStoragebackStore_; 5038 } 5039 set localStorage_(instance) { 5040 if (!instance) { 5041 // setting to undefined not allowed 5042 return; 5043 } 5044 if (this.localStoragebackStore_) { 5045 stateMgmtConsole.applicationError(`${this.debugInfo__()}: constructor: is setting LocalStorage instance twice. Application error.`); 5046 } 5047 this.localStoragebackStore_ = instance; 5048 } 5049 // globally unique id, this is different from compilerAssignedUniqueChildId! 5050 id__() { 5051 return this.id_; 5052 } 5053 updateId(elmtId) { 5054 this.id_ = elmtId; 5055 } 5056 aboutToReuse(params) { } 5057 aboutToRecycle() { } 5058 setDeleteStatusRecursively() { 5059 if (!this.childrenWeakrefMap_.size) { 5060 return; 5061 } 5062 this.childrenWeakrefMap_.forEach((value) => { 5063 let child = value.deref(); 5064 if (child) { 5065 child.isDeleting_ = true; 5066 child.setDeleteStatusRecursively(); 5067 } 5068 }); 5069 } 5070 // super class will call this function from 5071 // its aboutToBeDeleted implementation 5072 aboutToBeDeletedInternal() { 5073 5074 // if this.isDeleting_ is true already, it may be set delete status recursively by its parent, so it is not necessary 5075 // to set and resursively set its children any more 5076 if (!this.isDeleting_) { 5077 this.isDeleting_ = true; 5078 this.setDeleteStatusRecursively(); 5079 } 5080 // tell UINodeRegisterProxy that all elmtIds under 5081 // this ViewPU should be treated as already unregistered 5082 5083 // purge the elmtIds owned by this viewPU from the updateFuncByElmtId and also the state variable dependent elmtIds 5084 Array.from(this.updateFuncByElmtId.keys()).forEach((elemId) => { 5085 this.purgeDeleteElmtId(elemId); 5086 }); 5087 if (this.hasRecycleManager()) { 5088 this.getRecycleManager().purgeAllCachedRecycleNode(); 5089 } 5090 // unregistration of ElementIDs 5091 5092 // it will unregister removed elementids from all the viewpu, equals purgeDeletedElmtIdsRecursively 5093 this.purgeDeletedElmtIds(); 5094 5095 // in case ViewPU is currently frozen 5096 ViewPU.inactiveComponents_.delete(`${this.constructor.name}[${this.id__()}]`); 5097 this.updateFuncByElmtId.clear(); 5098 this.watchedProps.clear(); 5099 this.providedVars_.clear(); 5100 if (this.ownObservedPropertiesStore__) { 5101 this.ownObservedPropertiesStore__.clear(); 5102 } 5103 if (this.parent_) { 5104 this.parent_.removeChild(this); 5105 } 5106 this.localStoragebackStore_ = undefined; 5107 } 5108 purgeDeleteElmtId(rmElmtId) { 5109 5110 const result = this.updateFuncByElmtId.delete(rmElmtId); 5111 if (result) { 5112 this.purgeVariableDependenciesOnElmtIdOwnFunc(rmElmtId); 5113 // it means rmElmtId has finished all the unregistration from the js side, ElementIdToOwningViewPU_ does not need to keep it 5114 UINodeRegisterProxy.ElementIdToOwningViewPU_.delete(rmElmtId); 5115 } 5116 return result; 5117 } 5118 debugInfo__() { 5119 return `@Component '${this.constructor.name}'[${this.id__()}]`; 5120 } 5121 debugInfoRegisteredElmtIds() { 5122 return this.updateFuncByElmtId.debugInfoRegisteredElmtIds(); 5123 } 5124 // for given elmtIds look up their component name/type and format a string out of this info 5125 // use function only for debug output and DFX. 5126 debugInfoElmtIds(elmtIds) { 5127 let result = ""; 5128 let sepa = ""; 5129 elmtIds.forEach((elmtId) => { 5130 result += `${sepa}${this.debugInfoElmtId(elmtId)}`; 5131 sepa = ", "; 5132 }); 5133 return result; 5134 } 5135 debugInfoElmtId(elmtId) { 5136 return this.updateFuncByElmtId.debugInfoElmtId(elmtId); 5137 } 5138 dumpStateVars() { 5139 5140 } 5141 debugInfoStateVars() { 5142 let result = `|--${this.constructor.name}[${this.id__()}]`; 5143 Object.getOwnPropertyNames(this) 5144 .filter((varName) => varName.startsWith("__")) 5145 .forEach((varName) => { 5146 const prop = Reflect.get(this, varName); 5147 if ("debugInfoDecorator" in prop) { 5148 const observedProp = prop; 5149 result += `\n ${observedProp.debugInfoDecorator()} '${observedProp.info()}'[${observedProp.id__()}]`; 5150 result += `\n ${observedProp.debugInfoSubscribers()}`; 5151 result += `\n ${observedProp.debugInfoSyncPeers()}`; 5152 result += `\n ${observedProp.debugInfoDependentElmtIds()}`; 5153 } 5154 }); 5155 return result; 5156 } 5157 /** 5158 * ArkUI engine will call this function when the corresponding CustomNode's active status change. 5159 * @param active true for active, false for inactive 5160 */ 5161 setActiveInternal(active) { 5162 5163 if (!this.isCompFreezeAllowed) { 5164 5165 5166 return; 5167 } 5168 5169 this.isActive_ = active; 5170 if (this.isActive_) { 5171 this.onActiveInternal(); 5172 } 5173 else { 5174 this.onInactiveInternal(); 5175 } 5176 5177 } 5178 onActiveInternal() { 5179 if (!this.isActive_) { 5180 return; 5181 } 5182 5183 this.performDelayedUpdate(); 5184 // Remove the active component from the Map for Dfx 5185 ViewPU.inactiveComponents_.delete(`${this.constructor.name}[${this.id__()}]`); 5186 for (const child of this.childrenWeakrefMap_.values()) { 5187 const childViewPU = child.deref(); 5188 if (childViewPU) { 5189 childViewPU.setActiveInternal(this.isActive_); 5190 } 5191 } 5192 if (this.hasRecycleManager()) { 5193 this.getRecycleManager().setActive(this.isActive_); 5194 } 5195 } 5196 onInactiveInternal() { 5197 if (this.isActive_) { 5198 return; 5199 } 5200 5201 for (const stateLinkProp of this.ownObservedPropertiesStore_) { 5202 stateLinkProp.enableDelayedNotification(); 5203 } 5204 // Add the inactive Components to Map for Dfx listing 5205 ViewPU.inactiveComponents_.add(`${this.constructor.name}[${this.id__()}]`); 5206 for (const child of this.childrenWeakrefMap_.values()) { 5207 const childViewPU = child.deref(); 5208 if (childViewPU) { 5209 childViewPU.setActiveInternal(this.isActive_); 5210 } 5211 } 5212 if (this.hasRecycleManager()) { 5213 this.getRecycleManager().setActive(this.isActive_); 5214 } 5215 } 5216 setParent(parent) { 5217 if (this.parent_ && parent) { 5218 stateMgmtConsole.warn(`${this.debugInfo__()}: setChild: changing parent to '${parent === null || parent === void 0 ? void 0 : parent.debugInfo__()} (unsafe operation)`); 5219 } 5220 this.parent_ = parent; 5221 } 5222 /** 5223 * Indicate if this @Component is allowed to freeze by calling with freezeState=true 5224 * Called with value of the @Component decorator 'freezeWhenInactive' parameter 5225 * or depending how UI compiler works also with 'undefined' 5226 * @param freezeState only value 'true' will be used, otherwise inherits from parent 5227 * if not parent, set to false. 5228 */ 5229 initAllowComponentFreeze(freezeState) { 5230 // set to true if freeze parameter set for this @Component to true 5231 // otherwise inherit from parent @Component (if it exists). 5232 this.isCompFreezeAllowed = freezeState || this.isCompFreezeAllowed; 5233 5234 } 5235 /** 5236 * add given child and set 'this' as its parent 5237 * @param child child to add 5238 * @returns returns false if child with given child's id already exists 5239 * 5240 * framework internal function 5241 * Note: Use of WeakRef ensures child and parent do not generate a cycle dependency. 5242 * The add. Set<ids> is required to reliably tell what children still exist. 5243 */ 5244 addChild(child) { 5245 if (this.childrenWeakrefMap_.has(child.id__())) { 5246 stateMgmtConsole.warn(`${this.debugInfo__()}: addChild '${child === null || child === void 0 ? void 0 : child.debugInfo__()}' id already exists ${child.id__()}. Internal error!`); 5247 return false; 5248 } 5249 this.childrenWeakrefMap_.set(child.id__(), new WeakRef(child)); 5250 child.setParent(this); 5251 return true; 5252 } 5253 /** 5254 * remove given child and remove 'this' as its parent 5255 * @param child child to add 5256 * @returns returns false if child with given child's id does not exist 5257 */ 5258 removeChild(child) { 5259 const hasBeenDeleted = this.childrenWeakrefMap_.delete(child.id__()); 5260 if (!hasBeenDeleted) { 5261 stateMgmtConsole.warn(`${this.debugInfo__()}: removeChild '${child === null || child === void 0 ? void 0 : child.debugInfo__()}', child id ${child.id__()} not known. Internal error!`); 5262 } 5263 else { 5264 child.setParent(undefined); 5265 } 5266 return hasBeenDeleted; 5267 } 5268 /** 5269 * Retrieve child by given id 5270 * @param id 5271 * @returns child if in map and weak ref can still be downreferenced 5272 */ 5273 getChildById(id) { 5274 const childWeakRef = this.childrenWeakrefMap_.get(id); 5275 return childWeakRef ? childWeakRef.deref() : undefined; 5276 } 5277 updateStateVars(params) { 5278 stateMgmtConsole.error(`${this.debugInfo__()}: updateStateVars unimplemented. Pls upgrade to latest eDSL transpiler version. Application error.`); 5279 } 5280 initialRenderView() { 5281 5282 this.obtainOwnObservedProperties(); 5283 this.isRenderInProgress = true; 5284 this.initialRender(); 5285 this.isRenderInProgress = false; 5286 this.isInitialRenderDone = true; 5287 5288 } 5289 UpdateElement(elmtId) { 5290 5291 if (elmtId == this.id__()) { 5292 // do not attempt to update itself. 5293 // a @Prop can add a dependency of the ViewPU onto itself. Ignore it. 5294 5295 return; 5296 } 5297 // do not process an Element that has been marked to be deleted 5298 const entry = this.updateFuncByElmtId.get(elmtId); 5299 const updateFunc = entry ? entry.getUpdateFunc() : undefined; 5300 if (typeof updateFunc !== "function") { 5301 5302 } 5303 else { 5304 const componentName = entry.getComponentName(); 5305 5306 this.isRenderInProgress = true; 5307 5308 updateFunc(elmtId, /* isFirstRender */ false); 5309 5310 5311 this.finishUpdateFunc(elmtId); 5312 5313 this.isRenderInProgress = false; 5314 5315 } 5316 5317 } 5318 dumpReport() { 5319 stateMgmtConsole.warn(`Printing profiler information`); 5320 stateMgmtProfiler.report(); 5321 } 5322 /** 5323 * force a complete rerender / update by executing all update functions 5324 * exec a regular rerender first 5325 * 5326 * @param deep recurse all children as well 5327 * 5328 * framework internal functions, apps must not call 5329 */ 5330 forceCompleteRerender(deep = false) { 5331 5332 stateMgmtConsole.warn(`${this.debugInfo__()}: forceCompleteRerender - start.`); 5333 // see which elmtIds are managed by this View 5334 // and clean up all book keeping for them 5335 this.purgeDeletedElmtIds(); 5336 Array.from(this.updateFuncByElmtId.keys()).sort(ViewPU.compareNumber).forEach(elmtId => this.UpdateElement(elmtId)); 5337 if (deep) { 5338 this.childrenWeakrefMap_.forEach((weakRefChild) => { 5339 const child = weakRefChild.deref(); 5340 if (child) { 5341 child.forceCompleteRerender(true); 5342 } 5343 }); 5344 } 5345 stateMgmtConsole.warn(`${this.debugInfo__()}: forceCompleteRerender - end`); 5346 5347 } 5348 /** 5349 * force a complete rerender / update on specific node by executing update function. 5350 * 5351 * @param elmtId which node needs to update. 5352 * 5353 * framework internal functions, apps must not call 5354 */ 5355 forceRerenderNode(elmtId) { 5356 5357 // see which elmtIds are managed by this View 5358 // and clean up all book keeping for them 5359 this.purgeDeletedElmtIds(); 5360 this.UpdateElement(elmtId); 5361 // remove elemtId from dirtDescendantElementIds. 5362 this.dirtDescendantElementIds_.delete(elmtId); 5363 5364 } 5365 updateStateVarsOfChildByElmtId(elmtId, params) { 5366 5367 5368 if (elmtId < 0) { 5369 stateMgmtConsole.warn(`${this.debugInfo__()}: updateChildViewById(${elmtId}) - invalid elmtId - internal error!`); 5370 5371 return; 5372 } 5373 let child = this.getChildById(elmtId); 5374 if (!child) { 5375 stateMgmtConsole.warn(`${this.debugInfo__()}: updateChildViewById(${elmtId}) - no child with this elmtId - internal error!`); 5376 5377 return; 5378 } 5379 child.updateStateVars(params); 5380 5381 5382 } 5383 // implements IMultiPropertiesChangeSubscriber 5384 viewPropertyHasChanged(varName, dependentElmtIds) { 5385 5386 stateMgmtTrace.scopedTrace(() => { 5387 if (this.isRenderInProgress) { 5388 stateMgmtConsole.applicationError(`${this.debugInfo__()}: State variable '${varName}' has changed during render! It's illegal to change @Component state while build (initial render or re-render) is on-going. Application error!`); 5389 } 5390 this.syncInstanceId(); 5391 if (dependentElmtIds.size && !this.isFirstRender()) { 5392 if (!this.dirtDescendantElementIds_.size && !this.runReuse_) { 5393 // mark ComposedElement dirty when first elmtIds are added 5394 // do not need to do this every time 5395 this.markNeedUpdate(); 5396 } 5397 5398 for (const elmtId of dependentElmtIds) { 5399 if (this.hasRecycleManager()) { 5400 this.dirtDescendantElementIds_.add(this.recycleManager_.proxyNodeId(elmtId)); 5401 } 5402 else { 5403 this.dirtDescendantElementIds_.add(elmtId); 5404 } 5405 } 5406 5407 } 5408 else { 5409 5410 5411 } 5412 let cb = this.watchedProps.get(varName); 5413 if (cb) { 5414 5415 cb.call(this, varName); 5416 } 5417 this.restoreInstanceId(); 5418 }, "ViewPU.viewPropertyHasChanged", this.constructor.name, varName, dependentElmtIds.size); 5419 5420 } 5421 performDelayedUpdate() { 5422 if (!this.ownObservedPropertiesStore_.size) { 5423 return; 5424 } 5425 5426 stateMgmtTrace.scopedTrace(() => { 5427 5428 this.syncInstanceId(); 5429 for (const stateLinkPropVar of this.ownObservedPropertiesStore_) { 5430 const changedElmtIds = stateLinkPropVar.moveElmtIdsForDelayedUpdate(); 5431 if (changedElmtIds) { 5432 const varName = stateLinkPropVar.info(); 5433 if (changedElmtIds.size && !this.isFirstRender()) { 5434 for (const elmtId of changedElmtIds) { 5435 this.dirtDescendantElementIds_.add(elmtId); 5436 } 5437 } 5438 5439 const cb = this.watchedProps.get(varName); 5440 if (cb) { 5441 5442 cb.call(this, varName); 5443 } 5444 } 5445 } // for all ownStateLinkProps_ 5446 this.restoreInstanceId(); 5447 if (this.dirtDescendantElementIds_.size) { 5448 this.markNeedUpdate(); 5449 } 5450 }, "ViewPU.performDelayedUpdate", this.constructor.name); 5451 5452 } 5453 /** 5454 * Function to be called from the constructor of the sub component 5455 * to register a @Watch varibale 5456 * @param propStr name of the variable. Note from @Provide and @Consume this is 5457 * the variable name and not the alias! 5458 * @param callback application defined member function of sub-class 5459 */ 5460 declareWatch(propStr, callback) { 5461 this.watchedProps.set(propStr, callback); 5462 } 5463 /** 5464 * This View @Provide's a variable under given name 5465 * Call this function from the constructor of the sub class 5466 * @param providedPropName either the variable name or the alias defined as 5467 * decorator param 5468 * @param store the backing store object for this variable (not the get/set variable!) 5469 */ 5470 addProvidedVar(providedPropName, store, allowOverride = false) { 5471 if (!allowOverride && this.findProvide(providedPropName)) { 5472 throw new ReferenceError(`${this.constructor.name}: duplicate @Provide property with name ${providedPropName}. Property with this name is provided by one of the ancestor Views already. @Provide override not allowed.`); 5473 } 5474 this.providedVars_.set(providedPropName, store); 5475 } 5476 /* 5477 findProvide finds @Provided property recursively by traversing ViewPU's towards that of the UI tree root @Component: 5478 if 'this' ViewPU has a @Provide("providedPropName") return it, otherwise ask from its parent ViewPU. 5479 */ 5480 findProvide(providedPropName) { 5481 return this.providedVars_.get(providedPropName) || (this.parent_ && this.parent_.findProvide(providedPropName)); 5482 } 5483 /** 5484 * Method for the sub-class to call from its constructor for resolving 5485 * a @Consume variable and initializing its backing store 5486 * with the SyncedPropertyTwoWay<T> object created from the 5487 * @Provide variable's backing store. 5488 * @param providedPropName the name of the @Provide'd variable. 5489 * This is either the @Consume decorator parameter, or variable name. 5490 * @param consumeVarName the @Consume variable name (not the 5491 * @Consume decorator parameter) 5492 * @returns initializing value of the @Consume backing store 5493 */ 5494 initializeConsume(providedPropName, consumeVarName) { 5495 let providedVarStore = this.findProvide(providedPropName); 5496 if (providedVarStore === undefined) { 5497 throw new ReferenceError(`${this.debugInfo__()} missing @Provide property with name ${providedPropName}. 5498 Fail to resolve @Consume(${providedPropName}).`); 5499 } 5500 const factory = (source) => { 5501 const result = new SynchedPropertyTwoWayPU(source, this, consumeVarName); 5502 5503 return result; 5504 }; 5505 return providedVarStore.createSync(factory); 5506 } 5507 /** 5508 * given the elmtId of a child or child of child within this custom component 5509 * remember this component needs a partial update 5510 * @param elmtId 5511 */ 5512 markElemenDirtyById(elmtId) { 5513 // TODO ace-ets2bundle, framework, compilated apps need to update together 5514 // this function will be removed after a short transiition periode 5515 stateMgmtConsole.applicationError(`${this.debugInfo__()}: markElemenDirtyById no longer supported. 5516 Please update your ace-ets2bundle and recompile your application. Application error!`); 5517 } 5518 /** 5519 * For each recorded dirty Element in this custom component 5520 * run its update function 5521 * 5522 */ 5523 updateDirtyElements() { 5524 5525 do { 5526 5527 // see which elmtIds are managed by this View 5528 // and clean up all book keeping for them 5529 this.purgeDeletedElmtIds(); 5530 // process all elmtIds marked as needing update in ascending order. 5531 // ascending order ensures parent nodes will be updated before their children 5532 // prior cleanup ensure no already deleted Elements have their update func executed 5533 Array.from(this.dirtDescendantElementIds_).sort(ViewPU.compareNumber).forEach(elmtId => { 5534 if (this.hasRecycleManager()) { 5535 this.UpdateElement(this.recycleManager_.proxyNodeId(elmtId)); 5536 } 5537 else { 5538 this.UpdateElement(elmtId); 5539 } 5540 this.dirtDescendantElementIds_.delete(elmtId); 5541 }); 5542 if (this.dirtDescendantElementIds_.size) { 5543 stateMgmtConsole.applicationError(`${this.debugInfo__()}: New UINode objects added to update queue while re-render! - Likely caused by @Component state change during build phase, not allowed. Application error!`); 5544 } 5545 } while (this.dirtDescendantElementIds_.size); 5546 5547 this.dumpStateVars(); 5548 5549 } 5550 // request list of all (global) elmtIds of deleted UINodes and unregister from the all ViewPUs 5551 // this function equals purgeDeletedElmtIdsRecursively because it does unregistration for all ViewPUs 5552 purgeDeletedElmtIds() { 5553 5554 // request list of all (global) elmtIds of deleted UINodes that need to be unregistered 5555 UINodeRegisterProxy.obtainDeletedElmtIds(); 5556 // unregister the removed elementids requested from the cpp side for all viewpus, it will make the first viewpu slower 5557 // than before, but the rest viewpu will be faster 5558 UINodeRegisterProxy.unregisterElmtIdsFromViewPUs(); 5559 5560 } 5561 purgeVariableDependenciesOnElmtIdOwnFunc(elmtId) { 5562 this.ownObservedPropertiesStore_.forEach((stateVar) => { 5563 stateVar.purgeDependencyOnElmtId(elmtId); 5564 }); 5565 } 5566 /** 5567 * return its elmtId if currently rendering or re-rendering an UINode 5568 * otherwise return -1 5569 * set in observeComponentCreation(2) 5570 */ 5571 getCurrentlyRenderedElmtId() { 5572 return ViewPU.renderingPaused || this.currentlyRenderedElmtIdStack_.length == 0 ? -1 : this.currentlyRenderedElmtIdStack_.slice(-1)[0]; 5573 } 5574 static pauseRendering() { 5575 ViewPU.renderingPaused = true; 5576 } 5577 static restoreRendering() { 5578 ViewPU.renderingPaused = false; 5579 } 5580 // executed on first render only 5581 // kept for backward compatibility with old ace-ets2bundle 5582 observeComponentCreation(compilerAssignedUpdateFunc) { 5583 if (this.isDeleting_) { 5584 stateMgmtConsole.error(`View ${this.constructor.name} elmtId ${this.id__()} is already in process of destruction, will not execute observeComponentCreation `); 5585 return; 5586 } 5587 const updateFunc = (elmtId, isFirstRender) => { 5588 5589 this.currentlyRenderedElmtIdStack_.push(elmtId); 5590 compilerAssignedUpdateFunc(elmtId, isFirstRender); 5591 this.currentlyRenderedElmtIdStack_.pop(); 5592 5593 }; 5594 const elmtId = ViewStackProcessor.AllocateNewElmetIdForNextComponent(); 5595 // in observeComponentCreation function we do not get info about the component name, in 5596 // observeComponentCreation2 we do. 5597 this.updateFuncByElmtId.set(elmtId, { updateFunc: updateFunc }); 5598 // add element id -> owning ViewPU 5599 UINodeRegisterProxy.ElementIdToOwningViewPU_.set(elmtId, new WeakRef(this)); 5600 try { 5601 updateFunc(elmtId, /* is first render */ true); 5602 } 5603 catch (error) { 5604 // avoid the incompatible change that move set function before updateFunc. 5605 this.updateFuncByElmtId.delete(elmtId); 5606 UINodeRegisterProxy.ElementIdToOwningViewPU_.delete(elmtId); 5607 stateMgmtConsole.applicationError(`${this.debugInfo__()} has error in update func: ${error.message}`); 5608 throw error; 5609 } 5610 } 5611 // executed on first render only 5612 // added July 2023, replaces observeComponentCreation 5613 // classObject is the ES6 class object , mandatory to specify even the class lacks the pop function. 5614 // - prototype : Object is present for every ES6 class 5615 // - pop : () => void, static function present for JSXXX classes such as Column, TapGesture, etc. 5616 observeComponentCreation2(compilerAssignedUpdateFunc, classObject) { 5617 if (this.isDeleting_) { 5618 stateMgmtConsole.error(`View ${this.constructor.name} elmtId ${this.id__()} is already in process of destruction, will not execute observeComponentCreation2 `); 5619 return; 5620 } 5621 const _componentName = (classObject && ("name" in classObject)) ? Reflect.get(classObject, "name") : "unspecified UINode"; 5622 const _popFunc = (classObject && "pop" in classObject) ? classObject.pop : () => { }; 5623 const updateFunc = (elmtId, isFirstRender) => { 5624 this.syncInstanceId(); 5625 5626 ViewStackProcessor.StartGetAccessRecordingFor(elmtId); 5627 this.currentlyRenderedElmtIdStack_.push(elmtId); 5628 compilerAssignedUpdateFunc(elmtId, isFirstRender); 5629 if (!isFirstRender) { 5630 _popFunc(); 5631 } 5632 this.currentlyRenderedElmtIdStack_.pop(); 5633 ViewStackProcessor.StopGetAccessRecording(); 5634 5635 this.restoreInstanceId(); 5636 }; 5637 const elmtId = ViewStackProcessor.AllocateNewElmetIdForNextComponent(); 5638 // needs to move set before updateFunc. 5639 // make sure the key and object value exist since it will add node in attributeModifier during updateFunc. 5640 this.updateFuncByElmtId.set(elmtId, { updateFunc: updateFunc, classObject: classObject }); 5641 // add element id -> owning ViewPU 5642 UINodeRegisterProxy.ElementIdToOwningViewPU_.set(elmtId, new WeakRef(this)); 5643 try { 5644 updateFunc(elmtId, /* is first render */ true); 5645 } 5646 catch (error) { 5647 // avoid the incompatible change that move set function before updateFunc. 5648 this.updateFuncByElmtId.delete(elmtId); 5649 UINodeRegisterProxy.ElementIdToOwningViewPU_.delete(elmtId); 5650 stateMgmtConsole.applicationError(`${this.debugInfo__()} has error in update func: ${error.message}`); 5651 throw error; 5652 } 5653 5654 } 5655 getOrCreateRecycleManager() { 5656 if (!this.recycleManager_) { 5657 this.recycleManager_ = new RecycleManager; 5658 } 5659 return this.recycleManager_; 5660 } 5661 getRecycleManager() { 5662 return this.recycleManager_; 5663 } 5664 hasRecycleManager() { 5665 return !(this.recycleManager_ === undefined); 5666 } 5667 initRecycleManager() { 5668 if (this.recycleManager_) { 5669 stateMgmtConsole.error(`${this.debugInfo__()}: init recycleManager multiple times. Internal error.`); 5670 return; 5671 } 5672 this.recycleManager_ = new RecycleManager; 5673 } 5674 rebuildUpdateFunc(elmtId, compilerAssignedUpdateFunc) { 5675 const updateFunc = (elmtId, isFirstRender) => { 5676 this.currentlyRenderedElmtIdStack_.push(elmtId); 5677 compilerAssignedUpdateFunc(elmtId, isFirstRender); 5678 this.currentlyRenderedElmtIdStack_.pop(); 5679 }; 5680 if (this.updateFuncByElmtId.has(elmtId)) { 5681 this.updateFuncByElmtId.set(elmtId, { updateFunc: updateFunc }); 5682 } 5683 } 5684 /** 5685 * @function observeRecycleComponentCreation 5686 * @description custom node recycle creation 5687 * @param name custom node name 5688 * @param recycleUpdateFunc custom node recycle update which can be converted to a normal update function 5689 * @return void 5690 */ 5691 observeRecycleComponentCreation(name, recycleUpdateFunc) { 5692 // convert recycle update func to update func 5693 const compilerAssignedUpdateFunc = (element, isFirstRender) => { 5694 recycleUpdateFunc(element, isFirstRender, undefined); 5695 }; 5696 let node; 5697 // if there is no suitable recycle node, run a normal creation function. 5698 if (!this.hasRecycleManager() || !(node = this.getRecycleManager().popRecycleNode(name))) { 5699 5700 this.observeComponentCreation(compilerAssignedUpdateFunc); 5701 return; 5702 } 5703 // if there is a suitable recycle node, run a recycle update function. 5704 const newElmtId = ViewStackProcessor.AllocateNewElmetIdForNextComponent(); 5705 const oldElmtId = node.id__(); 5706 this.recycleManager_.updateNodeId(oldElmtId, newElmtId); 5707 this.hasBeenRecycled_ = true; 5708 this.rebuildUpdateFunc(oldElmtId, compilerAssignedUpdateFunc); 5709 recycleUpdateFunc(oldElmtId, /* is first render */ true, node); 5710 } 5711 aboutToReuseInternal() { 5712 this.runReuse_ = true; 5713 stateMgmtTrace.scopedTrace(() => { 5714 if (this.paramsGenerator_ && typeof this.paramsGenerator_ == "function") { 5715 const params = this.paramsGenerator_(); 5716 this.updateStateVars(params); 5717 this.aboutToReuse(params); 5718 } 5719 }, "aboutToReuse", this.constructor.name); 5720 this.childrenWeakrefMap_.forEach((weakRefChild) => { 5721 const child = weakRefChild.deref(); 5722 if (child && !child.hasBeenRecycled_) { 5723 child.aboutToReuseInternal(); 5724 } 5725 }); 5726 this.updateDirtyElements(); 5727 this.runReuse_ = false; 5728 } 5729 aboutToRecycleInternal() { 5730 this.runReuse_ = true; 5731 stateMgmtTrace.scopedTrace(() => { 5732 this.aboutToRecycle(); 5733 }, "aboutToRecycle", this.constructor.name); 5734 this.childrenWeakrefMap_.forEach((weakRefChild) => { 5735 const child = weakRefChild.deref(); 5736 if (child && !child.hasBeenRecycled_) { 5737 child.aboutToRecycleInternal(); 5738 } 5739 }); 5740 this.runReuse_ = false; 5741 } 5742 // add current JS object to it's parent recycle manager 5743 recycleSelf(name) { 5744 if (this.parent_ && !this.parent_.isDeleting_) { 5745 this.parent_.getOrCreateRecycleManager().pushRecycleNode(name, this); 5746 this.hasBeenRecycled_ = true; 5747 } 5748 else { 5749 this.resetRecycleCustomNode(); 5750 stateMgmtConsole.error(`${this.constructor.name}[${this.id__()}]: recycleNode must have a parent`); 5751 } 5752 } 5753 // performs the update on a branch within if() { branch } else if (..) { branch } else { branch } 5754 ifElseBranchUpdateFunction(branchId, branchfunc) { 5755 const oldBranchid = If.getBranchId(); 5756 if (branchId == oldBranchid) { 5757 5758 return; 5759 } 5760 // branchid identifies uniquely the if .. <1> .. else if .<2>. else .<3>.branch 5761 // ifElseNode stores the most recent branch, so we can compare 5762 // removedChildElmtIds will be filled with the elmtIds of all children and their children will be deleted in response to if .. else chnage 5763 let removedChildElmtIds = new Array(); 5764 If.branchId(branchId, removedChildElmtIds); 5765 //unregisters the removed child elementIDs using proxy 5766 UINodeRegisterProxy.unregisterRemovedElmtsFromViewPUs(removedChildElmtIds); 5767 // purging these elmtIds from state mgmt will make sure no more update function on any deleted child wi;ll be executed 5768 5769 this.purgeDeletedElmtIds(); 5770 branchfunc(); 5771 } 5772 /** 5773 Partial updates for ForEach. 5774 * @param elmtId ID of element. 5775 * @param itemArray Array of items for use of itemGenFunc. 5776 * @param itemGenFunc Item generation function to generate new elements. If index parameter is 5777 * given set itemGenFuncUsesIndex to true. 5778 * @param idGenFunc ID generation function to generate unique ID for each element. If index parameter is 5779 * given set idGenFuncUsesIndex to true. 5780 * @param itemGenFuncUsesIndex itemGenFunc optional index parameter is given or not. 5781 * @param idGenFuncUsesIndex idGenFunc optional index parameter is given or not. 5782 */ 5783 forEachUpdateFunction(elmtId, itemArray, itemGenFunc, idGenFunc, itemGenFuncUsesIndex = false, idGenFuncUsesIndex = false) { 5784 5785 5786 if (itemArray === null || itemArray === undefined) { 5787 stateMgmtConsole.applicationError(`${this.debugInfo__()}: forEachUpdateFunction (ForEach re-render): input array is null or undefined error. Application error!`); 5788 5789 return; 5790 } 5791 if (typeof itemGenFunc !== "function") { 5792 stateMgmtConsole.applicationError(`${this.debugInfo__()}: forEachUpdateFunction (ForEach re-render): Item generation function missing. Application error!`); 5793 5794 return; 5795 } 5796 if (idGenFunc !== undefined && typeof idGenFunc !== "function") { 5797 stateMgmtConsole.applicationError(`${this.debugInfo__()}: forEachUpdateFunction (ForEach re-render): id generator is not a function. Application error!`); 5798 5799 return; 5800 } 5801 if (idGenFunc === undefined) { 5802 5803 idGenFuncUsesIndex = true; 5804 // catch possible error caused by Stringify and re-throw an Error with a meaningful (!) error message 5805 idGenFunc = (item, index) => { 5806 try { 5807 return `${index}__${JSON.stringify(item)}`; 5808 } 5809 catch (e) { 5810 throw new Error(`${this.debugInfo__()}: ForEach id ${elmtId}: use of default id generator function not possible on provided data structure. Need to specify id generator function (ForEach 3rd parameter). Application Error!`); 5811 } 5812 }; 5813 } 5814 let diffIndexArray = []; // New indexes compared to old one. 5815 let newIdArray = []; 5816 let idDuplicates = []; 5817 const arr = itemArray; // just to trigger a 'get' onto the array 5818 // ID gen is with index. 5819 if (idGenFuncUsesIndex) { 5820 // Create array of new ids. 5821 arr.forEach((item, indx) => { 5822 newIdArray.push(idGenFunc(item, indx)); 5823 }); 5824 } 5825 else { 5826 // Create array of new ids. 5827 arr.forEach((item, index) => { 5828 newIdArray.push(`${itemGenFuncUsesIndex ? index + '_' : ''}` + idGenFunc(item)); 5829 }); 5830 } 5831 // Set new array on C++ side. 5832 // C++ returns array of indexes of newly added array items. 5833 // these are indexes in new child list. 5834 ForEach.setIdArray(elmtId, newIdArray, diffIndexArray, idDuplicates); 5835 // Its error if there are duplicate IDs. 5836 if (idDuplicates.length > 0) { 5837 idDuplicates.forEach((indx) => { 5838 stateMgmtConsole.error(`Error: ${newIdArray[indx]} generated for ${indx}${indx < 4 ? indx == 2 ? "nd" : "rd" : "th"} array item ${arr[indx]}.`); 5839 }); 5840 stateMgmtConsole.applicationError(`${this.debugInfo__()}: Ids generated by the ForEach id gen function must be unique. Application error!`); 5841 } 5842 5843 // Item gen is with index. 5844 5845 // Create new elements if any. 5846 5847 diffIndexArray.forEach((indx) => { 5848 ForEach.createNewChildStart(newIdArray[indx], this); 5849 if (itemGenFuncUsesIndex) { 5850 itemGenFunc(arr[indx], indx); 5851 } 5852 else { 5853 itemGenFunc(arr[indx]); 5854 } 5855 ForEach.createNewChildFinish(newIdArray[indx], this); 5856 }); 5857 5858 5859 5860 } 5861 UpdateLazyForEachElements(elmtIds) { 5862 if (!Array.isArray(elmtIds)) { 5863 return; 5864 } 5865 Array.from(elmtIds).sort(ViewPU.compareNumber).forEach((elmtId) => { 5866 const entry = this.updateFuncByElmtId.get(elmtId); 5867 const updateFunc = entry ? entry.getUpdateFunc() : undefined; 5868 if (typeof updateFunc !== "function") { 5869 5870 } 5871 else { 5872 this.isRenderInProgress = true; 5873 updateFunc(elmtId, false); 5874 this.finishUpdateFunc(elmtId); 5875 this.isRenderInProgress = false; 5876 } 5877 }); 5878 } 5879 /** 5880 * CreateStorageLink and CreateStorageLinkPU are used by the implementation of @StorageLink and 5881 * @LocalStotrageLink in full update and partial update solution respectively. 5882 * These are not part of the public AppStorage API , apps should not use. 5883 * @param storagePropName - key in LocalStorage 5884 * @param defaultValue - value to use when creating a new prop in the LocalStotage 5885 * @param owningView - the View/ViewPU owning the @StorageLink/@LocalStorageLink variable 5886 * @param viewVariableName - @StorageLink/@LocalStorageLink variable name 5887 * @returns SynchedPropertySimple/ObjectTwoWay/PU 5888 */ 5889 createStorageLink(storagePropName, defaultValue, viewVariableName) { 5890 const appStorageLink = AppStorage.__createSync(storagePropName, defaultValue, (source) => (source === undefined) 5891 ? undefined 5892 : new SynchedPropertyTwoWayPU(source, this, viewVariableName)); 5893 return appStorageLink; 5894 } 5895 createStorageProp(storagePropName, defaultValue, viewVariableName) { 5896 const appStorageProp = AppStorage.__createSync(storagePropName, defaultValue, (source) => (source === undefined) 5897 ? undefined 5898 : new SynchedPropertyOneWayPU(source, this, viewVariableName)); 5899 return appStorageProp; 5900 } 5901 createLocalStorageLink(storagePropName, defaultValue, viewVariableName) { 5902 const localStorageLink = this.localStorage_.__createSync(storagePropName, defaultValue, (source) => (source === undefined) 5903 ? undefined 5904 : new SynchedPropertyTwoWayPU(source, this, viewVariableName)); 5905 return localStorageLink; 5906 } 5907 createLocalStorageProp(storagePropName, defaultValue, viewVariableName) { 5908 const localStorageProp = this.localStorage_.__createSync(storagePropName, defaultValue, (source) => (source === undefined) 5909 ? undefined 5910 : new SynchedPropertyObjectOneWayPU(source, this, viewVariableName)); 5911 return localStorageProp; 5912 } 5913 createOrGetNode(elmtId, builder) { 5914 const entry = this.updateFuncByElmtId.get(elmtId); 5915 if (entry === undefined) { 5916 throw new Error(`${this.debugInfo__()} fail to create node, elmtId is illegal`); 5917 } 5918 let nodeInfo = entry.getNode(); 5919 if (nodeInfo === undefined) { 5920 nodeInfo = builder(); 5921 entry.setNode(nodeInfo); 5922 } 5923 return nodeInfo; 5924 } 5925 /** 5926 * onDumpInfo is used to process commands delivered by the hidumper process 5927 * @param commands - list of commands provided in the shell 5928 * @returns void 5929 */ 5930 onDumpInfo(commands) { 5931 let dfxCommands = this.processOnDumpCommands(commands); 5932 dfxCommands.forEach((command) => { 5933 let view = undefined; 5934 if (command.viewId) { 5935 view = this.findViewInHierarchy(command.viewId); 5936 if (!view) { 5937 DumpLog.print(0, `\nTarget view: ${command.viewId} not found for command: ${command.what}\n`); 5938 return; 5939 } 5940 } 5941 else { 5942 view = this; 5943 command.viewId = view.id__(); 5944 } 5945 switch (command.what) { 5946 case "-dumpAll": 5947 view.printDFXHeader("ViewPU Info", command); 5948 DumpLog.print(0, view.debugInfoView(command.isRecursive)); 5949 break; 5950 case "-viewHierarchy": 5951 view.printDFXHeader("ViewPU Hierarchy", command); 5952 DumpLog.print(0, view.debugInfoViewHierarchy(command.isRecursive)); 5953 break; 5954 case "-stateVariables": 5955 view.printDFXHeader("ViewPU State Variables", command); 5956 DumpLog.print(0, view.debugInfoStateVars()); 5957 break; 5958 case "-registeredElementIds": 5959 view.printDFXHeader("ViewPU Registered Element IDs", command); 5960 DumpLog.print(0, view.debugInfoUpdateFuncByElmtId(command.isRecursive)); 5961 break; 5962 case "-dirtyElementIds": 5963 view.printDFXHeader("ViewPU Dirty Registered Element IDs", command); 5964 DumpLog.print(0, view.debugInfoDirtDescendantElementIds(command.isRecursive)); 5965 break; 5966 case "-inactiveComponents": 5967 view.printDFXHeader("List of Inactive Components", command); 5968 DumpLog.print(0, view.debugInfoInactiveComponents()); 5969 break; 5970 case "-profiler": 5971 view.printDFXHeader("Profiler Info", command); 5972 view.dumpReport(); 5973 break; 5974 default: 5975 DumpLog.print(0, `\nUnsupported JS DFX dump command: [${command.what}, viewId=${command.viewId}, isRecursive=${command.isRecursive}]\n`); 5976 } 5977 }); 5978 } 5979 printDFXHeader(header, command) { 5980 let length = 50; 5981 let remainder = length - header.length < 0 ? 0 : length - header.length; 5982 DumpLog.print(0, `\n${'-'.repeat(remainder / 2)}${header}${'-'.repeat(remainder / 2)}`); 5983 DumpLog.print(0, `[${command.what}, viewId=${command.viewId}, isRecursive=${command.isRecursive}]\n`); 5984 } 5985 processOnDumpCommands(commands) { 5986 let isFlag = (param) => { 5987 return "-r".match(param) != null || param.startsWith("-viewId="); 5988 }; 5989 let dfxCommands = []; 5990 for (var i = 0; i < commands.length; i++) { 5991 let command = commands[i]; 5992 if (isFlag(command)) { 5993 if (command.startsWith("-viewId=")) { 5994 let dfxCommand = dfxCommands[dfxCommands.length - 1]; 5995 if (dfxCommand) { 5996 let input = command.split('='); 5997 if (input[1]) { 5998 let viewId = Number.parseInt(input[1]); 5999 dfxCommand.viewId = Number.isNaN(viewId) ? -1 : viewId; 6000 } 6001 } 6002 } 6003 else if (command.match("-r")) { 6004 let dfxCommand = dfxCommands[dfxCommands.length - 1]; 6005 if (dfxCommand) { 6006 dfxCommand.isRecursive = true; 6007 } 6008 } 6009 } 6010 else { 6011 dfxCommands.push({ 6012 what: command, 6013 viewId: undefined, 6014 isRecursive: false, 6015 }); 6016 } 6017 } 6018 return dfxCommands; 6019 } 6020 findViewInHierarchy(id) { 6021 let weak = this.childrenWeakrefMap_.get(id); 6022 if (weak) { 6023 return weak.deref(); 6024 } 6025 let retVal = undefined; 6026 for (const [key, value] of this.childrenWeakrefMap_.entries()) { 6027 retVal = value.deref().findViewInHierarchy(id); 6028 if (retVal) 6029 break; 6030 } 6031 return retVal; 6032 } 6033 debugInfoView(recursive = false) { 6034 return this.debugInfoViewInternal(recursive); 6035 } 6036 debugInfoViewInternal(recursive = false) { 6037 let retVal = `@Component\n${this.constructor.name}[${this.id__()}]`; 6038 retVal += `\n\nView Hierarchy:\n${this.debugInfoViewHierarchy(recursive)}`; 6039 retVal += `\n\nState variables:\n${this.debugInfoStateVars()}`; 6040 retVal += `\n\nRegistered Element IDs:\n${this.debugInfoUpdateFuncByElmtId(recursive)}`; 6041 retVal += `\n\nDirty Registered Element IDs:\n${this.debugInfoDirtDescendantElementIds(recursive)}`; 6042 return retVal; 6043 } 6044 debugInfoViewHierarchy(recursive = false) { 6045 return this.debugInfoViewHierarchyInternal(0, recursive); 6046 } 6047 debugInfoViewHierarchyInternal(depth = 0, recursive = false) { 6048 let retVaL = `\n${" ".repeat(depth)}|--${this.constructor.name}[${this.id__()}]`; 6049 if (this.isCompFreezeAllowed) { 6050 retVaL += ` {freezewhenInactive : ${this.isCompFreezeAllowed}}`; 6051 } 6052 if (depth < 1 || recursive) { 6053 this.childrenWeakrefMap_.forEach((value, key, map) => { 6054 var _a; 6055 retVaL += (_a = value.deref()) === null || _a === void 0 ? void 0 : _a.debugInfoViewHierarchyInternal(depth + 1, recursive); 6056 }); 6057 } 6058 return retVaL; 6059 } 6060 debugInfoUpdateFuncByElmtId(recursive = false) { 6061 return this.debugInfoUpdateFuncByElmtIdInternal({ total: 0 }, 0, recursive); 6062 } 6063 debugInfoUpdateFuncByElmtIdInternal(counter, depth = 0, recursive = false) { 6064 let retVaL = `\n${" ".repeat(depth)}|--${this.constructor.name}[${this.id__()}]: {`; 6065 this.updateFuncByElmtId.forEach((value, key, map) => { 6066 retVaL += `\n${" ".repeat(depth + 2)}${value.getComponentName()}[${key}]`; 6067 }); 6068 counter.total += this.updateFuncByElmtId.size; 6069 retVaL += `\n${" ".repeat(depth + 1)}}[${this.updateFuncByElmtId.size}]`; 6070 if (recursive) { 6071 this.childrenWeakrefMap_.forEach((value, key, map) => { 6072 var _a; 6073 retVaL += (_a = value.deref()) === null || _a === void 0 ? void 0 : _a.debugInfoUpdateFuncByElmtIdInternal(counter, depth + 1, recursive); 6074 }); 6075 } 6076 if (recursive && depth == 0) { 6077 retVaL += `\nTotal: ${counter.total}`; 6078 } 6079 return retVaL; 6080 } 6081 debugInfoDirtDescendantElementIds(recursive = false) { 6082 return this.debugInfoDirtDescendantElementIdsInternal(0, recursive, { total: 0 }); 6083 } 6084 debugInfoDirtDescendantElementIdsInternal(depth = 0, recursive = false, counter) { 6085 let retVaL = `\n${" ".repeat(depth)}|--${this.constructor.name}[${this.id__()}]: {`; 6086 this.dirtDescendantElementIds_.forEach((value) => { 6087 retVaL += `${value}, `; 6088 }); 6089 counter.total += this.dirtDescendantElementIds_.size; 6090 retVaL += `\n${" ".repeat(depth + 1)}}[${this.dirtDescendantElementIds_.size}]`; 6091 if (recursive) { 6092 this.childrenWeakrefMap_.forEach((value, key, map) => { 6093 var _a; 6094 retVaL += (_a = value.deref()) === null || _a === void 0 ? void 0 : _a.debugInfoDirtDescendantElementIdsInternal(depth + 1, recursive, counter); 6095 }); 6096 } 6097 if (recursive && depth == 0) { 6098 retVaL += `\nTotal: ${counter.total}`; 6099 } 6100 return retVaL; 6101 } 6102 debugInfoInactiveComponents() { 6103 return Array.from(ViewPU.inactiveComponents_) 6104 .map((component) => `- ${component}`).join('\n'); 6105 } 6106} 6107// Array.sort() converts array items to string to compare them! 6108ViewPU.compareNumber = (a, b) => { 6109 return (a < b) ? -1 : (a > b) ? 1 : 0; 6110}; 6111// List of inactive components used for Dfx 6112ViewPU.inactiveComponents_ = new Set(); 6113// static flag for paused rendering 6114// when paused, getCurrentlyRenderedElmtId() will return -1 6115ViewPU.renderingPaused = false; 6116class UpdateFuncsByElmtId { 6117 constructor() { 6118 this.map_ = new Map(); 6119 } 6120 delete(elmtId) { 6121 return this.map_.delete(elmtId); 6122 } 6123 set(elmtId, params) { 6124 (typeof params === 'object') ? 6125 this.map_.set(elmtId, new UpdateFuncRecord(params)) : 6126 this.map_.set(elmtId, new UpdateFuncRecord({ updateFunc: params })); 6127 } 6128 get(elmtId) { 6129 return this.map_.get(elmtId); 6130 } 6131 has(elmtId) { 6132 return this.map_.has(elmtId); 6133 } 6134 keys() { 6135 return this.map_.keys(); 6136 } 6137 clear() { 6138 return this.map_.clear(); 6139 } 6140 get size() { 6141 return this.map_.size; 6142 } 6143 forEach(callbackfn) { 6144 this.map_.forEach(callbackfn); 6145 } 6146 // dump info about known elmtIds to a string 6147 // use function only for debug output and DFX. 6148 debugInfoRegisteredElmtIds() { 6149 let result = ""; 6150 let sepa = ""; 6151 this.map_.forEach((value, elmtId) => { 6152 result += `${sepa}${value.getComponentName()}[${elmtId}]`; 6153 sepa = ", "; 6154 }); 6155 return result; 6156 } 6157 debugInfoElmtId(elmtId) { 6158 const updateFuncEntry = this.map_.get(elmtId); 6159 return updateFuncEntry ? `'${updateFuncEntry.getComponentName()}[${elmtId}]'` : `'unknown component type'[${elmtId}]`; 6160 } 6161} 6162/* 6163 * Copyright (c) 2023 Huawei Device Co., Ltd. 6164 * Licensed under the Apache License, Version 2.0 (the "License"); 6165 * you may not use this file except in compliance with the License. 6166 * You may obtain a copy of the License at 6167 * 6168 * http://www.apache.org/licenses/LICENSE-2.0 6169 * 6170 * Unless required by applicable law or agreed to in writing, software 6171 * distributed under the License is distributed on an "AS IS" BASIS, 6172 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 6173 * See the License for the specific language governing permissions and 6174 * limitations under the License. 6175 * 6176 * * RecycleManager - Recycle cache manager 6177 * 6178* all definitions in this file are framework internal 6179*/ 6180/** 6181 * @class RecycleManager 6182 * @description manage the JS object cached of current node 6183 */ 6184class RecycleManager { 6185 constructor() { 6186 // key: recycle node name 6187 // value: recycle node JS object 6188 this.cachedRecycleNodes_ = undefined; 6189 this.biMap_ = undefined; 6190 this.cachedRecycleNodes_ = new Map(); 6191 this.biMap_ = new BidirectionalMap(); 6192 } 6193 updateNodeId(oldElmtId, newElmtId) { 6194 this.biMap_.delete(oldElmtId); 6195 this.biMap_.add([oldElmtId, newElmtId]); 6196 } 6197 proxyNodeId(oldElmtId) { 6198 const proxy = this.biMap_.get(oldElmtId); 6199 if (!proxy) { 6200 return oldElmtId; 6201 } 6202 return proxy; 6203 } 6204 pushRecycleNode(name, node) { 6205 var _a; 6206 if (!this.cachedRecycleNodes_.get(name)) { 6207 this.cachedRecycleNodes_.set(name, new Array()); 6208 } 6209 (_a = this.cachedRecycleNodes_.get(name)) === null || _a === void 0 ? void 0 : _a.push(node); 6210 } 6211 popRecycleNode(name) { 6212 var _a; 6213 return (_a = this.cachedRecycleNodes_.get(name)) === null || _a === void 0 ? void 0 : _a.pop(); 6214 } 6215 // When parent JS View is deleted, release all cached nodes 6216 purgeAllCachedRecycleNode() { 6217 this.cachedRecycleNodes_.forEach((nodes, _) => { 6218 nodes.forEach((node) => { 6219 node.resetRecycleCustomNode(); 6220 }); 6221 }); 6222 this.cachedRecycleNodes_.clear(); 6223 } 6224 // Set active status for all cached nodes 6225 setActive(active) { 6226 this.cachedRecycleNodes_.forEach((nodes, _) => { 6227 nodes.forEach((node) => { 6228 node.setActiveInternal(active); 6229 }); 6230 }); 6231 } 6232} 6233class BidirectionalMap { 6234 constructor() { 6235 this.fwdMap_ = undefined; 6236 this.revMap_ = undefined; 6237 this.fwdMap_ = new Map(); 6238 this.revMap_ = new Map(); 6239 } 6240 delete(key) { 6241 if (!this.fwdMap_[key]) { 6242 return; 6243 } 6244 const rev = this.fwdMap_[key]; 6245 this.fwdMap_.delete(key); 6246 this.revMap_.delete(rev); 6247 } 6248 get(key) { 6249 return this.fwdMap_[key] || this.revMap_[key]; 6250 } 6251 add(pair) { 6252 this.fwdMap_[pair[0]] = pair[1]; 6253 this.revMap_[pair[1]] = pair[0]; 6254 } 6255} 6256/* 6257 * Copyright (c) 2022 Huawei Device Co., Ltd. 6258 * Licensed under the Apache License, Version 2.0 (the "License"); 6259 * you may not use this file except in compliance with the License. 6260 * You may obtain a copy of the License at 6261 * 6262 * http://www.apache.org/licenses/LICENSE-2.0 6263 * 6264 * Unless required by applicable law or agreed to in writing, software 6265 * distributed under the License is distributed on an "AS IS" BASIS, 6266 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 6267 * See the License for the specific language governing permissions and 6268 * limitations under the License. 6269 * 6270 * * ViewPU - View for Partial Update 6271 * 6272* all definitions in this file are framework internal 6273*/ 6274/** 6275 given parameters for calling a @Builder function 6276 this function wraps the Object of type T inside a ES6 Proxy. 6277 Each param, i.e. Object property is either a function or a value. 6278 If it is a function the function can either return a value of expected 6279 parameter type or an ObservedPropertyabstract<T> where T is the exected 6280 parameter type. The latter is the case when passing a state variable by 6281 reference. 6282 6283 Two purposes: 6284 1 - @Builder function boxy accesses params a '$$.paramA' 6285 However paramA can be a function, so to obtain the value the 6286 access would need to be '$$.param()' The proxy executes 6287 the function and return s the result 6288 2 - said function returns to ObservedPropertyAbstract backing store of 6289 a calling @Component state variable (whenever the state var is 6290 provided to the @Builder function). For this case the proxy can provide 6291 - the value by executing paramA() to return the ObservedPropertyAbstract 6292 and further (monitored!) get() to read its value 6293 - when requested to return '__param1' it returns the ObservedPropertyAbstract 6294 object. The scenario is to use to init a @Link source. 6295 */ 6296function makeBuilderParameterProxy(builderName, source) { 6297 return new Proxy(source, { 6298 set(target, prop, val) { 6299 throw Error(`@Builder '${builderName}': Invalid attempt to set(write to) parameter '${prop.toString()}' error!`); 6300 }, 6301 get(target, prop) { 6302 const prop1 = prop.toString().trim().startsWith("__") 6303 ? prop.toString().trim().substring(2) 6304 : prop.toString().trim(); 6305 6306 if (!(typeof target === "object") && (prop1 in target)) { 6307 throw Error(`@Builder '${builderName}': '${prop1}' used but not a function parameter error!`); 6308 } 6309 const value = target[prop1]; 6310 if (typeof value !== "function") { 6311 6312 return value; 6313 } 6314 const funcRet = value(); 6315 if ((typeof funcRet === "object") && ('get' in funcRet)) { 6316 if (prop1 !== prop) { 6317 6318 return funcRet; 6319 } 6320 else { 6321 6322 const result = funcRet.get(); 6323 6324 return result; 6325 } 6326 } 6327 6328 return funcRet; 6329 } // get 6330 }); // new Proxy 6331} 6332/* 6333 * Copyright (c) 2021-2023 Huawei Device Co., Ltd. 6334 * Licensed under the Apache License, Version 2.0 (the "License"); 6335 * you may not use this file except in compliance with the License. 6336 * You may obtain a copy of the License at 6337 * 6338 * http://www.apache.org/licenses/LICENSE-2.0 6339 * 6340 * Unless required by applicable law or agreed to in writing, software 6341 * distributed under the License is distributed on an "AS IS" BASIS, 6342 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 6343 * See the License for the specific language governing permissions and 6344 * limitations under the License. 6345 */ 6346 6347PersistentStorage.configureBackend(new Storage()); 6348Environment.configureBackend(new EnvironmentSetting()); 6349 6350 6351