FS-8045 [Verto-Communicator] Make the folder structure compliant with AngularJS best practices and adjust build system.

This commit is contained in:
Joao Mesquita
2015-08-25 17:59:54 -03:00
parent 5e380bbc54
commit aadb1fb3dd
71 changed files with 1337 additions and 1248 deletions

View File

@@ -1,102 +0,0 @@
'use strict';
/* App Module */
var vertoApp = angular.module('vertoApp', [
'timer',
'ngRoute',
'vertoControllers',
'vertoDirectives',
'vertoFilters',
'ngStorage',
'ngAnimate',
'toastr',
'FBAngular',
'cgPrompt',
'720kb.tooltips',
'ui.gravatar',
]);
vertoApp.config(['$routeProvider', 'gravatarServiceProvider', 'toastrConfig',
function($routeProvider, gravatarServiceProvider, toastrConfig) {
$routeProvider.
when('/login', {
title: 'Login',
templateUrl: 'partials/login.html',
controller: 'LoginController'
}).
when('/dialpad', {
title: 'Dialpad',
templateUrl: 'partials/dialpad.html',
controller: 'DialPadController'
}).
when('/incall', {
title: 'In a Call',
templateUrl: 'partials/incall.html',
controller: 'InCallController'
}).
/*when('/contributors', {
title: 'Contributors',
templateUrl: 'partials/contributors.html',
controller: 'ContributorsController',
}).*/
when('/browser-upgrade', {
title: '',
templateUrl: 'partials/browser_upgrade.html',
controller: 'BrowserUpgradeController'
}).
otherwise({
redirectTo: '/login'
});
gravatarServiceProvider.defaults = {
default: 'mm' // Mystery man as default for missing avatars
};
angular.extend(toastrConfig, {
maxOpened: 4
});
}
]);
vertoApp.run(['$rootScope', '$location', 'toastr', 'prompt',
function($rootScope, $location, toastr, prompt) {
$rootScope.$on('$routeChangeSuccess', function(event, current, previous) {
$rootScope.title = current.$$route.title;
});
$rootScope.safeProtocol = false;
if (window.location.protocol == 'https:') {
$rootScope.safeProtocol = true;
}
$rootScope.checkBrowser = function() {
navigator.getUserMedia = navigator.getUserMedia ||
navigator.webkitGetUserMedia ||
navigator.mozGetUserMedia;
if (!navigator.getUserMedia) {
$location.path('/browser-upgrade');
}
};
$rootScope.promptInput = function(title, message, label, callback) {
var ret = prompt({
title: title,
message: message,
input: true,
label: label
}).then(function(ret) {
if (angular.isFunction(callback)) {
callback(ret);
}
}, function() {
});
};
}
]);

View File

