@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/dist/angular-ts.esm.js +2 -2
- package/dist/angular-ts.umd.js +2 -2
- package/package.json +1 -1
- package/src/core/cache/cache.js +2 -29
- package/src/core/compile/compile.js +1 -1
- package/src/core/compile/compile.test.js +1 -1
- package/src/core/location/location.js +45 -39
- package/src/core/scope/scope.js +46 -14
- package/src/core/scope/scope.spec.js +1 -1
- package/src/core/url-utils/url-utils.spec.js +1 -1
- package/src/directive/{list/list.html → channel/channel.html} +1 -1
- package/src/directive/channel/channel.js +29 -0
- package/src/directive/channel/channel.spec.js +52 -0
- package/src/directive/{list/list.test.js → channel/channel.test.js} +2 -4
- package/src/public.js +78 -75
- package/src/router/state/state.test.js +1 -1
- package/src/services/browser.js +5 -8
- package/src/shared/jqlite/jqlite.js +0 -3
- package/types/core/cache/cache.d.ts +1 -1
- package/types/core/location/location.d.ts +36 -31
- package/types/core/scope/scope.d.ts +11 -10
- package/types/directive/channel/channel.d.ts +11 -0
- package/src/directive/list/list.js +0 -46
- package/src/directive/list/list.md +0 -22
- package/src/directive/list/list.spec.js +0 -172
- package/types/directive/list/list.d.ts +0 -4
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.
|
|
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",
|
package/src/core/cache/cache.js
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
*
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
export const EXPANDO = "
|
|
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
|
|
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.$
|
|
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(
|
|
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
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
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 {
|
|
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
|
-
|
|
561
|
-
if (
|
|
562
|
-
|
|
560
|
+
hashPrefix(prefix) {
|
|
561
|
+
if (typeof prefix !== "undefined") {
|
|
562
|
+
this.hashPrefixValue = prefix;
|
|
563
563
|
return this;
|
|
564
564
|
}
|
|
565
|
-
return
|
|
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
|
-
|
|
589
|
-
if (
|
|
590
|
-
|
|
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
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
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
|
|
609
|
-
}
|
|
607
|
+
return this.html5ModeConfig;
|
|
608
|
+
}
|
|
610
609
|
|
|
611
|
-
|
|
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(
|
|
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
|
-
|
|
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
|
];
|
package/src/core/scope/scope.js
CHANGED
|
@@ -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
|
|
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 =
|
|
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
|
|
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.$
|
|
138
|
+
const trans = $rootScope.$transcluded(isolated);
|
|
139
139
|
$rootScope.a = 123;
|
|
140
140
|
expect(isolated.a).toBeUndefined();
|
|
141
141
|
expect(trans.a).toEqual(123);
|
|
@@ -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/
|
|
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
|
-
|
|
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/,
|