opentok-angular.js
/*!
* opentok-angular (https://github.com/aullman/OpenTok-Angular)
*
* Angular module for OpenTok
*
* @Author: Adam Ullman (http://github.com/aullman)
* @Copyright (c) 2014 Adam Ullman
* @License: Released under the MIT license (http://opensource.org/licenses/MIT)
**/
if (!window.OT) throw new Error('You must include the OT library before the OT_Angular library');
var ng;
if (typeof angular === 'undefined' && typeof require !== 'undefined') {
ng = require('angular');
} else {
ng = angular;
}
var initLayoutContainer;
if (!window.hasOwnProperty('initLayoutContainer') && typeof require !== 'undefined') {
initLayoutContainer = require('opentok-layout-js').initLayoutContainer;
} else {
initLayoutContainer = window.initLayoutContainer;
}
ng.module('opentok', [])
.factory('OT', function() {
return OT;
})
.factory('OTSession', ['OT', '$rootScope',
function(OT, $rootScope) {
var OTSession = {
streams: [],
connections: [],
publishers: [],
init: function(apiKey, sessionId, token, cb) {
this.session = OT.initSession(apiKey, sessionId);
OTSession.session.on({
sessionConnected: function() {
OTSession.publishers.forEach(function(publisher) {
OTSession.session.publish(publisher, function(err) {
if (err) {
$rootScope.$broadcast('otPublisherError', err, publisher);
}
});
});
},
streamCreated: function(event) {
$rootScope.$apply(function() {
OTSession.streams.push(event.stream);
});
},
streamDestroyed: function(event) {
$rootScope.$apply(function() {
OTSession.streams.splice(OTSession.streams.indexOf(event.stream), 1);
});
},
sessionDisconnected: function() {
$rootScope.$apply(function() {
OTSession.streams.splice(0, OTSession.streams.length);
OTSession.connections.splice(0, OTSession.connections.length);
});
},
connectionCreated: function(event) {
$rootScope.$apply(function() {
OTSession.connections.push(event.connection);
});
},
connectionDestroyed: function(event) {
$rootScope.$apply(function() {
OTSession.connections.splice(OTSession.connections.indexOf(event.connection), 1);
});
}
});
this.session.connect(token, function(err) {
if (cb) cb(err, OTSession.session);
});
this.trigger('init');
},
addPublisher: function(publisher) {
this.publishers.push(publisher);
this.trigger('otPublisherAdded');
}
};
OT.$.eventing(OTSession);
return OTSession;
}
])
.directive('otLayout', ['$window', '$parse', 'OT', 'OTSession',
function($window, $parse, OT, OTSession) {
return {
restrict: 'E',
scope: {
props: '&'
},
link: function(scope, element, attrs) {
var layout = function() {
var props = scope.props() || {};
var container = initLayoutContainer(element[0], props);
container.layout();
scope.$emit('otLayoutComplete');
};
scope.$watch(function() {
return element.children().length;
}, layout);
$window.addEventListener('resize', layout);
scope.$on('otLayout', layout);
var listenForStreamChange = function listenForStreamChange() {
OTSession.session.on('streamPropertyChanged', function(event) {
if (event.changedProperty === 'videoDimensions') {
layout();
}
});
};
if (OTSession.session) listenForStreamChange();
else OTSession.on('init', listenForStreamChange);
}
};
}
])
.directive('otPublisher', ['OTSession', '$rootScope',
function(OTSession, $rootScope) {
return {
restrict: 'E',
scope: {
props: '&'
},
link: function(scope, element, attrs) {
var props = scope.props() || {};
props.width = props.width ? props.width : ng.element(element).width();
props.height = props.height ? props.height : ng.element(element).height();
var oldChildren = ng.element(element).children();
scope.publisher = OT.initPublisher(attrs.apikey || OTSession.session.apiKey,
element[0], props, function(err) {
if (err) {
scope.$emit('otPublisherError', err, scope.publisher);
}
});
// Make transcluding work manually by putting the children back in there
ng.element(element).append(oldChildren);
scope.publisher.on({
accessDenied: function() {
scope.$emit('otAccessDenied');
},
accessDialogOpened: function() {
scope.$emit('otAccessDialogOpened');
},
accessDialogClosed: function() {
scope.$emit('otAccessDialogClosed');
},
accessAllowed: function() {
ng.element(element).addClass('allowed');
scope.$emit('otAccessAllowed');
},
loaded: function() {
$rootScope.$broadcast('otLayout');
},
streamCreated: function(event) {
scope.$emit('otStreamCreated', event);
},
streamDestroyed: function(event) {
scope.$emit('otStreamDestroyed', event);
},
videoElementCreated: function(event) {
event.element.addEventListener('resize', function() {
$rootScope.$broadcast('otLayout');
});
}
});
scope.$on('$destroy', function() {
if (OTSession.session) OTSession.session.unpublish(scope.publisher);
else scope.publisher.destroy();
OTSession.publishers = OTSession.publishers.filter(function(publisher) {
return publisher !== scope.publisher;
});
scope.publisher = null;
});
if (OTSession.session && (OTSession.session.connected ||
(OTSession.session.isConnected && OTSession.session.isConnected()))) {
OTSession.session.publish(scope.publisher, function(err) {
if (err) {
scope.$emit('otPublisherError', err, scope.publisher);
}
});
}
OTSession.addPublisher(scope.publisher);
}
};
}
])
.directive('otSubscriber', ['OTSession', '$rootScope',
function(OTSession, $rootScope) {
return {
restrict: 'E',
scope: {
stream: '=',
props: '&'
},
link: function(scope, element) {
var stream = scope.stream,
props = scope.props() || {};
props.width = props.width ? props.width : ng.element(element).width();
props.height = props.height ? props.height : ng.element(element).height();
var oldChildren = ng.element(element).children();
var subscriber = OTSession.session.subscribe(stream, element[0], props, function(err) {
if (err) {
scope.$emit('otSubscriberError', err, subscriber);
}
});
subscriber.on({
loaded: function() {
$rootScope.$broadcast('otLayout');
},
videoElementCreated: function(event) {
event.element.addEventListener('resize', function() {
$rootScope.$broadcast('otLayout');
});
}
});
// Make transcluding work manually by putting the children back in there
ng.element(element).append(oldChildren);
scope.$on('$destroy', function() {
OTSession.session.unsubscribe(subscriber);
});
}
};
}
]);