@@ -1,940 +0,0 @@
'use strict';
/* Controllers */
var vertoControllers = angular.module('vertoControllers', ['ui.bootstrap',
'vertoService', 'storageService', 'ui.gravatar'
]);
vertoControllers.filter('gravatar',
function() {
return function(email, size) {
if (angular.isUndefined(size)) {
size = 40;
}
var hash = md5(email);
return 'https://secure.gravatar.com/avatar/' + hash + '?s=' + size + '&d=mm';
};
});
vertoControllers.controller('MainController', ['$scope', '$rootScope',
'$location', '$modal', '$timeout', 'verto', 'storage', 'toastr',
'Fullscreen', 'prompt',
function($scope, $rootScope, $location, $modal,
$timeout, verto, storage, toastr, Fullscreen, prompt) {
console.debug('Executing MainController.');
var myVideo = document.getElementById("webcam");
$scope.verto = verto;
$scope.storage = storage;
$scope.call_history = angular.element("#call_history").hasClass('active');
$scope.chatStatus = angular.element('#wrapper').hasClass('toggled');
/**
* (explanation) scope in another controller extends rootScope (singleton)
*/
$rootScope.chat_counter = 0;
$rootScope.activePane = 'members';
/**
* The number that will be called.
* @type {string}
*/
$rootScope.dialpadNumber = '';
/**
* if user data saved, use stored data for logon
*/
if (storage.data.ui_connected && storage.data.ws_connected) {
$scope.verto.data.name = storage.data.name;
$scope.verto.data.email = storage.data.email;
$scope.verto.data.login = storage.data.login;
$scope.verto.data.password = storage.data.password;
verto.connect(function(v, connected) {
$scope.$apply(function() {
if (connected) {
toastr.success('Nice to see you again.', 'Welcome back');
$location.path('/dialpad');
}
});
});
}
// If verto is not connected, redirects to login page.
if (!verto.data.connected) {
console.debug('MainController: WebSocket not connected. Redirecting to login.');
$location.path('/login');
}
/**
* Login the user to verto server and
* redirects him to dialpad page.
*/
$scope.login = function() {
var connectCallback = function(v, connected) {
$scope.$apply(function() {
if (connected) {
storage.data.ui_connected = verto.data.connected;
storage.data.ws_connected = verto.data.connected;
storage.data.name = verto.data.name;
storage.data.email = verto.data.email;
storage.data.login = verto.data.login;
storage.data.password = verto.data.password;
console.debug('Redirecting to dialpad page.');
storage.changeData(verto);
toastr.success('Login successful.', 'Welcome');
$location.path('/dialpad');
} else {
toastr.error('There was an error while trying to login. Please try again.', 'Error');
}
});
};
verto.connect(connectCallback);
};
/**
* Logout the user from verto server and
* redirects him to login page.
*/
$scope.logout = function() {
var disconnect = function() {
var disconnectCallback = function(v, connected) {
console.debug('Redirecting to login page.');
storage.reset();
$location.path('/login');
};
if (verto.data.call) {
verto.hangup();
}
$scope.closeChat();
verto.disconnect(disconnectCallback);
verto.hangup();
};
if (verto.data.call) {
prompt({
title: 'Oops, Active Call in Course.',
message: 'It seems that you are in a call. Do you want to hang up?'
}).then(function() {
disconnect();
});
} else {
disconnect();
}
};
/**
* Shows a modal with the settings.
*/
$scope.openModalSettings = function() {
var modalInstance = $modal.open({
animation: $scope.animationsEnabled,
templateUrl: 'partials/modal_settings.html',
controller: 'ModalSettingsController',
});
modalInstance.result.then(
function(result) {
console.log(result);
},
function() {
console.info('Modal dismissed at: ' + new Date());
}
);
modalInstance.rendered.then(
function() {
jQuery.material.init();
}
);
};
$scope.openModal = function(templateUrl, controller) {
var modalInstance = $modal.open({
animation: $scope.animationsEnabled,
templateUrl: templateUrl,
controller: controller,
});
modalInstance.result.then(
function(result) {
console.log(result);
},
function() {
console.info('Modal dismissed at: ' + new Date());
}
);
modalInstance.rendered.then(
function() {
jQuery.material.init();
}
);
};
$scope.showContributors = function() {
$scope.openModal('partials/contributors.html', 'ContributorsController');
};
/**
* Updates the display adding the new number touched.
*
* @param {String} number - New touched number.
*/
$rootScope.dtmf = function(number) {
$rootScope.dialpadNumber = $scope.dialpadNumber + number;
if (verto.data.call) {
verto.dtmf(number);
}
};
/**
* Removes the last character from the number.
*/
$rootScope.backspace = function() {
var number = $rootScope.dialpadNumber;
var len = number.length;
$rootScope.dialpadNumber = number.substring(0, len - 1);
};
$scope.toggleCallHistory = function() {
if (!$scope.call_history) {
angular.element("#call_history").addClass('active');
angular.element("#call-history-wrapper").addClass('active');
} else {
angular.element("#call_history").removeClass('active');
angular.element("#call-history-wrapper").removeClass('active');
}
$scope.call_history = angular.element("#call_history").hasClass('active');
};
$scope.clearCallHistory = function() {
storage.data.call_history = [];
};
$scope.toggleChat = function() {
if ($scope.chatStatus && $rootScope.activePane === 'chat') {
$rootScope.chat_counter = 0;
}
angular.element('#wrapper').toggleClass('toggled');
$scope.chatStatus = angular.element('#wrapper').hasClass('toggled');
};
$scope.openChat = function() {
$scope.chatStatus = false;
angular.element('#wrapper').removeClass('toggled');
};
$scope.closeChat = function() {
$scope.chatStatus = true;
angular.element('#wrapper').addClass('toggled');
};
$scope.goFullscreen = function() {
if (storage.data.userStatus !== 'connected') {
return;
}
$rootScope.fullscreenEnabled = !Fullscreen.isEnabled();
if (Fullscreen.isEnabled()) {
Fullscreen.cancel();
} else {
Fullscreen.enable(document.getElementsByTagName('body')[0]);
}
};
$rootScope.$on('call.video', function(event) {
storage.data.videoCall = true;
});
$rootScope.$on('call.hangup', function(event, data) {
if (Fullscreen.isEnabled()) {
Fullscreen.cancel();
}
console.log($scope.chatStatus);
if (!$scope.chatStatus) {
angular.element('#wrapper').toggleClass('toggled');
$scope.chatStatus = angular.element('#wrapper').hasClass('toggled');
}
$rootScope.dialpadNumber = '';
console.debug('Redirecting to dialpad page.');
$location.path('/dialpad');
try {
$rootScope.$digest();
} catch (e) {
console.log('not digest');
}
});
$rootScope.$on('page.incall', function(event, data) {
if (storage.data.askRecoverCall) {
prompt({
title: 'Oops, Active Call in Course.',
message: 'It seems you were in a call before leaving the last time. Wanna go back to that?'
}).then(function() {
verto.changeData(storage);
console.log('redirect to incall page');
$location.path('/incall');
}, function() {
storage.data.userStatus = 'connecting';
verto.hangup();
});
} else {
verto.changeData(storage);
console.log('redirect to incall page');
$location.path('/incall');
}
});
$rootScope.callActive = function(data) {
verto.data.mutedMic = storage.data.mutedMic;
verto.data.mutedVideo = storage.data.mutedVideo;
if (!storage.data.cur_call) {
storage.data.call_start = new Date();
}
storage.data.userStatus = 'connected';
var call_start = new Date(storage.data.call_start);
$rootScope.start_time = call_start;
$timeout(function() {
$scope.$broadcast('timer-start');
});
myVideo.play();
storage.data.calling = false;
storage.data.cur_call = 1;
};
$rootScope.$on('call.active', function(event, data) {
$rootScope.callActive(data);
});
$rootScope.$on('call.calling', function(event, data) {
storage.data.calling = true;
});
$rootScope.$on('call.incoming', function(event, data) {
console.log('Incoming call from: ' + data);
storage.data.cur_call = 0;
$scope.incomingCall = true;
storage.data.videoCall = false;
storage.data.mutedVideo = false;
storage.data.mutedMic = false;
prompt({
title: 'Incoming Call',
message: 'from ' + data
}).then(function() {
var call_start = new Date(storage.data.call_start);
$rootScope.start_time = call_start;
console.log($rootScope.start_time);
$scope.answerCall();
storage.data.called_number = data;
storage.data.call_history.unshift({
'number': data,
'direction': 'inbound',
'status': true,
'call_start': Date()
});
$location.path('/incall');
}, function() {
$scope.declineCall();
storage.data.call_history.unshift({
'number': data,
'direction': 'inbound',
'status': false,
'call_start': Date()
});
});
});
$scope.hold = function() {
storage.data.onHold = !storage.data.onHold;
verto.data.call.toggleHold();
};
/**
* Hangup the current call.
*/
$scope.hangup = function() {
if (!verto.data.call) {
toastr.warning('There is no call to hangup.');
$location.path('/dialpad');
}
//var hangupCallback = function(v, hangup) {
// if (hangup) {
// $location.path('/dialpad');
// } else {
// console.debug('The call could not be hangup.');
// }
//};
//
//verto.hangup(hangupCallback);
verto.hangup();
};
$scope.answerCall = function() {
storage.data.onHold = false;
verto.data.call.answer({
useStereo: verto.data.useStereo,
useCamera: verto.data.useCamera,
useMic: verto.data.useMic,
callee_id_name: verto.data.name,
callee_id_number: verto.data.login
});
$location.path('/incall');
};
$scope.declineCall = function() {
$scope.hangup();
$scope.incomingCall = false;
};
$scope.screenshare = function() {
if (verto.data.shareCall) {
verto.screenshareHangup();
return false;
}
verto.screenshare(storage.data.called_number);
};
$scope.play = function() {
var file = $scope.promptInput('Please, enter filename', '', 'File',
function(file) {
verto.data.conf.play(file);
console.log('play file :', file);
});
};
$scope.stop = function() {
verto.data.conf.stop();
};
$scope.record = function() {
var file = $scope.promptInput('Please, enter filename', '', 'File',
function(file) {
verto.data.conf.record(file);
console.log('recording file :', file);
});
};
$scope.stopRecord = function() {
verto.data.conf.stopRecord();
};
$scope.snapshot = function() {
var file = $scope.promptInput('Please, enter filename', '', 'File',
function(file) {
verto.data.conf.snapshot(file);
console.log('snapshot file :', file);
});
};
}
]);
vertoControllers.controller('ChatController', ['$scope', '$rootScope', '$http',
'$location', '$anchorScroll', '$timeout', 'toastr', 'verto', 'storage', 'prompt',
function($scope, $rootScope, $http, $location, $anchorScroll, $timeout,
toastr, verto, storage, prompt) {
console.debug('Executing ChatController.');
function scrollToChatBottom() {
// Going to the bottom of chat messages.
var obj = document.querySelector('.chat-messages');
obj.scrollTop = obj.scrollHeight;
//var chat_messages_top = jQuery('.chat-messages').scrollTop();
//var marker_position = jQuery('#chat-message-bottom').position().top;
//jQuery('.chat-messages').scrollTop(chat_messages_top + marker_position);
}
var CLEAN_MESSAGE = '';
function clearConferenceChat() {
$scope.members = [];
$scope.messages = [];
$scope.message = CLEAN_MESSAGE;
}
clearConferenceChat();
$scope.$watch('activePane', function() {
if ($scope.activePane == 'chat') {
$rootScope.chat_counter = 0;
}
$rootScope.activePane = $scope.activePane;
});
$rootScope.$on('chat.newMessage', function(event, data) {
data.created_at = new Date();
console.log('chat.newMessage', data);
$scope.$apply(function() {
$scope.messages.push(data);
if(data.from != storage.data.name &&
(angular.element('#wrapper').hasClass('toggled') ||
$scope.activePane != 'chat')) {
toastr.info(data.body, data.from, {onHidden: function(clicked) {
if(clicked) {
$scope.chatStatus = false;
angular.element('#wrapper').removeClass('toggled');
$rootScope.activePane = 'chat';
$scope.activePane = 'chat';
$scope.$apply();
return true;
}
}});
++$rootScope.chat_counter;
return true;
}
$timeout(function() {
scrollToChatBottom();
}, 300);
});
});
$rootScope.$on('call.recovering', function(event){
console.log('Our current call is recovering, clear the participant list.');
$scope.members = [];
});
function findMemberByUUID(uuid) {
var found = false;
for (var idx in $scope.members) {
var member = $scope.members[idx];
if (member.uuid == uuid) {
found = true;
break;
}
}
if (found) {
return idx;
} else {
return -1;
}
}
function translateMember(member) {
return {
'uuid': member[0],
'id': member[1][0],
'number': member[1][1],
'name': member[1][2],
'codec': member[1][3],
'status': JSON.parse(member[1][4]),
'email': member[1][5].email
};
}
function addMember(member) {
$scope.members.push(translateMember(member));
}
$rootScope.$on('members.boot', function(event, members) {
console.log('members.boot', event, members);
$scope.$apply(function() {
clearConferenceChat();
for (var idx in members) {
var member = members[idx];
addMember(member);
console.log($scope.members);
}
})
});
$rootScope.$on('members.add', function(event, member) {
$scope.$apply(function() {
addMember(member);
});
});
$rootScope.$on('members.del', function(event, uuid) {
$scope.$apply(function() {
var memberIdx = findMemberByUUID(uuid);
if (memberIdx != -1) {
// Removing the member.
$scope.members.splice(memberIdx, 1);
}
});
});
$rootScope.$on('members.update', function(event, member) {
member = translateMember(member);
var memberIdx = findMemberByUUID(member.uuid);
if (memberIdx < 0) {
console.log('Didn\'t find the member uuid ' + member.uuid);
} else {
$scope.$apply(function() {
console.log('Updating', memberIdx, ' <', $scope.members[memberIdx],
'> with <', member, '>');
angular.extend($scope.members[memberIdx], member);
});
}
});
$rootScope.$on('members.clear', function(event) {
$scope.$apply(function() {
clearConferenceChat();
$scope.closeChat();
});
});
/**
* Public methods.
*/
$scope.send = function() {
verto.sendMessage($scope.message, function() {
$scope.message = CLEAN_MESSAGE;
});
};
// Participants moderation.
$scope.confKick = function(memberID) {
console.log('$scope.confKick');
verto.data.conf.kick(memberID);
};
$scope.confMuteMic = function(memberID) {
console.log('$scope.confMuteMic');
verto.data.conf.muteMic(memberID);
};
$scope.confMuteVideo = function(memberID) {
console.log('$scope.confMuteVideo');
verto.data.conf.muteVideo(memberID);
};
$scope.confPresenter = function(memberID) {
console.log('$scope.confPresenter');
verto.data.conf.presenter(memberID);
};
$scope.confVideoFloor = function(memberID) {
console.log('$scope.confVideoFloor');
verto.data.conf.videoFloor(memberID);
};
$scope.confBanner = function(memberID) {
console.log('$scope.confBanner');
prompt({
title: 'Participant banner',
message: 'What would you like the banner to say?',
input: true,
label: 'Text',
value: 'New text'
}).then(function(text){
verto.data.conf.banner(memberID, text);
});
};
$scope.confVolumeDown = function(memberID) {
console.log('$scope.confVolumeDown');
verto.data.conf.volumeDown(memberID);
};
$scope.confVolumeUp = function(memberID) {
console.log('$scope.confVolumeUp');
verto.data.conf.volumeUp(memberID);
};
$scope.confTransfer = function(memberID) {
console.log('$scope.confTransfer');
var exten = '1800';
verto.data.conf.transfer(memberID, exten);
};
}
]);
vertoControllers.controller('MenuController', ['$scope', '$http', '$location',
'verto', 'storage',
function($scope, $http, $location, verto, storage) {
console.debug('Executing MenuController.');
}
]);
vertoControllers.controller('ModalSettingsController', ['$scope', '$http',
'$location', '$modalInstance', 'verto', 'storage',
function($scope, $http, $location, $modalInstance, verto, storage) {
console.debug('Executing ModalSettingsController.');
verto.changeData(storage);
$scope.verto = verto;
$scope.storage = storage;
$scope.ok = function() {
storage.changeData(verto);
$modalInstance.close('Ok.');
};
$scope.cancel = function() {
$modalInstance.dismiss('cancel');
};
$scope.refreshDeviceList = function() {
return verto.refreshDevices();
}
}
]);
vertoControllers.controller('ModalLoginInformationController', ['$scope',
'$http', '$location', '$modalInstance', 'verto', 'storage',
function($scope, $http, $location, $modalInstance, verto, storage) {
console.debug('Executing ModalLoginInformationController.');
$scope.verto = verto;
$scope.storage = storage;
$scope.ok = function() {
$modalInstance.close('Ok.');
};
$scope.cancel = function() {
$modalInstance.dismiss('cancel');
};
}
]);
vertoControllers.controller('LoginController', ['$scope', '$http', '$location',
'verto',
function($scope, $http, $location, verto) {
$scope.checkBrowser();
/**
* using stored data (localStorage) for logon
*/
verto.data.name = $scope.storage.data.name;
verto.data.email = $scope.storage.data.email;
if ($scope.storage.data.login != '' && $scope.storage.data.password != '') {
verto.data.login = $scope.storage.data.login;
verto.data.password = $scope.storage.data.password;
}
console.debug('Executing LoginController.');
}
]);
vertoControllers.controller('DialPadController', ['$rootScope', '$scope',
'$http', '$location', 'toastr', 'verto', 'storage',
function($rootScope, $scope, $http, $location, toastr, verto, storage) {
console.debug('Executing DialPadController.');
$scope.checkBrowser();
storage.data.videoCall = false;
storage.data.userStatus = 'connecting';
storage.data.calling = false;
/**
* fill dialpad via querystring [?autocall=\d+]
*/
if ($location.search().autocall) {
$rootScope.dialpadNumber = $location.search().autocall;
}
/**
* used to bind click on number in call history to fill dialpad
* 'cause inside a ng-repeat the angular isnt in ctrl scope
*/
$scope.fillDialpadNumber = function(number) {
$rootScope.dialpadNumber = number;
};
$rootScope.transfer = function() {
if (!$rootScope.dialpadNumber) {
return false;
}
verto.data.call.transfer($rootScope.dialpadNumber);
};
/**
* Call to the number in the $rootScope.dialpadNumber.
*/
$rootScope.call = function(extension) {
storage.data.onHold = false;
storage.data.cur_call = 0;
$rootScope.dialpadNumber = extension;
if (!$rootScope.dialpadNumber && storage.data.called_number) {
$rootScope.dialpadNumber = storage.data.called_number;
return false;
} else if (!$rootScope.dialpadNumber && !storage.data.called_number) {
toastr.warning('Enter an extension, please.');
return false;
}
if (verto.data.call) {
console.debug('A call is already in progress.');
return false;
}
storage.data.mutedVideo = false;
storage.data.mutedMic = false;
storage.data.videoCall = false;
verto.call($rootScope.dialpadNumber);
storage.data.called_number = $rootScope.dialpadNumber;
storage.data.call_history.unshift({
'number': $rootScope.dialpadNumber,
'direction': 'outbound',
'call_start': Date()
});
$location.path('/incall');
}
}
]);
vertoControllers.controller('InCallController', ['$rootScope', '$scope',
'$http', '$location', '$modal', '$timeout', 'toastr', 'verto', 'storage', 'prompt', 'Fullscreen',
function($rootScope, $scope, $http, $location, $modal, $timeout, toatr,
verto, storage, prompt, Fullscreen) {
console.debug('Executing InCallController.');
$scope.layout = null;
$scope.checkBrowser();
$rootScope.dialpadNumber = '';
$scope.callTemplate = 'partials/phone_call.html';
$scope.dialpadTemplate = '';
$scope.incall = true;
if (storage.data.videoCall) {
$scope.callTemplate = 'partials/video_call.html';
}
$rootScope.$on('call.video', function(event, data) {
$timeout(function() {
$scope.callTemplate = 'partials/video_call.html';
});
});
/**
* toggle dialpad in incall page
*/
$scope.toggleDialpad = function() {
$scope.openModal('partials/dialpad_widget.html',
'ModalDialpadController');
/*
if(!$scope.dialpadTemplate) {
$scope.dialpadTemplate = 'partials/dialpad_widget.html';
} else {
$scope.dialpadTemplate = '';
}
*/
}
/**
* TODO: useless?
*/
$scope.videoCall = function() {
prompt({
title: 'Would you like to activate video for this call?',
message: 'Video will be active during the next calls.'
}).then(function() {
storage.data.videoCall = true;
$scope.callTemplate = 'partials/video_call.html';
});
};
$scope.cbMuteVideo = function(event, data) {
storage.data.mutedVideo = !storage.data.mutedVideo;
}
$scope.cbMuteMic = function(event, data) {
storage.data.mutedMic = !storage.data.mutedMic;
}
$scope.confChangeVideoLayout = function(layout) {
verto.data.conf.setVideoLayout(layout);
};
$scope.muteMic = verto.muteMic;
$scope.muteVideo = verto.muteVideo;
$timeout(function() {
console.log('broadcast time-start incall');
$scope.$broadcast('timer-start');
}, 1000);
}
]);
vertoControllers.controller('ModalDialpadController', ['$scope',
'$modalInstance',
function($scope, $modalInstance) {
$scope.ok = function() {
$modalInstance.close('Ok.');
};
$scope.cancel = function() {
$modalInstance.dismiss('cancel');
};
}
]);
vertoControllers.controller('BrowserUpgradeController', ['$scope', '$http',
'$location', 'verto', 'storage', 'Fullscreen',
function($scope, $http, $location, verto, storage, Fullscreen) {
console.debug('Executing BrowserUpgradeController.');
}
]);
vertoControllers.controller('ContributorsController', ['$scope', '$http',
'toastr',
function($scope, $http, toastr) {
$http.get(window.location.pathname + '/contributors.txt')
.success(function(data) {
var contributors = [];
angular.forEach(data, function(value, key) {
var re = /(.*) <(.*)>/;
var name = value.replace(re, "$1");
var email = value.replace(re, "$2");
this.push({
'name': name,
'email': email
});
}, contributors);
$scope.contributors = contributors;
})
.error(function() {
toastr.error('contributors not found.');
});
}
]);

