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