@angular-wave/angular.ts 0.4.3 → 0.4.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@angular-wave/angular.ts",
3
3
  "description": "A modern, optimized and typesafe version of AngularJS",
4
4
  "license": "MIT",
5
- "version": "0.4.3",
5
+ "version": "0.4.5",
6
6
  "type": "module",
7
7
  "main": "dist/angular-ts.esm.js",
8
8
  "browser": "dist/angular-ts.umd.js",
@@ -7,7 +7,7 @@
7
7
  *
8
8
  */
9
9
 
10
- export const EXPANDO = "ngId";
10
+ export const EXPANDO = "ng";
11
11
 
12
12
  /**
13
13
  * Expando cache for adding properties to DOM nodes with JavaScript.
@@ -17,31 +17,4 @@ export const EXPANDO = "ngId";
17
17
  *
18
18
  * @type {Map<number, ExpandoStore>}
19
19
  */
20
- export const CACHE = new Proxy(new Map(), {
21
- get(target, prop, receiver) {
22
- if (prop === "size") {
23
- return target.size;
24
- }
25
- if (typeof target[prop] === "function") {
26
- return function (...args) {
27
- return target[prop].apply(target, args);
28
- };
29
- }
30
- return Reflect.get(target, prop, receiver);
31
- },
32
- set(target, prop, value, receiver) {
33
- return Reflect.set(target, prop, value, receiver);
34
- },
35
- deleteProperty(target, prop) {
36
- return Reflect.deleteProperty(target, prop);
37
- },
38
- has(target, prop) {
39
- return Reflect.has(target, prop);
40
- },
41
- ownKeys(target) {
42
- return Reflect.ownKeys(target);
43
- },
44
- getOwnPropertyDescriptor(target, prop) {
45
- return Reflect.getOwnPropertyDescriptor(target, prop);
46
- },
47
- });
20
+ export const CACHE = new Map();
@@ -906,7 +906,7 @@ export function CompileProvider($provide, $$sanitizeUriProvider) {
906
906
  containingScope,
907
907
  ) {
908
908
  if (!transcludedScope) {
909
- transcludedScope = scope.$new(false, containingScope);
909
+ transcludedScope = scope.$transcluded(containingScope);
910
910
  transcludedScope.$$transcluded = true;
911
911
  }
912
912
 
@@ -5,7 +5,7 @@ const TEST_URL = "src/core/compile/compile.html?random=false";
5
5
  test("unit tests contain no errors", async ({ page }) => {
6
6
  await page.goto(TEST_URL);
7
7
  await page.content();
8
- await page.waitForTimeout(1000);
8
+ await page.waitForTimeout(4000);
9
9
  await expect(page.locator(".jasmine-overall-result")).toHaveText(
10
10
  /0 failures/,
11
11
  );
@@ -2,7 +2,6 @@ import { JQLite } from "../../shared/jqlite/jqlite";
2
2
  import { urlResolve } from "../url-utils/url-utils";
3
3
  import {
4
4
  encodeUriSegment,
5
- isBoolean,
6
5
  isDefined,
7
6
  isNumber,
8
7
  isObject,
@@ -330,7 +329,6 @@ export class Location {
330
329
  // so the modification window is narrow.
331
330
  this.$$state = isUndefined(state) ? null : state;
332
331
  this.$$urlUpdatedByLocation = true;
333
-
334
332
  return this;
335
333
  }
336
334
 
@@ -544,26 +542,28 @@ export class LocationHashbangUrl extends Location {
544
542
  }
545
543
  }
546
544
 
547
- export function LocationProvider() {
548
- let hashPrefix = "!";
549
- const html5Mode = {
550
- enabled: false,
551
- requireBase: true,
552
- rewriteLinks: true,
553
- };
545
+ export class LocationProvider {
546
+ constructor() {
547
+ this.hashPrefixValue = "!";
548
+ this.html5ModeConfig = {
549
+ enabled: false,
550
+ requireBase: true,
551
+ rewriteLinks: true,
552
+ };
553
+ }
554
554
 
555
555
  /**
556
556
  * The default value for the prefix is `'!'`.
557
- * @param {string=} prefix Prefix for hash part (containing path and search)
558
- * @returns {*} current value if used as getter or itself (chaining) if used as setter
557
+ * @param {string=} prefix - Prefix for hash part (containing path and search)
558
+ * @returns {string|LocationProvider} current value if used as getter or itself (chaining) if used as setter
559
559
  */
560
- this.hashPrefix = function (prefix) {
561
- if (isDefined(prefix)) {
562
- hashPrefix = prefix;
560
+ hashPrefix(prefix) {
561
+ if (typeof prefix !== "undefined") {
562
+ this.hashPrefixValue = prefix;
563
563
  return this;
564
564
  }
565
- return hashPrefix;
566
- };
565
+ return this.hashPrefixValue;
566
+ }
567
567
 
568
568
  /**
569
569
  * @param {(boolean|Object)=} mode If boolean, sets `html5Mode.enabled` to value.
@@ -585,30 +585,29 @@ export function LocationProvider() {
585
585
  *
586
586
  * @returns {Object} html5Mode object if used as getter or itself (chaining) if used as setter
587
587
  */
588
- this.html5Mode = function (mode) {
589
- if (isBoolean(mode)) {
590
- html5Mode.enabled = mode;
588
+ html5Mode(mode) {
589
+ if (typeof mode === "boolean") {
590
+ this.html5ModeConfig.enabled = mode;
591
591
  return this;
592
592
  }
593
- if (isObject(mode)) {
594
- if (isBoolean(mode.enabled)) {
595
- html5Mode.enabled = mode.enabled;
596
- }
597
593
 
598
- if (isBoolean(mode.requireBase)) {
599
- html5Mode.requireBase = mode.requireBase;
600
- }
601
-
602
- if (isBoolean(mode.rewriteLinks) || isString(mode.rewriteLinks)) {
603
- html5Mode.rewriteLinks = mode.rewriteLinks;
594
+ if (typeof mode === "object") {
595
+ if (typeof mode.enabled === "boolean")
596
+ this.html5ModeConfig.enabled = mode.enabled;
597
+ if (typeof mode.requireBase === "boolean")
598
+ this.html5ModeConfig.requireBase = mode.requireBase;
599
+ if (
600
+ typeof mode.rewriteLinks === "boolean" ||
601
+ typeof mode.rewriteLinks === "string"
602
+ ) {
603
+ this.html5ModeConfig.rewriteLinks = mode.rewriteLinks;
604
604
  }
605
-
606
605
  return this;
607
606
  }
608
- return html5Mode;
609
- };
607
+ return this.html5ModeConfig;
608
+ }
610
609
 
611
- this.$get = [
610
+ $get = [
612
611
  "$rootScope",
613
612
  "$browser",
614
613
  "$rootElement",
@@ -627,8 +626,8 @@ export function LocationProvider() {
627
626
  const initialUrl = /** @type {string} */ ($browser.url());
628
627
  let appBase;
629
628
 
630
- if (html5Mode.enabled) {
631
- if (!baseHref && html5Mode.requireBase) {
629
+ if (this.html5Mode.enabled) {
630
+ if (!baseHref && this.html5Mode.requireBase) {
632
631
  throw $locationMinErr(
633
632
  "nobase",
634
633
  "$location in HTML5 mode requires a <base> tag to be present!",
@@ -642,7 +641,11 @@ export function LocationProvider() {
642
641
  }
643
642
  const appBaseNoFile = stripFile(appBase);
644
643
 
645
- $location = new LocationMode(appBase, appBaseNoFile, `#${hashPrefix}`);
644
+ $location = new LocationMode(
645
+ appBase,
646
+ appBaseNoFile,
647
+ `#${this.hashPrefix}`,
648
+ );
646
649
  $location.$$parseLinkUrl(initialUrl, initialUrl);
647
650
 
648
651
  $location.$$state = $browser.state();
@@ -669,7 +672,7 @@ export function LocationProvider() {
669
672
  }
670
673
 
671
674
  $rootElement.on("click", (event) => {
672
- const { rewriteLinks } = html5Mode;
675
+ const { rewriteLinks } = this.html5Mode;
673
676
  // TODO(vojta): rewrite link when opening in new tab/window (in legacy browser)
674
677
  // currently we open nice url link and redirect then
675
678
 
@@ -772,7 +775,7 @@ export function LocationProvider() {
772
775
  });
773
776
 
774
777
  // update browser
775
- $rootScope.$watch(() => {
778
+ function browserUpdate() {
776
779
  if (initializing || $location.$$urlUpdatedByLocation) {
777
780
  $location.$$urlUpdatedByLocation = false;
778
781
 
@@ -820,7 +823,9 @@ export function LocationProvider() {
820
823
 
821
824
  // we don't need to return anything because $evalAsync will make the digest loop dirty when
822
825
  // there is a change
823
- });
826
+ }
827
+
828
+ setTimeout(() => browserUpdate());
824
829
 
825
830
  return $location;
826
831
 
@@ -832,6 +837,7 @@ export function LocationProvider() {
832
837
  $location.$$state,
833
838
  oldState,
834
839
  );
840
+ browserUpdate();
835
841
  }
836
842
  },
837
843
  ];
@@ -208,11 +208,6 @@ export class Scope {
208
208
  /** @type {number} */
209
209
  this.$$watchersCount = 0;
210
210
  this.$$isolateBindings = null;
211
-
212
- /**
213
- * @type {?Scope}
214
- */
215
- this.$$ChildScope = null;
216
211
  }
217
212
 
218
213
  /**
@@ -230,16 +225,11 @@ export class Scope {
230
225
  * When creating widgets, it is useful for the widget to not accidentally read parent
231
226
  * state.
232
227
  *
233
- * @param {?Scope} [parent=this] The {@link ng.$rootScope.Scope `Scope`} that will be the `$parent`
234
- * of the newly created scope. Defaults to `this` scope if not provided.
235
- * This is used when creating a transclude scope to correctly place it
236
- * in the scope hierarchy while maintaining the correct prototypical
237
- * inheritance.
238
228
  *
239
229
  * @returns {Scope} The newly created child scope.
240
230
  *
241
231
  */
242
- $new(isolate, parent) {
232
+ $new(isolate) {
243
233
  let child = isolate ? new Scope() : Object.create(this);
244
234
 
245
235
  if (isolate) {
@@ -254,11 +244,10 @@ export class Scope {
254
244
  child.$$listeners = new Map();
255
245
  child.$$listenerCount = {};
256
246
  child.$$watchersCount = 0;
257
- child.$$ChildScope = null;
258
247
  child.$$suspended = false;
259
248
  }
260
249
 
261
- child.$parent = parent || this;
250
+ child.$parent = this;
262
251
  child.$$prevSibling = child.$parent.$$childTail;
263
252
 
264
253
  if (child.$parent.$$childHead) {
@@ -270,7 +259,7 @@ export class Scope {
270
259
  }
271
260
 
272
261
  // Add a destroy listener if isolated or the parent differs from `this`
273
- if (isolate || parent !== this) {
262
+ if (isolate) {
274
263
  child.$on("$destroy", ($event) => {
275
264
  $event.currentScope.$$destroyed = true;
276
265
  });
@@ -279,6 +268,49 @@ export class Scope {
279
268
  return child;
280
269
  }
281
270
 
271
+ /**
272
+ * Creates a transcluded scope
273
+ * @param {Scope} parent The {@link ng.$rootScope.Scope `Scope`} that will be the `$parent`
274
+ * of the newly created scope. This is used when creating a transclude scope to correctly place it
275
+ * in the scope hierarchy while maintaining the correct prototypical inheritance.
276
+ *
277
+ * @returns {Scope} The newly created child scope.
278
+ *
279
+ */
280
+ $transcluded(parent) {
281
+ let child = Object.create(this);
282
+
283
+ // Initialize properties for a non-isolated child scope
284
+ child.$id = nextUid();
285
+ child.$$watchers = [];
286
+ child.$$nextSibling = null;
287
+ child.$$childHead = null;
288
+ child.$$childTail = null;
289
+ child.$$listeners = new Map();
290
+ child.$$listenerCount = {};
291
+ child.$$watchersCount = 0;
292
+ child.$$suspended = false;
293
+
294
+ child.$parent = parent || this;
295
+ child.$$prevSibling = child.$parent.$$childTail;
296
+
297
+ if (child.$parent.$$childHead) {
298
+ child.$parent.$$childTail.$$nextSibling = child;
299
+ child.$parent.$$childTail = child;
300
+ } else {
301
+ child.$parent.$$childHead = child;
302
+ child.$parent.$$childTail = child;
303
+ }
304
+
305
+ // Add a destroy listener if isolated or the parent differs from `this`
306
+ if (parent !== this) {
307
+ child.$on("$destroy", ($event) => {
308
+ $event.currentScope.$$destroyed = true;
309
+ });
310
+ }
311
+
312
+ return child;
313
+ }
282
314
  /**
283
315
  * Registers a `listener` callback to be executed whenever the `watchExpression` changes.
284
316
  *
@@ -135,7 +135,7 @@ describe("Scope", function () {
135
135
 
136
136
  it("should attach the child scope to a specified parent", () => {
137
137
  const isolated = $rootScope.$new(true);
138
- const trans = $rootScope.$new(false, isolated);
138
+ const trans = $rootScope.$transcluded(isolated);
139
139
  $rootScope.a = 123;
140
140
  expect(isolated.a).toBeUndefined();
141
141
  expect(trans.a).toEqual(123);
@@ -65,7 +65,7 @@ describe("urlUtils", () => {
65
65
 
66
66
  expectIsSameOrigin("path", true);
67
67
 
68
- const origin = urlResolve(document.location.href);
68
+ const origin = urlResolve(window.location.href);
69
69
  expectIsSameOrigin(`//${origin.host}/path`, true);
70
70
 
71
71
  // Different domain.
@@ -10,7 +10,7 @@
10
10
  <script src="/jasmine/jasmine-5.1.2/jasmine-html.js"></script>
11
11
  <script src="/jasmine/jasmine-5.1.2/boot0.js"></script>
12
12
  <script src="/jasmine/jasmine-5.1.2/boot1.js"></script>
13
- <script type="module" src="/src/directive/list/list.spec.js"></script>
13
+ <script type="module" src="/src/directive/channel/channel.spec.js"></script>
14
14
  </head>
15
15
  <body>
16
16
  <div id="dummy"></div>
@@ -0,0 +1,29 @@
1
+ import { EventBus } from "../../core/pubsub/pubsub";
2
+
3
+ /**
4
+ * Dynamically updates an element's content based on events published on a specified channel.
5
+ * When the directive is applied, it listens for data sent via `EventBus` and
6
+ * updates the inner HTML of the element accordingly.
7
+ *
8
+ * When the scope is destroyed, the directive automatically unsubscribes from the channel.
9
+ *
10
+ *
11
+ * @returns {import("../../types").Directive}
12
+ */
13
+ export function ngChannelDirective() {
14
+ return {
15
+ restrict: "EA",
16
+ link: (scope, element, attrs) => {
17
+ const targetElement = element[0];
18
+ const channel = attrs["ngChannel"];
19
+
20
+ const key = EventBus.subscribe(channel, (val) => {
21
+ targetElement.innerHTML = val;
22
+ });
23
+
24
+ scope.$on("$destroy", () => {
25
+ EventBus.unsubscribeByKey(key);
26
+ });
27
+ },
28
+ };
29
+ }
@@ -0,0 +1,52 @@
1
+ import { Angular } from "../../loader";
2
+ import { EventBus } from "../../core/pubsub/pubsub";
3
+ import { wait } from "../../shared/test-utils";
4
+
5
+ describe("channel", () => {
6
+ let $compile, $scope, element, unsubscribeSpy;
7
+
8
+ beforeEach(() => {
9
+ window.angular = new Angular();
10
+ angular.module("myModule", ["ng"]);
11
+ angular
12
+ .bootstrap(document.getElementById("dummy"), ["myModule"])
13
+ .invoke((_$compile_, _$rootScope_) => {
14
+ $compile = _$compile_;
15
+ $scope = _$rootScope_;
16
+ });
17
+
18
+ spyOn(EventBus, "subscribe").and.callThrough();
19
+ unsubscribeSpy = spyOn(EventBus, "unsubscribeByKey").and.callThrough();
20
+ });
21
+
22
+ it("should subscribe to the specified EventBus channel", () => {
23
+ element = $compile('<div ng-channel="testChannel"></div>')($scope);
24
+ $scope.$digest();
25
+
26
+ expect(EventBus.subscribe).toHaveBeenCalledWith(
27
+ "testChannel",
28
+ jasmine.any(Function),
29
+ );
30
+ });
31
+
32
+ it("should update innerHtml when EventBus emits a value", async () => {
33
+ element = $compile('<div ng-channel="testChannel"></div>')($scope);
34
+ $scope.$digest();
35
+
36
+ expect(element[0].innerHTML).toBe("");
37
+
38
+ EventBus.publish("testChannel", "New Content");
39
+ await wait(10);
40
+
41
+ expect(element[0].innerHTML).toBe("New Content");
42
+ });
43
+
44
+ it("should unsubscribe from the EventBus when the scope is destroyed", () => {
45
+ element = $compile('<div ng-channel="testChannel"></div>')($scope);
46
+ $scope.$digest();
47
+
48
+ $scope.$destroy();
49
+
50
+ expect(unsubscribeSpy).toHaveBeenCalled();
51
+ });
52
+ });
@@ -1,9 +1,7 @@
1
1
  import { test, expect } from "@playwright/test";
2
2
 
3
- const TEST_URL = "src/directive/list/list.html";
4
-
5
- test("unit tests contain no errors", async ({ page }) => {
6
- await page.goto(TEST_URL);
3
+ test("unit observer tests contain no errors", async ({ page }) => {
4
+ await page.goto("src/directive/channel/channel.html");
7
5
  await page.content();
8
6
  await expect(page.locator(".jasmine-overall-result")).toHaveText(
9
7
  /0 failures/,