mirror of https://github.com/OpenVidu/openvidu.git
openvidu-testapp first approach
parent
681309ecfb
commit
64a68bb42e
|
@ -32,7 +32,7 @@ var logger = window.Logger || console
|
||||||
// }).eror(callback));
|
// }).eror(callback));
|
||||||
// }
|
// }
|
||||||
|
|
||||||
try {
|
/*try {
|
||||||
require('kurento-browser-extensions')
|
require('kurento-browser-extensions')
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (typeof getScreenConstraints === 'undefined') {
|
if (typeof getScreenConstraints === 'undefined') {
|
||||||
|
@ -42,7 +42,7 @@ try {
|
||||||
callback(new Error("This library is not enabled for screen sharing"))
|
callback(new Error("This library is not enabled for screen sharing"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}*/
|
||||||
|
|
||||||
var MEDIA_CONSTRAINTS = {
|
var MEDIA_CONSTRAINTS = {
|
||||||
audio: true,
|
audio: true,
|
||||||
|
|
|
@ -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!');
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -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"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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"
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,82 +0,0 @@
|
||||||
<div *ngIf="!session">
|
|
||||||
<h1>Join a video session</h1>
|
|
||||||
<form (submit)="joinSession()" accept-charset="UTF-8">
|
|
||||||
<div>
|
|
||||||
<label>Participant:</label>
|
|
||||||
<input type="text" name="participantId" [(ngModel)]="participantId" required>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<label>Session:</label>
|
|
||||||
<input type="text" name="sessionId" [(ngModel)]="sessionId" required>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<input type="checkbox" [(ngModel)]="joinWithVideo" id="join-with-video" name="join-with-video"> Send video
|
|
||||||
<div *ngIf="joinWithVideo">
|
|
||||||
<table>
|
|
||||||
<tr>
|
|
||||||
<th>Constraint</th>
|
|
||||||
<th>Value</th>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>width</td>
|
|
||||||
<td><input type="text" id="width" required></td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>height</td>
|
|
||||||
<td><input type="text" id="height" required></td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>frameRate</td>
|
|
||||||
<td><input type="text" id="frameRate" required></td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<input type="checkbox" [(ngModel)]="joinWithAudio" id="join-with-audio" name="join-with-audio"> Send audio
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<input type="submit" name="commit" value="Join!">
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div *ngIf="session">
|
|
||||||
<h2>{{sessionId}}</h2>
|
|
||||||
<input type="button" (click)="leaveSession()" value="Leave session">
|
|
||||||
<input type="checkbox" id="toggle-video" name="toggle-video" [checked]="toggleVideo" (change)="updateToggleVideo($event)"> Toggle your video
|
|
||||||
<input type="checkbox" id="toggle-audio" name="toggle-audio" [checked]="toggleAudio" (change)="updateToggleAudio($event)"> Toggle your audio
|
|
||||||
<div>
|
|
||||||
<div id="publisher"></div>
|
|
||||||
<div id="subscriber"></div>
|
|
||||||
<div *ngFor="let s of streams; let i = index">
|
|
||||||
<div *ngIf="stats[i]">
|
|
||||||
<div style="margin-left: 50px;">
|
|
||||||
<input type="checkbox" id="toggle-state" name="toggle-state" [checked]="true" (change)="updateToggleState(i)"> Show conexion state
|
|
||||||
<div [attr.id]="'state-' + i" style="display: block;">
|
|
||||||
<p><b>Signaling state:</b> {{signalingState[i]}}</p>
|
|
||||||
<p><b>ICE connection state:</b> {{iceConnectionState[i]}}</p>
|
|
||||||
</div>
|
|
||||||
<input type="checkbox" id="toggle-statistics" name="toggle-statistics" (change)="updateToggleStatistics(i)"> Show
|
|
||||||
statistics
|
|
||||||
<div>
|
|
||||||
<h2 *ngIf="stats[i].bitrate">Bitrate: {{stats[i].bitrate}}</h2>
|
|
||||||
<h2 *ngIf="stats[i].framerate">Framerate: {{stats[i].framerate}}</h2>
|
|
||||||
</div>
|
|
||||||
<table [attr.id]="'table-' + i" style="display: none;">
|
|
||||||
<tr>
|
|
||||||
<th>Type</th>
|
|
||||||
<th>Time</th>
|
|
||||||
<th>Other attributes</th>
|
|
||||||
</tr>
|
|
||||||
<tr *ngFor="let st of stats[i].statsArray">
|
|
||||||
<td>{{st.type}}</td>
|
|
||||||
<td>{{st.timestamp}}</td>
|
|
||||||
<td>{{getStatAttributes(st.res)}}</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
|
@ -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 = (<HTMLInputElement>document.getElementById('table-' + i));
|
|
||||||
(table.style.display == "none") ? table.style.display = "block" : table.style.display = "none";
|
|
||||||
}
|
|
||||||
|
|
||||||
updateToggleState(i) {
|
|
||||||
let state = (<HTMLInputElement>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((<HTMLInputElement>document.getElementById('width')).value) };
|
|
||||||
mediaConstraints.video['height'] = { exact: Number((<HTMLInputElement>document.getElementById('height')).value) };
|
|
||||||
mediaConstraints.video['frameRate'] = { ideal: Number((<HTMLInputElement>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;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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 { }
|
|
|
@ -1,2 +0,0 @@
|
||||||
export * from './app.component';
|
|
||||||
export * from './app.module';
|
|
|
@ -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: `
|
|
||||||
<div class='participant'>
|
|
||||||
<p>{{stream.getId()}}</p>
|
|
||||||
<video *ngIf="!stream.local" autoplay="true" [src]="videoSrc"></video>
|
|
||||||
<video *ngIf="stream.local" autoplay="true" [src]="videoSrc" muted></video>
|
|
||||||
</div>`
|
|
||||||
})
|
|
||||||
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();
|
|
||||||
//});
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -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';
|
|
|
@ -1 +0,0 @@
|
||||||
/* You can add global styles to this file, and also import other style files */
|
|
|
@ -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);
|
|
|
@ -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"
|
|
||||||
]
|
|
||||||
}
|
|
|
@ -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"
|
|
||||||
]
|
|
||||||
}
|
|
|
@ -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;
|
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
|
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
|
||||||
"project": {
|
"project": {
|
||||||
"name": "openvidu-ng-testapp"
|
"name": "openvidu-testapp"
|
||||||
},
|
},
|
||||||
"apps": [
|
"apps": [
|
||||||
{
|
{
|
||||||
|
@ -19,7 +19,8 @@
|
||||||
"testTsconfig": "tsconfig.spec.json",
|
"testTsconfig": "tsconfig.spec.json",
|
||||||
"prefix": "app",
|
"prefix": "app",
|
||||||
"styles": [
|
"styles": [
|
||||||
"styles.css"
|
"styles.css",
|
||||||
|
"openvidu-theme.scss"
|
||||||
],
|
],
|
||||||
"scripts": [],
|
"scripts": [],
|
||||||
"environmentSource": "environments/environment.ts",
|
"environmentSource": "environments/environment.ts",
|
||||||
|
@ -36,13 +37,16 @@
|
||||||
},
|
},
|
||||||
"lint": [
|
"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": {
|
"test": {
|
|
@ -3,6 +3,7 @@
|
||||||
# compiled output
|
# compiled output
|
||||||
/dist
|
/dist
|
||||||
/tmp
|
/tmp
|
||||||
|
/out-tsc
|
||||||
|
|
||||||
# dependencies
|
# dependencies
|
||||||
/node_modules
|
/node_modules
|
||||||
|
@ -26,7 +27,7 @@
|
||||||
# misc
|
# misc
|
||||||
/.sass-cache
|
/.sass-cache
|
||||||
/connect.lock
|
/connect.lock
|
||||||
/coverage/*
|
/coverage
|
||||||
/libpeerconnection.log
|
/libpeerconnection.log
|
||||||
npm-debug.log
|
npm-debug.log
|
||||||
testem.log
|
testem.log
|
||||||
|
@ -36,6 +37,6 @@ testem.log
|
||||||
/e2e/*.js
|
/e2e/*.js
|
||||||
/e2e/*.map
|
/e2e/*.map
|
||||||
|
|
||||||
#System Files
|
# System Files
|
||||||
.DS_Store
|
.DS_Store
|
||||||
Thumbs.db
|
Thumbs.db
|
|
@ -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
|
## 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.
|
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
|
## 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
|
## Build
|
||||||
|
|
||||||
|
@ -20,7 +21,6 @@ Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.
|
||||||
## Running end-to-end tests
|
## Running end-to-end tests
|
||||||
|
|
||||||
Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/).
|
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
|
## Further help
|
||||||
|
|
|
@ -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!');
|
||||||
|
});
|
||||||
|
});
|
|
@ -1,6 +1,6 @@
|
||||||
import { browser, element, by } from 'protractor';
|
import { browser, by, element } from 'protractor';
|
||||||
|
|
||||||
export class OpenviduNgTestappPage {
|
export class AppPage {
|
||||||
navigateTo() {
|
navigateTo() {
|
||||||
return browser.get('/');
|
return browser.get('/');
|
||||||
}
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
{
|
||||||
|
"extends": "../tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"outDir": "../out-tsc/e2e",
|
||||||
|
"baseUrl": "./",
|
||||||
|
"module": "commonjs",
|
||||||
|
"target": "es5",
|
||||||
|
"types": [
|
||||||
|
"jasmine",
|
||||||
|
"jasminewd2",
|
||||||
|
"node"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
// Karma configuration file, see link for more information
|
// 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) {
|
module.exports = function (config) {
|
||||||
config.set({
|
config.set({
|
||||||
|
@ -15,15 +15,6 @@ module.exports = function (config) {
|
||||||
client:{
|
client:{
|
||||||
clearContext: false // leave Jasmine Spec Runner output visible in browser
|
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: {
|
coverageIstanbulReporter: {
|
||||||
reports: [ 'html', 'lcovonly' ],
|
reports: [ 'html', 'lcovonly' ],
|
||||||
fixWebpackSourcePaths: true
|
fixWebpackSourcePaths: true
|
||||||
|
@ -31,9 +22,7 @@ module.exports = function (config) {
|
||||||
angularCli: {
|
angularCli: {
|
||||||
environment: 'dev'
|
environment: 'dev'
|
||||||
},
|
},
|
||||||
reporters: config.angularCli && config.angularCli.codeCoverage
|
reporters: ['progress', 'kjhtml'],
|
||||||
? ['progress', 'coverage-istanbul']
|
|
||||||
: ['progress', 'kjhtml'],
|
|
||||||
port: 9876,
|
port: 9876,
|
||||||
colors: true,
|
colors: true,
|
||||||
logLevel: config.LOG_INFO,
|
logLevel: config.LOG_INFO,
|
|
@ -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"
|
||||||
|
}
|
||||||
|
}
|
|
@ -19,12 +19,10 @@ exports.config = {
|
||||||
defaultTimeoutInterval: 30000,
|
defaultTimeoutInterval: 30000,
|
||||||
print: function() {}
|
print: function() {}
|
||||||
},
|
},
|
||||||
beforeLaunch: function() {
|
onPrepare() {
|
||||||
require('ts-node').register({
|
require('ts-node').register({
|
||||||
project: 'e2e/tsconfig.e2e.json'
|
project: 'e2e/tsconfig.e2e.json'
|
||||||
});
|
});
|
||||||
},
|
|
||||||
onPrepare() {
|
|
||||||
jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } }));
|
jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } }));
|
||||||
}
|
}
|
||||||
};
|
};
|
|
@ -0,0 +1,8 @@
|
||||||
|
#nav-img {
|
||||||
|
height: 32px;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
main {
|
||||||
|
padding: 50px
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
<md-sidenav-container fullscreen>
|
||||||
|
<md-toolbar class="mat-elevation-z5" color="primary">
|
||||||
|
<div layout-align='center center' layout='column'>
|
||||||
|
<a routerLink="/"><img id="nav-img" src="assets/images/openvidu_vert_white_bg_trans_cropped.png"/> TestApp</a>
|
||||||
|
</div>
|
||||||
|
</md-toolbar>
|
||||||
|
<main>
|
||||||
|
<router-outlet></router-outlet>
|
||||||
|
</main>
|
||||||
|
</md-sidenav-container>
|
|
@ -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!');
|
||||||
|
}));
|
||||||
|
});
|
|
@ -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/');
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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 { }
|
|
@ -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 { }
|
|
@ -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);
|
|
@ -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 { }
|
|
@ -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;
|
||||||
|
}
|
|
@ -0,0 +1,47 @@
|
||||||
|
<md-tab-group class="demo-tab-group" dynamicHeight="true">
|
||||||
|
<md-tab id="audio-video" label="Audio + Video">
|
||||||
|
<div class="demo-tab-content">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</md-tab>
|
||||||
|
<md-tab id="audio-only" label="Audio only">
|
||||||
|
<div class="demo-tab-content">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</md-tab>
|
||||||
|
<md-tab id="video-only" label="Video only">
|
||||||
|
|
||||||
|
</md-tab>
|
||||||
|
<md-tab id="screen-audio" label="Screen + Audio">
|
||||||
|
<div class="demo-tab-content">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</md-tab>
|
||||||
|
<md-tab id="screen-only" label="Screen only">
|
||||||
|
|
||||||
|
</md-tab>
|
||||||
|
<md-tab id="api-rest" label="API REST">
|
||||||
|
<button md-raised-button color="primary" (click)="getSessionId()">Get sessionId</button>
|
||||||
|
<button md-raised-button color="primary" (click)="getToken()">Get token</button>
|
||||||
|
</md-tab>
|
||||||
|
</md-tab-group>
|
||||||
|
|
||||||
|
<!--<div id="join">
|
||||||
|
<div id="img-div"><img src="assets/images/openvidu_grey_bg_transp_cropped.png" /></div>
|
||||||
|
<div id="join-dialog" class="jumbotron vertical-center">
|
||||||
|
<h1>Join a video session</h1>
|
||||||
|
<form class="form-group" (submit)="joinSession()">
|
||||||
|
<p>
|
||||||
|
<label>Participant</label>
|
||||||
|
<input class="form-control" type="text" id="clientData" name="clientData" [(ngModel)]="clientData" required>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<label>Session</label>
|
||||||
|
<input class="form-control" type="text" id="sessionName" name="sessionName" [(ngModel)]="sessionName" required>
|
||||||
|
</p>
|
||||||
|
<p class="text-center">
|
||||||
|
<input class="btn btn-lg btn-success" type="submit" name="commit" value="Join!">
|
||||||
|
</p>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>-->
|
|
@ -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<DashboardComponent>;
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [ DashboardComponent ]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(DashboardComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
|
@ -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);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
<div *ngIf="session" id="session">
|
||||||
|
<div id="session-header">
|
||||||
|
<h1 id="session-title">{{sessionName}}</h1>
|
||||||
|
<input class="btn btn-large btn-danger" type="button" id="buttonLeaveSession" (click)="leaveSession()" value="Leave session">
|
||||||
|
</div>
|
||||||
|
<div id="main-video" class="col-md-6">
|
||||||
|
<p></p>
|
||||||
|
<video #mainVideoElement autoplay></video>
|
||||||
|
</div>
|
||||||
|
<div id="video-container" class="col-md-6"></div>
|
||||||
|
</div>
|
|
@ -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<SessionComponent>;
|
||||||
|
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [ SessionComponent ]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(SessionComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
|
@ -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 = '<p>' + userData + '</p>';
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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();
|
||||||
|
}));
|
||||||
|
});
|
|
@ -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<String> {
|
||||||
|
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<String> {
|
||||||
|
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);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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();
|
||||||
|
}));
|
||||||
|
});
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Binary file not shown.
After Width: | Height: | Size: 12 KiB |
Binary file not shown.
After Width: | Height: | Size: 39 KiB |
Binary file not shown.
After Width: | Height: | Size: 26 KiB |
Binary file not shown.
After Width: | Height: | Size: 22 KiB |
|
@ -1,8 +1,10 @@
|
||||||
// The file contents for the current environment will overwrite these during build.
|
// 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
|
// 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.
|
// `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 = {
|
export const environment = {
|
||||||
production: false
|
production: false,
|
||||||
|
OPENVIDU_URL: 'https://localhost:8443',
|
||||||
|
OPENVIDU_SECRET: 'MY_SECRET'
|
||||||
};
|
};
|
Before Width: | Height: | Size: 5.3 KiB After Width: | Height: | Size: 5.3 KiB |
|
@ -1,14 +1,20 @@
|
||||||
<!doctype html>
|
<!doctype html>
|
||||||
<html>
|
<html lang="en">
|
||||||
|
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<title>OpenviduNg2Example</title>
|
<title>OpenVidu TestApp</title>
|
||||||
<base href="/">
|
<base href="/">
|
||||||
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<link rel="icon" type="image/x-icon" href="favicon.ico">
|
<link rel="icon" type="image/x-icon" href="favicon.ico">
|
||||||
|
|
||||||
|
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
|
||||||
|
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<app-root>Loading...</app-root>
|
<app-root>Loading...</app-root>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
|
@ -1,12 +1,14 @@
|
||||||
import './polyfills.ts';
|
|
||||||
|
|
||||||
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
|
|
||||||
import { enableProdMode } from '@angular/core';
|
import { enableProdMode } from '@angular/core';
|
||||||
|
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
|
||||||
|
|
||||||
|
import { AppModule } from './app/app.module';
|
||||||
import { environment } from './environments/environment';
|
import { environment } from './environments/environment';
|
||||||
import { AppModule } from './app/';
|
|
||||||
|
import 'hammerjs';
|
||||||
|
|
||||||
if (environment.production) {
|
if (environment.production) {
|
||||||
enableProdMode();
|
enableProdMode();
|
||||||
}
|
}
|
||||||
|
|
||||||
platformBrowserDynamic().bootstrapModule(AppModule);
|
platformBrowserDynamic().bootstrapModule(AppModule)
|
||||||
|
.catch(err => console.log(err));
|
|
@ -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);
|
|
@ -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';
|
|
@ -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;
|
||||||
|
}
|
|
@ -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();
|
|
@ -0,0 +1,13 @@
|
||||||
|
{
|
||||||
|
"extends": "../tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"outDir": "../out-tsc/app",
|
||||||
|
"baseUrl": "./",
|
||||||
|
"module": "es2015",
|
||||||
|
"types": []
|
||||||
|
},
|
||||||
|
"exclude": [
|
||||||
|
"test.ts",
|
||||||
|
"**/*.spec.ts"
|
||||||
|
]
|
||||||
|
}
|
|
@ -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"
|
||||||
|
]
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
/* SystemJS module definition */
|
||||||
|
declare var module: NodeModule;
|
||||||
|
interface NodeModule {
|
||||||
|
id: string;
|
||||||
|
}
|
|
@ -8,8 +8,11 @@
|
||||||
"emitDecoratorMetadata": true,
|
"emitDecoratorMetadata": true,
|
||||||
"experimentalDecorators": true,
|
"experimentalDecorators": true,
|
||||||
"target": "es5",
|
"target": "es5",
|
||||||
|
"typeRoots": [
|
||||||
|
"node_modules/@types"
|
||||||
|
],
|
||||||
"lib": [
|
"lib": [
|
||||||
"es2016",
|
"es2017",
|
||||||
"dom"
|
"dom"
|
||||||
]
|
]
|
||||||
}
|
}
|
|
@ -3,6 +3,7 @@
|
||||||
"node_modules/codelyzer"
|
"node_modules/codelyzer"
|
||||||
],
|
],
|
||||||
"rules": {
|
"rules": {
|
||||||
|
"arrow-return-shorthand": true,
|
||||||
"callable-types": true,
|
"callable-types": true,
|
||||||
"class-name": true,
|
"class-name": true,
|
||||||
"comment-format": [
|
"comment-format": [
|
||||||
|
@ -12,7 +13,10 @@
|
||||||
"curly": true,
|
"curly": true,
|
||||||
"eofline": true,
|
"eofline": true,
|
||||||
"forin": true,
|
"forin": true,
|
||||||
"import-blacklist": [true, "rxjs"],
|
"import-blacklist": [
|
||||||
|
true,
|
||||||
|
"rxjs"
|
||||||
|
],
|
||||||
"import-spacing": true,
|
"import-spacing": true,
|
||||||
"indent": [
|
"indent": [
|
||||||
true,
|
true,
|
||||||
|
@ -27,8 +31,14 @@
|
||||||
"member-access": false,
|
"member-access": false,
|
||||||
"member-ordering": [
|
"member-ordering": [
|
||||||
true,
|
true,
|
||||||
"static-before-instance",
|
{
|
||||||
"variables-before-functions"
|
"order": [
|
||||||
|
"static-field",
|
||||||
|
"instance-field",
|
||||||
|
"static-method",
|
||||||
|
"instance-method"
|
||||||
|
]
|
||||||
|
}
|
||||||
],
|
],
|
||||||
"no-arg": true,
|
"no-arg": true,
|
||||||
"no-bitwise": true,
|
"no-bitwise": true,
|
||||||
|
@ -42,16 +52,22 @@
|
||||||
],
|
],
|
||||||
"no-construct": true,
|
"no-construct": true,
|
||||||
"no-debugger": true,
|
"no-debugger": true,
|
||||||
"no-duplicate-variable": true,
|
"no-duplicate-super": true,
|
||||||
"no-empty": false,
|
"no-empty": false,
|
||||||
"no-empty-interface": true,
|
"no-empty-interface": true,
|
||||||
"no-eval": 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-shadowed-variable": true,
|
||||||
"no-string-literal": false,
|
"no-string-literal": false,
|
||||||
"no-string-throw": true,
|
"no-string-throw": true,
|
||||||
"no-switch-case-fall-through": true,
|
"no-switch-case-fall-through": true,
|
||||||
"no-trailing-whitespace": true,
|
"no-trailing-whitespace": true,
|
||||||
|
"no-unnecessary-initializer": true,
|
||||||
"no-unused-expression": true,
|
"no-unused-expression": true,
|
||||||
"no-use-before-declare": true,
|
"no-use-before-declare": true,
|
||||||
"no-var-keyword": true,
|
"no-var-keyword": true,
|
||||||
|
@ -70,6 +86,7 @@
|
||||||
],
|
],
|
||||||
"radix": true,
|
"radix": true,
|
||||||
"semicolon": [
|
"semicolon": [
|
||||||
|
true,
|
||||||
"always"
|
"always"
|
||||||
],
|
],
|
||||||
"triple-equals": [
|
"triple-equals": [
|
||||||
|
@ -97,9 +114,18 @@
|
||||||
"check-separator",
|
"check-separator",
|
||||||
"check-type"
|
"check-type"
|
||||||
],
|
],
|
||||||
|
"directive-selector": [
|
||||||
"directive-selector": [true, "attribute", "app", "camelCase"],
|
true,
|
||||||
"component-selector": [true, "element", "app", "kebab-case"],
|
"attribute",
|
||||||
|
"app",
|
||||||
|
"camelCase"
|
||||||
|
],
|
||||||
|
"component-selector": [
|
||||||
|
true,
|
||||||
|
"element",
|
||||||
|
"app",
|
||||||
|
"kebab-case"
|
||||||
|
],
|
||||||
"use-input-property-decorator": true,
|
"use-input-property-decorator": true,
|
||||||
"use-output-property-decorator": true,
|
"use-output-property-decorator": true,
|
||||||
"use-host-property-decorator": true,
|
"use-host-property-decorator": true,
|
||||||
|
@ -109,8 +135,6 @@
|
||||||
"use-pipe-transform-interface": true,
|
"use-pipe-transform-interface": true,
|
||||||
"component-class-suffix": true,
|
"component-class-suffix": true,
|
||||||
"directive-class-suffix": true,
|
"directive-class-suffix": true,
|
||||||
"no-access-missing-member": true,
|
|
||||||
"templates-use-public": true,
|
|
||||||
"invoke-injectable": true
|
"invoke-injectable": true
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue