From 64a68bb42e1f0db72170c067a4050b0936f2679e Mon Sep 17 00:00:00 2001 From: pabloFuente Date: Tue, 26 Sep 2017 18:13:00 +0200 Subject: [PATCH] openvidu-testapp first approach --- .../kurento-utils-js/WebRtcPeer.js | 4 +- openvidu-ng-testapp/e2e/app.e2e-spec.ts | 14 - openvidu-ng-testapp/e2e/tsconfig.e2e.json | 19 -- openvidu-ng-testapp/package.json | 48 --- .../src/app/app.component.html | 82 ------ openvidu-ng-testapp/src/app/app.component.ts | 278 ------------------ openvidu-ng-testapp/src/app/app.module.ts | 21 -- openvidu-ng-testapp/src/app/index.ts | 2 - .../src/app/stream.component.ts | 46 --- openvidu-ng-testapp/src/polyfills.ts | 19 -- openvidu-ng-testapp/src/styles.css | 1 - openvidu-ng-testapp/src/test.ts | 34 --- openvidu-ng-testapp/src/tsconfig.app.json | 22 -- openvidu-ng-testapp/src/tsconfig.spec.json | 26 -- openvidu-ng-testapp/src/typings.d.ts | 5 - .../.angular-cli.json | 14 +- .../.editorconfig | 0 .../.gitignore | 5 +- .../README.md | 8 +- openvidu-testapp/e2e/app.e2e-spec.ts | 14 + .../e2e/app.po.ts | 4 +- openvidu-testapp/e2e/tsconfig.e2e.json | 14 + .../karma.conf.js | 15 +- openvidu-testapp/package.json | 55 ++++ .../protractor.conf.js | 4 +- openvidu-testapp/src/app/app.component.css | 8 + openvidu-testapp/src/app/app.component.html | 10 + .../src/app/app.component.spec.ts | 27 ++ openvidu-testapp/src/app/app.component.ts | 17 ++ .../src/app/app.material.module.ts | 33 +++ openvidu-testapp/src/app/app.module.ts | 32 ++ openvidu-testapp/src/app/app.routing.ts | 18 ++ .../dashboard/app.material.module.ts | 41 +++ .../dashboard/dashboard.component.css | 93 ++++++ .../dashboard/dashboard.component.html | 47 +++ .../dashboard/dashboard.component.spec.ts | 25 ++ .../dashboard/dashboard.component.ts | 45 +++ .../components/session/session.component.css | 0 .../components/session/session.component.html | 11 + .../session/session.component.spec.ts | 25 ++ .../components/session/session.component.ts | 135 +++++++++ .../services/openvidu-rest.service.spec.ts | 15 + .../src/app/services/openvidu-rest.service.ts | 38 +++ .../app/services/session-conf.service.spec.ts | 15 + .../src/app/services/session-conf.service.ts | 17 ++ .../src/assets/.gitkeep | 0 openvidu-testapp/src/assets/.npmignore | 0 .../openvidu_globe_bg_transp_cropped.png | Bin 0 -> 11746 bytes .../openvidu_grey_bg_transp_cropped.png | Bin 0 -> 39532 bytes .../images/openvidu_vert_white_bg_trans.png | Bin 0 -> 26577 bytes .../openvidu_vert_white_bg_trans_cropped.png | Bin 0 -> 22426 bytes .../src/environments/environment.prod.ts | 0 .../src/environments/environment.ts | 6 +- .../src/favicon.ico | Bin .../src/index.html | 10 +- .../src/main.ts | 12 +- openvidu-testapp/src/openvidu-theme.scss | 57 ++++ openvidu-testapp/src/polyfills.ts | 72 +++++ openvidu-testapp/src/styles.css | 79 +++++ openvidu-testapp/src/test.ts | 32 ++ openvidu-testapp/src/tsconfig.app.json | 13 + openvidu-testapp/src/tsconfig.spec.json | 20 ++ openvidu-testapp/src/typings.d.ts | 5 + .../tsconfig.json | 5 +- .../tslint.json | 44 ++- 65 files changed, 1093 insertions(+), 668 deletions(-) delete mode 100644 openvidu-ng-testapp/e2e/app.e2e-spec.ts delete mode 100644 openvidu-ng-testapp/e2e/tsconfig.e2e.json delete mode 100644 openvidu-ng-testapp/package.json delete mode 100644 openvidu-ng-testapp/src/app/app.component.html delete mode 100644 openvidu-ng-testapp/src/app/app.component.ts delete mode 100644 openvidu-ng-testapp/src/app/app.module.ts delete mode 100644 openvidu-ng-testapp/src/app/index.ts delete mode 100644 openvidu-ng-testapp/src/app/stream.component.ts delete mode 100644 openvidu-ng-testapp/src/polyfills.ts delete mode 100644 openvidu-ng-testapp/src/styles.css delete mode 100644 openvidu-ng-testapp/src/test.ts delete mode 100644 openvidu-ng-testapp/src/tsconfig.app.json delete mode 100644 openvidu-ng-testapp/src/tsconfig.spec.json delete mode 100644 openvidu-ng-testapp/src/typings.d.ts rename {openvidu-ng-testapp => openvidu-testapp}/.angular-cli.json (74%) rename {openvidu-ng-testapp => openvidu-testapp}/.editorconfig (100%) rename {openvidu-ng-testapp => openvidu-testapp}/.gitignore (93%) rename {openvidu-ng-testapp => openvidu-testapp}/README.md (80%) create mode 100644 openvidu-testapp/e2e/app.e2e-spec.ts rename {openvidu-ng-testapp => openvidu-testapp}/e2e/app.po.ts (60%) create mode 100644 openvidu-testapp/e2e/tsconfig.e2e.json rename {openvidu-ng-testapp => openvidu-testapp}/karma.conf.js (65%) create mode 100644 openvidu-testapp/package.json rename {openvidu-ng-testapp => openvidu-testapp}/protractor.conf.js (95%) create mode 100644 openvidu-testapp/src/app/app.component.css create mode 100644 openvidu-testapp/src/app/app.component.html create mode 100644 openvidu-testapp/src/app/app.component.spec.ts create mode 100644 openvidu-testapp/src/app/app.component.ts create mode 100644 openvidu-testapp/src/app/app.material.module.ts create mode 100644 openvidu-testapp/src/app/app.module.ts create mode 100644 openvidu-testapp/src/app/app.routing.ts create mode 100644 openvidu-testapp/src/app/components/dashboard/app.material.module.ts create mode 100644 openvidu-testapp/src/app/components/dashboard/dashboard.component.css create mode 100644 openvidu-testapp/src/app/components/dashboard/dashboard.component.html create mode 100644 openvidu-testapp/src/app/components/dashboard/dashboard.component.spec.ts create mode 100644 openvidu-testapp/src/app/components/dashboard/dashboard.component.ts rename openvidu-ng-testapp/src/assets/.gitkeep => openvidu-testapp/src/app/components/session/session.component.css (100%) create mode 100644 openvidu-testapp/src/app/components/session/session.component.html create mode 100644 openvidu-testapp/src/app/components/session/session.component.spec.ts create mode 100644 openvidu-testapp/src/app/components/session/session.component.ts create mode 100644 openvidu-testapp/src/app/services/openvidu-rest.service.spec.ts create mode 100644 openvidu-testapp/src/app/services/openvidu-rest.service.ts create mode 100644 openvidu-testapp/src/app/services/session-conf.service.spec.ts create mode 100644 openvidu-testapp/src/app/services/session-conf.service.ts rename openvidu-ng-testapp/src/assets/.npmignore => openvidu-testapp/src/assets/.gitkeep (100%) create mode 100644 openvidu-testapp/src/assets/.npmignore create mode 100644 openvidu-testapp/src/assets/images/openvidu_globe_bg_transp_cropped.png create mode 100644 openvidu-testapp/src/assets/images/openvidu_grey_bg_transp_cropped.png create mode 100644 openvidu-testapp/src/assets/images/openvidu_vert_white_bg_trans.png create mode 100644 openvidu-testapp/src/assets/images/openvidu_vert_white_bg_trans_cropped.png rename {openvidu-ng-testapp => openvidu-testapp}/src/environments/environment.prod.ts (100%) rename {openvidu-ng-testapp => openvidu-testapp}/src/environments/environment.ts (62%) rename {openvidu-ng-testapp => openvidu-testapp}/src/favicon.ico (100%) rename {openvidu-ng-testapp => openvidu-testapp}/src/index.html (64%) rename {openvidu-ng-testapp => openvidu-testapp}/src/main.ts (60%) create mode 100644 openvidu-testapp/src/openvidu-theme.scss create mode 100644 openvidu-testapp/src/polyfills.ts create mode 100644 openvidu-testapp/src/styles.css create mode 100644 openvidu-testapp/src/test.ts create mode 100644 openvidu-testapp/src/tsconfig.app.json create mode 100644 openvidu-testapp/src/tsconfig.spec.json create mode 100644 openvidu-testapp/src/typings.d.ts rename {openvidu-ng-testapp => openvidu-testapp}/tsconfig.json (80%) rename {openvidu-ng-testapp => openvidu-testapp}/tslint.json (77%) diff --git a/openvidu-browser/ts/KurentoUtils/kurento-utils-js/WebRtcPeer.js b/openvidu-browser/ts/KurentoUtils/kurento-utils-js/WebRtcPeer.js index 62675c38..cd75fcfc 100644 --- a/openvidu-browser/ts/KurentoUtils/kurento-utils-js/WebRtcPeer.js +++ b/openvidu-browser/ts/KurentoUtils/kurento-utils-js/WebRtcPeer.js @@ -32,7 +32,7 @@ var logger = window.Logger || console // }).eror(callback)); // } -try { +/*try { require('kurento-browser-extensions') } catch (error) { if (typeof getScreenConstraints === 'undefined') { @@ -42,7 +42,7 @@ try { callback(new Error("This library is not enabled for screen sharing")) } } -} +}*/ var MEDIA_CONSTRAINTS = { audio: true, diff --git a/openvidu-ng-testapp/e2e/app.e2e-spec.ts b/openvidu-ng-testapp/e2e/app.e2e-spec.ts deleted file mode 100644 index 950be7ad..00000000 --- a/openvidu-ng-testapp/e2e/app.e2e-spec.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { OpenviduNgTestappPage } from './app.po'; - -describe('openvidu-ng-testapp App', () => { - let page: OpenviduNgTestappPage; - - beforeEach(() => { - page = new OpenviduNgTestappPage(); - }); - - it('should display message saying app works', () => { - page.navigateTo(); - expect(page.getParagraphText()).toEqual('app works!'); - }); -}); diff --git a/openvidu-ng-testapp/e2e/tsconfig.e2e.json b/openvidu-ng-testapp/e2e/tsconfig.e2e.json deleted file mode 100644 index 74c2bca1..00000000 --- a/openvidu-ng-testapp/e2e/tsconfig.e2e.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "compilerOptions": { - "sourceMap": true, - "declaration": false, - "moduleResolution": "node", - "emitDecoratorMetadata": true, - "experimentalDecorators": true, - "lib": [ - "es2016" - ], - "outDir": "../dist/out-tsc-e2e", - "module": "commonjs", - "target": "es6", - "types":[ - "jasmine", - "node" - ] - } -} diff --git a/openvidu-ng-testapp/package.json b/openvidu-ng-testapp/package.json deleted file mode 100644 index f55b9b19..00000000 --- a/openvidu-ng-testapp/package.json +++ /dev/null @@ -1,48 +0,0 @@ -{ - "name": "openvidu-ng-testapp", - "version": "1.1.0", - "license": "Apache-2.0", - "scripts": { - "ng": "ng", - "start": "ng serve", - "build": "ng build", - "test": "ng test", - "lint": "ng lint", - "e2e": "ng e2e" - }, - "private": true, - "dependencies": { - "@angular/common": "^2.4.0", - "@angular/compiler": "^2.4.0", - "@angular/core": "^2.4.0", - "@angular/forms": "^2.4.0", - "@angular/http": "^2.4.0", - "@angular/platform-browser": "^2.4.0", - "@angular/platform-browser-dynamic": "^2.4.0", - "@angular/router": "^3.4.0", - "core-js": "^2.4.1", - "rxjs": "^5.1.0", - "ts-helpers": "^1.1.1", - "zone.js": "^0.7.6", - "openvidu-browser": "1.0.2" - }, - "devDependencies": { - "@angular/cli": "1.0.0-rc.1", - "@angular/compiler-cli": "^2.4.0", - "@types/jasmine": "2.5.38", - "@types/node": "~6.0.60", - "codelyzer": "~2.0.0", - "jasmine-core": "~2.5.2", - "jasmine-spec-reporter": "~3.2.0", - "karma": "~1.4.1", - "karma-chrome-launcher": "~2.0.0", - "karma-cli": "~1.0.1", - "karma-jasmine": "~1.1.0", - "karma-jasmine-html-reporter": "^0.2.2", - "karma-coverage-istanbul-reporter": "^0.2.0", - "protractor": "~5.1.0", - "ts-node": "~2.0.0", - "tslint": "~4.4.2", - "typescript": "~2.0.0" - } -} diff --git a/openvidu-ng-testapp/src/app/app.component.html b/openvidu-ng-testapp/src/app/app.component.html deleted file mode 100644 index faa82782..00000000 --- a/openvidu-ng-testapp/src/app/app.component.html +++ /dev/null @@ -1,82 +0,0 @@ -
-

