@angular-wave/angular.ts 0.3.1 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/.github/workflows/ci.yml +57 -0
  2. package/README.md +1 -1
  3. package/dist/angular-ts.esm.js +2 -2
  4. package/dist/angular-ts.umd.js +2 -2
  5. package/package.json +1 -1
  6. package/src/core/compile/attributes.js +1 -1
  7. package/src/core/compile/compile.js +1 -1
  8. package/src/core/compile/compile.test.js +1 -1
  9. package/src/core/exception-handler.js +1 -5
  10. package/src/core/interpolate/interpolate.js +33 -26
  11. package/src/core/interval/interval-factory.js +6 -22
  12. package/src/core/interval/interval.spec.js +0 -25
  13. package/src/core/q/q.js +0 -27
  14. package/src/core/q/q.spec.js +0 -44
  15. package/src/core/scope/scope.js +35 -42
  16. package/src/core/scope/scope.spec.js +71 -13
  17. package/src/core/timeout/timeout.js +2 -6
  18. package/src/directive/attrs/attrs.js +1 -1
  19. package/src/directive/input/input.spec.js +0 -1
  20. package/src/directive/observe/observe.html +18 -0
  21. package/src/directive/observe/observe.js +37 -0
  22. package/src/directive/observe/observe.spec.js +92 -0
  23. package/src/directive/observe/observe.test.js +9 -0
  24. package/src/directive/observe/test.html +197 -0
  25. package/src/public.js +7 -6
  26. package/src/router/state/state-service.js +3 -3
  27. package/src/router/view-scroll.js +13 -8
  28. package/src/services/log.js +0 -6
  29. package/types/core/compile/attributes.d.ts +3 -3
  30. package/types/core/compile/compile.d.ts +1 -1
  31. package/types/core/exception-handler.d.ts +2 -2
  32. package/types/core/interpolate/interpolate.d.ts +7 -4
  33. package/types/core/interval/interval-factory.d.ts +1 -1
  34. package/types/core/q/q.d.ts +0 -5
  35. package/types/core/scope/scope.d.ts +11 -2
  36. package/types/core/timeout/timeout.d.ts +2 -2
  37. package/types/directive/observe/observe.d.ts +4 -0
  38. package/types/router/state/state-service.d.ts +1 -1
  39. package/types/router/view-scroll.d.ts +3 -3
  40. package/types/services/log.d.ts +0 -5
  41. package/.github/workflows/lint.yml +0 -19
  42. package/.github/workflows/playwright.yml +0 -27
  43. package/.github/workflows/types.yml +0 -19
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.3.1",
5
+ "version": "0.4.1",
6
6
  "type": "module",
7
7
  "main": "dist/angular-ts.esm.js",
8
8
  "browser": "dist/angular-ts.umd.js",
