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