Join a video session

-
-
- - -
-
- - -
-
- Send video -
- - - - - - - - - - - - - - - - - -
ConstraintValue
width
height
frameRate
-
-
-
- Send audio -
-
- -
-
-
- -
-

{{sessionId}}

- - Toggle your video - Toggle your audio -
-
-
-
-
-
- Show conexion state -
-

Signaling state: {{signalingState[i]}}

-

ICE connection state: {{iceConnectionState[i]}}

-
- Show - statistics -
-

Bitrate: {{stats[i].bitrate}}

-

Framerate: {{stats[i].framerate}}

-
- - - - - - - - - - - -
TypeTimeOther attributes
{{st.type}}{{st.timestamp}}{{getStatAttributes(st.res)}}
-
-
-
-
-
diff --git a/openvidu-ng-testapp/src/app/app.component.ts b/openvidu-ng-testapp/src/app/app.component.ts deleted file mode 100644 index 8d939101..00000000 --- a/openvidu-ng-testapp/src/app/app.component.ts +++ /dev/null @@ -1,278 +0,0 @@ -import { Observable } from 'rxjs/Rx'; -import { enableDebugTools } from '@angular/platform-browser'; -import { Component } from '@angular/core'; -import { OpenVidu, Session, Publisher, Stream } from 'openvidu-browser'; - -@Component({ - selector: 'app-root', - templateUrl: './app.component.html' -}) -export class AppComponent { - - private openVidu: OpenVidu; - private session: Session; - private publisher: Publisher; - - cameraOptions: any; - - // Join form - sessionId: string; - participantId: string; - - // Session - streams: Stream[] = []; - - // Publish options - joinWithVideo: boolean = false; - joinWithAudio: boolean = false; - toggleVideo: boolean; - toggleAudio: boolean; - - //Statistics - stats = []; - bytesPrev = []; - framesPrev = []; - timestampPrev = []; - signalingState = []; - iceConnectionState = []; - - - constructor() { - this.generateParticipantInfo(); - //this.obtainSupportedConstraints(); - } - - private generateParticipantInfo() { - this.sessionId = "SessionA"; - this.participantId = "Participant" + Math.floor(Math.random() * 100); - } - - private addVideoTag(stream: Stream) { - console.log("Stream added"); - let ind = (this.streams.push(stream) - 1); - - this.signalingState[ind] = ''; - this.iceConnectionState[ind] = ''; - - //Connection events - stream.getRTCPeerConnection().onsignalingstatechange = (event) => { - this.signalingState[ind] += " => " + stream.getRTCPeerConnection().signalingState; - console.info("Stream " + stream.getId() + " signaling state: " + stream.getRTCPeerConnection().signalingState); - } - - stream.getRTCPeerConnection().oniceconnectionstatechange = (event) => { - /*if (stream.getRTCPeerConnection().iceconnectionstate === "failed" || - stream.getRTCPeerConnection().iceconnectionstate === "disconnected" || - stream.getRTCPeerConnection().iceconnectionstate === "closed") { - // Handle the failure - };*/ - this.iceConnectionState[ind] += " => " + stream.getRTCPeerConnection().iceConnectionState; - console.info("Stream " + stream.getId() + " ice connection state: " + stream.getRTCPeerConnection().iceConnectionState); - } - - //For statistics - this.timestampPrev.push(0); - this.bytesPrev.push(0); - this.framesPrev.push(0); - } - - private removeVideoTag(stream: Stream) { - console.log("Stream removed"); - let index = this.streams.indexOf(stream); - this.streams.splice(index, 1); - - this.stats.splice(index, 1); - this.timestampPrev.splice(index, 1); - this.bytesPrev.splice(index, 1); - this.framesPrev.splice(index, 1); - - this.signalingState.splice(index, 1); - this.iceConnectionState.splice(index, 1); - } - - joinSession() { - let mediaConstraints = this.generateMediaConstraints(); - - console.log(mediaConstraints); - - this.cameraOptions = { - audio: this.joinWithAudio, - video: this.joinWithVideo, - data: true, - mediaConstraints: mediaConstraints - } - this.joinSessionShared(); - } - - joinSessionShared() { - - this.toggleVideo = this.joinWithVideo; - this.toggleAudio = this.joinWithAudio; - - this.openVidu = new OpenVidu("wss://" + location.hostname + ":8443/"); - this.session = this.openVidu.initSession(this.sessionId); - - this.session.on('streamCreated', (event) => { - this.session.subscribe(event.stream, 'subscriber', { - insertMode: 'append', - width: '100%', - height: '100%' - }); - this.addVideoTag(event.stream); - }); - - this.session.on('streamDestroyed', (event) => { - this.removeVideoTag(event.stream); - }); - - this.session.connect(this.participantId, (error) => { - - // If the connection is successful, initialize a publisher and publish to the session - if (!error) { - - // 4) Get your own camera stream with the desired resolution and publish it, only if the user is supposed to do so - this.publisher = this.openVidu.initPublisher('publisher', this.cameraOptions); - - // 5) Publish your stream - this.session.publish(this.publisher); - - this.intervalStats().subscribe(); - - } else { - console.log('There was an error connecting to the session:', error.code, error.message); - } - }); - } - - leaveSession() { - this.session.disconnect(); - this.session = null; - this.streams = []; - this.generateParticipantInfo(); - } - - updateToggleVideo(event) { - this.publisher.publishVideo(event.target.checked); - let msg = (event.target.checked) ? 'Publishing video...' : 'Unpublishing video...' - console.log(msg); - } - - updateToggleAudio(event) { - this.publisher.publishAudio(event.target.checked); - let msg = (event.target.checked) ? 'Publishing audio...' : 'Unpublishing audio...' - console.log(msg); - } - - updateToggleStatistics(i) { - let table = (document.getElementById('table-' + i)); - (table.style.display == "none") ? table.style.display = "block" : table.style.display = "none"; - } - - updateToggleState(i) { - let state = (document.getElementById('state-' + i)); - (state.style.display == "none") ? state.style.display = "block" : state.style.display = "none"; - } - - /*obtainSupportedConstraints() { - let constraints = Object.keys(navigator.mediaDevices.getSupportedConstraints()); - this.supportedVideoContstraints = constraints.filter((e) => { - return this.mediaTrackSettingsVideo.indexOf(e) > -1; - }); - this.supportedAudioContstraints = constraints.filter((e) => { - return this.mediaTrackSettingsAudio.indexOf(e) > -1; - }); - - console.log(constraints); - console.log(this.supportedVideoContstraints); - console.log(this.supportedAudioContstraints); - }*/ - - generateMediaConstraints() { - let mediaConstraints = { - audio: true, - video: {} - } - if (this.joinWithVideo) { - mediaConstraints.video['width'] = { exact: Number((document.getElementById('width')).value) }; - mediaConstraints.video['height'] = { exact: Number((document.getElementById('height')).value) }; - mediaConstraints.video['frameRate'] = { ideal: Number((document.getElementById('frameRate')).value) }; - } - - return mediaConstraints; - } - - - intervalStats() { - return Observable - .interval(1000) - .flatMap(() => { - let i = 0; - for (let str of this.streams) { - if (str.getWebRtcPeer().peerConnection) { - this.intervalStatsAux(i, str); - i++; - } - } - return []; - }); - } - - intervalStatsAux(i: number, stream: Stream) { - stream.getWebRtcPeer().peerConnection.getStats(null) - .then((results) => { - this.stats[i] = this.dumpStats(results, i); - console.info(results); - }); - } - - - dumpStats(results, i) { - var statsArray = []; - let bitrate; - let frames; - - results.forEach((res) => { - let date = new Date(res.timestamp); - statsArray.push({ res: res, type: res.type, timestamp: date.getHours() + ":" + date.getMinutes() + ":" + date.getSeconds() }); - - let now = res.timestamp; - - if (res.type === 'inbound-rtp' && res.mediaType === 'video') { - // firefox calculates the bitrate for us - // https://bugzilla.mozilla.org/show_bug.cgi?id=951496 - bitrate = Math.floor(res.bitrateMean / 1024); - if (res.framerateMean !== undefined && res.frameRate != "0") { - frames = (res.framerateMean).toFixed(2); - } - - } else if (res.type === 'ssrc' && res.bytesReceived && res.googFrameRateReceived) { - // chrome does not so we need to do it ourselves - var bytes = res.bytesReceived; - frames = (res.googFrameRateOutput == "0") ? Number(this.framesPrev[i]) : Number(res.googFrameRateOutput); - if (this.timestampPrev[i]) { - bitrate = 8 * (bytes - this.bytesPrev[i]) / (now - this.timestampPrev[i]); - bitrate = Math.floor(bitrate); - } - this.bytesPrev[i] = bytes; - this.timestampPrev[i] = now; - } - - }); - if (bitrate) { - bitrate += ' kbits/sec'; - } - if (frames) { - this.framesPrev[i] = frames; - frames += ' fps'; - } - return { statsArray: statsArray, bitrate: bitrate, framerate: frames }; - } - - getStatAttributes(stat) { - let s = ''; - Object.keys(stat).forEach((key) => { - if (key != 'type' && key != 'timestamp') s += (' | ' + key + ' | '); - }); - return s; - } -} \ No newline at end of file diff --git a/openvidu-ng-testapp/src/app/app.module.ts b/openvidu-ng-testapp/src/app/app.module.ts deleted file mode 100644 index 9d91ef7c..00000000 --- a/openvidu-ng-testapp/src/app/app.module.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { BrowserModule } from '@angular/platform-browser'; -import { NgModule } from '@angular/core'; -import { FormsModule } from '@angular/forms'; -import { HttpModule } from '@angular/http'; - -import { AppComponent } from './app.component'; -import { StreamComponent } from "./stream.component"; - -@NgModule({ - declarations: [ - AppComponent, StreamComponent - ], - imports: [ - BrowserModule, - FormsModule, - HttpModule - ], - providers: [], - bootstrap: [AppComponent] -}) -export class AppModule { } diff --git a/openvidu-ng-testapp/src/app/index.ts b/openvidu-ng-testapp/src/app/index.ts deleted file mode 100644 index 875bdb2f..00000000 --- a/openvidu-ng-testapp/src/app/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './app.component'; -export * from './app.module'; diff --git a/openvidu-ng-testapp/src/app/stream.component.ts b/openvidu-ng-testapp/src/app/stream.component.ts deleted file mode 100644 index 1f47dd8a..00000000 --- a/openvidu-ng-testapp/src/app/stream.component.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { Component, OnInit, Input, ViewChild } from '@angular/core'; -import { Stream, Session } from 'openvidu-browser'; -import { DomSanitizer, SafeUrl } from '@angular/platform-browser'; - -@Component({ - selector: 'stream', - styles: [` - .participant { - margin: 10px; - } - .participant video { - - }`], - template: ` -
-

{{stream.getId()}}

- - -
` -}) -export class StreamComponent { - - @Input() - stream: Stream; - - videoSrc: SafeUrl; - - constructor(private sanitizer: DomSanitizer) { } - - ngOnInit() { - - let int = setInterval(() => { - if (this.stream.getWrStream()) { - this.videoSrc = this.sanitizer.bypassSecurityTrustUrl( - URL.createObjectURL(this.stream.getWrStream())); - console.log("Video tag src=" + this.videoSrc); - clearInterval(int); - } - }, 1000); - - //this.stream.addEventListener('src-added', () => { - // this.video.src = this.sanitizer.bypassSecurityTrustUrl(URL.createObjectURL(this.stream.getWrStream())).toString(); - //}); - } - -} \ No newline at end of file diff --git a/openvidu-ng-testapp/src/polyfills.ts b/openvidu-ng-testapp/src/polyfills.ts deleted file mode 100644 index 3b4c55b0..00000000 --- a/openvidu-ng-testapp/src/polyfills.ts +++ /dev/null @@ -1,19 +0,0 @@ -// This file includes polyfills needed by Angular 2 and is loaded before -// the app. You can add your own extra polyfills to this file. -import 'core-js/es6/symbol'; -import 'core-js/es6/object'; -import 'core-js/es6/function'; -import 'core-js/es6/parse-int'; -import 'core-js/es6/parse-float'; -import 'core-js/es6/number'; -import 'core-js/es6/math'; -import 'core-js/es6/string'; -import 'core-js/es6/date'; -import 'core-js/es6/array'; -import 'core-js/es6/regexp'; -import 'core-js/es6/map'; -import 'core-js/es6/set'; -import 'core-js/es6/reflect'; - -import 'core-js/es7/reflect'; -import 'zone.js/dist/zone'; diff --git a/openvidu-ng-testapp/src/styles.css b/openvidu-ng-testapp/src/styles.css deleted file mode 100644 index e50a47e7..00000000 --- a/openvidu-ng-testapp/src/styles.css +++ /dev/null @@ -1 +0,0 @@ -/* You can add global styles to this file, and also import other style files */ \ No newline at end of file diff --git a/openvidu-ng-testapp/src/test.ts b/openvidu-ng-testapp/src/test.ts deleted file mode 100644 index 7727c8e6..00000000 --- a/openvidu-ng-testapp/src/test.ts +++ /dev/null @@ -1,34 +0,0 @@ -import './polyfills.ts'; - -import 'zone.js/dist/long-stack-trace-zone'; -import 'zone.js/dist/proxy.js'; -import 'zone.js/dist/sync-test'; -import 'zone.js/dist/jasmine-patch'; -import 'zone.js/dist/async-test'; -import 'zone.js/dist/fake-async-test'; - -// Unfortunately there's no typing for the `__karma__` variable. Just declare it as any. -declare var __karma__: any; -declare var require: any; - -// Prevent Karma from running prematurely. -__karma__.loaded = function () {}; - - -Promise.all([ - System.import('@angular/core/testing'), - System.import('@angular/platform-browser-dynamic/testing') -]) - // First, initialize the Angular testing environment. - .then(([testing, testingBrowser]) => { - testing.getTestBed().initTestEnvironment( - testingBrowser.BrowserDynamicTestingModule, - testingBrowser.platformBrowserDynamicTesting() - ); - }) - // Then we find all the tests. - .then(() => require.context('./', true, /\.spec\.ts/)) - // And load the modules. - .then(context => context.keys().map(context)) - // Finally, start Karma to run the tests. - .then(__karma__.start, __karma__.error); diff --git a/openvidu-ng-testapp/src/tsconfig.app.json b/openvidu-ng-testapp/src/tsconfig.app.json deleted file mode 100644 index 9f12c4b8..00000000 --- a/openvidu-ng-testapp/src/tsconfig.app.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "compilerOptions": { - "sourceMap": true, - "declaration": false, - "moduleResolution": "node", - "emitDecoratorMetadata": true, - "experimentalDecorators": true, - "lib": [ - "es2016", - "dom" - ], - "outDir": "../out-tsc/app", - "target": "es5", - "module": "es2015", - "baseUrl": "", - "types": [] - }, - "exclude": [ - "test.ts", - "**/*.spec.ts" - ] -} diff --git a/openvidu-ng-testapp/src/tsconfig.spec.json b/openvidu-ng-testapp/src/tsconfig.spec.json deleted file mode 100644 index 6c5160e1..00000000 --- a/openvidu-ng-testapp/src/tsconfig.spec.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "compilerOptions": { - "sourceMap": true, - "declaration": false, - "moduleResolution": "node", - "emitDecoratorMetadata": true, - "experimentalDecorators": true, - "lib": [ - "es2016" - ], - "outDir": "../out-tsc/spec", - "module": "commonjs", - "target": "es6", - "baseUrl": "", - "types": [ - "jasmine", - "node" - ] - }, - "files": [ - "test.ts" - ], - "include": [ - "**/*.spec.ts" - ] -} diff --git a/openvidu-ng-testapp/src/typings.d.ts b/openvidu-ng-testapp/src/typings.d.ts deleted file mode 100644 index a73f5867..00000000 --- a/openvidu-ng-testapp/src/typings.d.ts +++ /dev/null @@ -1,5 +0,0 @@ -// Typings reference file, see links for more information -// https://github.com/typings/typings -// https://www.typescriptlang.org/docs/handbook/writing-declaration-files.html - -declare var System: any; diff --git a/openvidu-ng-testapp/.angular-cli.json b/openvidu-testapp/.angular-cli.json similarity index 74% rename from openvidu-ng-testapp/.angular-cli.json rename to openvidu-testapp/.angular-cli.json index 55bd2927..5e1d83ee 100644 --- a/openvidu-ng-testapp/.angular-cli.json +++ b/openvidu-testapp/.angular-cli.json @@ -1,7 +1,7 @@ { "$schema": "./node_modules/@angular/cli/lib/config/schema.json", "project": { - "name": "openvidu-ng-testapp" + "name": "openvidu-testapp" }, "apps": [ { @@ -19,7 +19,8 @@ "testTsconfig": "tsconfig.spec.json", "prefix": "app", "styles": [ - "styles.css" + "styles.css", + "openvidu-theme.scss" ], "scripts": [], "environmentSource": "environments/environment.ts", @@ -36,13 +37,16 @@ }, "lint": [ { - "project": "src/tsconfig.app.json" + "project": "src/tsconfig.app.json", + "exclude": "**/node_modules/**" }, { - "project": "src/tsconfig.spec.json" + "project": "src/tsconfig.spec.json", + "exclude": "**/node_modules/**" }, { - "project": "e2e/tsconfig.e2e.json" + "project": "e2e/tsconfig.e2e.json", + "exclude": "**/node_modules/**" } ], "test": { diff --git a/openvidu-ng-testapp/.editorconfig b/openvidu-testapp/.editorconfig similarity index 100% rename from openvidu-ng-testapp/.editorconfig rename to openvidu-testapp/.editorconfig diff --git a/openvidu-ng-testapp/.gitignore b/openvidu-testapp/.gitignore similarity index 93% rename from openvidu-ng-testapp/.gitignore rename to openvidu-testapp/.gitignore index 8ce87385..54bfd200 100644 --- a/openvidu-ng-testapp/.gitignore +++ b/openvidu-testapp/.gitignore @@ -3,6 +3,7 @@ # compiled output /dist /tmp +/out-tsc # dependencies /node_modules @@ -26,7 +27,7 @@ # misc /.sass-cache /connect.lock -/coverage/* +/coverage /libpeerconnection.log npm-debug.log testem.log @@ -36,6 +37,6 @@ testem.log /e2e/*.js /e2e/*.map -#System Files +# System Files .DS_Store Thumbs.db diff --git a/openvidu-ng-testapp/README.md b/openvidu-testapp/README.md similarity index 80% rename from openvidu-ng-testapp/README.md rename to openvidu-testapp/README.md index 0157fda7..79588e52 100644 --- a/openvidu-ng-testapp/README.md +++ b/openvidu-testapp/README.md @@ -1,13 +1,14 @@ -# OpenviduNgTestapp +# OpenviduTestApp -This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 1.0.0-rc.1. +This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 1.4.3. ## Development server + Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files. ## Code scaffolding -Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive/pipe/service/class/module`. +Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`. ## Build @@ -20,7 +21,6 @@ Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github. ## Running end-to-end tests Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/). -Before running the tests make sure you are serving the app via `ng serve`. ## Further help diff --git a/openvidu-testapp/e2e/app.e2e-spec.ts b/openvidu-testapp/e2e/app.e2e-spec.ts new file mode 100644 index 00000000..c8389373 --- /dev/null +++ b/openvidu-testapp/e2e/app.e2e-spec.ts @@ -0,0 +1,14 @@ +import { AppPage } from './app.po'; + +describe('openvidu-testapp App', () => { + let page: AppPage; + + beforeEach(() => { + page = new AppPage(); + }); + + it('should display welcome message', () => { + page.navigateTo(); + expect(page.getParagraphText()).toEqual('Welcome to app!'); + }); +}); diff --git a/openvidu-ng-testapp/e2e/app.po.ts b/openvidu-testapp/e2e/app.po.ts similarity index 60% rename from openvidu-ng-testapp/e2e/app.po.ts rename to openvidu-testapp/e2e/app.po.ts index 06b2b502..82ea75ba 100644 --- a/openvidu-ng-testapp/e2e/app.po.ts +++ b/openvidu-testapp/e2e/app.po.ts @@ -1,6 +1,6 @@ -import { browser, element, by } from 'protractor'; +import { browser, by, element } from 'protractor'; -export class OpenviduNgTestappPage { +export class AppPage { navigateTo() { return browser.get('/'); } diff --git a/openvidu-testapp/e2e/tsconfig.e2e.json b/openvidu-testapp/e2e/tsconfig.e2e.json new file mode 100644 index 00000000..1d9e5edf --- /dev/null +++ b/openvidu-testapp/e2e/tsconfig.e2e.json @@ -0,0 +1,14 @@ +{ + "extends": "../tsconfig.json", + "compilerOptions": { + "outDir": "../out-tsc/e2e", + "baseUrl": "./", + "module": "commonjs", + "target": "es5", + "types": [ + "jasmine", + "jasminewd2", + "node" + ] + } +} diff --git a/openvidu-ng-testapp/karma.conf.js b/openvidu-testapp/karma.conf.js similarity index 65% rename from openvidu-ng-testapp/karma.conf.js rename to openvidu-testapp/karma.conf.js index 84b4cd5a..af139fad 100644 --- a/openvidu-ng-testapp/karma.conf.js +++ b/openvidu-testapp/karma.conf.js @@ -1,5 +1,5 @@ // Karma configuration file, see link for more information -// https://karma-runner.github.io/0.13/config/configuration-file.html +// https://karma-runner.github.io/1.0/config/configuration-file.html module.exports = function (config) { config.set({ @@ -15,15 +15,6 @@ module.exports = function (config) { client:{ clearContext: false // leave Jasmine Spec Runner output visible in browser }, - files: [ - { pattern: './src/test.ts', watched: false } - ], - preprocessors: { - './src/test.ts': ['@angular/cli'] - }, - mime: { - 'text/x-typescript': ['ts','tsx'] - }, coverageIstanbulReporter: { reports: [ 'html', 'lcovonly' ], fixWebpackSourcePaths: true @@ -31,9 +22,7 @@ module.exports = function (config) { angularCli: { environment: 'dev' }, - reporters: config.angularCli && config.angularCli.codeCoverage - ? ['progress', 'coverage-istanbul'] - : ['progress', 'kjhtml'], + reporters: ['progress', 'kjhtml'], port: 9876, colors: true, logLevel: config.LOG_INFO, diff --git a/openvidu-testapp/package.json b/openvidu-testapp/package.json new file mode 100644 index 00000000..2e2bf3d7 --- /dev/null +++ b/openvidu-testapp/package.json @@ -0,0 +1,55 @@ +{ + "name": "openvidu-testapp", + "version": "0.0.0", + "license": "Apache-2.0", + "scripts": { + "ng": "ng", + "start": "ng serve", + "build": "ng build", + "test": "ng test", + "lint": "ng lint", + "e2e": "ng e2e" + }, + "private": true, + "dependencies": { + "@angular/animations": "^4.4.3", + "@angular/cdk": "^2.0.0-beta.11", + "@angular/common": "^4.2.4", + "@angular/compiler": "^4.2.4", + "@angular/core": "^4.2.4", + "@angular/flex-layout": "^2.0.0-beta.9", + "@angular/forms": "^4.2.4", + "@angular/http": "^4.2.4", + "@angular/material": "^2.0.0-beta.11", + "@angular/platform-browser": "^4.2.4", + "@angular/platform-browser-dynamic": "^4.2.4", + "@angular/router": "^4.2.4", + "core-js": "^2.4.1", + "hammerjs": "^2.0.8", + "openvidu-browser": "1.1.0", + "openvidu-node-client": "1.1.0", + "rxjs": "^5.4.2", + "zone.js": "^0.8.14" + }, + "devDependencies": { + "@angular/cli": "1.4.3", + "@angular/compiler-cli": "^4.2.4", + "@angular/language-service": "^4.2.4", + "@types/jasmine": "~2.5.53", + "@types/jasminewd2": "~2.0.2", + "@types/node": "~6.0.60", + "codelyzer": "~3.1.1", + "jasmine-core": "~2.6.2", + "jasmine-spec-reporter": "~4.1.0", + "karma": "~1.7.0", + "karma-chrome-launcher": "~2.1.1", + "karma-cli": "~1.0.1", + "karma-coverage-istanbul-reporter": "^1.2.1", + "karma-jasmine": "~1.1.0", + "karma-jasmine-html-reporter": "^0.2.2", + "protractor": "~5.1.2", + "ts-node": "~3.2.0", + "tslint": "~5.3.2", + "typescript": "~2.3.3" + } +} diff --git a/openvidu-ng-testapp/protractor.conf.js b/openvidu-testapp/protractor.conf.js similarity index 95% rename from openvidu-ng-testapp/protractor.conf.js rename to openvidu-testapp/protractor.conf.js index 1c5e1e5a..7ee3b5ee 100644 --- a/openvidu-ng-testapp/protractor.conf.js +++ b/openvidu-testapp/protractor.conf.js @@ -19,12 +19,10 @@ exports.config = { defaultTimeoutInterval: 30000, print: function() {} }, - beforeLaunch: function() { + onPrepare() { require('ts-node').register({ project: 'e2e/tsconfig.e2e.json' }); - }, - onPrepare() { jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } })); } }; diff --git a/openvidu-testapp/src/app/app.component.css b/openvidu-testapp/src/app/app.component.css new file mode 100644 index 00000000..cc54e7cd --- /dev/null +++ b/openvidu-testapp/src/app/app.component.css @@ -0,0 +1,8 @@ +#nav-img { + height: 32px; + vertical-align: middle; +} + +main { + padding: 50px +} diff --git a/openvidu-testapp/src/app/app.component.html b/openvidu-testapp/src/app/app.component.html new file mode 100644 index 00000000..f47d98fc --- /dev/null +++ b/openvidu-testapp/src/app/app.component.html @@ -0,0 +1,10 @@ + + + + +
+ +
+
\ No newline at end of file diff --git a/openvidu-testapp/src/app/app.component.spec.ts b/openvidu-testapp/src/app/app.component.spec.ts new file mode 100644 index 00000000..bcbdf36b --- /dev/null +++ b/openvidu-testapp/src/app/app.component.spec.ts @@ -0,0 +1,27 @@ +import { TestBed, async } from '@angular/core/testing'; +import { AppComponent } from './app.component'; +describe('AppComponent', () => { + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ + AppComponent + ], + }).compileComponents(); + })); + it('should create the app', async(() => { + const fixture = TestBed.createComponent(AppComponent); + const app = fixture.debugElement.componentInstance; + expect(app).toBeTruthy(); + })); + it(`should have as title 'app'`, async(() => { + const fixture = TestBed.createComponent(AppComponent); + const app = fixture.debugElement.componentInstance; + expect(app.title).toEqual('app'); + })); + it('should render title in a h1 tag', async(() => { + const fixture = TestBed.createComponent(AppComponent); + fixture.detectChanges(); + const compiled = fixture.debugElement.nativeElement; + expect(compiled.querySelector('h1').textContent).toContain('Welcome to app!'); + })); +}); diff --git a/openvidu-testapp/src/app/app.component.ts b/openvidu-testapp/src/app/app.component.ts new file mode 100644 index 00000000..655c40c9 --- /dev/null +++ b/openvidu-testapp/src/app/app.component.ts @@ -0,0 +1,17 @@ +import { Component } from '@angular/core'; +import { Router } from '@angular/router'; + +@Component({ + selector: 'app-root', + templateUrl: './app.component.html', + styleUrls: ['./app.component.css'] +}) +export class AppComponent { + + constructor(private router: Router) { } + + isVideoSessionUrl() { + return (this.router.url.substring(0, '/lesson/'.length) === '/lesson/'); + } + +} diff --git a/openvidu-testapp/src/app/app.material.module.ts b/openvidu-testapp/src/app/app.material.module.ts new file mode 100644 index 00000000..7fed5df1 --- /dev/null +++ b/openvidu-testapp/src/app/app.material.module.ts @@ -0,0 +1,33 @@ +import { NgModule } from '@angular/core'; +import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { + MdButtonModule, + MdIconModule, + MdCheckboxModule, + MdCardModule, + MdInputModule, + MdProgressSpinnerModule, + MdTooltipModule, + MdDialogModule, + MdToolbarModule, + MdTabsModule, + MdSlideToggleModule +} from '@angular/material'; + +@NgModule({ + exports: [ + BrowserAnimationsModule, + MdButtonModule, + MdIconModule, + MdCheckboxModule, + MdCardModule, + MdInputModule, + MdProgressSpinnerModule, + MdTooltipModule, + MdDialogModule, + MdToolbarModule, + MdTabsModule, + MdSlideToggleModule + ], +}) +export class AppMaterialModule { } diff --git a/openvidu-testapp/src/app/app.module.ts b/openvidu-testapp/src/app/app.module.ts new file mode 100644 index 00000000..24a2504e --- /dev/null +++ b/openvidu-testapp/src/app/app.module.ts @@ -0,0 +1,32 @@ +import { BrowserModule } from '@angular/platform-browser'; +import { NgModule } from '@angular/core'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { FlexLayoutModule } from '@angular/flex-layout'; +import { AppMaterialModule } from './app.material.module'; + +import { routing } from './app.routing'; +import { AppComponent } from './app.component'; +import { DashboardComponent } from './components/dashboard/dashboard.component'; +import { SessionComponent } from './components/session/session.component'; + +import { OpenviduRestService } from './services/openvidu-rest.service'; + +@NgModule({ + declarations: [ + AppComponent, + DashboardComponent, + SessionComponent + ], + imports: [ + BrowserModule, + FormsModule, + BrowserAnimationsModule, + AppMaterialModule, + FlexLayoutModule, + routing + ], + providers: [OpenviduRestService], + bootstrap: [AppComponent] +}) +export class AppModule { } diff --git a/openvidu-testapp/src/app/app.routing.ts b/openvidu-testapp/src/app/app.routing.ts new file mode 100644 index 00000000..909bf8f4 --- /dev/null +++ b/openvidu-testapp/src/app/app.routing.ts @@ -0,0 +1,18 @@ +import { ModuleWithProviders } from '@angular/core'; +import { Routes, RouterModule } from '@angular/router'; + +import { DashboardComponent } from './components/dashboard/dashboard.component'; +import { SessionComponent } from './components/session/session.component'; + +const appRoutes: Routes = [ + { + path: '', + component: DashboardComponent + }, + { + path: 'session/:session-name', + component: SessionComponent + } +]; + +export const routing: ModuleWithProviders = RouterModule.forRoot(appRoutes); diff --git a/openvidu-testapp/src/app/components/dashboard/app.material.module.ts b/openvidu-testapp/src/app/components/dashboard/app.material.module.ts new file mode 100644 index 00000000..314e4aec --- /dev/null +++ b/openvidu-testapp/src/app/components/dashboard/app.material.module.ts @@ -0,0 +1,41 @@ +import { NgModule } from '@angular/core'; +import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { + MdButtonModule, + MdIconModule, + MdCheckboxModule, + MdCardModule, + MdInputModule, + MdProgressSpinnerModule, + MdTooltipModule, + MdDialogModule, + MdSlideToggleModule +} from '@angular/material'; + +@NgModule({ + imports: [ + BrowserAnimationsModule, + MdButtonModule, + MdIconModule, + MdCheckboxModule, + MdCardModule, + MdInputModule, + MdProgressSpinnerModule, + MdTooltipModule, + MdDialogModule, + MdSlideToggleModule + ], + exports: [ + BrowserAnimationsModule, + MdButtonModule, + MdIconModule, + MdCheckboxModule, + MdCardModule, + MdInputModule, + MdProgressSpinnerModule, + MdTooltipModule, + MdDialogModule, + MdSlideToggleModule + ], +}) +export class AppMaterialModule { } diff --git a/openvidu-testapp/src/app/components/dashboard/dashboard.component.css b/openvidu-testapp/src/app/components/dashboard/dashboard.component.css new file mode 100644 index 00000000..506bed37 --- /dev/null +++ b/openvidu-testapp/src/app/components/dashboard/dashboard.component.css @@ -0,0 +1,93 @@ +#join-dialog h1 { + color: #4d4d4d; + font-weight: bold; + text-align: center; +} + +#img-div { + text-align: center; + position: absolute; + top: 19%; + left: 50%; + transform: translate(-50%, -50%); +} + +#img-div img { + height: 15%; +} + +#join-dialog label { + color: #0088aa; +} + +#join-dialog input.btn { + margin-top: 15px; +} + +#session-header { + margin-bottom: 20px; +} + +#session-title { + display: inline-block; +} + +#buttonLeaveSession { + float: right; + margin-top: 20px; +} + +#video-container video { + position: relative; + float: left; + cursor: pointer; +} + +#video-container p { + display: inline-block; + background: #f8f8f8; + padding-right: 5px; + padding-left: 5px; + color: #777777; + font-weight: bold; + border-bottom-right-radius: 4px; +} + +video { + width: 100%; + height: auto; +} + +#main-video p { + position: absolute; + display: inline-block; + background: #f8f8f8; + padding-right: 5px; + padding-left: 5px; + left: 0; + font-size: 22px; + color: #777777; + font-weight: bold; + border-bottom-right-radius: 4px; +} + +#main-video video { + cursor: initial; +} + +#session img { + width: 100%; + height: auto; + display: inline-block; + object-fit: contain; + vertical-align: baseline; +} + +#session #video-container img { + position: relative; + float: left; + width: 50%; + cursor: pointer; + object-fit: cover; + height: 180px; +} diff --git a/openvidu-testapp/src/app/components/dashboard/dashboard.component.html b/openvidu-testapp/src/app/components/dashboard/dashboard.component.html new file mode 100644 index 00000000..e9457a7d --- /dev/null +++ b/openvidu-testapp/src/app/components/dashboard/dashboard.component.html @@ -0,0 +1,47 @@ + + +
+ +
+
+ +
+ +
+
+ + + + +
+ +
+
+ + + + + + + +
+ + diff --git a/openvidu-testapp/src/app/components/dashboard/dashboard.component.spec.ts b/openvidu-testapp/src/app/components/dashboard/dashboard.component.spec.ts new file mode 100644 index 00000000..9c996c37 --- /dev/null +++ b/openvidu-testapp/src/app/components/dashboard/dashboard.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { DashboardComponent } from './dashboard.component'; + +describe('DashboardComponent', () => { + let component: DashboardComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ DashboardComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(DashboardComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/openvidu-testapp/src/app/components/dashboard/dashboard.component.ts b/openvidu-testapp/src/app/components/dashboard/dashboard.component.ts new file mode 100644 index 00000000..8ce9494d --- /dev/null +++ b/openvidu-testapp/src/app/components/dashboard/dashboard.component.ts @@ -0,0 +1,45 @@ +import { Component, OnInit } from '@angular/core'; +import { OpenviduRestService } from '../../services/openvidu-rest.service'; + +@Component({ + selector: 'app-dashboard', + templateUrl: './dashboard.component.html', + styleUrls: ['./dashboard.component.css'] +}) +export class DashboardComponent implements OnInit { + + // Join form + sessionName: string; + clientData: string; + + constructor(private openviduRestService: OpenviduRestService) { + this.generateSessionInfo(); + } + + ngOnInit() { } + + private generateSessionInfo() { + this.sessionName = 'TestSession'; + this.clientData = 'RandomClient' + Math.floor(Math.random() * 100); + } + + private getSessionId() { + this.openviduRestService.getSessionId() + .then((sessionId) => { + alert(sessionId); + }) + .catch((error) => { + console.error('Error getting a sessionId', error); + }); + } + + private getToken() { + this.openviduRestService.getToken() + .then((token) => { + alert(token); + }) + .catch((error) => { + console.error('Error getting a token', error); + }); + } +} diff --git a/openvidu-ng-testapp/src/assets/.gitkeep b/openvidu-testapp/src/app/components/session/session.component.css similarity index 100% rename from openvidu-ng-testapp/src/assets/.gitkeep rename to openvidu-testapp/src/app/components/session/session.component.css diff --git a/openvidu-testapp/src/app/components/session/session.component.html b/openvidu-testapp/src/app/components/session/session.component.html new file mode 100644 index 00000000..6a817399 --- /dev/null +++ b/openvidu-testapp/src/app/components/session/session.component.html @@ -0,0 +1,11 @@ +
+
+

{{sessionName}}

+ +
+
+

+ +
+
+
\ No newline at end of file diff --git a/openvidu-testapp/src/app/components/session/session.component.spec.ts b/openvidu-testapp/src/app/components/session/session.component.spec.ts new file mode 100644 index 00000000..b6161fd6 --- /dev/null +++ b/openvidu-testapp/src/app/components/session/session.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { SessionComponent } from './session.component'; + +describe('SessionComponent', () => { + let component: SessionComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ SessionComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(SessionComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/openvidu-testapp/src/app/components/session/session.component.ts b/openvidu-testapp/src/app/components/session/session.component.ts new file mode 100644 index 00000000..50ee54ee --- /dev/null +++ b/openvidu-testapp/src/app/components/session/session.component.ts @@ -0,0 +1,135 @@ +import { OpenVidu, Session } from 'openvidu-browser'; +import { Component, ElementRef, ViewChild, HostListener, OnDestroy } from '@angular/core'; + +declare var $: any; + +@Component({ + selector: 'app-session', + templateUrl: './session.component.html', + styleUrls: ['./session.component.css'] +}) +export class SessionComponent implements OnDestroy { + + @ViewChild('mainVideoElement') elementRef: ElementRef; + mainVideoElement: HTMLVideoElement; + + // OpenVidu objects + OV: OpenVidu; + session: Session; + + // Join form + sessionName: string; + clientData: string; + + constructor() { } + + @HostListener('window:beforeunload') + beforeunloadHandler() { + // On window closed leave session + this.leaveSession(); + } + + ngOnDestroy() { + // On component destroyed leave session + this.leaveSession(); + } + + joinSession() { + + this.OV = new OpenVidu(); + + this.session = this.OV.initSession('wss://' + location.hostname + ':8443/' + this.sessionName + '?secret=MY_SECRET'); + + this.mainVideoElement = this.elementRef.nativeElement; + + this.session.on('streamCreated', (event) => { + const subscriber = this.session.subscribe(event.stream, 'video-container'); + subscriber.on('videoElementCreated', (e) => { + this.appendUserData(e.element, subscriber.stream.connection); + }); + }); + + this.session.on('streamDestroyed', (event) => { + this.removeUserData(event.stream.connection); + }); + + this.session.connect(null, this.clientData, (error) => { + + if (!error) { + const publisher = this.OV.initPublisher('video-container', { + audio: true, + video: true, + quality: 'MEDIUM' + }); + + publisher.on('videoElementCreated', (event) => { + this.initMainVideo(event.element, this.clientData); + this.appendUserData(event.element, this.clientData); + event.element['muted'] = true; + }); + + this.session.publish(publisher); + + } else { + console.log('There was an error connecting to the session:', error.code, error.message); + } + }); + + return false; + } + + leaveSession() { + if (this.OV) { + this.session.disconnect(); + } + this.removeAllUserData(); + this.session = null; + this.OV = null; + } + + private appendUserData(videoElement, connection) { + let userData; + let nodeId; + if (typeof connection === 'string') { + userData = connection; + nodeId = connection; + } else { + userData = JSON.parse(connection.data).clientData; + nodeId = connection.connectionId; + } + const dataNode = document.createElement('div'); + dataNode.className = 'data-node'; + dataNode.id = 'data-' + nodeId; + dataNode.innerHTML = '

' + userData + '

'; + videoElement.parentNode.insertBefore(dataNode, videoElement.nextSibling); + this.addClickListener(videoElement, userData); + } + + private removeUserData(connection) { + const dataNode = $('#data-' + connection.connectionId); + dataNode.parentNode.removeChild(dataNode); + } + + private removeAllUserData() { + const nicknameElements = $('.data-node'); + while (nicknameElements[0]) { + nicknameElements[0].parentNode.removeChild(nicknameElements[0]); + } + } + + private addClickListener(videoElement: HTMLVideoElement, userData) { + videoElement.addEventListener('click', () => { + const mainUserData = $('#main-video p'); + if (this.mainVideoElement.srcObject !== videoElement.srcObject) { + mainUserData.innerHTML = userData; + this.mainVideoElement.srcObject = videoElement.srcObject; + } + }); + } + + private initMainVideo(videoElement: HTMLVideoElement, userData) { + this.mainVideoElement.srcObject = videoElement.srcObject; + $('#main-video p').innerHTML = userData; + this.mainVideoElement['muted'] = true; + } +} diff --git a/openvidu-testapp/src/app/services/openvidu-rest.service.spec.ts b/openvidu-testapp/src/app/services/openvidu-rest.service.spec.ts new file mode 100644 index 00000000..f2273860 --- /dev/null +++ b/openvidu-testapp/src/app/services/openvidu-rest.service.spec.ts @@ -0,0 +1,15 @@ +import { TestBed, inject } from '@angular/core/testing'; + +import { OpenviduRestService } from './openvidu-rest.service'; + +describe('OpenviduRestService', () => { + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [OpenviduRestService] + }); + }); + + it('should be created', inject([OpenviduRestService], (service: OpenviduRestService) => { + expect(service).toBeTruthy(); + })); +}); diff --git a/openvidu-testapp/src/app/services/openvidu-rest.service.ts b/openvidu-testapp/src/app/services/openvidu-rest.service.ts new file mode 100644 index 00000000..54bd9a85 --- /dev/null +++ b/openvidu-testapp/src/app/services/openvidu-rest.service.ts @@ -0,0 +1,38 @@ +import { Injectable } from '@angular/core'; +import { + OpenVidu as OpenViduAPI, + Session as SessionAPI, + TokenOptions as TokenOptionsAPI, + OpenViduRole as OpenViduRoleAPI +} from 'openvidu-node-client'; +import { environment } from '../../environments/environment'; + +@Injectable() +export class OpenviduRestService { + + constructor() { } + + getSessionId(): Promise { + const OV = new OpenViduAPI(environment.OPENVIDU_URL, environment.OPENVIDU_SECRET); + const session = OV.createSession(); + + return new Promise(resolve => { + session.getSessionId((sessionId) => { + resolve(sessionId); + }); + }); + } + + getToken(): Promise { + const OV = new OpenViduAPI(environment.OPENVIDU_URL, environment.OPENVIDU_SECRET); + const session = OV.createSession(); + + return new Promise(resolve => { + let tokenOptions: TokenOptionsAPI; + session.generateToken((token) => { + resolve(token); + }); + }); + } + +} diff --git a/openvidu-testapp/src/app/services/session-conf.service.spec.ts b/openvidu-testapp/src/app/services/session-conf.service.spec.ts new file mode 100644 index 00000000..a4b153e8 --- /dev/null +++ b/openvidu-testapp/src/app/services/session-conf.service.spec.ts @@ -0,0 +1,15 @@ +import { TestBed, inject } from '@angular/core/testing'; + +import { SessionConfService } from './session-conf.service'; + +describe('SessionConfService', () => { + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [SessionConfService] + }); + }); + + it('should be created', inject([SessionConfService], (service: SessionConfService) => { + expect(service).toBeTruthy(); + })); +}); diff --git a/openvidu-testapp/src/app/services/session-conf.service.ts b/openvidu-testapp/src/app/services/session-conf.service.ts new file mode 100644 index 00000000..2fd946ba --- /dev/null +++ b/openvidu-testapp/src/app/services/session-conf.service.ts @@ -0,0 +1,17 @@ +import { Injectable } from '@angular/core'; +import { Subject } from 'rxjs/Subject'; + +@Injectable() +export class SessionConfService { + + private conf$ = new Subject(); + + getConf() { + return this.conf$; + } + + updateConf(configuration: any) { + this.conf$.next(configuration); + } + +} diff --git a/openvidu-ng-testapp/src/assets/.npmignore b/openvidu-testapp/src/assets/.gitkeep similarity index 100% rename from openvidu-ng-testapp/src/assets/.npmignore rename to openvidu-testapp/src/assets/.gitkeep diff --git a/openvidu-testapp/src/assets/.npmignore b/openvidu-testapp/src/assets/.npmignore new file mode 100644 index 00000000..e69de29b diff --git a/openvidu-testapp/src/assets/images/openvidu_globe_bg_transp_cropped.png b/openvidu-testapp/src/assets/images/openvidu_globe_bg_transp_cropped.png new file mode 100644 index 0000000000000000000000000000000000000000..e0309e62181608cf3543664ced79b6355779533b GIT binary patch literal 11746 zcma)ibyOTr@aN+0wh&yGKyVB0?he5v!C45hxclM+cL){&!NOue7Iz2|0wKTx!Gb#k zZoj|#>+ZdK@7~Px%yd_ENq@F#Uri_IYO4_7(%=FB00K2tMSTDOg#h{d7aJ4Veq>^l zM&8ih$!W;}08OcQkG2@dcSd_veJubWkR1RBj|KqlkzL_?0Dvza0B~Rf0Ep)T094?@ zPCZHF0H&RWiX!0ozoWFLIuqH016DPA4*+lx|97F7e;>d>c4GOcX(?fCVgYf1f|ztY zp#T7Py_%w&A>`-ZqJY9mBj20eKhj2vt4A;30(^XEiQ#VWts2llsoFxcBj)y^Vy4rA zV#R4hUWAFFq6kH@1~UzGmq3nE3-uU#oK!a#1qB65j*>2GrM=kdAmBLQ8Fo`!cT8rTxekMFo?=SC=Sk08;clj*4U($5u3Id7`2|X;2We zCR!Dmr7VgQOR%PSDnbC7!}x<_)0JTs?UJz`B{cP1;@(svu^LS^+{T|(HdbYyW9?!j zy!FQ~@yhRu6w!EQfO`^hEQq>Z>IY`(IMfkRXqO293-BF#%La&`AacW^$D&ufeGSG_ zDlUJKi|UhE3gR(mpx@4Il0KTVZ%1mj{o8Oo4^T{)MXr)(f)a-Ehy&*9U;43M#W+6E zw~TM!nx5a7Qk0rtwPCeE6Ya@TA9wp@k|A@>BOSv&M7+Yk--`4fh7p#01&|d|S_Cx+ zCjvCmw@z_?vp=U_L6r+AszJ5M) zGOQ0+&Jw2t8>Hh75LO-AdQEyE5(&D~4`d-y{R6}T8$Er?wJ&Qsm}BKM0eD+Yh0HmK z(+^{jVeZnKKWc-@xyBH~9T~;=!38c~zSrZyf$^ln>L@V4PNwQk#H!)4)y;Bd+|87iR|#g0VVz#Mn&9|>Gp->rof0P#0PVkkZtJ{-dD!Z7d* z1vA>xEa^3vjjn9q6Qfwl@iho(H#BT3M2{-dUkc$PhP-5Pk5DX9uq4&fs{*zOB?&O* z)pg&AeDSFcdj}j&(qn|3b0Ea1zFvPbv zG*w6GPuG(LL9aIPOX@J1(!U%jkDY2_u9Jn?dnDK%N#7|BcvTkCwA9hHKx&Z@nLZF<-y+F91yrMN_i zbiE8t2I~h*X~!`iDdqClN;$*%{1})9(KVvc)d0)n*~*&YV;Ea)eXNth{LsL()G{)) z@RZ?H7xWv4o$yQvp3p0WAH}vnv@wQZm9oiFDq_VGnnO6vF-|~qh1xz~u ztnET-Fr~K)pt4C&;k~Q;R_$MfJvlp|^1o5bEJhmjrBT1`B#!jw0ut*+5l%k*3M_${3!~8EA+)i;2|2_5wp@nt2|sXY&4_J) zn*a`conabSUgpJn)?y31s^Fv_W+#ocJ)&f%`M+B&-KJSSnm8?IDuXKj_Y$tvuF{)DuIa0AJ4M^O+&30nl@;7g7rQF*}0J!(AjfgX7R9c5ssFe?iG z91uw+!kX*Tn5+&i4RuQy_E;{-O^GDq#xvu?@@vN^DYf-OyD`Rx2j;cIx919?aU~s60ftr038b7c8`Zm>E-8hjOY)`WkJ^l5 zHV6mU#gEfyV1R&);3@Gb-UcP@vqtaryk(@CBh|~?s^V6LpItU9Tb44yWXb6xCgOUW zZ$RGe0wvGji;j^g=1fv2%7=Xr&(kNnP<0mlJ9Odr*xy2C566MNB9Z0kq9uMuk5vIQ zFp|Z_C`semaBn$O;_L7-+Ba&IJbMhSKr`Slv&kr#ln-Y}i&V=P=Zm6s^NZ|$SKxMQv zypgjsOPV)r*wO?zABA(B7Se+kM>}E=rV3QSVO7LGV{?DX&-cQ@NU`q7atBgy%_q9qA7GB z|4n;aCiBid1Qe@BN&W1@yntYR@^E4j!vJ2pZAq*^1F zUFHChG!OWdy+z%Y`*ChS^|5j0raWI?ReUea4x(ic7{com``*2^gSpOyLYIIn*;t8K zvbg!gUha=vA^(nP%Dz~5Poe?mhG~hrz0q$5Sj&UgxArT!(B&HyQj^MUb%>EG*5W{} zQ!683Sv_Y_!ywgl0M$v+wK%4UTJ8jX%0cc#o`KcSaD9QR$S?T2rKj+B_r?Zbx-5%` z$-xS?rd;JDCT!|rKa=g@f6|i=)kX1u+Iw|wmgk(I) z5{(oYF8wPV6o6ClBUGydS`$E4eh zNtrUsL^shhr$9|1^LRt41lK>e)3Y_^4o>jEaL7j9oBXkyn|lr8$!GL1QN&aht8Gvx z0S0N)oBEY6I;~2?eHA{PBGr(%c#Y#E; zVroj}<2U)5B=4=58IUcISc&E<&8`@ffW z(uKJYsEVc#l)u7QvJksk+p5nJt8k?4Av6~*aZ@dNSY`d7B{@vZPBWaF*_bu8V2~Cq zSVcP`bs;@0x*MDpwfe_!kcIyYNR{gZM4&S1JmPN6v12^B9JsXlpwdWbuu73LE(4uv zuCucJB-4NmrC`64UOR1POeFmDwX`IasmTcJEd)&^**pBE^Uo;%0|V7b0DDW4K{>?1 z_;pAY-y9!JWd9cbtNpD^9pIxb$j_PO##-=rhc{ZQWc(*EA*0po8kQy@C2)^KR9aUs)hJp&=DEB=;?uwB6F%1}1ogQ0JBe>Jn{x%myAK$#G$bY}=`_8?0Uw>6n_ zdRfPY9kt^nzdQkRT^|v1AD921fmtVY?+F%zneED`!|Q)R(}jVkQnffE;HFRSgoG2s zBoh^Gf3BZw{%FXV%Z%B%8J_*Z^-f5NkjY4=+7*$plH`@Kab_Q4N|1y!j+DGy<2)7z z>*AmxYVYQs2!(_qa+$N4j8Yl7j|z#JwBwo{ei#XwV5BYz3KfPG7|NkZBbEBb~W!{P-*H13xXUeFz{{&y)Q0 zfP@4$18hBG(0%A{ZEJMmBA$_=-b-Qcgp{PEL^;GvqSAhXcz+l=_|es)T;)NF?;skf zDEUI7l9^z`%Hw&ed8!HUIbMti!E31#hbNMK%@;GBvf<6+)p+DvoOj zF?!>w{~z9CS&1BWEq6Uno`g#*-jlVvhXBK`E6EI}I)TW13XNsHELc{zzQYPE-rQ;v z_4>X`QJFu#D~>6YBXW?66i*CFG#N_R086glsPMdbkZSnlBii#VPGnha?!)Lp=8Dfj zyj{61)YS#4|H#3)vUJxmmPOB|-`C1FAhseOR!ceVg+V(Zq>G2yqK523z9#cFKI2$h zJ(De6Hdg2Q%F4viO>`nH6Kh$}0QBeFl9cI_corRR4iCmfC!k-pNno^=}X8mlgp1V1#C4FiU zDe_b<*+Z~?ML4pU^2dCw`L9W^_mtLJ$6r14GO7J$izKi3gjoIq^d?=_UGqn~hs^(m zAHj!c4hPQubm8XdgVkRyO1;&ZX<==c@_v;MK&CD^oDiA7*2$Kg zt4Xe@#HpwQmKSXul#Wv{kBbnCZ`Ti!uz#Yg4TdCmE!;E=NcwWdl)w8Tp(_{M9Da1r zI>I-C@?I}$x6L0Rt8k0fUG^vc)e13Mx)_}7+(iduA}6j37yH(GB_}P8m|}(RIp%Y` z?Eq2w`!sHC35$9iF2$WexFWv~{zpk&oZP!tc5KhT{mo!*1$9?gi#;`D9yprzt6SvL+MO+`& zB9P0NnN~`Ia#H%{zN$FPew8Kow8OL9$rKdJVU()E^*rE8ooF0cIP*c&Yl-106GO+M zulI8=1=0TibF$M3XHv>pZs0yeyTK~##Y~RqMMuy2*yB96VWgNLiJ^S`rT-4We{gc>Z+g_#_@HgnMmVm0m}nU)+O=DS4u?DYGVQ-6Pb zx;4A8cYEpib>F}qYYtfSXlHM+apnWl?rbaZtndBc>2^!)-cVG|3dypv9)7SQPDn7b zWT(W$FtTyndFNXF%gzBpO8apwu9Vds>`3uw)+aPo`$_qNy($L5*4Ov}qBRFW#|=N|=F6R?WGbd1%j5%_R$K4jdEe)gRE>811K`QPFRi)n9&w|HZeSXq+z}^8+JI0Y}_QCMHqj zfQ_i)4}fHG$+0Ozp{)=GYNzc>2Q{*U*yv{5rVRuy;)Le+odC2+nfyNK)>p7kcq9$C z+>4y?gFf!fZneY`);l0UGA=81X83GjQ8mI%RL>gBUX+n%s&ZLsL zO2Ly7?usZ8IWKav(N~JGppC={>HP&vyN<%-a6BkFoPA_ zhcFJ`_6I!HMcK)lz-9dLATbLjzsLXqY_nM59B6^J;$W(NA?g(o1cCa~(HCoess=_J=usBY%XiENv#06x2{C)4mE1Z zu4D*cr?bF{LR{=h#%?bE7#LMs7RxbuTo1j#4yBlp{04<#j2CCO4qtX_C-yT#T7il_K!jXIP4kz zri6@Yn!F*T0A59yTBCT#jG@;&LR9(K+bG|z?N;3Pj!`Ck@q-DE`T3SM ztO0oXR)siGA-oZ#kr_&2eesYDXA#Q^TI1FRefylT=O{$oK^#BajW^pSsA@tN-;hVK z^g$>T-@cF9;Pa;omks(pz3d#GWD6Er01h=Zj?#u_cq&Se9zn*gF;45~k$tFRkeXPx z4{quN`A;=2%J{j`1;WrdMr<`qXB11BqNCb>r(wASd9EUd<=z0$20~u+laR`~vzD1tD+GvcpLBkp@+BV^BbUSvja_!{el8v3!9xl22eFhPrQweyC zSHAc)$r3*-Ax4G65UXu2+pay>n3oXdq2q&CV5E#`HN!xw!|aoC@L*ZT`Pe+bWSb70l`lVkzPm2hw589Z|r@@>B9Mo z*JJr?CZ*rDmsw{%>Y|jO^Q+?RU2FRsqWgR&f4r~VTf))hiRsl60KHxOq5MAMw@`Zq zL^T-oFDL%ySR+@&)2#ykuz)1B()oTgk7OKLBU%dUpzJQ7NT>#MH1C^G{aiiNeu~!4 zKzEsHxvp&S5bjii&^62c)hc@1uxS~ubPFPOX2Mo94__B)ZG5$D;^QAn&*se3JrV#m zYs-LK;3{b^9#X!hh0qQ)xi!Vvt|@g!ZiJJMQ7uZKE7|>P^s^EFl8-u09vz~0-N#Sh zD4>h_6I*_sfEKH}VmJInBDZ$W`quSsu)1Ws|P(B_{#ea5vObIIJ*~KD{UFKohz*g?G6(z3b-TuBT9cDGHjFab0xl^l{e55Ie#Z=ixiTffm zOq)q$3!$F#9q|dl$jCS7O44hUzdm6(v?9quw#P_kCd%foRH(8%$@=eV0txH^*N>qu z0KCdPUzK0oDLfR!chU8yR~6g&nlAlI;2AkS%^^TsIHK1(3nzb;NV{9 zhAQQHI*;aMQZbk4-Nj69g$ym9rX9(phvLpzt~j366jAISE(QOpb0SPldmPkAGh?o5Zgl zHNSoD-=F^+#+^i`XQ_-3{@~gS4={1T2Z1CxEFW_UCWtaaZaWHwFn^wsqX>*z$mypa zYd3Qc_Q^<8FHzKL!99N-DqS(nPyP|Ja5+KyHFijhAJxBns<-h8a)V=#GinKzlEBXdaSiEy#~;j|P(&?D-rAj;3arSi zw2ZjB9*6!C`Km3JWfve){K)i z7Ncjq&|#_NaE>aL27Sm#Gc}u^mHNyuEfJFAC)ru<#T$^{fl7y+3V8S~pFPYd@Di_S z=T15U?_0t2-F=w$PnKU_RC>D_-OH~ve=7;4*3DHBCKf`@u9whmS z+S0N3D@B75_F9Ytf;XHCTlkGjqvfZJH@nstPlV~{CxV?1j0U;VW~vYGy$F(o4HXLE zLuFqrHwM@ge%|dR4)Olrnp9-FAG?SM%Gp+lxdXd71n@5?4UoVh# zP}6@JTWPAQFeT@fE5C~dQ*7@wNpE|N?=1HVX4)7NwdT;N zf~IiEB8+{Jb|0c_g6xx#seYAh^zWQy$)gkl@>T_S#Jk4nt3^#yLg$sxii z5?pzRVtfV%!xX`Oh+cLv;e$H?+smGtnqhXUh5}6KC5G zgC`B+h8cl8W3s)?>@Jf~%W6c!?Oku;Par;+?)n#Dn_7M7bz0f^5kI@1&`l%G0|nNj z&Mhp=NzXenPN}>nQ+9}wEp1jLCUYNqjs>D&m?w@Uit z$ZAMU%aISLKBkgrFd&0ffvtanQoxdO!b-FNY2KzN;}|(B4jr*Q$$I+QW<2qGLDuWg zS0v^lDYv02;Yb6Wrt0I<%DjvnZ!* zbhXRH+t;wm^$ru=0MeS{@&d4Oy2X05!F6x6Bledlan{AdbcROEQYgN_ij(I4{4 z$IXwq!^kk;)s5XsAf!OT)8nP+Z|r}WL%jTx02#Ya8pdo)whKUU6~XDKoNAi0FE_ko zd)b_lgR+ZI{uJUmV>-(?WpARBYW-+^vPCZI`=~DNJB8a)u&6p@>W0WZlXr5IU}I63 zBh!mGjp*~4i{sH61<87HQnG#P?%S6msegp+Lw*eNv|P5P(JG_9|H}3JPjNVFf6j64 z5KOC9vpVO*qv6r7@E5+4tZ89ChzYj9E#cLDz}iSMtd1Lg(KfNPj7F7yMiCuq5q!Qq zhAaV5YV}hr$EBR{@bu3F>(@GFjM|o#FyZ$pn}wDzAfz<+0)<0D)bnwj%L~I?Ed7O; zvz5v5Yw{sUv0p!u{E4wh=S>^*NHh!nnaeeAQP{<5O24i^`Ob{C-_h)OWUm>Fd5WLB z*}NU;C$!p0R9+Z++80m_{gHBzxUN!zk*|Zn*^61l7n;%OmtE>hnIeJRL20`Zs3kiY zS*QhlY2k;asSi8mx&6Kz`ykJX#$FLfe`-4%W@qX#7M5$XLbbi^U*1%GUisLBLMh=Gv;}_vQ{7)|RXzCK3%wTkCYgxM-Me7#h$`SO69n(;A zC(^8YQ(-8+Bur@Yh9YN&4r-ml#xTPX9UREO{m}YNaMagCt*0co&dAFr{S5Sl*Zxob z(Ox31eUCdTC>jja#^=xlMIxEY29J6MOlk9R@ytRQ_b$q<+=F?%PV=n2KPYrdega^Dd`WMd!^<$$ut;dB$ zQ;D<@gx*T$ZOpl*K;FLd?;C|fy4TSHXUGtn!B+nOhjUv7lOiS@#|&uJ6`9Gv5b!|f zzVE7>Wyw<$r4vYkh5WQ0-D+$PL(bnvDXT@#LuISk7x}-jETfBCiU?k<9j7C;I}oyi z;7*cYxFtr6I0gtVlHVQ0boQO_TuGH#g`27|zc4)Y*S&)M(KQm^c?l&ENyULlvlP*%Zjp1M=5aARMJdcnb|(OaO$BO!jH zq5F?=JiV`)`d=IOU2YJ@7U3su8?NgGUj$s)6OEmO>@E*aQr^i%kksIF8;<#l@TBmt zvir^GAIfVjW|YJ6@At15&0i%{kdnc5NeR(i89lT69q3bYh5ZToEhTfsPV=_?17!62-RcP-IvG9_T9ZUk%EdUX4dQ#n zqg$XBVv%WX;y?z*IaP{YS0`g;DBz%SR~`habeB#rtCv?GLx8K^6XESkU0GJg{nNmn zr4cE;x)%2F!urc^efE3ca#f^dCCVj4opWr@5JQ9v2?BpDolrn*@ena$FY%{gm_J?KXTCy5Jy0qJFd}B^6333k!Wj_3u<%9y`(N? zeMBNFJGnBN_gylRJ1Pz{s@fNTF1S(gvSne}aqQL8_i#@%oQ5>7Phf5ihgvslWuk~=EMvjX$3S_||?tQyQ`jO551LEOkDioR91T@;~JS&^Ha1iSB0O7`|iY z0q1WvBZY7#`1^4PUTJgXf7M09A1X2gka`x z__B6x8Q~GhtmjNm$B`hClC6c1s zn7tl$6M4bk18N=z`DcO(=NTiGHWaS)>)vbykx9-I{CY8N22JU9wOZ^>!;;hmYNkwLQGJA`i zV)*@bto3`%okh!|cDA1u^$A`gb;r*qJnbnNb?P08+no~eLH>hF+Ito-&OlE1`VUDn z`x%^xc0T-YE{_@>u)nI){FcI*zE+CzEw^pq-*IjY>8Sa*Q}wJ7l#ymOg>${wlj2Eq zTu>1W8T}ZMa6V7rPjLGm$DWfJE)hE{mh)Ik~Thb0Jj5JUhmG=nD z&@F^zR9+DD1u^l=Sz8=>(Pu+!3q zV`!OOhz~-MsT?iQCARH)IT2_4)EIgtWUQZTf$qvY({wu5jLkGHAFRMIIxyF>i~QDS z;YW8XmNpo-giF@`E?(@b&m#qx{|nV!-50)>VaccO7<-i#H}z*a-Vr+AT%81+^MQe< z(i=TTPN0AS5`L)d`$eNKp){9}J@p7XQt|!-ESXF`RBN*5d+n@Gx=SOqYhNz?R%VU6 zQmV7Pk(=XqOe}~F>=)$G2O}IlK(St-pD5uB#auw^G&j5X{dDl*Kn0!-!VFk3+eMj& zM{U+7#k_aqZeAs6A``Df$Y__q93S-URx_+hG5)i~n8|lcgnfMfjQNv{!Lr~eZ7F>$ zaw{{NkCLg6gRPIFxV^U{@&*v#;}?C!C;Cc2$dI2;TtG-%nEw@@kT@S7PkZn8|AWEZ z)4};|!2g@U`T)xl$w2*|1Ov~vKK{1ejsSmue;#KK*Z20eU`HNL?{@{qk~BynfSQuF KVuQR*wOeuWT3;&BESMc5c{Qzs4Eae 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
6<-acu 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 NQp-@YMBO3!e*nzNs#O30 literal 0 HcmV?d00001 diff --git a/openvidu-ng-testapp/src/environments/environment.prod.ts b/openvidu-testapp/src/environments/environment.prod.ts similarity index 100% rename from openvidu-ng-testapp/src/environments/environment.prod.ts rename to openvidu-testapp/src/environments/environment.prod.ts diff --git a/openvidu-ng-testapp/src/environments/environment.ts b/openvidu-testapp/src/environments/environment.ts similarity index 62% rename from openvidu-ng-testapp/src/environments/environment.ts rename to openvidu-testapp/src/environments/environment.ts index 00313f16..9c78397b 100644 --- a/openvidu-ng-testapp/src/environments/environment.ts +++ b/openvidu-testapp/src/environments/environment.ts @@ -1,8 +1,10 @@ // The file contents for the current environment will overwrite these during build. // The build system defaults to the dev environment which uses `environment.ts`, but if you do // `ng build --env=prod` then `environment.prod.ts` will be used instead. -// The list of which env maps to which file can be found in `angular-cli.json`. +// The list of which env maps to which file can be found in `.angular-cli.json`. export const environment = { - production: false + production: false, + OPENVIDU_URL: 'https://localhost:8443', + OPENVIDU_SECRET: 'MY_SECRET' }; diff --git a/openvidu-ng-testapp/src/favicon.ico b/openvidu-testapp/src/favicon.ico similarity index 100% rename from openvidu-ng-testapp/src/favicon.ico rename to openvidu-testapp/src/favicon.ico diff --git a/openvidu-ng-testapp/src/index.html b/openvidu-testapp/src/index.html similarity index 64% rename from openvidu-ng-testapp/src/index.html rename to openvidu-testapp/src/index.html index 08101d16..94e061e0 100644 --- a/openvidu-ng-testapp/src/index.html +++ b/openvidu-testapp/src/index.html @@ -1,14 +1,20 @@ - + + - OpenviduNg2Example + OpenVidu TestApp + + + + Loading... + diff --git a/openvidu-ng-testapp/src/main.ts b/openvidu-testapp/src/main.ts similarity index 60% rename from openvidu-ng-testapp/src/main.ts rename to openvidu-testapp/src/main.ts index 5c3c5204..a7ea96c2 100644 --- a/openvidu-ng-testapp/src/main.ts +++ b/openvidu-testapp/src/main.ts @@ -1,12 +1,14 @@ -import './polyfills.ts'; - -import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; import { enableProdMode } from '@angular/core'; +import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; + +import { AppModule } from './app/app.module'; import { environment } from './environments/environment'; -import { AppModule } from './app/'; + +import 'hammerjs'; if (environment.production) { enableProdMode(); } -platformBrowserDynamic().bootstrapModule(AppModule); +platformBrowserDynamic().bootstrapModule(AppModule) + .catch(err => console.log(err)); diff --git a/openvidu-testapp/src/openvidu-theme.scss b/openvidu-testapp/src/openvidu-theme.scss new file mode 100644 index 00000000..893960bb --- /dev/null +++ b/openvidu-testapp/src/openvidu-theme.scss @@ -0,0 +1,57 @@ +@import '~@angular/material/theming'; +// Plus imports for other components in your app. + +// Include the common styles for Angular Material. We include this here so that you only +// have to load a single css file for Angular Material in your app. +// Be sure that you only ever include this mixin once! +@include mat-core(); + +$mat-openvidu: ( + 50: #eaeaea, + 100: #cacaca, + 200: #a6a6a6, + 300: #828282, + 400: #686868, + 500: #4d4d4d, + 600: #464646, + 700: #3d3d3d, + 800: #343434, + 900: #252525, + A100: #83E9B1, + A200: #06D362, + A400: #04C850, + A700: #02B734, + contrast: ( + 50 : #fff9e0, + 100 : #fff0b3, + 200 : #ffe680, + 300 : #ffdb4d, + 400 : #ffd426, + 500 : #ffcc00, + 600 : #ffc700, + 700 : #ffc000, + 800 : #ffb900, + 900 : #ffad00, + A100 : #ffffff, + A200 : #fffaf2, + A400 : #ffe8bf, + A700 : #ffdfa6, + ) +); + +// Define the palettes for your theme using the Material Design palettes available in palette.scss +// (imported above). For each palette, you can optionally specify a default, lighter, and darker +// hue. +$openvidu-primary: mat-palette($mat-openvidu); +$openvidu-accent: mat-palette($mat-openvidu, A200, A100, A400); + +// The warn palette is optional (defaults to red). +$openvidu-warn: mat-palette($mat-red); + +// Create the theme object (a Sass map containing all of the palettes). +$openvidu-theme: mat-light-theme($openvidu-primary, $openvidu-accent, $openvidu-warn); + +// Include theme styles for core and each component used in your app. +// Alternatively, you can import and @include the theme mixins for each component +// that you are using. +@include angular-material-theme($openvidu-theme); \ No newline at end of file diff --git a/openvidu-testapp/src/polyfills.ts b/openvidu-testapp/src/polyfills.ts new file mode 100644 index 00000000..581aadfe --- /dev/null +++ b/openvidu-testapp/src/polyfills.ts @@ -0,0 +1,72 @@ +/** + * This file includes polyfills needed by Angular and is loaded before the app. + * You can add your own extra polyfills to this file. + * + * This file is divided into 2 sections: + * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers. + * 2. Application imports. Files imported after ZoneJS that should be loaded before your main + * file. + * + * The current setup is for so-called "evergreen" browsers; the last versions of browsers that + * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera), + * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile. + * + * Learn more in https://angular.io/docs/ts/latest/guide/browser-support.html + */ + +/*************************************************************************************************** + * BROWSER POLYFILLS + */ + +/** IE9, IE10 and IE11 requires all of the following polyfills. **/ +// import 'core-js/es6/symbol'; +// import 'core-js/es6/object'; +// import 'core-js/es6/function'; +// import 'core-js/es6/parse-int'; +// import 'core-js/es6/parse-float'; +// import 'core-js/es6/number'; +// import 'core-js/es6/math'; +// import 'core-js/es6/string'; +// import 'core-js/es6/date'; +// import 'core-js/es6/array'; +// import 'core-js/es6/regexp'; +// import 'core-js/es6/map'; +// import 'core-js/es6/weak-map'; +// import 'core-js/es6/set'; + +/** IE10 and IE11 requires the following for NgClass support on SVG elements */ +// import 'classlist.js'; // Run `npm install --save classlist.js`. + +/** Evergreen browsers require these. **/ +import 'core-js/es6/reflect'; +import 'core-js/es7/reflect'; + + +/** + * Required to support Web Animations `@angular/platform-browser/animations`. + * Needed for: All but Chrome, Firefox and Opera. http://caniuse.com/#feat=web-animation + **/ +// import 'web-animations-js'; // Run `npm install --save web-animations-js`. + + + +/*************************************************************************************************** + * Zone JS is required by Angular itself. + */ +import 'zone.js/dist/zone'; // Included with Angular CLI. + + + +/*************************************************************************************************** + * APPLICATION IMPORTS + */ + +/** + * Date, currency, decimal and percent pipes. + * Needed for: All but Chrome, Firefox, Edge, IE11 and Safari 10 + */ +// import 'intl'; // Run `npm install --save intl`. +/** + * Need to import at least one locale-data with intl. + */ +// import 'intl/locale-data/jsonp/en'; diff --git a/openvidu-testapp/src/styles.css b/openvidu-testapp/src/styles.css new file mode 100644 index 00000000..8285dafe --- /dev/null +++ b/openvidu-testapp/src/styles.css @@ -0,0 +1,79 @@ +* { + font-family: 'Exo 2', sans-serif; +} + +html, +body { + height: 100%; + margin: 0; + padding: 0; + -webkit-tap-highlight-color: rgba(0, 0, 0, 0.05); +} + +a { + color: inherit; + cursor: inherit; + text-decoration: inherit; +} + + +/* Elevation */ + +.z-depth-1 { + box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 1px 5px 0 rgba(0, 0, 0, 0.12), 0 3px 1px -2px rgba(0, 0, 0, 0.2); +} + +.z-depth-1-half { + box-shadow: 0 3px 3px 0 rgba(0, 0, 0, 0.14), 0 1px 7px 0 rgba(0, 0, 0, 0.12), 0 3px 1px -1px rgba(0, 0, 0, 0.2); +} + +.z-depth-2 { + box-shadow: 0 4px 5px 0 rgba(0, 0, 0, 0.14), 0 1px 10px 0 rgba(0, 0, 0, 0.12), 0 2px 4px -1px rgba(0, 0, 0, 0.3); +} + +.z-depth-3 { + box-shadow: 0 6px 10px 0 rgba(0, 0, 0, 0.14), 0 1px 18px 0 rgba(0, 0, 0, 0.12), 0 3px 5px -1px rgba(0, 0, 0, 0.3); +} + +.z-depth-4 { + box-shadow: 0 8px 10px 1px rgba(0, 0, 0, 0.14), 0 3px 14px 2px rgba(0, 0, 0, 0.12), 0 5px 5px -3px rgba(0, 0, 0, 0.3); +} + +.z-depth-5 { + box-shadow: 0 16px 24px 2px rgba(0, 0, 0, 0.14), 0 6px 30px 5px rgba(0, 0, 0, 0.12), 0 8px 10px -5px rgba(0, 0, 0, 0.3); +} + + +/* Colors of Deep Purple Material Theme */ + +.back-primary { + background: #673ab7 !important; +} + +.back-accent { + background: #ffd740; +} + +.back-warn { + background: #f44336; +} + +.back-secondary { + background: #D1C4E9; +} + +.color-primary { + color: #673ab7 !important; +} + +.color-accent { + color: #ffd740; +} + +.color-warn { + color: #f44336; +} + +.color-secondary { + color: #D1C4E9; +} diff --git a/openvidu-testapp/src/test.ts b/openvidu-testapp/src/test.ts new file mode 100644 index 00000000..cd612eeb --- /dev/null +++ b/openvidu-testapp/src/test.ts @@ -0,0 +1,32 @@ +// This file is required by karma.conf.js and loads recursively all the .spec and framework files + +import 'zone.js/dist/long-stack-trace-zone'; +import 'zone.js/dist/proxy.js'; +import 'zone.js/dist/sync-test'; +import 'zone.js/dist/jasmine-patch'; +import 'zone.js/dist/async-test'; +import 'zone.js/dist/fake-async-test'; +import { getTestBed } from '@angular/core/testing'; +import { + BrowserDynamicTestingModule, + platformBrowserDynamicTesting +} from '@angular/platform-browser-dynamic/testing'; + +// Unfortunately there's no typing for the `__karma__` variable. Just declare it as any. +declare const __karma__: any; +declare const require: any; + +// Prevent Karma from running prematurely. +__karma__.loaded = function () {}; + +// First, initialize the Angular testing environment. +getTestBed().initTestEnvironment( + BrowserDynamicTestingModule, + platformBrowserDynamicTesting() +); +// Then we find all the tests. +const context = require.context('./', true, /\.spec\.ts$/); +// And load the modules. +context.keys().map(context); +// Finally, start Karma to run the tests. +__karma__.start(); diff --git a/openvidu-testapp/src/tsconfig.app.json b/openvidu-testapp/src/tsconfig.app.json new file mode 100644 index 00000000..39ba8dba --- /dev/null +++ b/openvidu-testapp/src/tsconfig.app.json @@ -0,0 +1,13 @@ +{ + "extends": "../tsconfig.json", + "compilerOptions": { + "outDir": "../out-tsc/app", + "baseUrl": "./", + "module": "es2015", + "types": [] + }, + "exclude": [ + "test.ts", + "**/*.spec.ts" + ] +} diff --git a/openvidu-testapp/src/tsconfig.spec.json b/openvidu-testapp/src/tsconfig.spec.json new file mode 100644 index 00000000..63d89ff2 --- /dev/null +++ b/openvidu-testapp/src/tsconfig.spec.json @@ -0,0 +1,20 @@ +{ + "extends": "../tsconfig.json", + "compilerOptions": { + "outDir": "../out-tsc/spec", + "baseUrl": "./", + "module": "commonjs", + "target": "es5", + "types": [ + "jasmine", + "node" + ] + }, + "files": [ + "test.ts" + ], + "include": [ + "**/*.spec.ts", + "**/*.d.ts" + ] +} diff --git a/openvidu-testapp/src/typings.d.ts b/openvidu-testapp/src/typings.d.ts new file mode 100644 index 00000000..ef5c7bd6 --- /dev/null +++ b/openvidu-testapp/src/typings.d.ts @@ -0,0 +1,5 @@ +/* SystemJS module definition */ +declare var module: NodeModule; +interface NodeModule { + id: string; +} diff --git a/openvidu-ng-testapp/tsconfig.json b/openvidu-testapp/tsconfig.json similarity index 80% rename from openvidu-ng-testapp/tsconfig.json rename to openvidu-testapp/tsconfig.json index 7a9d9e8c..a6c016bf 100644 --- a/openvidu-ng-testapp/tsconfig.json +++ b/openvidu-testapp/tsconfig.json @@ -8,8 +8,11 @@ "emitDecoratorMetadata": true, "experimentalDecorators": true, "target": "es5", + "typeRoots": [ + "node_modules/@types" + ], "lib": [ - "es2016", + "es2017", "dom" ] } diff --git a/openvidu-ng-testapp/tslint.json b/openvidu-testapp/tslint.json similarity index 77% rename from openvidu-ng-testapp/tslint.json rename to openvidu-testapp/tslint.json index 9113f136..b6e591b2 100644 --- a/openvidu-ng-testapp/tslint.json +++ b/openvidu-testapp/tslint.json @@ -3,6 +3,7 @@ "node_modules/codelyzer" ], "rules": { + "arrow-return-shorthand": true, "callable-types": true, "class-name": true, "comment-format": [ @@ -12,7 +13,10 @@ "curly": true, "eofline": true, "forin": true, - "import-blacklist": [true, "rxjs"], + "import-blacklist": [ + true, + "rxjs" + ], "import-spacing": true, "indent": [ true, @@ -27,8 +31,14 @@ "member-access": false, "member-ordering": [ true, - "static-before-instance", - "variables-before-functions" + { + "order": [ + "static-field", + "instance-field", + "static-method", + "instance-method" + ] + } ], "no-arg": true, "no-bitwise": true, @@ -42,16 +52,22 @@ ], "no-construct": true, "no-debugger": true, - "no-duplicate-variable": true, + "no-duplicate-super": true, "no-empty": false, "no-empty-interface": true, "no-eval": true, - "no-inferrable-types": [true, "ignore-params"], + "no-inferrable-types": [ + true, + "ignore-params" + ], + "no-misused-new": true, + "no-non-null-assertion": true, "no-shadowed-variable": true, "no-string-literal": false, "no-string-throw": true, "no-switch-case-fall-through": true, "no-trailing-whitespace": true, + "no-unnecessary-initializer": true, "no-unused-expression": true, "no-use-before-declare": true, "no-var-keyword": true, @@ -70,6 +86,7 @@ ], "radix": true, "semicolon": [ + true, "always" ], "triple-equals": [ @@ -97,9 +114,18 @@ "check-separator", "check-type" ], - - "directive-selector": [true, "attribute", "app", "camelCase"], - "component-selector": [true, "element", "app", "kebab-case"], + "directive-selector": [ + true, + "attribute", + "app", + "camelCase" + ], + "component-selector": [ + true, + "element", + "app", + "kebab-case" + ], "use-input-property-decorator": true, "use-output-property-decorator": true, "use-host-property-decorator": true, @@ -109,8 +135,6 @@ "use-pipe-transform-interface": true, "component-class-suffix": true, "directive-class-suffix": true, - "no-access-missing-member": true, - "templates-use-public": true, "invoke-injectable": true } }