'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.'); }); } ]);