View File

@@ -1,109 +0,0 @@
'use strict';
/* Directives */
var vertoDirectives = angular.module('vertoDirectives', []);
/**
* To RTC work properly we need to give a <video> tag as soon as possible
* because it needs to attach the video and audio stream to the tag.
*
* This directive is responsible for moving the video tag from the body to
* the right place when a call start and move back to the body when the
* call ends. It also hides and display the tag when its convenient.
*/
vertoDirectives.directive('videoTag',
function() {
function link(scope, element, attrs) {
// Moving the video tag to the new place inside the incall page.
console.log('Moving the video to element.');
jQuery('video').removeClass('hide').appendTo(element);
jQuery('video').css('display', 'block');
scope.callActive();
element.on('$destroy', function() {
// Move the video back to the body.
console.log('Moving the video back to body.');
jQuery('video').addClass('hide').appendTo(jQuery('body'));
});
}
return {
link: link
}
});
vertoDirectives.directive('userStatus',
function() {
var link = function(scope, element, attrs) {
scope.$watch('condition', function(condition) {
element.removeClass('connected');
element.removeClass('disconnected');
element.removeClass('connecting');
element.addClass(condition);
});
}
return {
scope: {
'condition': '='
},
link: link
};
});
vertoDirectives.directive('showControls',
function(Fullscreen) {
var link = function(scope, element, attrs) {
var i = null;
jQuery('.video-footer').fadeIn('slow');
jQuery('.video-hover-buttons').fadeIn('slow');
element.parent().bind('mousemove', function() {
if (Fullscreen.isEnabled()) {
clearTimeout(i);
jQuery('.video-footer').fadeIn('slow');
jQuery('.video-hover-buttons').fadeIn(500);
i = setTimeout(function() {
if (Fullscreen.isEnabled()) {
jQuery('.video-footer').fadeOut('slow');
jQuery('.video-hover-buttons').fadeOut(500);
}
}, 3000);
}
});
element.parent().bind('mouseleave', function() {
jQuery('.video-footer').fadeIn();
jQuery('.video-hover-buttons').fadeIn();
});
}
return {
link: link
};
});
/*
Sometimes autofocus HTML5 directive just isn't enough with SPAs.
This directive will force autofocus to work properly under those circumstances.
*/
(function () {
'use strict';
vertoDirectives.directive('autofocus', ['$timeout',
function ($timeout) {
return {
restrict: 'A',
link: function ($scope, $element) {
$timeout(function () {
console.log('Focusing...');
$element[0].focus();
});
}
};
}
]);
})();

