@reveldigital/player-client 0.0.15 → 0.0.16-beta.0
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/karma.conf.js +32 -0
- package/ng-package.json +7 -0
- package/package.json +20 -39
- package/src/lib/app-init.service.ts +82 -0
- package/src/lib/player-client.module.ts +27 -0
- package/src/lib/player-client.service.spec.ts +16 -0
- package/src/lib/player-client.service.ts +419 -0
- package/{public-api.d.ts → src/public-api.ts} +6 -2
- package/src/test.ts +26 -0
- package/tsconfig.lib.json +27 -0
- package/tsconfig.lib.prod.json +10 -0
- package/tsconfig.spec.json +17 -0
- package/tslint.json +17 -0
- package/esm2020/lib/player-client.module.mjs +0 -32
- package/esm2020/lib/player-client.service.mjs +0 -240
- package/esm2020/public-api.mjs +0 -6
- package/esm2020/reveldigital-player-client.mjs +0 -5
- package/fesm2015/reveldigital-player-client.mjs +0 -312
- package/fesm2015/reveldigital-player-client.mjs.map +0 -1
- package/fesm2020/reveldigital-player-client.mjs +0 -280
- package/fesm2020/reveldigital-player-client.mjs.map +0 -1
- package/lib/player-client.module.d.ts +0 -6
- package/lib/player-client.service.d.ts +0 -62
- package/reveldigital-player-client.d.ts +0 -5
package/karma.conf.js
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
// Karma configuration file, see link for more information
|
|
2
|
+
// https://karma-runner.github.io/1.0/config/configuration-file.html
|
|
3
|
+
|
|
4
|
+
module.exports = function (config) {
|
|
5
|
+
config.set({
|
|
6
|
+
basePath: '',
|
|
7
|
+
frameworks: ['jasmine', '@angular-devkit/build-angular'],
|
|
8
|
+
plugins: [
|
|
9
|
+
require('karma-jasmine'),
|
|
10
|
+
require('karma-chrome-launcher'),
|
|
11
|
+
require('karma-jasmine-html-reporter'),
|
|
12
|
+
require('karma-coverage-istanbul-reporter'),
|
|
13
|
+
require('@angular-devkit/build-angular/plugins/karma')
|
|
14
|
+
],
|
|
15
|
+
client: {
|
|
16
|
+
clearContext: false // leave Jasmine Spec Runner output visible in browser
|
|
17
|
+
},
|
|
18
|
+
coverageIstanbulReporter: {
|
|
19
|
+
dir: require('path').join(__dirname, '../../../coverage/reveldigital/player-client'),
|
|
20
|
+
reports: ['html', 'lcovonly', 'text-summary'],
|
|
21
|
+
fixWebpackSourcePaths: true
|
|
22
|
+
},
|
|
23
|
+
reporters: ['progress', 'kjhtml'],
|
|
24
|
+
port: 9876,
|
|
25
|
+
colors: true,
|
|
26
|
+
logLevel: config.LOG_INFO,
|
|
27
|
+
autoWatch: true,
|
|
28
|
+
browsers: ['Chrome'],
|
|
29
|
+
singleRun: false,
|
|
30
|
+
restartOnFileChange: true
|
|
31
|
+
});
|
|
32
|
+
};
|
package/ng-package.json
ADDED
package/package.json
CHANGED
|
@@ -1,39 +1,20 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@reveldigital/player-client",
|
|
3
|
-
"version": "0.0.
|
|
4
|
-
"description": "Helper library for interfacing with the Revel Digital player",
|
|
5
|
-
"repository": {
|
|
6
|
-
"url": "https://github.com/RevelDigital/reveldigital-client-library"
|
|
7
|
-
},
|
|
8
|
-
"author": {
|
|
9
|
-
"name": "Mike Tinnes"
|
|
10
|
-
},
|
|
11
|
-
"license": "",
|
|
12
|
-
"peerDependencies": {
|
|
13
|
-
"@angular/common": "
|
|
14
|
-
"@angular/core": "
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
"esm2020": "esm2020/reveldigital-player-client.mjs",
|
|
22
|
-
"fesm2020": "fesm2020/reveldigital-player-client.mjs",
|
|
23
|
-
"fesm2015": "fesm2015/reveldigital-player-client.mjs",
|
|
24
|
-
"typings": "reveldigital-player-client.d.ts",
|
|
25
|
-
"exports": {
|
|
26
|
-
"./package.json": {
|
|
27
|
-
"default": "./package.json"
|
|
28
|
-
},
|
|
29
|
-
".": {
|
|
30
|
-
"types": "./reveldigital-player-client.d.ts",
|
|
31
|
-
"esm2020": "./esm2020/reveldigital-player-client.mjs",
|
|
32
|
-
"es2020": "./fesm2020/reveldigital-player-client.mjs",
|
|
33
|
-
"es2015": "./fesm2015/reveldigital-player-client.mjs",
|
|
34
|
-
"node": "./fesm2015/reveldigital-player-client.mjs",
|
|
35
|
-
"default": "./fesm2020/reveldigital-player-client.mjs"
|
|
36
|
-
}
|
|
37
|
-
},
|
|
38
|
-
"sideEffects": false
|
|
39
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@reveldigital/player-client",
|
|
3
|
+
"version": "0.0.16-beta.0",
|
|
4
|
+
"description": "Helper library for interfacing Angular apps with the Revel Digital player",
|
|
5
|
+
"repository": {
|
|
6
|
+
"url": "https://github.com/RevelDigital/reveldigital-client-library"
|
|
7
|
+
},
|
|
8
|
+
"author": {
|
|
9
|
+
"name": "Mike Tinnes"
|
|
10
|
+
},
|
|
11
|
+
"license": "",
|
|
12
|
+
"peerDependencies": {
|
|
13
|
+
"@angular/common": "^13.0.0",
|
|
14
|
+
"@angular/core": "^13.0.0",
|
|
15
|
+
"js-yaml": "^4.1.0"
|
|
16
|
+
},
|
|
17
|
+
"dependencies": {
|
|
18
|
+
"tslib": "^2.4.1"
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { Injectable, isDevMode } from '@angular/core';
|
|
2
|
+
import { HttpClient } from "@angular/common/http";
|
|
3
|
+
import yaml from "js-yaml";
|
|
4
|
+
import { ActivatedRoute, Router } from "@angular/router";
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@Injectable({
|
|
8
|
+
providedIn: 'root'
|
|
9
|
+
})
|
|
10
|
+
export class AppInitService {
|
|
11
|
+
|
|
12
|
+
timestamp: any = new Date().getTime();
|
|
13
|
+
|
|
14
|
+
constructor(public http: HttpClient, private _route: ActivatedRoute, private _router: Router) {
|
|
15
|
+
let lastUptime = localStorage.getItem('uptime') || '30';
|
|
16
|
+
console.log('Last uptime was', lastUptime, 'seconds');
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
init(): Promise<any> {
|
|
20
|
+
return new Promise<void>(async (resolve) => {
|
|
21
|
+
if (isDevMode()) {
|
|
22
|
+
console.log('Development Mode');
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Shim the shindig prefs functionality for dev mode
|
|
26
|
+
*/
|
|
27
|
+
(<any>window).gadgets = {
|
|
28
|
+
|
|
29
|
+
Prefs: class {
|
|
30
|
+
getString(key: string) { return this.getParameterByName(key) }
|
|
31
|
+
|
|
32
|
+
getArray(key: string) { return this.getParameterByName(key).split(',') }
|
|
33
|
+
|
|
34
|
+
getBool(key: string) { return this.getParameterByName(key) === 'true' }
|
|
35
|
+
|
|
36
|
+
getCountry() { }
|
|
37
|
+
|
|
38
|
+
getFloat(key: string) { return parseFloat(this.getParameterByName(key)) }
|
|
39
|
+
|
|
40
|
+
getInt(key: string) { return parseInt(this.getParameterByName(key)) }
|
|
41
|
+
|
|
42
|
+
getLang() { }
|
|
43
|
+
|
|
44
|
+
getParameterByName(name: string, url = window.location.href): string {
|
|
45
|
+
name = name.replace(/[\[\]]/g, '\\$&');
|
|
46
|
+
let regex = new RegExp('[?&]' + name + '(=([^&#]*)|&|#|$)'),
|
|
47
|
+
results = regex.exec(url);
|
|
48
|
+
if (!results) return '';
|
|
49
|
+
if (!results[2]) return '';
|
|
50
|
+
return decodeURIComponent(results[2].replace(/\+/g, ' '));
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
this.http.get('assets/user-prefs.yml', {
|
|
56
|
+
responseType: 'text'
|
|
57
|
+
}).subscribe(data => {
|
|
58
|
+
try {
|
|
59
|
+
const doc: any = yaml.load(data);
|
|
60
|
+
let params: any = {}
|
|
61
|
+
for (const val of doc.prefs) {
|
|
62
|
+
params[val.name] = val.default_value
|
|
63
|
+
}
|
|
64
|
+
this.navigate(params)
|
|
65
|
+
} catch (e) {
|
|
66
|
+
console.log(`Unable to load user preferences YAML definition file: ${e}`)
|
|
67
|
+
}
|
|
68
|
+
})
|
|
69
|
+
}
|
|
70
|
+
resolve();
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
navigate(params: any) {
|
|
75
|
+
// changes the route without moving from the current view or
|
|
76
|
+
// triggering a navigation event,
|
|
77
|
+
this._router.navigate([], {
|
|
78
|
+
relativeTo: this._route,
|
|
79
|
+
queryParams: params,
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { APP_INITIALIZER, NgModule } from '@angular/core';
|
|
2
|
+
import { PlayerClientService } from './player-client.service';
|
|
3
|
+
import { AppInitService } from './app-init.service';
|
|
4
|
+
import { HttpClientModule } from '@angular/common/http';
|
|
5
|
+
import { RouterModule, Routes } from '@angular/router';
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@NgModule({
|
|
9
|
+
imports: [
|
|
10
|
+
HttpClientModule,
|
|
11
|
+
RouterModule.forRoot([])
|
|
12
|
+
],
|
|
13
|
+
providers: [{
|
|
14
|
+
provide: APP_INITIALIZER,
|
|
15
|
+
useFactory: initializeApp,
|
|
16
|
+
deps: [AppInitService, PlayerClientService],
|
|
17
|
+
multi: true
|
|
18
|
+
}]
|
|
19
|
+
})
|
|
20
|
+
export class PlayerClientModule { }
|
|
21
|
+
|
|
22
|
+
function initializeApp(appInitService: AppInitService) {
|
|
23
|
+
return async () => {
|
|
24
|
+
PlayerClientService.init({});
|
|
25
|
+
await appInitService.init();
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { TestBed } from '@angular/core/testing';
|
|
2
|
+
|
|
3
|
+
import { PlayerClientService } from './player-client.service';
|
|
4
|
+
|
|
5
|
+
describe('PlayerClientService', () => {
|
|
6
|
+
let service: PlayerClientService;
|
|
7
|
+
|
|
8
|
+
beforeEach(() => {
|
|
9
|
+
TestBed.configureTestingModule({});
|
|
10
|
+
service = TestBed.inject(PlayerClientService);
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
it('should be created', () => {
|
|
14
|
+
expect(service).toBeTruthy();
|
|
15
|
+
});
|
|
16
|
+
});
|
|
@@ -0,0 +1,419 @@
|
|
|
1
|
+
import { Injectable, NgZone, OnDestroy, OnInit } from '@angular/core';
|
|
2
|
+
import { BehaviorSubject, fromEvent, Subject, Subscription } from 'rxjs';
|
|
3
|
+
import { filter, map, share, tap } from 'rxjs/operators';
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
// So that TypeScript doesn't complain, we're going to augment the GLOBAL / WINDOW
|
|
7
|
+
// name-space definition to include the Tracker API. This also provides us with a place
|
|
8
|
+
// to actually DOCUMENT the API so that our developers aren't guessing about what's
|
|
9
|
+
// available on the library.
|
|
10
|
+
declare global {
|
|
11
|
+
var Client: Client;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface Client {
|
|
15
|
+
|
|
16
|
+
callback(...args: any[]): void;
|
|
17
|
+
|
|
18
|
+
getDeviceTime(date?: Date): Promise<string>;
|
|
19
|
+
|
|
20
|
+
getDeviceTimeZoneName(): Promise<string>;
|
|
21
|
+
|
|
22
|
+
getDeviceTimeZoneID(): Promise<string>;
|
|
23
|
+
|
|
24
|
+
getDeviceTimeZoneOffset(): Promise<number>;
|
|
25
|
+
|
|
26
|
+
getLanguageCode(): Promise<string>;
|
|
27
|
+
|
|
28
|
+
getDeviceKey(): Promise<string>;
|
|
29
|
+
|
|
30
|
+
sendCommand(name: string, arg: string): void;
|
|
31
|
+
|
|
32
|
+
sendRemoteCommand(deviceKeys: string[], name: string, arg: string): void;
|
|
33
|
+
|
|
34
|
+
track(eventName: string, properties?: string): void;
|
|
35
|
+
|
|
36
|
+
timeEvent(eventName: string): void;
|
|
37
|
+
|
|
38
|
+
newEventSession(id?: string): void;
|
|
39
|
+
|
|
40
|
+
getRevelRoot(): Promise<string>;
|
|
41
|
+
|
|
42
|
+
getCommandMap(): Promise<string>;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export interface EventProperties {
|
|
46
|
+
[key: string]: any;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export interface Command {
|
|
50
|
+
name: string;
|
|
51
|
+
arg: string;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
@Injectable({
|
|
55
|
+
providedIn: 'root'
|
|
56
|
+
})
|
|
57
|
+
export class PlayerClientService implements OnDestroy {
|
|
58
|
+
|
|
59
|
+
private clientPromise: Promise<Client> | null;
|
|
60
|
+
|
|
61
|
+
public onCommand$ = new Subject<Command>();
|
|
62
|
+
public onReady$ = new BehaviorSubject(false);
|
|
63
|
+
public onStart$ = new Subject();
|
|
64
|
+
public onStop$ = new Subject();
|
|
65
|
+
|
|
66
|
+
//
|
|
67
|
+
// Three methods available for calling into the library:
|
|
68
|
+
//
|
|
69
|
+
// 1) Using dispatchEvent() with the following custom events
|
|
70
|
+
// 2) Using the window scoped RevelDigital object as defined in the constructor
|
|
71
|
+
//
|
|
72
|
+
private onStartSub: Subscription;
|
|
73
|
+
private onStartEvt$ = fromEvent(document, 'RevelDigital.Start').pipe(
|
|
74
|
+
share(),
|
|
75
|
+
tap(this.onStart$)
|
|
76
|
+
);
|
|
77
|
+
private onStopSub: Subscription;
|
|
78
|
+
private onStopEvt$ = fromEvent(document, 'RevelDigital.Stop').pipe(
|
|
79
|
+
share(),
|
|
80
|
+
tap(this.onStop$)
|
|
81
|
+
);
|
|
82
|
+
private onCommandSub: Subscription;
|
|
83
|
+
private onCommandEvt$ = fromEvent<Command>(document, 'RevelDigital.Command').pipe(
|
|
84
|
+
map((e: any) => { return { name: e.detail.name, arg: e.detail.arg } as Command }),
|
|
85
|
+
share(),
|
|
86
|
+
tap(this.onCommand$)
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
// private onPostMessageSub: Subscription;
|
|
90
|
+
// private onPostMessageEvt$ = fromEvent(window, 'message').pipe(
|
|
91
|
+
// filter((messageEvent: MessageEvent) =>
|
|
92
|
+
// messageEvent.source !== window.parent &&
|
|
93
|
+
// typeof messageEvent.data === 'string' &&
|
|
94
|
+
// messageEvent.data.startsWith('reveldigital:')),
|
|
95
|
+
// map((e: any) => { return JSON.parse(e.substring(13)) as Command }),
|
|
96
|
+
// share(),
|
|
97
|
+
// tap(this.onCommand$)
|
|
98
|
+
// );
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
constructor(zone: NgZone) {
|
|
102
|
+
|
|
103
|
+
let self = this;
|
|
104
|
+
(window as any).RevelDigital = {
|
|
105
|
+
Controller: {
|
|
106
|
+
onCommand: function (name: string, arg: string) {
|
|
107
|
+
zone.run(() => {
|
|
108
|
+
self.onCommand$.next({ name: name, arg: arg });
|
|
109
|
+
});
|
|
110
|
+
},
|
|
111
|
+
onStart: function () {
|
|
112
|
+
zone.run(() => {
|
|
113
|
+
self.onStart$.next();
|
|
114
|
+
});
|
|
115
|
+
},
|
|
116
|
+
onStop: function () {
|
|
117
|
+
zone.run(() => {
|
|
118
|
+
self.onStop$.next();
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
this.onStartSub = this.onStartEvt$.subscribe(() => { });
|
|
125
|
+
this.onStopSub = this.onStopEvt$.subscribe(() => { });
|
|
126
|
+
this.onCommandSub = this.onCommandEvt$.subscribe(() => { });
|
|
127
|
+
|
|
128
|
+
this.clientPromise = null;
|
|
129
|
+
|
|
130
|
+
this.onReady$.next(true);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
ngOnDestroy(): void {
|
|
134
|
+
|
|
135
|
+
this.onStartSub?.unsubscribe();
|
|
136
|
+
this.onStopSub?.unsubscribe();
|
|
137
|
+
this.onCommandSub?.unsubscribe();
|
|
138
|
+
|
|
139
|
+
this.onReady$.next(false);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
public static init(data: any) {
|
|
143
|
+
|
|
144
|
+
console.log("init()");
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
public callback(...args: any[]): void {
|
|
148
|
+
|
|
149
|
+
this.getClient().then((client) => {
|
|
150
|
+
|
|
151
|
+
switch (args.length) {
|
|
152
|
+
case 0:
|
|
153
|
+
client.callback();
|
|
154
|
+
break;
|
|
155
|
+
case 1:
|
|
156
|
+
client.callback(args[0]);
|
|
157
|
+
break;
|
|
158
|
+
case 2:
|
|
159
|
+
client.callback(args[1]);
|
|
160
|
+
break;
|
|
161
|
+
case 3:
|
|
162
|
+
client.callback(args[2]);
|
|
163
|
+
break;
|
|
164
|
+
case 4:
|
|
165
|
+
client.callback(args[3]);
|
|
166
|
+
break;
|
|
167
|
+
case 5:
|
|
168
|
+
client.callback(args[4]);
|
|
169
|
+
break;
|
|
170
|
+
}
|
|
171
|
+
})
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
public async getDeviceTime(date?: Date): Promise<string> {
|
|
175
|
+
|
|
176
|
+
const client = await this.getClient();
|
|
177
|
+
|
|
178
|
+
if (date !== undefined) {
|
|
179
|
+
return client.getDeviceTime(date);
|
|
180
|
+
}
|
|
181
|
+
return client.getDeviceTime();
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
public async getDeviceTimeZoneName(): Promise<string> {
|
|
185
|
+
|
|
186
|
+
const client = await this.getClient();
|
|
187
|
+
|
|
188
|
+
return client.getDeviceTimeZoneName();
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
public async getDeviceTimeZoneID(): Promise<string> {
|
|
192
|
+
|
|
193
|
+
const client = await this.getClient();
|
|
194
|
+
|
|
195
|
+
return client.getDeviceTimeZoneID();
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
public async getDeviceTimeZoneOffset(): Promise<number> {
|
|
199
|
+
|
|
200
|
+
const client = await this.getClient();
|
|
201
|
+
|
|
202
|
+
return client.getDeviceTimeZoneOffset();
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
public async getLanguageCode(): Promise<string> {
|
|
206
|
+
|
|
207
|
+
const client = await this.getClient();
|
|
208
|
+
|
|
209
|
+
return client.getLanguageCode();
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
public async getDeviceKey(): Promise<string> {
|
|
213
|
+
|
|
214
|
+
const client = await this.getClient();
|
|
215
|
+
|
|
216
|
+
return client.getDeviceKey();
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
public sendCommand(name: string, arg: string): void {
|
|
220
|
+
|
|
221
|
+
this.getClient().then((client) => {
|
|
222
|
+
client.sendCommand(name, arg);
|
|
223
|
+
})
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
public sendRemoteCommand(deviceKeys: string[], name: string, arg: string): void {
|
|
227
|
+
|
|
228
|
+
this.getClient().then((client) => {
|
|
229
|
+
client.sendRemoteCommand(deviceKeys, name, arg);
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
public track(eventName: string, properties?: EventProperties): void {
|
|
234
|
+
|
|
235
|
+
this.getClient().then((client) => {
|
|
236
|
+
client.track(eventName, JSON.stringify(properties));
|
|
237
|
+
})
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
public timeEvent(eventName: string): void {
|
|
241
|
+
|
|
242
|
+
this.getClient().then((client) => {
|
|
243
|
+
client.timeEvent(eventName);
|
|
244
|
+
})
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
public newEventSession(id?: string): void {
|
|
248
|
+
|
|
249
|
+
this.getClient().then((client) => {
|
|
250
|
+
if (id !== undefined) {
|
|
251
|
+
client.newEventSession();
|
|
252
|
+
} else {
|
|
253
|
+
client.newEventSession(id);
|
|
254
|
+
}
|
|
255
|
+
})
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
public async getRevelRoot(): Promise<string> {
|
|
259
|
+
|
|
260
|
+
const client = await this.getClient();
|
|
261
|
+
|
|
262
|
+
return client.getRevelRoot();
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
public async getCommandMap(): Promise<any> {
|
|
266
|
+
|
|
267
|
+
const client = await this.getClient();
|
|
268
|
+
|
|
269
|
+
return JSON.parse(await client.getCommandMap());
|
|
270
|
+
|
|
271
|
+
// let map = new Map<string, any>();
|
|
272
|
+
|
|
273
|
+
// let obj = JSON.parse(await client.getCommandMap());
|
|
274
|
+
// for (let key in obj) {
|
|
275
|
+
// map.set(key, obj[key]);
|
|
276
|
+
// }
|
|
277
|
+
// return map;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
// ---
|
|
281
|
+
// PRIVATE METHODS.
|
|
282
|
+
// ---
|
|
283
|
+
|
|
284
|
+
private getClient(): Promise<Client> {
|
|
285
|
+
|
|
286
|
+
if (this.clientPromise) {
|
|
287
|
+
|
|
288
|
+
return (this.clientPromise);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
if (window.Client) {
|
|
292
|
+
|
|
293
|
+
return (this.clientPromise = Promise.resolve(window.Client));
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// A "complete" status indicates that the "load" event has been fired on the
|
|
297
|
+
// window; and, that all sub-resources such as Scripts, Images, and Frames have
|
|
298
|
+
// been loaded.
|
|
299
|
+
if (window.document.readyState === "complete") {
|
|
300
|
+
|
|
301
|
+
// If this event has fired AND the 3rd-party script isn't available (see IF-
|
|
302
|
+
// condition BEFORE this one), it means that the 3rd-party script either
|
|
303
|
+
// failed on the network or was BLOCKED by an ad-blocker. As such, we have to
|
|
304
|
+
// fall-back to using a mock API.
|
|
305
|
+
return (this.clientPromise = Promise.resolve(new NoopClient()));
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
// ASSERT: If we made it this far, the document has not completed loading (but it
|
|
309
|
+
// may be in an "interactive" state which is when I believe that the Angular app
|
|
310
|
+
// gets bootstrapped). As such, we need bind to the LOAD event to wait for our
|
|
311
|
+
// third-party scripts to load (or fail to load, or be blocked).
|
|
312
|
+
this.clientPromise = new Promise<Client>(
|
|
313
|
+
(resolve) => {
|
|
314
|
+
|
|
315
|
+
window.addEventListener(
|
|
316
|
+
"load",
|
|
317
|
+
function handleWindowLoad() {
|
|
318
|
+
|
|
319
|
+
// At this point, the 3rd-party library is either available or
|
|
320
|
+
// it's not - there's no further loading to do. If it's not
|
|
321
|
+
// present on the global scope, we're going to fall-back to using
|
|
322
|
+
// a mock API.
|
|
323
|
+
resolve(window.Client || new NoopClient());
|
|
324
|
+
}
|
|
325
|
+
);
|
|
326
|
+
|
|
327
|
+
}
|
|
328
|
+
);
|
|
329
|
+
|
|
330
|
+
return (this.clientPromise);
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
|
|
336
|
+
|
|
337
|
+
// ----------------------------------------------------------------------------------- //
|
|
338
|
+
// ----------------------------------------------------------------------------------- //
|
|
339
|
+
|
|
340
|
+
// I provide a mock API for the 3rd-party script. This just allows the consuming code to
|
|
341
|
+
// act as though the library is available even if it failed to load (example, it was
|
|
342
|
+
// blocked by an ad-blocker).
|
|
343
|
+
class NoopClient implements Client {
|
|
344
|
+
|
|
345
|
+
constructor() {
|
|
346
|
+
|
|
347
|
+
console.warn("Client API not available, falling back to mock API.");
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
public callback(...args: any[]): void {
|
|
351
|
+
|
|
352
|
+
// NOOP implement, nothing to do....
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
public getDeviceTime(date?: Date): Promise<string> {
|
|
356
|
+
|
|
357
|
+
return Promise.resolve(null);
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
public async getDeviceTimeZoneName(): Promise<string> {
|
|
361
|
+
|
|
362
|
+
return Promise.resolve(null);
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
public async getDeviceTimeZoneID(): Promise<string> {
|
|
366
|
+
|
|
367
|
+
return Promise.resolve(null);
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
public async getDeviceTimeZoneOffset(): Promise<number> {
|
|
371
|
+
|
|
372
|
+
return Promise.resolve(null);
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
public async getLanguageCode(): Promise<string> {
|
|
376
|
+
|
|
377
|
+
return Promise.resolve(null);
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
public async getDeviceKey(): Promise<string> {
|
|
381
|
+
|
|
382
|
+
return Promise.resolve(null);
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
public sendCommand(name: string, arg: string): void {
|
|
386
|
+
|
|
387
|
+
// NOOP implement, nothing to do....
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
public sendRemoteCommand(deviceKeys: string[], name: string, arg: string) {
|
|
391
|
+
|
|
392
|
+
// NOOP implement, nothing to do....
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
public track(eventName: string, properties?: string): void {
|
|
396
|
+
|
|
397
|
+
// NOOP implement, nothing to do....
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
public timeEvent(eventName: string): void {
|
|
401
|
+
|
|
402
|
+
// NOOP implement, nothing to do....
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
public newEventSession(id?: string): void {
|
|
406
|
+
|
|
407
|
+
// NOOP implement, nothing to do....
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
public async getRevelRoot(): Promise<string> {
|
|
411
|
+
|
|
412
|
+
return Promise.resolve(null);
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
public async getCommandMap(): Promise<string> {
|
|
416
|
+
|
|
417
|
+
return Promise.resolve('{}');
|
|
418
|
+
}
|
|
419
|
+
}
|
package/src/test.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// This file is required by karma.conf.js and loads recursively all the .spec and framework files
|
|
2
|
+
|
|
3
|
+
import 'zone.js';
|
|
4
|
+
import 'zone.js/testing';
|
|
5
|
+
import { getTestBed } from '@angular/core/testing';
|
|
6
|
+
import {
|
|
7
|
+
BrowserDynamicTestingModule,
|
|
8
|
+
platformBrowserDynamicTesting
|
|
9
|
+
} from '@angular/platform-browser-dynamic/testing';
|
|
10
|
+
|
|
11
|
+
declare const require: {
|
|
12
|
+
context(path: string, deep?: boolean, filter?: RegExp): {
|
|
13
|
+
keys(): string[];
|
|
14
|
+
<T>(id: string): T;
|
|
15
|
+
};
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
// First, initialize the Angular testing environment.
|
|
19
|
+
getTestBed().initTestEnvironment(
|
|
20
|
+
BrowserDynamicTestingModule,
|
|
21
|
+
platformBrowserDynamicTesting()
|
|
22
|
+
);
|
|
23
|
+
// Then we find all the tests.
|
|
24
|
+
const context = require.context('./', true, /\.spec\.ts$/);
|
|
25
|
+
// And load the modules.
|
|
26
|
+
context.keys().map(context);
|