@@ -27,7 +27,7 @@ export class Attributes {
27
27
  /**
28
28
  * @param {import('../scope/scope').Scope} $rootScope
29
29
  * @param {*} $animate
30
- * @param {import("../exception-handler").ExceptionHandlerProvider} $exceptionHandler
30
+ * @param {import("../exception-handler").ErrorHandler} $exceptionHandler
31
31
  * @param {*} $sce
32
32
  * @param {import('../../shared/jqlite/jqlite').JQLite} [element]
33
33
  * @param {*} [attributesToCopy]
@@ -526,7 +526,7 @@ export function CompileProvider($provide, $$sanitizeUriProvider) {
526
526
  /**
527
527
  * @param {import("../../core/di/internal-injector").InjectorService} $injector
528
528
  * @param {*} $interpolate
529
- * @param {import("../exception-handler").ExceptionHandlerProvider} $exceptionHandler
529
+ * @param {import("../exception-handler").ErrorHandler} $exceptionHandler
530
530
  * @param {*} $templateRequest
531
531
  * @param {import("../parser/parse").ParseService} $parse
532
532
  * @param {*} $controller
@@ -1,6 +1,6 @@
1
1
  import { test, expect } from "@playwright/test";
2
2
 
3
- const TEST_URL = "src/core/compile/compile.html";
3
+ const TEST_URL = "src/core/compile/compile.html?random=false";
4
4
 
5
5
  test("unit tests contain no errors", async ({ page }) => {
6
6
  await page.goto(TEST_URL);
@@ -31,10 +31,6 @@
31
31
  *
32
32
  */
33
33
 
34
- /**
35
- * @typedef {import('../types').ServiceProvider} ExceptionHandlerProvider
36
- */
37
-
38
34
  /** @type {import('../services/log').LogService} */
39
35
  let log;
40
36
 
@@ -50,7 +46,7 @@ export const errorHandler = (exception, cause) => {
50
46
 
51
47
  /**
52
48
  * @constructor
53
- * @this {ExceptionHandlerProvider}
49
+ * @this {import('../types').ServiceProvider}
54
50
  */
55
51
  export function ExceptionHandlerProvider() {
56
52
  this.$get = [
@@ -40,9 +40,13 @@ function interr(text, err) {
40
40
  * security bugs!
41
41
  * </div>
42
42
  */
43
- export function InterpolateProvider() {
44
- let startSymbol = "{{";
45
- let endSymbol = "}}";
43
+ export class InterpolateProvider {
44
+ constructor() {
45
+ /** @type {string} */
46
+ this.start = "{{";
47
+ /** @type {string} */
48
+ this.end = "}}";
49
+ }
46
50
 
47
51
  /**
48
52
  * Symbol to denote start of expression in the interpolated string. Defaults to `{{`.
@@ -50,13 +54,13 @@ export function InterpolateProvider() {
50
54
  * @param {string=} value new value to set the starting symbol to.
51
55
  * @returns {string|InterpolateProvider} Returns the symbol when used as getter and self if used as setter.
52
56
  */
53
- this.startSymbol = function (value) {
57
+ startSymbol(value) {
54
58
  if (value) {
55
- startSymbol = value;
59
+ this.start = value;
56
60
  return this;
57
61
  }
58
- return startSymbol;
59
- };
62
+ return this.start;
63
+ }
60
64
 
61
65
  /**
62
66
  * Symbol to denote the end of expression in the interpolated string. Defaults to `}}`.
@@ -64,33 +68,36 @@ export function InterpolateProvider() {
64
68
  * @param {string=} value new value to set the ending symbol to.
65
69
  * @returns {string|InterpolateProvider} Returns the symbol when used as getter and self if used as setter.
66
70
  */
67
- this.endSymbol = function (value) {
71
+ endSymbol(value) {
68
72
  if (value) {
69
- endSymbol = value;
73
+ this.end = value;
70
74
  return this;
71
75
  }
72
- return endSymbol;
73
- };
76
+ return this.end;
77
+ }
74
78
 
75
- this.$get = [
79
+ $get = [
76
80
  "$parse",
77
- "$exceptionHandler",
78
81
  "$sce",
79
82
  /**
80
83
  *
81
84
  * @param {import("../parser/parse").ParseService} $parse
82
- * @param {import('../exception-handler').ErrorHandler} $exceptionHandler
83
85
  * @param {*} $sce
84
86
  * @returns
85
87
  */
86
- function ($parse, $exceptionHandler, $sce) {
87
- const startSymbolLength = startSymbol.length;
88
- const endSymbolLength = endSymbol.length;
88
+ function ($parse, $sce) {
89
+ /** @type {InterpolateProvider} */
90
+ const provider = this;
91
+ const startSymbolLength = provider.start.length;
92
+ const endSymbolLength = provider.end.length;
89
93
  const escapedStartRegexp = new RegExp(
90
- startSymbol.replace(/./g, escape),
94
+ this.start.replace(/./g, escape),
95
+ "g",
96
+ );
97
+ const escapedEndRegexp = new RegExp(
98
+ provider.end.replace(/./g, escape),
91
99
  "g",
92
100
  );
93
- const escapedEndRegexp = new RegExp(endSymbol.replace(/./g, escape), "g");
94
101
 
95
102
  function escape(ch) {
96
103
  return `\\\\\\${ch}`;
@@ -98,8 +105,8 @@ export function InterpolateProvider() {
98
105
 
99
106
  function unescapeText(text) {
100
107
  return text
101
- .replace(escapedStartRegexp, startSymbol)
102
- .replace(escapedEndRegexp, endSymbol);
108
+ .replace(escapedStartRegexp, provider.start)
109
+ .replace(escapedEndRegexp, provider.end);
103
110
  }
104
111
 
105
112
  /**
@@ -222,7 +229,7 @@ export function InterpolateProvider() {
222
229
  trustedContext === $sce.URL || trustedContext === $sce.MEDIA_URL;
223
230
 
224
231
  // Provide a quick exit and simplified result function for text with no interpolation
225
- if (!text.length || text.indexOf(startSymbol) === -1) {
232
+ if (!text.length || text.indexOf(provider.start) === -1) {
226
233
  if (mustHaveExpression) return;
227
234
 
228
235
  let unescapedText = unescapeText(text);
@@ -255,9 +262,9 @@ export function InterpolateProvider() {
255
262
 
256
263
  while (index < textLength) {
257
264
  if (
258
- (startIndex = text.indexOf(startSymbol, index)) !== -1 &&
265
+ (startIndex = text.indexOf(provider.start, index)) !== -1 &&
259
266
  (endIndex = text.indexOf(
260
- endSymbol,
267
+ provider.end,
261
268
  startIndex + startSymbolLength,
262
269
  )) !== -1
263
270
  ) {
@@ -390,7 +397,7 @@ export function InterpolateProvider() {
390
397
  * @returns {string} start symbol.
391
398
  */
392
399
  $interpolate.startSymbol = function () {
393
- return startSymbol;
400
+ return provider.start;
394
401
  };
395
402
 
396
403
  /**
@@ -402,7 +409,7 @@ export function InterpolateProvider() {
402
409
  * @returns {string} end symbol.
403
410
  */
404
411
  $interpolate.endSymbol = function () {
405
- return endSymbol;
412
+ return provider.end;
406
413
  };
407
414
 
408
415
  return $interpolate;
@@ -2,26 +2,20 @@ import { isDefined, sliceArgs } from "../../shared/utils";
2
2
 
3
3
  export function $IntervalFactoryProvider() {
4
4
  this.$get = [
5
- "$browser",
6
5
  "$q",
7
- "$$q",
8
6
  "$rootScope",
9
7
  /**
10
- *
11
- * @param {import('../../services/browser').Browser} $browser
12
8
  * @param {*} $q
13
- * @param {*} $$q
14
9
  * @param {import('../scope/scope').Scope} $rootScope
15
10
  * @returns
16
11
  */
17
- function ($browser, $q, $$q, $rootScope) {
12
+ function ($q, $rootScope) {
18
13
  return function intervalFactory(setIntervalFn, clearIntervalFn) {
19
- return function intervalFn(fn, delay, count, invokeApply) {
14
+ return function intervalFn(fn, delay, count) {
20
15
  const hasParams = arguments.length > 4;
21
16
  const args = hasParams ? sliceArgs(arguments, 4) : [];
22
17
  let iteration = 0;
23
- const skipApply = isDefined(invokeApply) && !invokeApply;
24
- const deferred = (skipApply ? $$q : $q).defer();
18
+ const deferred = $q.defer();
25
19
  const { promise } = deferred;
26
20
 
27
21
  count = isDefined(count) ? count : 0;
@@ -35,11 +29,7 @@ export function $IntervalFactoryProvider() {
35
29
  }
36
30
 
37
31
  function tick() {
38
- if (skipApply) {
39
- $browser.defer(callback);
40
- } else {
41
- $rootScope.$evalAsync(callback);
42
- }
32
+ $rootScope.$evalAsync(callback);
43
33
 
44
34
  iteration++;
45
35
 
@@ -47,16 +37,10 @@ export function $IntervalFactoryProvider() {
47
37
  deferred.resolve(iteration);
48
38
  clearIntervalFn(promise.$$intervalId);
49
39
  }
50
-
51
- if (!skipApply) $rootScope.$apply();
40
+ $rootScope.$apply();
52
41
  }
53
42
 
54
- promise.$$intervalId = setIntervalFn(
55
- tick,
56
- delay,
57
- deferred,
58
- skipApply,
59
- );
43
+ promise.$$intervalId = setIntervalFn(tick, delay, deferred);
60
44
 
61
45
  return promise;
62
46
  };
@@ -55,31 +55,6 @@ describe("$interval", () => {
55
55
  }, 1);
56
56
  });
57
57
 
58
- it("should NOT call $apply if invokeApply is set to false", (done) => {
59
- const applySpy = spyOn($rootScope, "$apply").and.callThrough();
60
-
61
- $interval(() => {}, 1, 0, false);
62
- expect(applySpy).not.toHaveBeenCalled();
63
-
64
- setTimeout(() => {
65
- expect(applySpy).not.toHaveBeenCalled();
66
- done();
67
- }, 2);
68
- });
69
-
70
- it("should NOT call $evalAsync or $digest if invokeApply is set to false", async () => {
71
- const evalAsyncSpy = spyOn($rootScope, "$evalAsync").and.callThrough();
72
- const digestSpy = spyOn($rootScope, "$digest").and.callThrough();
73
- const notifySpy = jasmine.createSpy("notify");
74
-
75
- $interval(notifySpy, 1, 1, false);
76
-
77
- await wait(100);
78
- expect(notifySpy).toHaveBeenCalled();
79
- expect(evalAsyncSpy).not.toHaveBeenCalled();
80
- expect(digestSpy).not.toHaveBeenCalled();
81
- });
82
-
83
58
  it("should allow you to specify a number of iterations", async () => {
84
59
  let counter = 0;
85
60
  $interval(
package/src/core/q/q.js CHANGED
@@ -98,33 +98,6 @@ export class $QProvider {
98
98
  }
99
99
  }
100
100
 
101
- export class $$QProvider {
102
- constructor() {
103
- this.errorOn = true;
104
- }
105
-
106
- $get = [
107
- "$exceptionHandler",
108
- function ($exceptionHandler) {
109
- return qFactory(
110
- (callback) => {
111
- window.setTimeout(callback);
112
- },
113
- $exceptionHandler,
114
- this.errorOn,
115
- );
116
- },
117
- ];
118
-
119
- errorOnUnhandledRejections(value) {
120
- if (isDefined(value)) {
121
- this.errorOn = value;
122
- return this;
123
- }
124
- return this.errorOn;
125
- }
126
- }
127
-
128
101
  /**
129
102
  * Constructs a promise manager.
130
103
  *
@@ -626,50 +626,6 @@ describe("all", function () {
626
626
  });
627
627
  });
628
628
 
629
- describe("$qq", function () {
630
- let $q, $$q, $rootScope, $injector;
631
-
632
- beforeEach(() => {
633
- window.angular = new Angular();
634
- $injector = createInjector(["ng"]);
635
- $q = $injector.get("$q");
636
- $$q = $injector.get("$$q");
637
- $rootScope = $injector.get("$rootScope");
638
- });
639
-
640
- afterEach(function () {});
641
-
642
- it("uses deferreds that do not resolve at digest", function () {
643
- var d = $$q.defer();
644
- var fulfilledSpy = jasmine.createSpy();
645
- d.promise.then(fulfilledSpy);
646
- d.resolve("ok");
647
- $rootScope.$apply();
648
- expect(fulfilledSpy).not.toHaveBeenCalled();
649
- });
650
-
651
- it("uses deferreds that resolve later", (done) => {
652
- var d = $$q.defer();
653
- d.promise.then((val) => {
654
- expect(val).toBe("ok");
655
- done();
656
- });
657
- d.resolve("ok");
658
- });
659
-
660
- it("does not invoke digest", (done) => {
661
- var d = $$q.defer();
662
- var watchSpy = jasmine.createSpy();
663
- d.promise.then(() => {
664
- watchSpy();
665
- done();
666
- });
667
- d.resolve("ok");
668
- $rootScope.$watch(watchSpy);
669
- expect(watchSpy).not.toHaveBeenCalled();
670
- });
671
- });
672
-
673
629
  describe("$Q", () => {
674
630
  let resolve;
675
631
  let reject;
@@ -76,11 +76,6 @@ let $browser;
76
76
  /**@type {import('../exception-handler').ErrorHandler} */
77
77
  let $exceptionHandler;
78
78
 
79
- /**
80
- * @type {Scope}
81
- */
82
- let rootScope;
83
-
84
79
  /**
85
80
  * Provider responsible for instantiating the initial scope, aka - root scope.
86
81
  * Every application has a single root {@link ng.$rootScope.Scope scope}.
@@ -94,7 +89,7 @@ let rootScope;
94
89
  */
95
90
  export class RootScopeProvider {
96
91
  constructor() {
97
- rootScope = new Scope();
92
+ this.rootScope = new Scope(true);
98
93
  }
99
94
 
100
95
  $get = [
@@ -107,11 +102,11 @@ export class RootScopeProvider {
107
102
  * @param {import('../../services/browser').Browser} browser
108
103
  * @returns {Scope} root scope
109
104
  */
110
- function (exceptionHandler, parse, browser) {
105
+ (exceptionHandler, parse, browser) => {
111
106
  $exceptionHandler = exceptionHandler;
112
107
  $parse = parse;
113
108
  $browser = browser;
114
- return rootScope;
109
+ return this.rootScope;
115
110
  },
116
111
  ];
117
112
  }
@@ -142,7 +137,15 @@ export class RootScopeProvider {
142
137
  */
143
138
 
144
139
  export class Scope {
145
- constructor() {
140
+ /**
141
+ * @param {boolean} [root=false] - Indicates if this scope is the root scope.
142
+ */
143
+ constructor(root = false) {
144
+ /**
145
+ * @type {boolean}
146
+ */
147
+ this.isRoot = root;
148
+
146
149
  /**
147
150
  * @type {number} Unique scope ID (monotonically increasing) useful for debugging.
148
151
  */
@@ -194,13 +197,11 @@ export class Scope {
194
197
  /** @type {boolean} */
195
198
  this.$$destroyed = false;
196
199
 
197
- // TODO use maps
198
200
  /** @type {boolean} */
199
201
  this.$$suspended = false;
200
202
 
201
- // TODO use maps
202
- /** @type {object} */
203
- this.$$listeners = {};
203
+ /** @type {Map<String, Function[]>} */
204
+ this.$$listeners = new Map();
204
205
 
205
206
  /** @type {object} */
206
207
  this.$$listenerCount = {};
@@ -240,19 +241,18 @@ export class Scope {
240
241
  *
241
242
  */
242
243
  $new(isolate, parent) {
243
- /** @type {Scope} */
244
- let child;
244
+ let child = isolate ? new Scope() : Object.create(this);
245
+
245
246
  if (isolate) {
246
- child = new Scope();
247
- child.$root = rootScope;
247
+ child.$root = this.$root;
248
248
  } else {
249
- child = Object.create(this);
249
+ // Initialize properties for a non-isolated child scope
250
250
  child.$id = nextUid();
251
251
  child.$$watchers = [];
252
252
  child.$$nextSibling = null;
253
253
  child.$$childHead = null;
254
254
  child.$$childTail = null;
255
- child.$$listeners = {};
255
+ child.$$listeners = new Map();
256
256
  child.$$listenerCount = {};
257
257
  child.$$watchersCount = 0;
258
258
  child.$$ChildScope = null;
@@ -261,6 +261,7 @@ export class Scope {
261
261
 
262
262
  child.$parent = parent || this;
263
263
  child.$$prevSibling = child.$parent.$$childTail;
264
+
264
265
  if (child.$parent.$$childHead) {
265
266
  child.$parent.$$childTail.$$nextSibling = child;
266
267
  child.$parent.$$childTail = child;
@@ -269,20 +270,13 @@ export class Scope {
269
270
  child.$parent.$$childTail = child;
270
271
  }
271
272
 
272
- // When the new scope is not isolated or we inherit from `this`, and
273
- // the parent scope is destroyed, the property `$$destroyed` is inherited
274
- // prototypically. In all other cases, this property needs to be set
275
- // when the parent scope is destroyed.
276
- // The listener needs to be added after the parent is set
273
+ // Add a destroy listener if isolated or the parent differs from `this`
277
274
  if (isolate || parent !== this) {
278
- child.$on(
279
- "$destroy",
280
- /** @param {any} $event */ //angular.IAngularEvent
281
- ($event) => {
282
- $event.currentScope.$$destroyed = true;
283
- },
284
- );
275
+ child.$on("$destroy", ($event) => {
276
+ $event.currentScope.$$destroyed = true;
277
+ });
285
278
  }
279
+
286
280
  return child;
287
281
  }
288
282
 
@@ -732,7 +726,6 @@ export class Scope {
732
726
  *
733
727
  */
734
728
  $digest() {
735
- let watch;
736
729
  let value;
737
730
  let last;
738
731
  let fn;
@@ -741,10 +734,9 @@ export class Scope {
741
734
  let dirty;
742
735
  let ttl = TTL;
743
736
  let next;
744
- /**
745
- * @type {Scope}
746
- */
737
+ /** @type {Scope} */
747
738
  let current;
739
+ /** @type {Scope} */
748
740
  const target = $$asyncQueue.length ? this.$root : this;
749
741
  const watchLog = [];
750
742
  let logIdx;
@@ -755,7 +747,7 @@ export class Scope {
755
747
  // TODO Implement browser
756
748
  $browser.$$checkUrlChange();
757
749
 
758
- if (this === this.$root && applyAsyncId !== null) {
750
+ if (this.isRoot && applyAsyncId !== null) {
759
751
  // If this is the root scope, and $applyAsync has scheduled a deferred $apply(), then
760
752
  // cancel the scheduled $apply and flush the queue of expressions to be evaluated.
761
753
  $browser.cancel(applyAsyncId);
@@ -795,7 +787,7 @@ export class Scope {
795
787
  current.$$digestWatchIndex = watchers.length;
796
788
  while (current.$$digestWatchIndex--) {
797
789
  try {
798
- watch = watchers[current.$$digestWatchIndex];
790
+ const watch = watchers[current.$$digestWatchIndex];
799
791
  // Most common watches are on primitives, in which case we can short
800
792
  // circuit it with === operator, only when === fails do we use .equals
801
793
  if (watch) {
@@ -1038,7 +1030,7 @@ export class Scope {
1038
1030
  function () {
1039
1031
  return () => {};
1040
1032
  };
1041
- this.$$listeners = {};
1033
+ this.$$listeners.clear();
1042
1034
 
1043
1035
  // Disconnect the next sibling to prevent `cleanUpScope` destroying those too
1044
1036
  this.$$nextSibling = null;
@@ -1251,9 +1243,10 @@ export class Scope {
1251
1243
  * @returns {function()} Returns a deregistration function for this listener.
1252
1244
  */
1253
1245
  $on(name, listener) {
1254
- let namedListeners = this.$$listeners[name];
1246
+ let namedListeners = this.$$listeners.get(name);
1255
1247
  if (!namedListeners) {
1256
- this.$$listeners[name] = namedListeners = [];
1248
+ namedListeners = [];
1249
+ this.$$listeners.set(name, namedListeners);
1257
1250
  }
1258
1251
  namedListeners.push(listener);
1259
1252
 
@@ -1342,7 +1335,7 @@ export class Scope {
1342
1335
  let length;
1343
1336
 
1344
1337
  do {
1345
- namedListeners = scope.$$listeners[name] || empty;
1338
+ namedListeners = scope.$$listeners.get(name) || empty;
1346
1339
  event.currentScope = scope;
1347
1340
  for (i = 0, length = namedListeners.length; i < length; i++) {
1348
1341
  // if listeners were deregistered, defragment the array
@@ -1414,7 +1407,7 @@ export class Scope {
1414
1407
  // down while you can, then up and next sibling or up and next sibling until back at root
1415
1408
  while ((current = next)) {
1416
1409
  event.currentScope = current;
1417
- listeners = current.$$listeners[name] || [];
1410
+ listeners = current.$$listeners.get(name) || [];
1418
1411
  for (i = 0, length = listeners.length; i < length; i++) {
1419
1412
  // if listeners were deregistered, defragment the array
1420
1413
  if (!listeners[i]) {