View File

@@ -1,16 +0,0 @@
'use strict';
/* Filters */
var vertoFilters = angular.module('vertoFilters', []);
vertoFilters.filter('gravatar',
function() {
return function (email, size) {
if (angular.isUndefined(size)) {
size = 40;
}
var hash = md5(email);
return 'https://secure.gravatar.com/avatar/' + hash + '?s=' + size + '&d=mm';
}
});

View File

@@ -1,70 +0,0 @@
'use strict';
var storageService = angular.module('storageService', ['ngStorage']);
storageService.service('storage', ['$rootScope', '$localStorage',
function($rootScope, $localStorage) {
var data = $localStorage;
data.$default({
ui_connected: false,
ws_connected: false,
cur_call: 0,
called_number: '',
useVideo: true,
call_history: [],
call_start: false,
name: '',
email: '',
login: '',
password: '',
userStatus: 'disconnected',
mutedVideo: false,
mutedMic: false,
selectedVideo: null,
selectedAudio: null,
selectedShare: null,
useVideo: '',
useCamera: '',
useVideo: '',
useCamera: '',
useStereo: '',
useSTUN: '',
useDedenc: '',
mirrorInput: '',
outgoingBandwidth: '',
incomingBandwidth: '',
vidQual: '',
askRecoverCall: false,
googNoiseSuppression: false,
googHighpassFilter: false,
googEchoCancellation: false
});
function changeData(verto_data) {
data.selectedVideo = verto_data.data.selectedVideo;
data.selectedAudio = verto_data.data.selectedAudio;
data.selectedShare = verto_data.data.selectedShare;
data.useVideo = verto_data.data.useVideo;
data.useCamera = verto_data.data.useCamera;
data.useStereo = verto_data.data.useStereo;
data.useDedenc = verto_data.data.useDedenc;
data.useSTUN = verto_data.data.useSTUN;
data.vidQual = verto_data.data.vidQual;
data.mirrorInput = verto_data.data.mirrorInput;
data.outgoingBandwidth = verto_data.data.outgoingBandwidth;
data.incomingBandwidth = verto_data.data.incomingBandwidth;
}
return {
data: data,
changeData: changeData,
reset: function() {
data.ui_connected = false;
data.ws_connected = false;
data.cur_call = 0;
data.userStatus = 'disconnected';
},
};
}
]);

View File

@@ -1,801 +0,0 @@
'use strict';
/* Controllers */
var videoQuality = [{
id: 'qvga',
label: 'QVGA 320x240',
width: 320,
height: 240
}, {
id: 'vga',
label: 'VGA 640x480',
width: 640,
height: 480
}, {
id: 'qvga_wide',
label: 'QVGA WIDE 320x180',
width: 320,
height: 180
}, {
id: 'vga_wide',
label: 'VGA WIDE 640x360',
width: 640,
height: 360
}, {
id: 'hd',
label: 'HD 1280x720',
width: 1280,
height: 720
}, {
id: 'hhd',
label: 'HHD 1920x1080',
width: 1920,
height: 1080
}, ];
var videoResolution = {
qvga: {
width: 320,
height: 240
},
vga: {
width: 640,
height: 480
},
qvga_wide: {
width: 320,
height: 180
},
vga_wide: {
width: 640,
height: 360
},
hd: {
width: 1280,
height: 720
},
hhd: {
width: 1920,
height: 1080
},
};
var bandwidth = [{
id: '250',
label: '250kb'
}, {
id: '500',
label: '500kb'
}, {
id: '1024',
label: '1mb'
}, {
id: '1536',
label: '1.5mb'
}, {
id: '2048',
label: '2mb'
}, {
id: '5120',
label: '5mb'
}, {
id: '0',
label: 'No Limit'
}, {
id: 'default',
label: 'Server Default'
}, ];
var vertoService = angular.module('vertoService', ['ngCookies']);
vertoService.service('verto', ['$rootScope', '$cookieStore', '$location', 'storage',
function($rootScope, $cookieStore, $location, storage) {
var data = {
// Connection data.
instance: null,
connected: false,
// Call data.
call: null,
shareCall: null,
callState: null,
conf: null,
confLayouts: [],
confRole: null,
chattingWith: null,
liveArray: null,
// Settings data.
videoDevices: [],
audioDevices: [],
shareDevices: [],
extension: $cookieStore.get('verto_demo_ext'),
name: $cookieStore.get('verto_demo_name'),
email: $cookieStore.get('verto_demo_email'),
cid: $cookieStore.get('verto_demo_cid'),
textTo: $cookieStore.get('verto_demo_textto') || "1000",
login: $cookieStore.get('verto_demo_login') || "1008",
password: $cookieStore.get('verto_demo_passwd') || "1234",
hostname: $cookieStore.get('verto_demo_hostname') || window.location.hostname,
wsURL: $cookieStore.get('verto_demo_wsurl') || ("wss://" + window.location.hostname + ":8082"),
useVideo: $cookieStore.get('verto_demo_vid_checked') || true,
useCamera: $cookieStore.get('verto_demo_camera_checked') || true,
useStereo: $cookieStore.get('verto_demo_stereo_checked') || true,
useSTUN: $cookieStore.get('verto_demo_stun_checked') || true,
useDedenc: $cookieStore.get('verto_demo_dedenc_checked') || false,
mirrorInput: $cookieStore.get('verto_demo_mirror_input_checked') || false,
outgoingBandwidth: $cookieStore.get('verto_demo_outgoingBandwidth') || 'default',
incomingBandwidth: $cookieStore.get('verto_demo_incomingBandwidth') || 'default',
vidQual: $cookieStore.get('verto_demo_vqual') || 'qvga',
localVideo: $cookieStore.get('verto_demo_local_video_checked') || false,
bestWidth: '',
bestHeight: '',
selectedVideo: null,
selectedAudio: null,
selectedShare: null
};
function cleanShareCall(that) {
that.refreshVideoResolution();
data.shareCall = null;
data.callState = 'active';
that.refreshDevices();
}
function cleanCall() {
data.call = null;
data.callState = null;
data.conf = null;
data.confLayouts = [];
data.confRole = null;
data.chattingWith = null;
$rootScope.$emit('call.hangup', 'hangup');
}
function inCall() {
$rootScope.$emit('page.incall', 'call');
}
function callActive(last_state) {
$rootScope.$emit('call.active', last_state);
}
function calling() {
$rootScope.$emit('call.calling', 'calling');
}
function incomingCall(number) {
$rootScope.$emit('call.incoming', number);
}
function recoveringCall() {
$rootScope.$emit('call.recovering');
}
function getVideoParams() {
var maxWidth, maxHeight;
maxWidth = data.bestWidth;
maxHeight = data.bestHeight;
if(!data.bestWidth) {
maxWidth = videoResolution[data.vidQual].width;
}
if(!data.bestHeight) {
maxHeight = videoResolution[data.vidQual].height;
}
return {
minWidth: videoResolution[data.vidQual].width,
minHeight: videoResolution[data.vidQual].height,
maxWidth: maxWidth,
maxHeight: maxHeight,
minFrameRate: 15,
vertoBestFrameRate: 30
};
}
function changeData(verto_data) {
$cookieStore.put('verto_demo_vid_checked', verto_data.data.useVideo);
$cookieStore.put('verto_demo_camera_checked', verto_data.data.useCamera);
$cookieStore.put('verto_demo_stereo_checked', verto_data.data.useStereo);
$cookieStore.put('verto_demo_stun_checked', verto_data.data.useSTUN);
$cookieStore.put('verto_demo_dedenc_checked', verto_data.data.useDedenc);
$cookieStore.put('verto_demo_mirror_input_checked', verto_data.data.mirrorInput);
$cookieStore.put('verto_demo_outgoingBandwidth', verto_data.data.outgoingBandwidth);
$cookieStore.put('verto_demo_incomingBandwidth', verto_data.data.incomingBandwidth);
$cookieStore.put('verto_demo_vqual', verto_data.data.vidQual);
data.selectedVideo = verto_data.data.selectedVideo;
data.selectedAudio = verto_data.data.selectedAudio;
data.selectedShare = verto_data.data.selectedShare;
data.useVideo = verto_data.data.useVideo;
data.useCamera = verto_data.data.useCamera;
data.useStereo = verto_data.data.useStereo;
data.useDedenc = verto_data.data.useDedenc;
data.useSTUN = verto_data.data.useSTUN;
data.vidQual = verto_data.data.vidQual;
data.mirrorInput = verto_data.data.mirrorInput;
data.outgoingBandwidth = verto_data.data.outgoingBandwidth;
data.incomingBandwidth = verto_data.data.incomingBandwidth;
}
var callState = {
muteMic: false,
muteVideo: false
};
return {
data: data,
callState: callState,
changeData: changeData,
// Options to compose the interface.
videoQuality: videoQuality,
videoResolution: videoResolution,
bandwidth: bandwidth,
refreshDevices: function(callback) {
console.debug('Attempting to refresh the devices.');
function refreshDevicesCallback() {
data.videoDevices = [{
id: 'none',
label: 'No camera'
}];
data.shareDevices = [{
id: 'screen',
label: 'Screen'
}];
data.audioDevices = [];
for (var i in jQuery.verto.videoDevices) {
var device = jQuery.verto.videoDevices[i];
if (!device.label) {
data.videoDevices.push({
id: 'Camera ' + i,
label: 'Camera ' + i
});
} else {
data.videoDevices.push({
id: device.id,
label: device.label || device.id
});
}
// Selecting the first source.
if (i == 0) {
data.selectedVideo = device.id;
}
if (!device.label) {
data.shareDevices.push({
id: 'Share Device ' + i,
label: 'Share Device ' + i
});
continue;
}
data.shareDevices.push({
id: device.id,
label: device.label || device.id
});
}
for (var i in jQuery.verto.audioInDevices) {
var device = jQuery.verto.audioInDevices[i];
// Selecting the first source.
if (i == 0) {
data.selectedAudio = device.id;
}
if (!device.label) {
data.audioDevices.push({
id: 'Microphone ' + i,
label: 'Microphone ' + i
});
continue;
}
data.audioDevices.push({
id: device.id,
label: device.label || device.id
});
}
console.debug('Devices were refreshed.');
};
jQuery.verto.refreshDevices(refreshDevicesCallback);
},
/**
* Updates the video resolutions based on settings.
*/
refreshVideoResolution: function() {
console.debug('Attempting to refresh video resolutions.');
if (data.instance) {
data.instance.videoParams(getVideoParams());
} else {
console.debug('There is no instance of verto.');
}
},
updateResolutions: function(supportedResolutions) {
console.debug('Attempting to sync supported and available resolutions');
var removed = 0;
angular.forEach(videoQuality, function(resolution, id) {
var supported = false;
angular.forEach(supportedResolutions, function(res) {
var width = res[0];
var height = res[1];
if(resolution.width == width && resolution.height == height) {
supported = true;
}
});
if(!supported) {
delete videoQuality[id];
++removed;
}
});
videoQuality.length = videoQuality.length - removed;
this.videoQuality = videoQuality;
this.data.vidQual = videoQuality[videoQuality.length - 1].id;
return videoQuality;
},
/**
* Connects to the verto server. Automatically calls `onWSLogin`
* callback set in the verto object.
*
* @param callback
*/
connect: function(callback) {
console.debug('Attempting to connect to verto.');
var that = this;
function startConference(v, dialog, pvtData) {
$rootScope.$emit('call.video', 'video');
data.chattingWith = pvtData.chatID;
data.confRole = pvtData.role;
var conf = new $.verto.conf(v, {
dialog: dialog,
hasVid: data.useVideo,
laData: pvtData,
onBroadcast: function(v, conf, message) {
console.log('>>> conf.onBroadcast:', arguments);
if (message.action == 'response') {
// This is a response with the video layouts list.
if (message['conf-command'] == 'list-videoLayouts') {
data.confLayouts = message.responseData.sort();
} else {
$rootScope.$emit('conference.broadcast', message);
}
}
}
});
console.log('>>> conf.listVideoLayouts();');
conf.listVideoLayouts();
data.conf = conf;
data.liveArray = new $.verto.liveArray(
data.instance, pvtData.laChannel,
pvtData.laName, {
subParams: {
callID: dialog ? dialog.callID : null
}
});
data.liveArray.onErr = function(obj, args) {
console.log('liveArray.onErr', obj, args);
};
data.liveArray.onChange = function(obj, args) {
console.log('liveArray.onChange', obj, args);
switch (args.action) {
case 'bootObj':
$rootScope.$emit('members.boot', args.data);
break;
case 'add':
var member = [args.key, args.data];
$rootScope.$emit('members.add', member);
break;
case 'del':
var uuid = args.key;
$rootScope.$emit('members.del', uuid);
break;
case 'clear':
$rootScope.$emit('members.clear');
break;
case 'modify':
var member = [args.key, args.data];
$rootScope.$emit('members.update', member);
break;
default:
console.log('NotImplemented', args.action);
}
};
}
function stopConference() {
console.log('stopConference()');
if (data.liveArray) {
console.log('Has data.liveArray.');
$rootScope.$emit('members.clear');
data.liveArray.destroy();
data.liveArray = null;
} else {
console.log('Doesn\'t found data.liveArray.');
}
}
var callbacks = {
onWSLogin: function(v, success) {
data.connected = success;
console.debug('Connected to verto server:', success);
if (angular.isFunction(callback)) {
callback(v, success);
}
},
onMessage: function(v, dialog, msg, params) {
console.debug('onMessage:', v, dialog, msg, params);
switch (msg) {
case $.verto.enum.message.pvtEvent:
if (params.pvtData) {
switch (params.pvtData.action) {
case "conference-liveArray-join":
console.log("conference-liveArray-join");
startConference(v, dialog, params.pvtData);
break;
case "conference-liveArray-part":
console.log("conference-liveArray-part");
stopConference();
break;
}
}
break;
case $.verto.enum.message.info:
var body = params.body;
var from = params.from_msg_name || params.from;
$rootScope.$emit('chat.newMessage', {
from: from,
body: body
});
break;
default:
break;
}
},
onDialogState: function(d) {
if (!data.call) {
data.call = d;
if (d.state.name !== 'ringing') {
inCall();
}
}
console.debug('onDialogState:', d);
switch (d.state.name) {
case "ringing":
incomingCall(d.params.caller_id_number);
break;
case "trying":
console.debug('Calling:', d.cidString());
data.callState = 'trying';
break;
case "early":
console.debug('Talking to:', d.cidString());
data.callState = 'active';
calling();
break;
case "recovering":
console.debug('We are recovering a call!');
recoveringCall();
break;
case "active":
console.debug('Talking to:', d.cidString());
data.callState = 'active';
callActive(d.lastState.name);
break;
case "hangup":
console.debug('Call ended with cause: ' + d.cause);
data.callState = 'hangup';
break;
case "destroy":
console.debug('Destroying: ' + d.cause);
if (d.params.screenShare) {
cleanShareCall(that);
} else {
cleanCall();
}
break;
}
},
onWSClose: function(v, success) {
console.debug('onWSClose:', success);
},
onEvent: function(v, e) {
console.debug('onEvent:', e);
}
};
var init = function(resolutions) {
data.bestWidth = resolutions['bestResSupported'][0];
data.bestHeight = resolutions['bestResSupported'][1];
that.updateResolutions(resolutions['validRes']);
that.refreshVideoResolution();
data.instance = new jQuery.verto({
login: data.login + '@' + data.hostname,
passwd: data.password,
socketUrl: data.wsURL,
tag: "webcam",
ringFile: "sounds/bell_ring2.wav",
loginParams: {
foo: true,
bar: "yes"
},
videoParams: getVideoParams(),
// TODO: Add options for this.
audioParams: {
googEchoCancellation: storage.data.googEchoCancellation || false,
googNoiseSuppression: storage.data.googNoiseSuppression || false,
googHighpassFilter: storage.data.googHighpassFilter || false
},
iceServers: data.useSTUN
}, callbacks);
that.refreshDevices();
};
jQuery.verto.init({}, init);
},
/**
* Login the client.
*
* @param callback
*/
login: function(callback) {
data.instance.loginData({
login: data.login + '@' + data.hostname,
passwd: data.password
});
data.instance.login();
if (angular.isFunction(callback)) {
callback(data.instance, true);
}
},
/**
* Disconnects from the verto server. Automatically calls `onWSClose`
* callback set in the verto object.
*
* @param callback
*/
disconnect: function(callback) {
console.debug('Attempting to disconnect to verto.');
data.instance.logout();
data.connected = false;
console.debug('Disconnected from verto server.');
if (angular.isFunction(callback)) {
callback(data.instance, data.connected);
}
},
/**
* Make a call.
*
* @param callback
*/
call: function(destination, callback) {
console.debug('Attempting to call destination ' + destination + '.');
this.refreshVideoResolution();
var call = data.instance.newCall({
destination_number: destination,
caller_id_name: data.name,
caller_id_number: data.login,
outgoingBandwidth: data.outgoingBandwidth,
incomingBandwidth: data.incomingBandwidth,
useVideo: data.useVideo,
useStereo: data.useStereo,
useCamera: data.selectedVideo,
useMic: data.selectedAudio,
dedEnc: data.useDedenc,
mirrorInput: data.mirrorInput,
userVariables: {
email : storage.data.email
}
});
data.call = call;
data.mutedMic = false;
data.mutedVideo = false;
this.refreshDevices();
if (angular.isFunction(callback)) {
callback(data.instance, call);
}
},
screenshare: function(destination, callback) {
console.log('share screen video');
this.refreshVideoResolution();
var that = this;
getScreenId(function(error, sourceId, screen_constraints) {
var call = data.instance.newCall({
destination_number: destination + '-screen',
caller_id_name: data.name + ' (Screen)',
caller_id_number: data.login + ' (Screen)',
outgoingBandwidth: data.outgoingBandwidth,
incomingBandwidth: data.incomingBandwidth,
videoParams: screen_constraints.video.mandatory,
useVideo: data.useVideo,
screenShare: true,
dedEnc: data.useDedenc,
mirrorInput: data.mirrorInput,
userVariables: {
email : storage.data.email
}
});
data.shareCall = call;
console.log('shareCall', data);
data.mutedMic = false;
data.mutedVideo = false;
that.refreshDevices();
});
},
screenshareHangup: function() {
if (!data.shareCall) {
console.debug('There is no call to hangup.');
return false;
}
console.log('shareCall End', data.shareCall);
data.shareCall.hangup();
console.debug('The screencall was hangup.');
},
/**
* Hangup the current call.
*
* @param callback
*/
hangup: function(callback) {
console.debug('Attempting to hangup the current call.');
if (!data.call) {
console.debug('There is no call to hangup.');
return false;
}
data.call.hangup();
console.debug('The call was hangup.');
if (angular.isFunction(callback)) {
callback(data.instance, true);
}
},
/**
* Send a DTMF to the current call.
*
* @param {string|integer} number
* @param callback
*/
dtmf: function(number, callback) {
console.debug('Attempting to send DTMF "' + number + '".');
if (!data.call) {
console.debug('There is no call to send DTMF.');
return false;
}
data.call.dtmf(number);
console.debug('The DTMF was sent for the call.');
if (angular.isFunction(callback)) {
callback(data.instance, true);
}
},
/**
* Mute the microphone for the current call.
*
* @param callback
*/
muteMic: function(callback) {
console.debug('Attempting to mute mic for the current call.');
if (!data.call) {
console.debug('There is no call to mute.');
return false;
}
data.call.dtmf('0');
data.mutedMic = !data.mutedMic;
console.debug('The mic was muted for the call.');
if (angular.isFunction(callback)) {
callback(data.instance, true);
}
},
/**
* Mute the video for the current call.
*
* @param callback
*/
muteVideo: function(callback) {
console.debug('Attempting to mute video for the current call.');
if (!data.call) {
console.debug('There is no call to mute.');
return false;
}
data.call.dtmf('*0');
data.mutedVideo = !data.mutedVideo;
console.debug('The video was muted for the call.');
if (angular.isFunction(callback)) {
callback(data.instance, true);
}
},
sendMessage: function(body, callback) {
data.call.message({
to: data.chattingWith,
body: body,
from_msg_name: data.name,
from_msg_number: data.cid
});
if (angular.isFunction(callback)) {
callback(data.instance, true);
}
}
};
}
]);