var myschedule = angular.module('myschedule-V2_1');

/**
 * @ngdoc controller
 * @name myschedule-V2_1.controller:MyScheduleListController
 * @description
 * Controller for MySchedule list view
 */
myschedule.controller('MyScheduleListController', ['$scope', 'qmMyScheduleService', 'qmList', 'qmLocalization',
    'qmMyScheduleDependencyService','qmUtilities', 'qmMoment',
function($scope, qmMyScheduleService, qmList, qmLocalization, qmMyScheduleDependencyService, qmUtilities, qmMoment) {
    var qmEventsDependencyService = qmMyScheduleDependencyService.getEventsService('qmEventsDependencyService');

    $scope.eventListItemUrl = qmEventsDependencyService.getTemplateUrl('event-list-item');
    $scope.loadMySchedule = qmMyScheduleService.getList();

    $scope.loadMySchedule.then(function(myschedule) {
        $scope.myschedule = qmList.groupByGroup(myschedule, 'eventDate', 'eventDateFormatted');
        var componentTitle = qmLocalization.getString('componentMyscheduleTitle');
        $scope.emptyText = qmLocalization.getString('LABEL_EMPTY_MY_SCHEDULE_TITLE', componentTitle);
        var eventsTitle = qmLocalization.getString('componentEventsTitle');
        $scope.emptySubText = qmLocalization.getString('LABEL_EMPTY_MY_SCHEDULE_MESSAGE', eventsTitle);
    }).finally(function() {
        //Check if event is happening soon so that the schedule would auto-scroll to day.
        var currentDate = new Date();
        currentDate.setHours(0, 0, 0, 0);
        var momentCurrentDate = qmMoment(currentDate).startOf('day');
        for (var i = 0; i < $scope.myschedule.length; i++) {
            var timeStringBeforeParse = $scope.myschedule[i].value;
            var timeStringSplit = timeStringBeforeParse.split("T");
            // we only really care about the date
            var tabDate = timeStringSplit[0];
            var momentEventGroupDate = qmMoment(tabDate).startOf('day');

            if (momentEventGroupDate.isAfter(momentCurrentDate) || momentEventGroupDate.isSame(momentCurrentDate)) {
                var dayContainer = 'index' + $scope.myschedule[i].value;
                qmUtilities.scrollToLocation(dayContainer);
                break;
            }
        }
    });
}]);

/**
 * @ngdoc controller
 * @name myschedule-V2_1.controller:MyScheduleHeaderController
 * @description
 * Controller for MySchedule list header view
 */
myschedule.controller('MyScheduleHeaderController', ['$scope', 'qmLocalization', function($scope, qmLocalization) {
    $scope.headerTitle = qmLocalization.getString('componentMyscheduleTitle');
}]);

var myschedule = angular.module('myschedule-V2_1');

/**
 * @ngdoc service
 * @name myschedule-V2_1.service:qmMyScheduleDependencyService
 * @description
 * Manages communication with dependency manager
 */
myschedule.factory('qmMyScheduleDependencyService', ['qmDependencyManager', function(qmDependencyManager) {
    return {
        /**
         * @ngdoc method
         * @name getEventsService
         * @methodOf myschedule-V2_1.service:qmMyScheduleDependencyService
         * @description
         * Get service from events component
         *
         * @param {string} service Service name
         * @returns {service} Angular service or null
         */
        getEventsService: function(service) {
            return qmDependencyManager.getRequiredService('events', '2.4', 'component', service);
        },

        /**
         * @ngdoc method
         * @name hasLikesOnEvent
         * @methodOf myschedule-V2_1.service:qmMyScheduleDependencyService
         * @description
         * Check if 2.7 configurable for this project
         *
         * @returns {boolean} True or False
         */
        hasLikesOnEvent: function() {
            return qmDependencyManager.hasComponent('events', '2.7');
        }

    };
}]);

var optin = angular.module('opt-in-V2_0');

/**
 * @ngdoc controller
 * @name opt-in-V2_0.controller:OptInController
 * @description
 * Controller for opt-in
 */
optin.controller('OptInController',
['$scope', 'qmOptInService', '$modalInstance', 'qmLocalization', 'qmLogin', 'qmAnalytics', 'qmEventConfig',
function($scope, qmOptInService, $modalInstance, qmLocalization, qmLogin, qmAnalytics, qmEventConfig) {
    if (qmLogin.isLoggedIn()) {
        $scope.userId = qmLogin.getUserInfo().attendeeId;
    }
    $scope.useTransparentView = qmEventConfig.isTransparentView();
    $scope.loadOptIn = qmOptInService.getDetail();
    $scope.loadOptIn.then(function(optIn) {
        $scope.optIn = optIn;
        $scope.continueText = qmLocalization.getString('BUTTON_OPTIN_CONTINUE');
    });

    $scope.continue = function() {
        qmAnalytics.markEvent('OptInSent');
        qmOptInService.updateDetail({
            statements: $scope.optIn.statements
        });
        $modalInstance.close();
    };
}]);

var optin = angular.module('opt-in-V2_0');

/**
 * @ngdoc service
 * @name opt-in-V2_0.service:qmOptInService
 * @description
 * Manages REST API communication for opt-in
 */
optin.factory('qmOptInService', ['qmRest', '$q', 'qmWebAppService', 'qmLocalization', '$modal',
function(qmRest, $q, qmWebAppService, qmLocalization, $modal) {
    return {
        /**
         * @ngdoc method
         * @name checkOptIn
         * @methodOf opt-in-V2_0.service:qmOptInService
         * @description
         * Check if user has been prompted to opt-in and prompt if not
         *
         * @returns {promise} Promise that resolves when opt-in has
         */
        checkOptIn: function() {
            var defer = $q.defer();
            this.getDetail().then(function(optIn) {
                if (!optIn || !optIn.hasSubmitted) {
                    var openParams = {
                        templateUrl: '/asset/component/opt-in/2.0/webapp/html/optin-modal.html',
                        controller: 'OptInController',
                        backdrop: 'static',
                        keyboard: false
                    };
                    var optInModal = $modal.open(openParams);
                    optInModal.result.then(function() {
                        defer.resolve();
                    }, function(err) {
                        defer.reject(err);
                    });
                } else {
                    defer.resolve();
                }
            }, function(err) {
                defer.reject(err);
            });
            return defer.promise;
        },
        /**
         * @ngdoc method
         * @name getDetail
         * @methodOf opt-in-V2_0.service:qmOptInService
         * @description
         * Get detail of opt-in
         *
         * @returns {promise} Promise that resolves when opt-in detail has been retrieved
         */
        getDetail: function() {
            var url = qmWebAppService.getBaseRestUrl('component', 'opt-in') + '/optin';
            url += '?locale=' + qmLocalization.getLocale();
            var defer = $q.defer();
            // get optin data
            qmRest.get(url).then(function(optIn) {
                defer.resolve(optIn);
            }, function(err) {
                defer.reject(err);
                err.config.errorHandled = true;
            });

            return defer.promise;
        },
        /**
         * @ngdoc method
         * @name updateDetail
         * @methodOf opt-in-V2_0.service:qmOptInService
         * @description
         * Update opt-in detail
         *
         * @returns {promise} Promise that resolves when opt-in detail has been updated
         */
        updateDetail: function(detail) {
            var url = qmWebAppService.getBaseRestUrl('component', 'opt-in') + '/optin';
            var defer = $q.defer();
            // update optin data
            qmRest.put(url, detail).then(function() {
                defer.resolve();
            }, function(err) {
                defer.reject(err);
            });

            return defer.promise;
        }
    };
}]);

var quickmeetings = angular.module('quick-meetings-V2_0');

quickmeetings.run(['$rootScope', 'qmLogin', 'qmQuickMeetingsService', 'qmDependencyManager',
    function($rootScope, qmLogin, qmQuickMeetingsService, qmDependencyManager) {
    $rootScope.$on('refreshEventData', function() {
        if (qmLogin.isLoggedIn() && qmDependencyManager.hasComponent('quick-meetings')) {
            qmQuickMeetingsService.getNumberOfInvitesAndResponses(true);
        }
    });
}]);

quickmeetings.config(['$stateProvider', function($stateProvider) {
    $stateProvider
        .state('event.quick-meetings-V2_0', {
            url: '/QuickMeetings',
            views: {
                'component@event': {
                    templateUrl: '/asset/component/quick-meetings/2.0/webapp/html/quickmeetings-main-list.html',
                    controller: 'QuickMeetingsMainListController'
                },
                'header@event': {
                    controller: 'QuickMeetingsMainListHeaderController'
                }
            }
        })
        .state('event.quick-meetings-V2_0.create', {
            url: '/create',
            views: {
                'component@event': {
                    templateUrl: '/asset/component/quick-meetings/2.0/webapp/html/quickmeetings-create.html',
                    controller: 'QuickMeetingsCreateController'
                },
                'header@event': {
                    controller: 'QuickMeetingsCreateHeaderController'
                }
            }
        })
        .state('event.quick-meetings-V2_0.create.attendees', {
            url: '',
            views: {
                'component@event': {
                    templateUrl: '/asset/component/quick-meetings/2.0/webapp/html/quickmeetings-attendee-list.html',
                    controller: 'QuickMeetingsAttendeeSelectController'
                },
                'header@event': {
                    controller: 'QuickMeetingsAttendeeSelectHeaderController'
                }
            }
        })
        .state('event.quick-meetings-V2_0.invites', {
            url: '/invites',
            views: {
                'component@event': {
                    templateUrl: '/asset/component/quick-meetings/2.0/webapp/html/quickmeetings-invite-list.html',
                    controller: 'QuickMeetingsInviteListController'
                },
                'header@event': {
                    controller: 'QuickMeetingsInviteListHeaderController'
                }
            }
        })
        .state('event.quick-meetings-V2_0.schedule', {
            url: '/schedule',
            views: {
                'component@event': {
                    templateUrl: '/asset/component/quick-meetings/2.0/webapp/html/quickmeetings-schedule-list.html',
                    controller: 'QuickMeetingsScheduleListController'
                },
                'header@event': {
                    controller: 'QuickMeetingsScheduleListHeaderController'
                }
            }
        })
        .state('event.quick-meetings-V2_0.detail', {
            url: '/:meetingId',
            params: {
                inviteeId: null
            },
            views: {
                'component@event': {
                    controller: 'QuickMeetingsDetailController'
                }
            }
        })
        .state('event.quick-meetings-V2_0.detail.inviter', {
            url: '/inviter',
            views: {
                'component@event': {
                    templateUrl: '/asset/component/quick-meetings/2.0/webapp/html/quickmeetings-inviter-detail.html',
                    controller: 'QuickMeetingsInviterDetailController'
                },
                'header@event': {
                    controller: 'QuickMeetingsInviterDetailHeaderController'
                }
            }
        })
        .state('event.quick-meetings-V2_0.detail.invitee', {
            url: '/invitee',
            views: {
                'component@event': {
                    templateUrl: '/asset/component/quick-meetings/2.0/webapp/html/quickmeetings-invitee-detail.html',
                    controller: 'QuickMeetingsInviteeDetailController'
                },
                'header@event': {
                    controller: 'QuickMeetingsInviteeDetailHeaderController'
                }
            }
        })
        // state for declining meeting with message
        .state('event.quick-meetings-V2_0.detail.invitee.decline', {
            url: '/decline',
            views: {
                'component@event': {
                    templateUrl: '/asset/component/quick-meetings/2.0/webapp/html/quickmeetings-invitee-decline.html',
                    controller: 'QuickMeetingsInviteeDeclineController'
                },
                'header@event': {
                    controller: 'QuickMeetingsInviteeDeclineHeaderController'
                }
            }
        })
        .state('event.quick-meetings-V2_0.detail.cancel-acknowledge', {
            url: '/cancel-acknowledge',
            views: {
                'component@event': {
                    templateUrl: '/asset/component/quick-meetings/2.0/webapp/html/quickmeetings-cancel-acknowledge.html',
                    controller: 'QuickMeetingsCancelAcknowledgeController'
                },
                'header@event': {
                    controller: 'QuickMeetingsCancelAcknowledgeHeaderController'
                }
            }
        });
}]);

quickmeetings.run(['$rootScope', 'qmQuickMeetingsCreateDataService', 'qmQuickMeetingsAttendeeRouteService',
    function($rootScope, qmQuickMeetingsCreateDataService, qmQuickMeetingsAttendeeRouteService) {
    $rootScope.$on('$stateChangeSuccess', function(event, toState, toParams, fromState) {
        // Reset attendee list if we are navigating to QuickMeetings create view from another view that
        // is not a child of the create view or from the attendee detail view which uses the qmQuickMeetingsCreateDataService
        // Also initialize service if user navigates to create view through url
        if (toState.name.match(/^event\.quick-meetings(-V[0-9]+_[0-9]+)\.create$/) &&
            ((!fromState.name.match(/event\.quick-meetings(-V[0-9]+_[0-9]+)\.create/) &&    // not a child state of event.quick-meetings.create
            !fromState.name.match(/^event\.attendees(-V[0-9]+_[0-9]+)\.detail$/) ||
            !fromState.name))) {
            qmQuickMeetingsCreateDataService.init();
        } else if (toState.name.match(/^event\.quick-meetings(-V[0-9]+_[0-9]+)\.create$/) &&
            fromState.name.match(/^event\.attendees(-V[0-9]+_[0-9]+)\.detail$/)) {
            qmQuickMeetingsAttendeeRouteService.setFromAttendeeFlag(true);
        }
    });
}]);

var quickmeetings = angular.module('quick-meetings-V2_0');

/**
 * @ngdoc controller
 * @name quick-meetings-V2_0.controller:QuickMeetingsMainListController
 * @description
 * Controller for Quick Meetings main list view
 */
quickmeetings.controller('QuickMeetingsMainListController', ['$scope', 'qmList', 'qmLocalization', '$state', 'qmQuickMeetingsService',
function($scope, qmList, qmLocalization, $state, qmQuickMeetingsService) {
    var mainList = [
        {
            content: qmLocalization.getString('LABEL_INVITES_TITLE'),
            badge: '',
            state: 'event.quick-meetings.invites',
            class: 'fa fa-inbox'
        },
        {
            content: qmLocalization.getString('LABEL_MY_SCHEDULE'),
            state: 'event.quick-meetings.schedule',
            class: 'fa fa-calendar-o'
        }
    ];
    qmQuickMeetingsService.getNumberOfInvitesAndResponses(true).then(function(numberOfInvitesAndResponses) {
        if (numberOfInvitesAndResponses) {
            mainList[0].badge = numberOfInvitesAndResponses;
        }
    });
    $scope.mainList = qmList.transformList(mainList);
    $scope.$on('createQuickMeeting', function() {
        $state.go('event.quick-meetings.create');
    });
}]);

/**
 * @ngdoc controller
 * @name quick-meetings-V2_0.controller:QuickMeetingMainListHeaderController
 * @description
 * Controller for Quick Meetings main list header view
 */
quickmeetings.controller('QuickMeetingsMainListHeaderController', ['$scope', 'qmLocalization', '$rootScope', function($scope, qmLocalization, $rootScope) {
    $scope.headerTitle = qmLocalization.getString('componentQuickMeetingsTitle');

    $scope.headerRightElements = [];
    var createElement = {};
    createElement.type = 'icon';
    createElement.class = 'fa fa-user-plus';
    createElement.clickHandler = function() {
        $rootScope.$broadcast('createQuickMeeting');
    };
    $scope.headerRightElements.push(createElement);
}]);

var quickmeetings = angular.module('quick-meetings-V2_0');

/**
 * @ngdoc controller
 * @name quick-meetings-V2_0.controller:QuickMeetingsInviteListController
 * @description
 * Controller for Quick Meetings invites list view
 */
quickmeetings.controller('QuickMeetingsInviteListController', ['$scope', 'qmQuickMeetingsService', 'qmList', 'qmLocalization',
    '$q', function($scope, qmQuickMeetingsService, qmList, qmLocalization, $q) {
    $scope.loadInviteList = $q.all([qmQuickMeetingsService.getMyInvitesList(), qmQuickMeetingsService.getMyMeetingResponsesList()]);
    $scope.loadInviteList.then(function(data) {
        if (data[0].length === 0 && data[1].length === 0) {
            $scope.emptyList = true;
            $scope.emptyText = qmLocalization.getString('LABEL_EMPTY_QUICKMEETINGS_TITLE');
        } else {
            $scope.emptyList = false;
            $scope.invites = qmList.groupByGroup(data[0], 'groupValue', 'groupLabel');
            $scope.meetingResponses = qmList.groupByGroup(data[1], 'groupValue', 'groupLabel');
        }
    });
    $scope.invitesTitle = qmLocalization.getString('LABEL_MEETING_INVITES_TITLE');
    $scope.meetingResponsesTitle = qmLocalization.getString('LABEL_INVITE_RESPONSES_TITLE');

}]);

/**
 * @ngdoc controller
 * @name quick-meetings-V2_0.controller:QuickMeetingsInviteListHeaderController
 * @description
 * Controller for Quick Meetings invites list header view
 */
quickmeetings.controller('QuickMeetingsInviteListHeaderController', ['$scope', 'qmLocalization', function($scope, qmLocalization) {
    $scope.headerBackState = true;
    $scope.backState = 'event.quick-meetings';
    $scope.headerTitle = qmLocalization.getString('LABEL_INVITES_TITLE');
}]);

var quickmeetings = angular.module('quick-meetings-V2_0');

/**
 * @ngdoc controller
 * @name quick-meetings-V2_0.controller:QuickMeetingsDetailController
 * @description
 * Controller for Quick Meetings detail view which should direct to inviter or invitee view
 */
quickmeetings.controller('QuickMeetingsDetailController', ['$state', 'qmQuickMeetingsService', 'QUICKMEETINGS', 'qmLogin', '$rootScope', 'qmNavigation',
function($state, qmQuickMeetingsService, QUICKMEETINGS, qmLogin, $rootScope) {
    var meetingId = $state.params.meetingId;
    qmQuickMeetingsService.getMeetingDetails(meetingId).then(function(meetingDetails) {
        var currentAttendeeId = qmLogin.getUserInfo().attendeeId;
        if (meetingDetails.status === QUICKMEETINGS.meetingCancelled) {
            $state.go('event.quick-meetings.detail.cancel-acknowledge', null, {
                location: false,
                reload: true
            });
        } else {
            if (meetingDetails.attendeeId === currentAttendeeId) {
                $state.go('event.quick-meetings.detail.inviter');
            } else {
                $state.go('event.quick-meetings.detail.invitee');
            }
        }
    }, function() {
        $rootScope.$broadcast('resourceNotFound');
    });
}]);

/**
 * @ngdoc controller
 * @name quick-meetings-V2_0.controller:QuickMeetingsInviterDetailController
 * @description
 * Controller for Quick Meetings inviter detail view
 */
quickmeetings.controller('QuickMeetingsInviterDetailController',
['$scope', '$state', 'qmQuickMeetingsService', 'qmLocalization', 'qmQuickMeetingsDependencyService', '$q', 'QUICKMEETINGS', 'toastr', 'qmList',
function($scope, $state, qmQuickMeetingsService, qmLocalization, qmQuickMeetingsDependencyService, $q, QUICKMEETINGS, toastr, qmList) {
    var loadInviterDetailsDefer = $q.defer();
    var loadInviterDetailsPromise = loadInviterDetailsDefer.promise;
    $scope.loadInviterDetails = loadInviterDetailsPromise;
    qmQuickMeetingsService._quickMeetings = null;
    qmQuickMeetingsService.getMeetingDetails($state.params.meetingId).then(function(inviterDetails) {
        for (var i = 0; i < inviterDetails.attendeeLinks.length; i++) {
            // Translate status for view
            switch (inviterDetails.attendeeLinks[i].status) {
                case QUICKMEETINGS.meetingResponsePending:
                    inviterDetails.attendeeLinks[i].inviteeResponse = qmLocalization.getString('LABEL_PENDING');
                    break;
                case QUICKMEETINGS.meetingResponseAccepted:
                    inviterDetails.attendeeLinks[i].inviteeResponse = qmLocalization.getString('LABEL_ACCEPTED');
                    break;
                case QUICKMEETINGS.meetingResponseDeclined:
                    inviterDetails.attendeeLinks[i].inviteeResponse = qmLocalization.getString('LABEL_DECLINED');
                    if (inviterDetails.attendeeLinks[i].response && inviterDetails.attendeeLinks[i].response !== '') {
                        inviterDetails.attendeeLinks[i].inviteeResponseMsg = inviterDetails.attendeeLinks[i].response;
                    }
                    break;
                default:
                    break;
            }
            // mark responses as read if invitee has sent a response
            if (inviterDetails.attendeeLinks[i].status !== QUICKMEETINGS.meetingResponsePending) {
                qmQuickMeetingsService.markResponseRead($state.params.meetingId, inviterDetails.attendeeLinks[i].attendeeId);
                qmQuickMeetingsService.getNumberOfInvitesAndResponses();
            }
        }

        var qmAttendeeService = qmQuickMeetingsDependencyService.getAttendeeService('qmAttendeeService');
        var setting = qmAttendeeService.getSetting();
        $scope.showImage = setting.photo.isVisible;
        var attendees = inviterDetails.attendeeLinks;
        qmAttendeeService.getList(attendees).then(function(attendees) {
            var attendeesMap = {};
            attendees.forEach(function(attendee) {
                attendeesMap[attendee.attendeeId] = attendee;
            });
            inviterDetails.attendeeLinks.forEach(function(attendeeLink) {
                if (attendeeLink.attendeeId in attendeesMap) {
                    var attendee = attendeesMap[attendeeLink.attendeeId];
                    attendeeLink.inviteeName = attendee.firstName + ' ' + attendee.lastName;
                }
            });
            inviterDetails.attendeeLinks = qmList.transformList(inviterDetails.attendeeLinks);
            loadInviterDetailsDefer.resolve(inviterDetails);
        }, function() {
            loadInviterDetailsDefer.reject();
        });
    });
    $scope.loadInviterDetails.then(function(details) {
        $scope.inviterDetails = details;
    });
    $scope.inviteeListTitle = qmLocalization.getString('LABEL_INVITEES');

    // confirmation dialog for cancelling meeting
    $scope.confirmTitle = qmLocalization.getString('BUTTON_CANCEL');
    $scope.confirmContent = qmLocalization.getString('ALERT_QUESTION_CANCEL_MEETING_MESSAGE');
    $scope.confirmNo = qmLocalization.getString('LABEL_NO');
    $scope.confirmYes = qmLocalization.getString('LABEL_YES');
    $scope.cancelMeeting = function() {
        return qmQuickMeetingsService.cancelMeeting($state.params.meetingId).then(function() {
            $state.go('event.quick-meetings.schedule');
            toastr.success(qmLocalization.getString('ALERT_CANCEL_MEETING_MESSAGE'), '', {
                closeButton: true,
                timeOut: 5000
            });
        });
    };
}]);

/**
 * @ngdoc controller
 * @name quick-meetings-V2_0.controller:QuickMeetingsInviterDetailHeaderController
 * @description
 * Controller for Quick Meetings inviter detail header view
 */
quickmeetings.controller('QuickMeetingsInviterDetailHeaderController', ['$scope', 'qmLocalization', function($scope, qmLocalization) {
    $scope.headerBackState = true;
    $scope.backState = 'event.quick-meetings.schedule';
    $scope.headerTitle = qmLocalization.getString('LABEL_DETAILS');
}]);

/**
 * @ngdoc controller
 * @name quick-meetings-V2_0.controller:QuickMeetingsInviteeDetailController
 * @description
 * Controller for Quick Meetings invitee detail view
 */
quickmeetings.controller('QuickMeetingsInviteeDetailController',
['$scope', '$state', 'qmQuickMeetingsService', 'qmLocalization', 'qmQuickMeetingsDependencyService', '$q', 'QUICKMEETINGS', 'toastr',
function($scope, $state, qmQuickMeetingsService, qmLocalization, qmQuickMeetingsDependencyService, $q, QUICKMEETINGS, toastr) {
    var loadInviteeDetailsDefer = $q.defer();
    var loadInviteeDetailsPromise = loadInviteeDetailsDefer.promise;
    $scope.loadInviteeDetails = loadInviteeDetailsPromise;
    qmQuickMeetingsService.getMeetingDetails($state.params.meetingId).then(function(inviteeDetails) {
        // Translate status for view
        switch (inviteeDetails.attendeeLink.status) {
            case QUICKMEETINGS.meetingResponsePending:
                inviteeDetails.myResponse = qmLocalization.getString('LABEL_PENDING');
                break;
            case QUICKMEETINGS.meetingResponseAccepted:
                inviteeDetails.myResponse = qmLocalization.getString('LABEL_ACCEPTED');
                break;
            case QUICKMEETINGS.meetingResponseDeclined:
                inviteeDetails.myResponse = qmLocalization.getString('LABEL_DECLINED');
                break;
            default:
                break;
        }

        qmQuickMeetingsService.getNumberOfInvitesAndResponses();
        qmQuickMeetingsService.markInviteRead($state.params.meetingId);

        var qmAttendeeService = qmQuickMeetingsDependencyService.getAttendeeService('qmAttendeeService');
        // Get attendee name and populate for view
        qmAttendeeService.getDetail(inviteeDetails.attendeeId).then(function(attendee) {
            inviteeDetails.inviterName = attendee.firstName + ' ' + attendee.lastName;
            loadInviteeDetailsDefer.resolve(inviteeDetails);
        }, function() {
            loadInviteeDetailsDefer.reject();
        });
    });
    $scope.loadInviteeDetails.then(function(inviteeDetails) {
        $scope.inviteeDetails = inviteeDetails;
        // if meeting has already been accepted, disable the accept button
        $scope.isAcceptDisabled = (inviteeDetails.attendeeLink.status === QUICKMEETINGS.meetingResponseAccepted);
        $scope.isDeclineDisabled = (inviteeDetails.attendeeLink.status === QUICKMEETINGS.meetingResponseDeclined &&
                                    inviteeDetails.attendeeLink.response === '');
        $scope.isDeclineWithMsgDisabled = (inviteeDetails.attendeeLink.status === QUICKMEETINGS.meetingResponseDeclined &&
                                            inviteeDetails.attendeeLink.response !== '');
    });

    $scope.acceptInvite = function() {
        qmQuickMeetingsService.acceptInvite($state.params.meetingId).then(function() {
            $state.reload();
            toastr.success(qmLocalization.getString('ALERT_SENT_MEETING_RESPONSE_MESSAGE'), '', {
                closeButton: true,
                timeOut: 5000
            });
            qmQuickMeetingsService.markInviteUnread($state.params.meetingId);
        });
    };

    $scope.declineInvite = function() {
        qmQuickMeetingsService.declineInvite($state.params.meetingId).then(function() {
            $state.reload();
            toastr.success(qmLocalization.getString('ALERT_SENT_MEETING_RESPONSE_MESSAGE'), '', {
                closeButton: true,
                timeOut: 5000
            });
        });
    };

    $scope.declineWithMsgInvite = function() {
        $state.go('event.quick-meetings.detail.invitee.decline', null, {
            location: false,
            reload: true
        });
    };
}]);

/**
 * @ngdoc controller
 * @name quick-meetings-V2_0.controller:QuickMeetingsInviteeDetailHeaderController
 * @description
 * Controller for Quick Meetings invitee detail header view
 */
quickmeetings.controller('QuickMeetingsInviteeDetailHeaderController', ['$scope', 'qmLocalization', function($scope, qmLocalization) {
    $scope.headerBackState = true;
    $scope.headerTitle = qmLocalization.getString('LABEL_DETAILS');
}]);

/**
 * @ngdoc controller
 * @name quick-meetings-V2_0.controller:QuickMeetingsInviteeDeclineController
 * @description
 * Controller for Quick Meetings decline invite view
 */
quickmeetings.controller('QuickMeetingsInviteeDeclineController', ['$scope', '$state', 'qmQuickMeetingsService', 'toastr', 'qmLocalization',
function($scope, $state, qmQuickMeetingsService, toastr, qmLocalization) {
    $scope.confirm = {
        no: qmLocalization.getString('ALERT_NO'),
        yes: qmLocalization.getString('ALERT_YES'),
        title: qmLocalization.getString('ALERT_WARNING_TITLE'),
        content: qmLocalization.getString('ALERT_QUICKMEETINGS_RESPONSE_CLOSE_WITHOUT_SAVING_MESSAGE'),
        isEmpty: function() {
            return !$scope.declineMsg;
        },
        isSubmitting: function() {
            return ($scope.sendingResponse === true);
        }
    };
    $scope.$on('sendMeetingDeclineResponse', function() {
        $scope.sendingResponse = true;
        qmQuickMeetingsService.declineWithMsgInvite($state.params.meetingId, $scope.declineMsg).then(function() {
            if ($scope.declineMsg) {
                $state.go('event.quick-meetings.detail');
                toastr.success(qmLocalization.getString('ALERT_SENT_MEETING_RESPONSE_MESSAGE'), '', {
                    closeButton: true,
                    timeOut: 5000
                });
            }
        },
        function() {
            $scope.sendingResponse = false;
        });
    });
}]);

/**
 * @ngdoc controller
 * @name quick-meetings-V2_0.controller:QuickMeetingsInviteeDeclineHeaderController
 * @description
 * Controller for Quick Meetings decline invite header view
 */
quickmeetings.controller('QuickMeetingsInviteeDeclineHeaderController', ['$scope', 'qmLocalization', '$rootScope',
function($scope, qmLocalization, $rootScope) {
    $scope.headerCancelState = '^';
    $scope.headerCancelText = qmLocalization.getString('BUTTON_CANCEL');
    $scope.headerTitle = qmLocalization.getString('LABEL_DECLINE_MEETING_TITLE');
    $scope.headerRightElements = [];

    var sendElement = {};
    sendElement.type = 'text';
    sendElement.text = qmLocalization.getString('BUTTON_SEND');
    sendElement.clickHandler = function() {
        $rootScope.$broadcast('sendMeetingDeclineResponse');
    };
    $scope.headerRightElements.push(sendElement);
}]);

/**
 * @ngdoc controller
 * @name quick-meetings-V2_0.controller:QuickMeetingsCancelAcknowledgeController
 * @description
 * Controller for Quick Meetings cancel acknowledge detail view
 */
quickmeetings.controller('QuickMeetingsCancelAcknowledgeController',
['$scope', '$state', 'qmQuickMeetingsService', '$q', 'qmQuickMeetingsDependencyService',
function($scope, $state, qmQuickMeetingsService, $q, qmQuickMeetingsDependencyService) {
    var loadMeetingDetailsDefer = $q.defer();
    var loadMeetingDetailsPromise = loadMeetingDetailsDefer.promise;
    $scope.loadMeetingDetails = loadMeetingDetailsPromise;
    qmQuickMeetingsService.getMeetingDetails($state.params.meetingId).then(function(meetingDetails) {
        var qmAttendeeService = qmQuickMeetingsDependencyService.getAttendeeService('qmAttendeeService');
        // Get attendee name and populate for view
        qmAttendeeService.getDetail(meetingDetails.attendeeId).then(function(attendee) {
            meetingDetails.inviterName = attendee.firstName + ' ' + attendee.lastName;
            loadMeetingDetailsDefer.resolve(meetingDetails);
        }, function() {
            loadMeetingDetailsDefer.reject();
        });
    });
    $scope.loadMeetingDetails.then(function(meetingDetails) {
        qmQuickMeetingsService.markCancelledRead($state.params.meetingId);
        $scope.meetingDetails = meetingDetails;
    });
    $scope.cancelAcknowledge = function() {
        qmQuickMeetingsService.cancelAcknowledge($state.params.meetingId).then(function() {
            $state.go('event.quick-meetings.invites');
        });
    };
}]);

/**
 * @ngdoc controller
 * @name quick-meetings-V2_0.controller:QuickMeetingsCancelAcknowledgeHeaderController
 * @description
 * Controller for Quick Meetings cancel acknowledge detail header view
 */
quickmeetings.controller('QuickMeetingsCancelAcknowledgeHeaderController', ['$scope', 'qmLocalization', function($scope, qmLocalization) {
    $scope.headerBackState = true;
    $scope.headerTitle = qmLocalization.getString('LABEL_DETAILS');
}]);

var quickmeetings = angular.module('quick-meetings-V2_0');

/**
 * @ngdoc controller
 * @name quick-meetings-V2_0.controller:QuickMeetingsScheduleListController
 * @description
 * Controller for Quick Meetings schedule list view
 */
quickmeetings.controller('QuickMeetingsScheduleListController', ['$scope', 'qmQuickMeetingsService', 'qmList', 'qmLocalization', '$q',
    'qmQuickMeetingsDependencyService', 'qmMoment', 'qmEventConfig', 'qmLogin', 'qmEventsService',
    function($scope, qmQuickMeetingsService, qmList, qmLocalization, $q, qmQuickMeetingsDependencyService, qmMoment, qmEventConfig, qmLogin, qmEventsService) {
        var getMyScheduleSessions = function() {
            if (qmQuickMeetingsDependencyService.hasComponent('myschedule')) {
                var qmMyScheduleService = qmQuickMeetingsDependencyService.getMyScheduleService('qmMyScheduleService');
                return qmMyScheduleService.getList();
            } else {
                var defer = $q.defer();
                defer.resolve([]);
                return defer.promise;
            }
        };

        var qmEventsDependencyService = qmQuickMeetingsDependencyService.getEventsService('qmEventsDependencyService');
        var qmSocialCheck = qmEventsDependencyService.getSocialService('qmSocialService');
        var loadMyScheduleMeetings = qmQuickMeetingsService.getMySchedule();
        var loadMyScheduleSessions = getMyScheduleSessions();
        var socialConfig;
        var likesEnabled;
        var commentsEnabled;
        $scope.eventListItemUrl = qmEventsDependencyService.getTemplateUrl('event-list-item');

        if (qmSocialCheck) {
            socialConfig = qmEventConfig.getComponentConfig('social');
            likesEnabled = socialConfig['@attributes'].sessionLikes === 'true' ? true : false;
            commentsEnabled = socialConfig['@attributes'].sessionComments === 'true' ? true : false;
            $scope.enableLikes = likesEnabled && qmLogin.isLoggedIn();
            $scope.enableComments = commentsEnabled && qmLogin.isLoggedIn();
        }
        $scope.loadMySchedule = $q.all([loadMyScheduleMeetings, loadMyScheduleSessions]);
        $scope.loadMySchedule.then(function(schedules) {
            qmEventsService.getEventsLike(schedules[1]);
            var meetings = schedules[0].map(function(meeting) {
                var meetingCopy = angular.copy(meeting);
                meetingCopy.startDateTime = new Date(meeting.startTime * 1000);
                meetingCopy.endDateTime = new Date(meeting.endTime * 1000);
                return meetingCopy;
            });
            var sessions = schedules[1].map(function(session) {
                var startMoment = qmMoment(session.startDateTime);
                var endMoment = qmMoment(session.endDateTime);
                session.groupValue = session.startDateTime.slice(0, 10);
                session.startDateTime = startMoment;
                session.endDateTime = endMoment;
                session.groupLabel = startMoment.format('dddd LL');
                session.timeRange = startMoment.format('LT') + ' - ' + endMoment.format('LT');

                return session;
            });
            var schedule = meetings.concat(sessions);
            schedule.sort(function(item1, item2) {
                var numCmp = function(a, b) {
                    return (a || b) ? (!a ? -1 : !b ? 1 : (a - b)) : 0;
                };
                var localeCmp = function(a, b) {
                    return (a || b) ? (!a ? -1 : !b ? 1 : a.localeCompare(b)) : 0;
                };
                return (
                    localeCmp(item1.groupValue, item2.groupValue) ||
                    numCmp(item1.startDateTime, item2.startDateTime) ||
                    numCmp(item1.endDateTime, item2.endDateTime) ||
                    localeCmp(item1.title, item2.title)
                );
            });
            $scope.schedule = qmList.groupByGroup(schedule, 'groupValue', 'groupLabel');
            var componentTitle = qmLocalization.getString('componentQuickMeetingsTitle');
            $scope.emptyText = qmLocalization.getString('LABEL_EMPTY_MY_SCHEDULE_TITLE', componentTitle);
        });
    }
]);

/**
 * @ngdoc controller
 * @name quick-meetings-V2_0.controller:QuickMeetingsScheduleListHeaderController
 * @description
 * Controller for Quick Meetings schedule list header view
 */
quickmeetings.controller('QuickMeetingsScheduleListHeaderController', ['$scope', 'qmLocalization', function($scope, qmLocalization) {
    $scope.headerBackState = true;
    $scope.backState = 'event.quick-meetings';
    $scope.headerTitle = qmLocalization.getString('LABEL_MY_SCHEDULE');
}]);

var quickmeetings = angular.module('quick-meetings-V2_0');

/**
 * @ngdoc controller
 * @name quick-meetings-V2_0.controller:QuickMeetingsCreateController
 * @description
 * Controller for Quick Meetings create view
 */
quickmeetings.controller('QuickMeetingsCreateController',
['$scope', 'qmQuickMeetingsService', 'qmQuickMeetingsCreateDataService', 'qmList', 'qmQuickMeetingsDependencyService',
    '$state', 'toastr', 'qmLocalization', 'qmQuickMeetingsAttendeeRouteService',
function($scope, qmQuickMeetingsService, qmQuickMeetingsCreateDataService, qmList, qmQuickMeetingsDependencyService,
$state, toastr, qmLocalization, qmQuickMeetingsAttendeeRouteService) {
    $scope.today = qmQuickMeetingsCreateDataService.getToday();
    $scope.dateFormat = 'yyyy-MM-dd';
    $scope.inviteesTitle = qmLocalization.getString('LABEL_INVITEES');

    // Should we display attendee images ?
    var qmAttendeeService = qmQuickMeetingsDependencyService.getAttendeeService('qmAttendeeService');
    var setting = qmAttendeeService.getSetting();
    $scope.showImage = setting.photo.isVisible;

    $scope.openStartDate = function($event) {
        $event.preventDefault();
        $event.stopPropagation();
        $scope.openedStartDate = true;
    };
    $scope.openEndDate = function($event) {
        $event.preventDefault();
        $event.stopPropagation();
        $scope.openedEndDate = true;
    };
    $scope.removeAttendee = function(index) {
        qmQuickMeetingsCreateDataService.removeAttendee(index);
        var attendees = qmQuickMeetingsCreateDataService.getAttendees();
        // if selectedAttendees does not have anything in the list, set it back to undefined so that the container surrounding it does not appear
        if (attendees && attendees.length === 0) {
            $scope.selectedAttendees = undefined;
        }
    };

    $scope.title = qmQuickMeetingsCreateDataService.getTitle();
    $scope.$watch('title', function(newValue) {
        qmQuickMeetingsCreateDataService.setTitle(newValue);
    });
    $scope.location = qmQuickMeetingsCreateDataService.getLocation();
    $scope.$watch('location', function(newValue) {
        qmQuickMeetingsCreateDataService.setLocation(newValue);
    });
    $scope.startDate = qmQuickMeetingsCreateDataService.getStartDate();
    $scope.$watch('startDate', function(newValue) {
        qmQuickMeetingsCreateDataService.setStartDate(newValue);
    });
    $scope.endDate = qmQuickMeetingsCreateDataService.getEndDate();
    $scope.$watch('endDate', function(newValue) {
        qmQuickMeetingsCreateDataService.setEndDate(newValue);
    });
    $scope.startTime = qmQuickMeetingsCreateDataService.getStartTime();
    $scope.$watch('startTime', function(newValue) {
        qmQuickMeetingsCreateDataService.setStartTime(newValue);
    });
    $scope.endTime = qmQuickMeetingsCreateDataService.getEndTime();
    $scope.$watch('endTime', function(newValue) {
        qmQuickMeetingsCreateDataService.setEndTime(newValue);
    });

    var selectedAttendees = qmQuickMeetingsCreateDataService.getAttendees();
    if (selectedAttendees && selectedAttendees.length) {
        $scope.selectedAttendees = qmList.transformList(selectedAttendees);
    }

    $scope.$on('submitQuickMeeting', function() {
        // todo: need better error checking
        var hasError = false;
        var errorMessage = null;
        if ($scope.title) {
            $scope.missingTitle = false;
        } else {
            $scope.missingTitle = true;
            hasError = true;
            errorMessage = qmLocalization.getString('MESSAGE_MISSING_FIELDS');
        }
        if ($scope.location) {
            $scope.missingLocation = false;
        } else {
            $scope.missingLocation = true;
            hasError = true;
            errorMessage = qmLocalization.getString('MESSAGE_MISSING_FIELDS');
        }
        if ($scope.startDate && $scope.startDate >= $scope.today) {
            $scope.invalidStartDate = false;
        } else {
            $scope.invalidStartDate = true;
            hasError = true;
            errorMessage = qmLocalization.getString('MESSAGE_INCONSISTENT_TIME');
        }
        if ($scope.endDate && $scope.endDate >= $scope.today) {
            $scope.invalidEndDate = false;
        } else {
            $scope.invalidEndDate = true;
            hasError = true;
            errorMessage = qmLocalization.getString('MESSAGE_INCONSISTENT_TIME');
        }
        if ($scope.startTime) {
            $scope.invalidStartTime = false;
        } else {
            $scope.invalidStartTime = true;
            hasError = true;
            errorMessage = qmLocalization.getString('MESSAGE_INCONSISTENT_TIME');
        }
        if ($scope.endTime) {
            $scope.invalidEndTime = false;
        } else {
            $scope.invalidEndTime = true;
            hasError = true;
            errorMessage = qmLocalization.getString('MESSAGE_INCONSISTENT_TIME');
        }
        $scope.invalidTime = false;
        if (!$scope.invalidStartDate && !$scope.invalidEndDate && !$scope.invalidStartTime && !$scope.invalidEndTime) {
            if (($scope.startDate > $scope.endDate) ||
                ($scope.startDate.getTime() === $scope.endDate.getTime() && $scope.startTime > $scope.endTime)) {
                $scope.invalidTime = true;
                hasError = true;
                errorMessage = qmLocalization.getString('MESSAGE_INCONSISTENT_TIME');
            }
        }
        if ($scope.selectedAttendees && $scope.selectedAttendees.length) {
            $scope.missingAttendees = false;
        } else {
            $scope.missingAttendees = true;
            hasError = true;
            errorMessage = qmLocalization.getString('MESSAGE_MISSING_FIELDS');
        }

        if (hasError) {
            if (errorMessage) {
                toastr.error(errorMessage, '', {
                    closeButton: true,
                    timeOut: 5000,
                    extendedTimeOut: 5000
                });
            }
            return;
        }

        var title = $scope.title;
        var location = $scope.location;

        var formattedStartTime = (
            $scope.startDate.getFullYear() + '-' + ($scope.startDate.getMonth() + 1) + '-' + $scope.startDate.getDate() + ' ' +
            $scope.startTime.getHours() + ':' + $scope.startTime.getMinutes() + ':00');
        var formattedEndTime = (
            $scope.endDate.getFullYear() + '-' + ($scope.endDate.getMonth() + 1) + '-' + $scope.endDate.getDate() + ' ' +
            $scope.endTime.getHours() + ':' + $scope.endTime.getMinutes() + ':00');

        var attendeeIds = [];
        if ($scope.selectedAttendees) {
            for (var i = 0; i < $scope.selectedAttendees[0]['items'].length; i++) {
                attendeeIds.push($scope.selectedAttendees[0]['items'][i].attendeeId);
            }
        }

        qmQuickMeetingsService.create(title, location, formattedStartTime, formattedEndTime, attendeeIds).then(function() {
            var fromAttendeeFlag = qmQuickMeetingsAttendeeRouteService.getFromAttendeeFlag();
            if (fromAttendeeFlag) {
                $state.go('event.attendees.detail', {'attendeeId':attendeeIds[0]});
                qmQuickMeetingsAttendeeRouteService.setFromAttendeeFlag(false);
            } else {
                $state.go('event.quick-meetings.schedule');
            }

            toastr.success(qmLocalization.getString('LABEL_SENT_CONFIRMATION'), '', {
                closeButton: true,
                timeOut: 5000
            });
        });
    });
}]);

/**
 * @ngdoc controller
 * @name quick-meetings-V2_0.controller:QuickMeetingsCreateHeaderController
 * @description
 * Controller for Quick Meetings create header view
 */
quickmeetings.controller('QuickMeetingsCreateHeaderController', ['$scope', 'qmLocalization', '$rootScope',
    function($scope, qmLocalization, $rootScope) {

    $scope.headerTitle = qmLocalization.getString('LABEL_NEW_MEETING_TITLE');
    $scope.headerCancelState = 'event.quick-meetings';
    $scope.headerCancelText = qmLocalization.getString('BUTTON_CANCEL');

    $scope.headerRightElements = [];
    var sendElement = {};
    sendElement.type = 'text';
    sendElement.text = qmLocalization.getString('BUTTON_SEND');
    sendElement.clickHandler = function() {
        $rootScope.$broadcast('submitQuickMeeting');
    };
    $scope.headerRightElements.push(sendElement);
}]);

/**
 * @ngdoc controller
 * @name quick-meetings-V2_0.controller:QuickMeetingsAttendeeSelectController
 * @description
 * Controller for Quick Meetings attendee select view
 */
quickmeetings.controller('QuickMeetingsAttendeeSelectController',
['$scope', 'qmQuickMeetingsDependencyService', 'qmList', 'qmQuickMeetingsCreateDataService', '$state', '$q', 'qmLogin', 'toastr', 'qmLocalization',
    function($scope, qmQuickMeetingsDependencyService, qmList, qmQuickMeetingsCreateDataService, $state, $q, qmLogin, toastr, qmLocalization) {
    var qmAttendeeService = qmQuickMeetingsDependencyService.getAttendeeService('qmAttendeeService');
    var qmAttendeeDependencyService = qmQuickMeetingsDependencyService.getAttendeeService('qmAttendeeDependencyService');

    $scope.attendeeListItemUrl = qmAttendeeDependencyService.getTemplateUrl('attendee-list-item');
    $scope.setting = qmAttendeeService.getSetting();
    $scope.showImage = $scope.setting.photo.isVisible;

    $scope.loadAttendees = function(filter, itemOffset, moreItemsNum) {
        var defer = $q.defer();
        qmAttendeeService.getList([], filter, itemOffset, moreItemsNum).then(function(data) {
            var currentAttendeeId = qmLogin.getUserInfo().attendeeId;
            var filteredData = data.filter(function(attendee) {
                return (attendee.attendeeId !== currentAttendeeId);
            });
            var attendees = qmList.groupByLetter(filteredData, 'lastName');
            defer.resolve(attendees);
        }, function() {
            defer.reject();
        });
        return defer.promise;
    };
    $scope.addAttendee = function(attendee) {
        if (!qmQuickMeetingsCreateDataService.addAttendee(attendee)) {
            toastr.warning(qmLocalization.getString('ALERT_QUICKMEETINGS_MESSAGE_PERSON_IN_LIST'), '', {
                closeButton: true,
                timeOut: 5000
            });
        } else {
            $state.go('^');
        }
    };
}]);

/**
 * @ngdoc controller
 * @name quick-meetings-V2_0.controller:QuickMeetingsAttendeeSelectHeaderController
 * @description
 * Controller for Quick Meetings attendee select header view
 */
quickmeetings.controller('QuickMeetingsAttendeeSelectHeaderController', ['$scope', 'qmLocalization', function($scope, qmLocalization) {
    $scope.headerCancelState = '^';
    $scope.headerCancelText = qmLocalization.getString('BUTTON_CANCEL');
    $scope.headerTitle = qmLocalization.getString('LABEL_SELECT_TITLE');
}]);

var quickmeetings = angular.module('quick-meetings-V2_0');

/**
 * @ngdoc service
 * @name quick-meetings-V2_0.service:qmQuickMeetingsService
 * @description
 * Manages REST API communication for Quick Meetings
 */
quickmeetings.factory('qmQuickMeetingsService', ['qmRpc', 'qmEventConfig', 'qmLogin', '$q', 'qmMoment',
    'qmLocalStorage', 'QUICKMEETINGS', '$rootScope',
function(qmRpc, qmEventConfig, qmLogin, $q, qmMoment,
         qmLocalStorage, QUICKMEETINGS, $rootScope) {
    /**
     * Formats a meeting timestamp
     * @param {Number} time stamp
     * @param {string} format
     * @return {string} formatted date and time
     */
    var formatTimestamp = function(time, format) {
        return qmMoment(new Date(time * 1000)).format(format);
    };
    /**
     * Formats a time range
     * @param {Number} meetingStartTime
     * @param {Number} meetingEndTime
     * @return {string} start time - end time
     */
    var formatTimeRange = function(meetingStartTime, meetingEndTime) {
        var formattedStartTime = formatTimestamp(meetingStartTime, 'LT');
        var formattedEndTime = formatTimestamp(meetingEndTime, 'LT');
        var timeRange = formattedStartTime + ' - ' + formattedEndTime;
        return timeRange;
    };
    /**
     * Formats a meeting date
     * @param {string} meetingDate
     * @return {string} start time - end time
     */
    var formatDate = function(meetingDate) {
        // return qmMoment(meetingDate).format('dddd LL');
        return qmMoment(meetingDate).format('dddd LL');
    };
    /**
     * Returns true if meeting response is read
     * @param {string} meetingId
     * @param {string} attendeeId of invitee
     * @return {boolean} true if meeting response is read, else false
     */
    var hasReadMeetingResponse = function(meetingId, attendeeId) {
        var obj = qmLocalStorage.getItem('readMeetingResponses');
        var meetings = obj ? obj : {};
        return (meetingId in meetings) && (attendeeId in meetings[meetingId]);
    };
    /**
     * Returns true if meeting invite is read
     * @param {string} meetingId
     * @return {boolean} true if meeting invite is read, else false
     */
    var hasReadMeetingInvite = function(meetingId) {
        var obj = qmLocalStorage.getItem('readMeetingInvites');
        var meetings = obj ? obj : {};
        return (meetingId in meetings);
    };
    /**
     * Returns true if cancelled meeting invite is read
     * @param {string} meetingId
     * @return {boolean} true if meeting invite is read, else false
     */
    var hasReadCancelledMeetingInvite = function(meetingId) {
        var obj = qmLocalStorage.getItem('readCancelledMeetingInvites');
        var meetings = obj ? obj : {};
        return (meetingId in meetings);
    };
    /**
     * Filter meetings for those that are invitations to the current user
     * @param {object} response from the getMyMeetings RPC
     * @param {string} currentAttendeeId
     * @return {Array} invites
     */
    var getMeetingInvites = function(response, currentAttendeeId) {
        var invites = [];
        var attendeeMeetingLinks = response.AttendeeMeetingLinks;
        var meetings = response.Meetings;
        for (var i = 0; i < meetings.length; i++) {
            for (var j = 0; j < attendeeMeetingLinks.length; j++) {
                if ((meetings[i].meetingId === attendeeMeetingLinks[j].meetingId) &&
                    (meetings[i].qmActive === '1' && attendeeMeetingLinks[j].qmActive === '1' &&
                    meetings[i].attendeeId !== currentAttendeeId &&
                    attendeeMeetingLinks[j].attendeeId === currentAttendeeId) &&
                    ((meetings[i].status === QUICKMEETINGS.meetingActivated &&
                        attendeeMeetingLinks[j].status === QUICKMEETINGS.meetingResponsePending) ||
                    (meetings[i].status === QUICKMEETINGS.meetingCancelled &&
                        attendeeMeetingLinks[j].status === QUICKMEETINGS.meetingResponseAccepted))) {
                    meetings[i].inviteeStatus = attendeeMeetingLinks[j].status;
                    invites.push(meetings[i]);
                    break;
                }
            }
        }
        return invites;
    };
    /**
     * Filter meetings for those that are responses to invites from the current user
     * @param {object} response from the getMyMeetings RPC
     * @param {string} currentAttendeeId
     * @return {Array} inviteResponses
     */
    var getInviteResponses = function(response, currentAttendeeId) {
        var inviteResponses = [];
        var attendeeMeetingLinks = response.AttendeeMeetingLinks;
        var meetings = response.Meetings;
        for (var i = 0; i < meetings.length; i++) {
            for (var j = 0; j < attendeeMeetingLinks.length; j++) {
                if ((meetings[i].meetingId === attendeeMeetingLinks[j].meetingId) && (attendeeMeetingLinks[j].ownerHasViewedResponse === '0') &&
                    (meetings[i].qmActive === '1' && attendeeMeetingLinks[j].qmActive === '1' &&
                    meetings[i].attendeeId === currentAttendeeId && attendeeMeetingLinks[j].attendeeId !== currentAttendeeId) &&
                    (meetings[i].status === QUICKMEETINGS.meetingActivated &&
                        attendeeMeetingLinks[j].status !== QUICKMEETINGS.meetingResponsePending) &&
                    (!hasReadMeetingResponse(meetings[i].meetingId, attendeeMeetingLinks[j].attendeeId))) {
                    inviteResponses.push({meeting: meetings[i], attendeeMeetingLink: attendeeMeetingLinks[j]});
                    break;
                }
            }
        }
        return inviteResponses;
    };
    var _quickMeetings = null;
    return {
        /**
         * @ngdoc method
         * @name create
         * @methodOf quick-meetings-V2_0.service:qmQuickMeetingsService
         * @description
         * Create a new quick meeting
         *
         * @param {string} title Title given to meeting
         * @param {string} location Location of meeting
         * @param {string} startTime Start date and time of event (YYYY-MM-DD HH::mm)
         * @param {string} endTime End date and time of event (YYYY-MM-DD HH::mm)
         * @param {Array} attendees Array of attendee ids invited to meeting
         * @returns {promise} Promise that resolves when quick meeting has successfully been created
         */
        create: function(title, location, startTime, endTime, attendees) {
            var defer = $q.defer();
            var createMeetingUrl = qmEventConfig.getRpcUrl('createMeeting');
            var params = [];
            params.push(qmLogin.getUserToken());
            params.push(title);
            params.push(startTime);
            params.push(endTime);
            params.push('');
            params.push(location);
            params.push(QUICKMEETINGS.meetingActivated);
            params.push(attendees);
            params.push('Attendee');
            qmRpc.post(createMeetingUrl, {method: 'createMeeting', params: params}).then(function(response) {
                defer.resolve(response);
            }, function(err) {
                defer.reject(err);
            });
            return defer.promise;
        },
        /**
         * @ngdoc method
         * @name getMeetings
         * @methodOf quick-meetings-V2_0.service:qmQuickMeetingsService
         * @description
         * Get a list of Meetings
         *
         * @returns {promise} Promise that resolves when meetings list have been retrieved
         */
        getMeetings: function() {
            var defer = $q.defer();
            var getMyMeetingsUrl = qmEventConfig.getRpcUrl('getMyMeetings');
            var params = [];
            params.push(qmLogin.getUserToken());

            qmRpc.post(getMyMeetingsUrl, {method: 'getMyMeetings', params: params}).then(function(response) {
                _quickMeetings = response;
                defer.resolve(response);
            }, function(err) {
                defer.reject(err);
            });
            return defer.promise;
        },
        /**
         * @ngdoc method
         * @name getMySchedule
         * @methodOf quick-meetings-V2_0.service:qmQuickMeetingsService
         * @description
         * Get a list of my quickmeetings and my schedule
         *
         * @returns {promise} Promise that resolves when invites list have been retrieved
         */
        getMySchedule: function() {
            var defer = $q.defer();
            this.getMeetings().then(function(response) {
                var schedule = [];
                var currentAttendeeId = qmLogin.getUserInfo().attendeeId;
                var attendeeMeetingLinks = response.AttendeeMeetingLinks;
                var meetings = response.Meetings;
                // To get my quickmeetings schedule list we must filter the meetings object based on conditions from the
                // attendeeMeetingLinks object that receive from the rpc call
                for (var i = 0; i < meetings.length; i++) {
                    // if user is inviter of the meeting and it is still activated add to quickmeetings schedule list
                    if (meetings[i].qmActive === '1' && meetings[i].attendeeId === currentAttendeeId &&
                        meetings[i].status === QUICKMEETINGS.meetingActivated) {
                        meetings[i].timeRange = formatTimeRange(meetings[i].startTime, meetings[i].endTime);
                        meetings[i].groupValue = meetings[i].meetingDate;
                        meetings[i].groupLabel = formatDate(meetings[i].meetingDate);
                        schedule.push(meetings[i]);
                    } else {
                        // if user is invitee of meeting that is activated and has accepted the invitation then add to quickmeetings schedule list
                        for (var j = 0; j < attendeeMeetingLinks.length; j++) {
                            if ((meetings[i].meetingId === attendeeMeetingLinks[j].meetingId) &&
                                (meetings[i].qmActive === '1' && attendeeMeetingLinks[j].qmActive === '1') &&
                                (attendeeMeetingLinks[j].attendeeId === currentAttendeeId) &&
                                (meetings[i].status === QUICKMEETINGS.meetingActivated &&
                                    attendeeMeetingLinks[j].status === QUICKMEETINGS.meetingResponseAccepted)) {
                                meetings[i].timeRange = formatTimeRange(meetings[i].startTime, meetings[i].endTime);
                                meetings[i].groupValue = meetings[i].meetingDate;
                                meetings[i].groupLabel = formatDate(meetings[i].meetingDate);
                                schedule.push(meetings[i]);
                                break;
                            }
                        }
                    }
                }
                schedule.sort(function(a, b) {
                    // sort by startTime which is a unix timestamp
                    if (a.startTime < b.startTime) {
                        return -1;
                    }
                    return 1;
                });
                defer.resolve(schedule);
            }, function(err) {
                defer.reject(err);
            });
            return defer.promise;
        },
        /**
         * @ngdoc method
         * @name getMyInvitesList
         * @methodOf quick-meetings-V2_0.service:qmQuickMeetingsService
         * @description
         * Get a list of invites that the user has been invited to
         *
         * @returns {promise} Promise that resolves when invites list have been retrieved
         */
        getMyInvitesList: function() {
            var defer = $q.defer();
            this.getMeetings().then(function(response) {
                var invites = [];
                var currentAttendeeId = qmLogin.getUserInfo().attendeeId;
                // To get invites list we must filter the meetings object based on conditions from the
                // attendeeMeetingLinks object that receive from the rpc call
                getMeetingInvites(response, currentAttendeeId).forEach(function(meeting) {
                    var hasRead = hasReadMeetingInvite(meeting.meetingId);
                    var hasCancelledRead = hasReadCancelledMeetingInvite(meeting.meetingId);
                    var isCancelledMeeting = meeting.status === QUICKMEETINGS.meetingCancelled &&
                            meeting.inviteeStatus === QUICKMEETINGS.meetingResponseAccepted;
                    meeting.showUnRead = !hasRead || (!hasCancelledRead && isCancelledMeeting);
                    meeting.timeRange = formatTimeRange(meeting.startTime, meeting.endTime);
                    meeting.groupValue = meeting.meetingDate;
                    meeting.groupLabel = formatDate(meeting.meetingDate);
                    invites.push(meeting);
                });
                invites.sort(function(a, b) {
                    // sort by startTime which is a unix timestamp
                    if (a.startTime < b.startTime) {
                        return -1;
                    }
                    return 1;
                });
                defer.resolve(invites);
            }, function(err) {
                defer.reject(err);
            });
            return defer.promise;
        },
        /**
         * @ngdoc method
         * @name getMyMeetingResponsesList
         * @methodOf quick-meetings-V2_0.service:qmQuickMeetingsService
         * @description
         * Get a list of invite responses for meetings the user has created
         *
         * @returns {promise} Promise that resolves when invite responses list have been retrieved
         */
        getMyMeetingResponsesList: function() {
            var defer = $q.defer();
            this.getMeetings().then(function(response) {
                var inviteResponses = [];
                var currentAttendeeId = qmLogin.getUserInfo().attendeeId;
                // To get invite responses list we must filter the meetings object based on conditions from the
                // attendeeMeetingLinks object that receive from the rpc call
                getInviteResponses(response, currentAttendeeId).forEach(function(meetingAndInvite) {
                    var meeting = meetingAndInvite.meeting;
                    var attendeeMeetingLink = meetingAndInvite.attendeeMeetingLink;
                    meeting.hasRead = false;
                    meeting.timeRange = formatTimeRange(meeting.startTime, meeting.endTime);
                    meeting.groupValue = meeting.meetingDate;
                    meeting.groupLabel = formatDate(meeting.meetingDate);
                    meeting.inviteeId = attendeeMeetingLink.attendeeId;
                    inviteResponses.push(meeting);
                });
                inviteResponses.sort(function(a, b) {
                    // sort by startTime which is a unix timestamp
                    if (a.startTime < b.startTime) {
                        return -1;
                    }
                    return 1;
                });
                defer.resolve(inviteResponses);
            }, function(err) {
                defer.reject(err);
            });
            return defer.promise;
        },
        /**
         * @ngdoc method
         * @name getMeetingDetails
         * @methodOf quick-meetings-V2_0.service:qmQuickMeetingsService
         * @description
         * Get details of meeting from meeting id. Attendee links will be appended (Inviters get links to all attendees, invitees get link to their own)
         *
         * @param {string} meetingId Meeting Id
         * @returns {promise} Promise that resolves when meeting details has been retrieved
         */
        getMeetingDetails: function(meetingId) {
            var mainDefer = $q.defer();
            var getMeetingsDefer = $q.defer();
            var getMeetingsPromise = getMeetingsDefer.promise;
            // There is no specific call to get meeting details so we will have to get the details from the list
            // If meeting list has not been retrieved, get it now
            if (!_quickMeetings) {
                this.getMeetings().then(function() {
                    getMeetingsDefer.resolve();
                }, function() {
                    getMeetingsDefer.reject();
                });
            } else {
                getMeetingsDefer.resolve();
            }
            getMeetingsPromise.then(function() {
                var found = false;
                var meetings = _quickMeetings.Meetings;
                var attendeeMeetingLinks = _quickMeetings.AttendeeMeetingLinks;
                for (var i = 0; i < meetings.length; i++) {
                    if (meetings[i].meetingId === meetingId) {
                        var details = meetings[i];
                        var currentAttendeeId = qmLogin.getUserInfo().attendeeId;
                        var j = 0;
                        // append associated attendee links to meeting object if user is the creator of the meeting
                        if (meetings[i].attendeeId === currentAttendeeId) {
                            var attendeeLinks = [];
                            for (j = 0; j < attendeeMeetingLinks.length; j++) {
                                if (attendeeMeetingLinks[j].meetingId === meetingId) {
                                    attendeeLinks.push(attendeeMeetingLinks[j]);
                                }
                            }
                            details.attendeeLinks = attendeeLinks;
                        } else {
                            // append attendee link info to meeting object for invitee of meeting
                            var attendeeLink = {};
                            for (j = 0; j < attendeeMeetingLinks.length; j++) {
                                if (attendeeMeetingLinks[j].meetingId === meetingId &&
                                    attendeeMeetingLinks[j].attendeeId === currentAttendeeId) {
                                    attendeeLink = attendeeMeetingLinks[j];
                                    break;
                                }
                            }
                            details.attendeeLink = attendeeLink;
                        }
                        details.formattedDate = formatDate(meetings[i].meetingDate);
                        details.formattedStartDate = formatTimestamp(meetings[i].startTime, 'dddd LL');
                        details.formattedEndDate = formatTimestamp(meetings[i].endTime, 'dddd LL');
                        details.formattedStartTime = formatTimestamp(meetings[i].startTime, 'LT');
                        details.formattedEndTime = formatTimestamp(meetings[i].endTime, 'LT');
                        details.timeRange = formatTimeRange(meetings[i].startTime, meetings[i].endTime);

                        mainDefer.resolve(details);
                        found = true;
                        break;
                    }
                }
                if (!found) {
                    // todo: need to display something to user when not found
                    mainDefer.reject();
                }
            }, function(err) {
                mainDefer.reject(err);
            });
            return mainDefer.promise;
        },
        /**
         * @ngdoc method
         * @name sendMeetingResponse
         * @methodOf quick-meetings-V2_0.service:qmQuickMeetingsService
         * @description
         * Send meeting response to backend
         *
         * @param {string} meetingId Meeting Id
         * @param {string} status Meeting status
         * @param {string} message Content of message response
         * @returns {promise} Promise that resolves when meeting response has successfully processed
         */
        sendMeetingResponse: function(meetingId, status, message) {
            var defer = $q.defer();
            var sendMeetingResponseUrl = qmEventConfig.getRpcUrl('sendMeetingResponse');
            var params = [];
            params.push(qmLogin.getUserToken());
            params.push(meetingId);
            params.push(status);
            params.push(message);
            var that = this;
            qmRpc.post(sendMeetingResponseUrl, {method: 'sendMeetingResponse', params: params}).then(function(response) {
                // updated cached meeting detail with correct meeting status
                that.updateMeetingStatus(meetingId, status, message);
                defer.resolve(response);
            }, function(err) {
                defer.reject(err);
            });
            return defer.promise;
        },
        /**
         * @ngdoc method
         * @name updateMeetingStatus
         * @methodOf quick-meetings-V2_0.service:qmQuickMeetingsService
         * @description
         * Set status of meeting invite in cached meeting data, and then updates menu badge with unread meetings.
         *
         * @param {string} meetingId Meeting Id
         * @param {string} status New status
         * @param {string} response New response
         */
        updateMeetingStatus: function(meetingId, status, response) {
            if (_quickMeetings) {
                var currentAttendeeId = qmLogin.getUserInfo().attendeeId;
                var attendeeMeetingLinks = _quickMeetings.AttendeeMeetingLinks;
                for (var j = 0; j < attendeeMeetingLinks.length; j++) {
                    if (attendeeMeetingLinks[j].meetingId === meetingId &&
                        attendeeMeetingLinks[j].attendeeId === currentAttendeeId) {
                        attendeeMeetingLinks[j].status = status;
                        attendeeMeetingLinks[j].response = response;
                        break;
                    }
                }
            }
        },
        /**
         * @ngdoc method
         * @name acceptInvite
         * @methodOf quick-meetings-V2_0.service:qmQuickMeetingsService
         * @description
         * Send accept status to meeting invite
         *
         * @param {string} meetingId Meeting Id
         * @returns {promise} Promise that resolves when meeting response has successfully processed
         */
        acceptInvite: function(meetingId) {
            var defer = $q.defer();
            this.sendMeetingResponse(meetingId, QUICKMEETINGS.meetingResponseAccepted, '').then(function(response) {
                defer.resolve(response);
            }, function(err) {
                defer.reject(err);
            });
            return defer.promise;
        },
        /**
         * @ngdoc method
         * @name declineInvite
         * @methodOf quick-meetings-V2_0.service:qmQuickMeetingsService
         * @description
         * Send decline status to meeting invite
         *
         * @param {string} meetingId Meeting Id
         * @returns {promise} Promise that resolves when meeting response has successfully processed
         */
        declineInvite: function(meetingId) {
            var defer = $q.defer();
            this.sendMeetingResponse(meetingId, QUICKMEETINGS.meetingResponseDeclined, '').then(function(response) {
                defer.resolve(response);
            }, function(err) {
                defer.reject(err);
            });
            return defer.promise;
        },
        /**
         * @ngdoc method
         * @name declineWithMsgInvite
         * @methodOf quick-meetings-V2_0.service:qmQuickMeetingsService
         * @description
         * Send decline status to meeting invite with message
         *
         * @param {string} meetingId Meeting Id
         * @param {string} message Content fo message response
         * @returns {promise} Promise that resolves when meeting response has successfully processed
         */
        declineWithMsgInvite: function(meetingId, message) {
            var defer = $q.defer();
            this.sendMeetingResponse(meetingId, QUICKMEETINGS.meetingResponseDeclined, message).then(function(response) {
                defer.resolve(response);
            }, function(err) {
                defer.reject(err);
            });
            return defer.promise;
        },
        /**
         * @ngdoc method
         * @name cancelMeeting
         * @methodOf quick-meetings-V2_0.service:qmQuickMeetingsService
         * @description
         * Cancel a meeting
         *
         * @param {string} meetingId Meeting Id
         * @returns {promise} Promise that resolves when meeting cancellation has successfully processed
         */
        cancelMeeting: function(meetingId) {
            var defer = $q.defer();
            var editMeetingUrl = qmEventConfig.getRpcUrl('editMeeting');
            var params = [];
            params.push(qmLogin.getUserToken());
            params.push(meetingId);
            params.push(QUICKMEETINGS.meetingCancelled);
            qmRpc.post(editMeetingUrl, {method: 'editMeeting', params: params}).then(function(response) {
                defer.resolve(response);
            }, function(err) {
                defer.reject(err);
            });
            return defer.promise;
        },
        /**
         * @ngdoc method
         * @name cancelAcknowledge
         * @methodOf quick-meetings-V2_0.service:qmQuickMeetingsService
         * @description
         * Acknowledge the cancellation of the meeting
         *
         * @param {string} meetingId Meeting Id
         * @returns {promise} Promise that resolves when acknowledgement has successfully processed
         */
        cancelAcknowledge: function(meetingId) {
            var defer = $q.defer();
            var sendMeetingResponseUrl = qmEventConfig.getRpcUrl('sendMeetingResponse');
            var params = [];
            params.push(qmLogin.getUserToken());
            params.push(meetingId);
            params.push(QUICKMEETINGS.meetingResponseCancelAcknowledged);
            qmRpc.post(sendMeetingResponseUrl, {method: 'sendMeetingResponse', params: params}).then(function(response) {
                defer.resolve(response);
            }, function(err) {
                defer.reject(err);
            });
            return defer.promise;
        },
        /**
         * @ngdoc method
         * @name markResponseRead
         * @methodOf quick-meetings-V2_0.service:qmQuickMeetingsService
         * @description
         * Mark meeting response as read
         *
         * @param {string} meetingId Meeting Id
         * @param {string} attendeeId of invitee
         */
        markResponseRead: function(meetingId, attendeeId) {
            var obj = qmLocalStorage.getItem('readMeetingResponses');
            var meetings = obj ? obj : {};
            if (!(meetingId in meetings)) {
                meetings[meetingId] = {};
            }
            if (!(attendeeId in meetings[meetingId])) {
                meetings[meetingId][attendeeId] = {};
                qmLocalStorage.setItem('readMeetingResponses', meetings);
            }
        },
        /**
         * @ngdoc method
         * @name markInviteRead
         * @methodOf quick-meetings-V2_0.service:qmQuickMeetingsService
         * @description
         * Mark meeting response as read
         *
         * @param {string} meetingId Meeting Id
         */
        markInviteRead: function(meetingId) {
            var obj = qmLocalStorage.getItem('readMeetingInvites');
            var meetings = obj ? obj : {};
            if (!(meetingId in meetings)) {
                meetings[meetingId] = {};
                qmLocalStorage.setItem('readMeetingInvites', meetings);
            }
        },
        /**
         * @ngdoc method
         * @name markCancelledRead
         * @methodOf quick-meetings-V2_0.service:qmQuickMeetingsService
         * @description
         * Mark Cancelled meeting response as read
         *
         * @param {string} meetingId Meeting Id
         */
        markCancelledRead: function(meetingId) {
            var obj = qmLocalStorage.getItem('readCancelledMeetingInvites');
            var meetings = obj ? obj : {};
            if (!(meetingId in meetings)) {
                meetings[meetingId] = {};
                qmLocalStorage.setItem('readCancelledMeetingInvites', meetings);
            }
        },
        /**
         * @ngdoc method
         * @name markInviteUnread
         * @methodOf quick-meetings-V2_0.service:qmQuickMeetingsService
         * @description
         * Unmark meeting response as read
         *
         * @param {string} meetingId Meeting Id
         */
        markInviteUnread: function(meetingId) {
            var obj = qmLocalStorage.getItem('readMeetingInvites');
            var meetings = obj ? obj : {};
            if (meetingId in meetings) {
                delete meetings[meetingId];
                qmLocalStorage.setItem('readMeetingInvites', meetings);
            }
        },
        /**
         * @ngdoc method
         * @name ownerHasReadMeetingResponse
         * @methodOf quick-meetings-V2_0.service:qmQuickMeetingsService
         * @description
         * Mark meeting response as read. This is done using an RPC.
         *
         * @deprecated in favour of markResponseRead. iOS/Android currently do not call this RPC.
         *
         * @param {string} meetingId Meeting Id
         * @param {string} attendeeId of invitee
         * @returns {promise} Promise that resolves when marking has successfully processed
         */
        ownerHasReadMeetingResponse: function(meetingId, attendeeId) {
            var defer = $q.defer();
            var ownerHasReadMeetingResponseUrl = qmEventConfig.getRpcUrl('ownerHasReadMeetingResponse');
            var params = [];
            params.push(qmLogin.getUserToken());
            params.push(attendeeId);
            params.push(meetingId);
            qmRpc.post(ownerHasReadMeetingResponseUrl, {method: 'ownerHasReadMeetingResponse', params: params}).then(function(response) {
                defer.resolve(response);
            }, function(err) {
                defer.reject(err);
            });
            return defer.promise;
        },
        /**
         * @ngdoc method
         * @name getNumberOfInvitesAndResponses
         * @methodOf quick-meetings-V2_0.service:qmQuickMeetingsService
         * @description
         * Get the number of my unread meeting invites and responses.
         *
         * Updates the unread count badge beside Quick Meetings in the feature menu by
         * broadcasting a "menuBadgeUpdate" event.
         *
         * @param {boolean} refreshCache If true, refresh the cache with new meeting data. Default false.
         * @returns {promise} Promise that resolves to {Number} of my unread meeting invites and responses.
         */
        getNumberOfInvitesAndResponses: function(refreshCache) {
            if (typeof(refreshCache) === 'undefined') {
                refreshCache = false;
            }
            var defer = $q.defer();
            var getMeetingsDefer = $q.defer();
            var getMeetingsPromise = getMeetingsDefer.promise;
            // Don't call the getMyMeetings RPC call if data is already cached here.
            if (refreshCache || !_quickMeetings) {
                this.getMeetings().then(function() {
                    getMeetingsDefer.resolve();
                }, function() {
                    getMeetingsDefer.reject();
                });
            } else {
                getMeetingsDefer.resolve();
            }
            getMeetingsPromise.then(function() {
                // Check for login here in case user navigates to container before promise finishes
                if (qmLogin.isLoggedIn()) {
                    var currentAttendeeId = qmLogin.getUserInfo().attendeeId;
                    var numberOfUnread = 0;
                    getMeetingInvites(_quickMeetings, currentAttendeeId).forEach(function(meeting) {
                        if (!hasReadMeetingInvite(meeting.meetingId)) {
                            numberOfUnread++;
                        } else if (!hasReadCancelledMeetingInvite(meeting.meetingId) &&
                            meeting.status === QUICKMEETINGS.meetingCancelled &&
                            meeting.inviteeStatus === QUICKMEETINGS.meetingResponseAccepted) {
                            numberOfUnread++;
                        }
                    });
                    var numberOfResponses = getInviteResponses(_quickMeetings, currentAttendeeId).length;
                    var numberOfInvitesAndResponses = numberOfUnread + numberOfResponses;
                    $rootScope.$broadcast('menuBadgeUpdate', 'quick-meetings', numberOfInvitesAndResponses);
                    defer.resolve(numberOfInvitesAndResponses);
                } else {
                    defer.reject();
                }
            }, function(err) {
                defer.reject(err);
            });
            return defer.promise;
        }
    };
}]);

/**
 * @ngdoc service
 * @name quick-meetings-V2_0.service:qmQuickMeetingsCreateDataService
 * @description
 * Manages Quick Meetings create data across controllers
 */
quickmeetings.factory('qmQuickMeetingsCreateDataService', [function() {
    var title, location, today, startDate, endDate, startTime, endTime, attendees, attendeeFlag;
    return {
        /**
         * @ngdoc method
         * @name init
         * @methodOf quick-meetings-V2_0.service:qmQuickMeetingsCreateDataService
         * @description
         * Initialize Quick Meeting create data service
         *
         */
        init: function() {
            title = null;
            location = null;
            today = new Date();
            startDate = new Date();
            endDate = new Date();
            startTime = new Date();
            startTime.setMinutes(startTime.getMinutes() + 30);
            endTime = new Date();
            endTime.setMinutes(endTime.getMinutes() + 60);
            attendees = [];
            attendeeFlag = false;
        },
        /**
         * @ngdoc method
         * @name setTitle
         * @methodOf quick-meetings-V2_0.service:qmQuickMeetingsCreateDataService
         * @description
         * Set Quick Meeting title
         *
         * @param {string} newTitle Title of Quick Meeting
         */
        setTitle: function(newTitle) {
            title = newTitle;
        },
        /**
         * @ngdoc method
         * @name getTitle
         * @methodOf quick-meetings-V2_0.service:qmQuickMeetingsCreateDataService
         * @description
         * Get Quick Meeting title
         *
         * @returns {string} Title of Quick Meeting
         */
        getTitle: function() {
            return title;
        },
        /**
         * @ngdoc method
         * @name setLocation
         * @methodOf quick-meetings-V2_0.service:qmQuickMeetingsCreateDataService
         * @description
         * Set Quick Meeting location
         *
         * @param {string} newLocation Location of Quick Meeting
         */
        setLocation: function(newLocation) {
            location = newLocation;
        },
        /**
         * @ngdoc method
         * @name getLocation
         * @methodOf quick-meetings-V2_0.service:qmQuickMeetingsCreateDataService
         * @description
         * Get Quick Meeting location
         *
         * @returns {string} Location of Quick Meeting
         */
        getLocation: function() {
            return location;
        },
        /**
         * @ngdoc method
         * @name getToday
         * @methodOf quick-meetings-V2_0.service:qmQuickMeetingsCreateDataService
         * @description
         * Get Quick Meeting now
         *
         * @returns {Date} Start date of Quick Meeting
         */
        getToday: function() {
            return today;
        },
        /**
         * @ngdoc method
         * @name setStartDate
         * @methodOf quick-meetings-V2_0.service:qmQuickMeetingsCreateDataService
         * @description
         * Set Quick Meeting start date
         *
         * @param {Date} newDate Date of Quick Meeting
         */
        setStartDate: function(newStartDate) {
            startDate = newStartDate;
        },
        /**
         * @ngdoc method
         * @name getStartDate
         * @methodOf quick-meetings-V2_0.service:qmQuickMeetingsCreateDataService
         * @description
         * Get Quick Meeting start date
         *
         * @returns {Date} Start date of Quick Meeting
         */
        getStartDate: function() {
            return startDate;
        },
        /**
         * @ngdoc method
         * @name setEndDate
         * @methodOf quick-meetings-V2_0.service:qmQuickMeetingsCreateDataService
         * @description
         * Set Quick Meeting end date
         *
         * @param {Date} End date of Quick Meeting
         */
        setEndDate: function(newEndDate) {
            endDate = newEndDate;
        },
        /**
         * @ngdoc method
         * @name getEndDate
         * @methodOf quick-meetings-V2_0.service:qmQuickMeetingsCreateDataService
         * @description
         * Get Quick Meeting end date
         *
         * @returns {Date} End date of Quick Meeting
         */
        getEndDate: function() {
            return endDate;
        },
        /**
         * @ngdoc method
         * @name setStartTime
         * @methodOf quick-meetings-V2_0.service:qmQuickMeetingsCreateDataService
         * @description
         * Set Quick Meeting start time
         *
         * @param {Date} newStartTime Start time of Quick Meeting
         */
        setStartTime: function(newStartTime) {

            var sDate = this.getStartDate();

            if (null !== newStartTime) {
                var hours = newStartTime.getHours();
                var minutes = newStartTime.getMinutes();
                sDate.setHours(hours);
                sDate.setMinutes(minutes);
            }

            startTime = sDate;

        },
        /**
         * @ngdoc method
         * @name getStartTime
         * @methodOf quick-meetings-V2_0.service:qmQuickMeetingsCreateDataService
         * @description
         * Get Quick Meeting start time
         *
         * @returns {Date} Start time of Quick Meeting
         */
        getStartTime: function() {
            return startTime;
        },
        /**
         * @ngdoc method
         * @name setEndTime
         * @methodOf quick-meetings-V2_0.service:qmQuickMeetingsCreateDataService
         * @description
         * Set Quick Meeting end time
         *
         * @param {Date} newEndTime End time of Quick Meeting
         */
        setEndTime: function(newEndTime) {
            var eDate = this.getEndDate();

            if (null !== newEndTime) {
                var hours = newEndTime.getHours();
                var minutes = newEndTime.getMinutes();
                eDate.setHours(hours);
                eDate.setMinutes(minutes);
            }

            endTime = eDate;
        },
        /**
         * @ngdoc method
         * @name getEndTime
         * @methodOf quick-meetings-V2_0.service:qmQuickMeetingsCreateDataService
         * @description
         * Get Quick Meeting end time
         *
         * @returns {Date} End time of Quick Meeting
         */
        getEndTime: function() {
            return endTime;
        },
        /**
         * @ngdoc method
         * @name addAttendee
         * @methodOf quick-meetings-V2_0.service:qmQuickMeetingsCreateDataService
         * @description
         * Add attendee to list of selected attendees
         *
         * @param {object} newAttendee Attendee to be added
         * @returns {boolean} true if attendee has been added, false if it already exists
         */
        addAttendee: function(newAttendee) {
            if (!this.hasAttendee(newAttendee.attendeeId)) {
                attendees.push(newAttendee);
                return true;
            }
            return false;
        },
        /**
         * @ngdoc method
         * @name removeAttendee
         * @methodOf quick-meetings-V2_0.service:qmQuickMeetingsCreateDataService
         * @description
         * Add attendee to list of selected attendees
         *
         * @param {number} index Index of attendee to be removed
         * @returns {boolean} true if attendee has been removed, false if it has not
         */
        removeAttendee: function(index) {
            if (attendees.length) {
                attendees.splice(index, 1);
                return true;
            }
            return false;
        },
        /**
         * @ngdoc method
         * @name hasAttendee
         * @methodOf quick-meetings-V2_0.service:qmQuickMeetingsCreateDataService
         * @description
         * Check if attendee is selected already
         *
         * @param {string} id Attendee id to check if attendee has been added already
         * @returns {boolean} true if attendee has been added, false otherwise
         */
        hasAttendee: function(id) {
            for (var i = 0; i < attendees.length; i++) {
                if (attendees[i].attendeeId === id) {
                    return true;
                }
            }
            return false;
        },
        /**
         * @ngdoc method
         * @name getAttendees
         * @methodOf quick-meetings-V2_0.service:qmQuickMeetingsCreateDataService
         * @description
         * Get list of attendees that have been selected
         *
         * @returns {Array} List of attendees that have been selected
         */
        getAttendees: function() {
            return attendees;
        }
    };
}]);

/**
 * @ngdoc service
 * @name quick-meetings-V2_0.service:qmQuickMeetingsAttendeeRouteService
 * @description
 * Manages Quick Meetings create data across controllers
 */
quickmeetings.factory('qmQuickMeetingsAttendeeRouteService', [function() {

    var fromAttendeeFlag;

    return {
        /**
         * @ngdoc method
         * @name init
         * @methodOf quick-meetings-V2_0.service:qmQuickMeetingsAttendeeRouteService
         * @description
         * Initialize Quick Meeting create data service
         *
         */
        init: function() {
            fromAttendeeFlag = false;
        },
        /**
         * @ngdoc method
         * @name setAttendeeFlag
         * @methodOf quick-meetings-V2_0.service:qmQuickMeetingsAttendeeRouteService
         * @description
         * Set Quick Meeting AttendeeFlag
         *
         * @param {string} newTitle Title of Quick Meeting
         */
        setFromAttendeeFlag: function(value) {
            fromAttendeeFlag = value;
        },
        /**
         * @ngdoc method
         * @name getAttendeeFlag
         * @methodOf quick-meetings-V2_0.service:qmQuickMeetingsAttendeeRouteService
         * @description
         * Get Quick Meeting attendeeFlag
         *
         * @returns {bool} attendeeFlag of Quick Meeting
         */
        getFromAttendeeFlag: function() {
            return fromAttendeeFlag;
        }
    };
}]);

var quickmeetings = angular.module('quick-meetings-V2_0');

/**
 * @ngdoc service
 * @name quick-meetings-V2_0.service:qmQuickMeetingsDependencyService
 * @description
 * Manages communication with dependency manager
 */
quickmeetings.factory('qmQuickMeetingsDependencyService', ['qmDependencyManager', function(qmDependencyManager) {
    return {
        /**
         * @ngdoc method
         * @name getAttendeeService
         * @methodOf quick-meetings-V2_0.service:qmQuickMeetingsDependencyService
         * @description
         * Get service from attendees component
         *
         * @param {string} service Service name
         * @returns {service} Angular service or null
         */
        getAttendeeService: function(service) {
            return qmDependencyManager.getRequiredService('attendees', '2.2', 'component', service);
        },
        /**
         * @ngdoc method
         * @name getMyScheduleService
         * @methodOf quick-meetings-V2_0.service:qmQuickMeetingsDependencyService
         * @description
         * Get service from my-schedule component
         *
         * @param {string} service Service name
         * @returns {service} Angular service or null
         */
        getMyScheduleService: function(service) {
            return qmDependencyManager.getService('myschedule', '2.0', 'component', service);
        },
        /**
         * @ngdoc method
         * @name getEventsService
         * @methodOf quick-meetings-V2_0.service:qmQuickMeetingsDependencyService
         * @description
         * Get service from events component
         *
         * @param {string} service Service name
         * @returns {service} Angular service or null
         */
        getEventsService: function(service) {
            return qmDependencyManager.getRequiredService('events', '2.4', 'component', service);
        },
        /**
         * @ngdoc method
         * @name hasComponent
         * @methodOf quick-meetings-V2_0.service:qmQuickMeetingsDependencyService
         * @description
         * Check if component is configured
         *
         * @param {string} componentKey Key identifying the component
         * @returns {boolean} Is component configured?
         */
        hasComponent: function(componentKey) {
            return qmDependencyManager.hasComponent(componentKey);
        }
    };
}]);

var quickmeetings = angular.module('quick-meetings-V2_0');

quickmeetings.constant('QUICKMEETINGS', {
    meetingPending: 'Pending',
    meetingActivated: 'Activated',
    meetingCancelled: 'Cancelled',
    meetingResponsePending: 'Pending',
    meetingResponseAccepted: 'Accepted',
    meetingResponseDeclined: 'Declined',
    meetingResponseCancelAcknowledged: 'CancelAcknowledged'
});

var search = angular.module('search-V2_0');

search.config(['$stateProvider', function($stateProvider) {
    $stateProvider
        .state('event.search-V2_0', {
            url: '/Search',
            views: {
                'component@event': {
                    templateUrl: '/asset/component/search/2.0/webapp/html/search-list.html',
                    controller: 'SearchListController'
                },
                'header@event': {
                    controller: 'SearchListHeaderController'
                }
            }
        });
}]);

var search = angular.module('search-V2_0');

/**
 * @ngdoc service
 * @name search-V2_0.service:qmSearchService
 * @description
 * Manages REST API communication for Search
 */
search.factory('qmSearchService', ['qmSearchDependencyService', 'qmLocalization', 'qmList', '$q',
function(qmSearchDependencyService, qmLocalization, qmList, $q) {

    // Custom object that we can use for components available to search
    var ComponentDataSource = function ComponentDataSource(component, qmService) {
        this.name = component.name;
        this.key = component.key;
        this.service = qmService;
        this.data = [];
        this.title = qmLocalization.getString(component.titleStringKey);
        this.promise = {};
    };

    // Define a generic getList method which will delegate the call to the actual component service
    ComponentDataSource.prototype.getList = function(searchTerm, offset, limit) {
        offset = (typeof offset === 'undefined') ? 0 : offset;
        limit = (typeof limit === 'undefined') ? 0 : limit;
        if ('attendees' === this.key || 'exhibitors' === this.key || 'events' === this.key) {
            this.promise = this.service.getList([], searchTerm, offset, limit);
        } else {
            this.promise = this.service.getList(searchTerm, offset, limit);
        }
        var that = this;
        this.promise.then(function(data) {
            if ('exhibitors' === that.key) {
                var exhibitors = [];
                if (data.success && data.exhibitors && data.exhibitors.length > 0) {
                    exhibitors = data.exhibitors;
                }
                that.data = qmList.transformList(exhibitors);
            } else if ('events' === that.key) {
                that.data = qmList.groupByGroup(data, 'eventDate', 'eventDateFormatted');
            } else {
                that.data = qmList.transformList(data);
            }
        });

        return this.promise;
    };

    // Custom object to represent the data source of the search component
    // It will wrapp all the individual component services
    var DataSource = function DataSource() {
        this.promises = [];
        this.hasContent = false;
        this.componentDataSources = [];
    };
    DataSource.prototype.getPromises = function() {
        return this.promises;
    };
    DataSource.prototype.getComponentDataSources = function() {
        return this.componentDataSources;
    };
    DataSource.prototype.addComponentDataSource = function(componentDataSource) {
        this.componentDataSources.push(componentDataSource);
    };
    DataSource.prototype.clearContent = function() {
        var componentDataSources = this.getComponentDataSources();
        for (var i = 0; i < componentDataSources.length; i++) {
            var componentDataSource = componentDataSources[i];
            componentDataSource.data = [];
            this.hasContent = false;
        }
    };
    // Go through all available components to Search and search all their services based on search term
    DataSource.prototype.search = function(searchTerm) {
        var componentDataSources = this.getComponentDataSources();
        var that = this;
        that.promises = [];
        for (var i = 0; i < componentDataSources.length; i++) {
            var componentDataSource = componentDataSources[i];
            var promise = componentDataSource.getList(searchTerm);
            that.promises.push(promise);
        }
        $q.all(that.promises).then(function(dataArray) {
            that.hasContent = false;
            for (var i = 0; i < dataArray.length; i++) {
                if (dataArray[i].length > 0) {
                    that.hasContent = true;
                    break;
                }
                if (typeof dataArray[i].exhibitors !== 'undefined' && dataArray[i].exhibitors.length > 0) {
                    that.hasContent = true;
                    break;
                }
            }
        });
    };

    return {
        /**
         * @ngdoc method
         * @name getDataSource
         * @methodOf search-V2_0.service:qmSearchService
         * @description
         * Return the DataSource object
         *
         * @returns {object} Data source
         */
        getDataSource: function() {
            var searchComponents = qmSearchDependencyService.getSearchComponents();
            var dataSource = new DataSource();
            angular.forEach(searchComponents, function(searchComponent, componentKey) {
                var qmService = qmSearchDependencyService.getQmService(componentKey);
                if (qmService) {
                    var componentDataSource = new ComponentDataSource(searchComponent, qmService);
                    dataSource.addComponentDataSource(componentDataSource);
                }
            });
            return dataSource;
        }
    };
}]);

var search = angular.module('search-V2_0');

/**
 * @ngdoc service
 * @name search-V2_0.service:qmSearchDependencyService
 * @description
 * Manages communication with dependency manager
 */
search.factory('qmSearchDependencyService', ['qmDependencyManager', 'qmEventConfig', 'qmLogin',
function(qmDependencyManager, qmEventConfig, qmLogin) {

    var supportedSearchComponents = {
        'attendees': {
            'componentService': 'qmAttendeeService',
            'dependencyService': 'qmAttendeeDependencyService',
            'version': '2.2'
        },
        'speakers': {
            'componentService': 'qmSpeakerService',
            'dependencyService': 'qmSpeakersDependencyService',
            'version': '2.0'
        },
        'sponsors': {
            'componentService': 'qmSponsorsService',
            'dependencyService': 'qmSponsorsDependencyService',
            'version': '2.0'
        },
        'exhibitors': {
            'componentService': 'qmExhibitorsService',
            'dependencyService': 'qmExhibitorsDependencyService',
            'version': '2.2'
        },
        'tracks': {
            'componentService': 'qmTracksService',
            'dependencyService': 'qmTracksDependencyService',
            'version': '2.0'
        },
        'events': {
            'componentService': 'qmEventsService',
            'dependencyService': 'qmEventsDependencyService',
            'version': '2.4'
        },
        'authors': {
            'componentService': 'qmAuthorService',
            'dependencyService': 'qmAuthorsDependencyService',
            'version': '2.0'
        }
    };

    return {
        /**
         * @ngdoc method
         * @name getSearchComponents
         * @methodOf search-V2_0.service:qmSearchDependencyService
         * @description
         * Get components available to Search
         *
         * @returns {object} Components available to search
         */
        getSearchComponents: function() {

            var searchComponents = {};
            var visibleComponents = this.getSupportedVisibleComponents();
            var isLoggedIn = qmLogin.isLoggedIn();
            var featureLevelLogin = !qmEventConfig.isEventLoginRequired();
            // Loop through the visible components and extract the information we need from component object
            for (var i = 0; i < visibleComponents.length; i++) {
                var searchComponent = {};
                var component = visibleComponents[i];
                var loginRequired = qmEventConfig.isComponentLoginRequired(component.key);
                if (!featureLevelLogin || (featureLevelLogin && (!loginRequired || (loginRequired && isLoggedIn)))) {
                    searchComponent.key = component.key;
                    searchComponent.name = component.name;
                    searchComponent.titleStringKey = component.titleStringKey;
                    searchComponents[component.key] = searchComponent;
                }
            }

            return searchComponents;
        },

        /**
         * @ngdoc method
         * @name getSupportedVisibleComponents
         * @methodOf search-V2_0.service:qmSearchDependencyService
         * @description
         * Get visible components that are available to Search
         *
         * @returns {object} Supported visible components
         */
        getSupportedVisibleComponents: function() {

            var visibleComponents = qmEventConfig.getVisibleComponents();
            var supportedVisibleComponents = [];
            var eventsComponent = qmEventConfig.getComponentConfig('events');
            for (var i = 0; i < visibleComponents.length; i++) {
                var component = visibleComponents[i];
                if (supportedSearchComponents[component.key]) {
                    supportedVisibleComponents.push(component);
                    // If tracks is visible, make sure Events component is visible as well
                    if ('tracks' === component.key && eventsComponent['@attributes'].visible === 'false') {
                        supportedVisibleComponents.push(eventsComponent['@attributes']);
                    }
                }
            }

            return supportedVisibleComponents;
        },

        /**
         * @ngdoc method
         * @name getTemplateUrl
         * @methodOf search-V2_0.service:qmSearchDependencyService
         * @description
         * Get a component's template url through it's dependency service
         *
         * @param {string} Component unit key
         * @param {string} Template name
         * @returns {string} The template url or empty string if qmDependency service of the requested component isn't available
         */
        getTemplateUrl: function(componentKey, templateName) {
            var templateUrl = '';
            var service = this.getQmDependencyService(componentKey);
            if (service) {
                templateUrl = service.getTemplateUrl(templateName);
            }

            return templateUrl;
        },

        /**
         * @ngdoc method
         * @name getQmService
         * @methodOf search-V2_0.service:qmSearchDependencyService
         * @description
         * Get a component's service only if we support the component in search and is configured and visible for the project
         *
         * @param {string} Component unit key
         * @returns {service} Angular service or false
         */
        getQmService: function(componentKey) {

            var service = false;
            if (supportedSearchComponents[componentKey]) {
                var componentVersion = supportedSearchComponents[componentKey].version;
                var serviceName = supportedSearchComponents[componentKey].componentService;
                if (this.hasComponent(componentKey)) {
                    service = qmDependencyManager.getService(componentKey, componentVersion, 'component', serviceName);
                }
            }

            return service;
        },

        /**
         * @ngdoc method
         * @name getQmDependencyService
         * @methodOf search-V2_0.service:qmSearchDependencyService
         * @description
         * Get a component's dependency service only if we support the component in search and is configured and visible for the project
         *
         * @param {string} Component unit key
         * @returns {service} Angular service or false
         */
        getQmDependencyService: function(componentKey) {
            var service = false;
            if (supportedSearchComponents[componentKey]) {
                var componentVersion = supportedSearchComponents[componentKey].version;
                var serviceName = supportedSearchComponents[componentKey].dependencyService;
                if (this.hasComponent(componentKey)) {
                    service = qmDependencyManager.getService(componentKey, componentVersion, 'component', serviceName);
                }
            }

            return service;
        },

        /**
         * @ngdoc method
         * @name getAttendeeService
         * @methodOf search-V2_0.service:qmSearchDependencyService
         * @description
         * Request attendee settings from qmAttendeeService
         *
         * @returns {object} Attendee settings object. Return empty object if service is unavailable
         */
        getAttendeeSettings: function() {
            var settings = {};
            var service = this.getQmService('attendees');
            if (service) {
                settings = service.getSetting();
            }

            return settings;
        },

        /**
         * @ngdoc method
         * @name hasComponent
         * @methodOf search-V2_0.service:qmSearchDependencyService
         * @description
         * Check if component is configured
         *
         * @param {string} componentKey Key identifying the component
         * @returns {boolean} Is component configured?
         */
        hasComponent: function(componentKey) {
            return qmDependencyManager.hasComponent(componentKey);
        }
    };
}]);

var search = angular.module('search-V2_0');

/**
 * @ngdoc controller
 * @name search-V2_0.controller:SearchListController
 * @description
 * Controller for search list view
 */
search.controller('SearchListController', ['$scope', 'qmSearchService', 'qmSearchDependencyService', '$q', '$timeout',
    'qmLocalStorage', '$rootScope',
    function($scope, qmSearchService, qmSearchDependencyService, $q, $timeout, qmLocalStorage, $rootScope) {
        var timerPromise = null;
        var cachedSearchTerm = qmLocalStorage.getItem('searchTerm');
        $scope.isSearchActive = false;
        $scope.searching = false;
        $scope.hasContent = false;
        $scope.hasMessaging = qmSearchDependencyService.hasComponent('messaging');
        $scope.setting = qmSearchDependencyService.getAttendeeSettings();
        $scope.attendeeListItemUrl = qmSearchDependencyService.getTemplateUrl('attendees', 'attendee-list-item');
        $scope.attendeeListItemMoreUrl = qmSearchDependencyService.getTemplateUrl('attendees', 'attendee-list-item-more');
        $scope.speakerListItemUrl = qmSearchDependencyService.getTemplateUrl('speakers', 'speaker-list-item');
        $scope.sponsorListItemUrl = qmSearchDependencyService.getTemplateUrl('sponsors', 'sponsor-list-item');
        $scope.exhibitorListItemUrl = qmSearchDependencyService.getTemplateUrl('exhibitors', 'exhibitor-list-item');
        $scope.trackListItemUrl = qmSearchDependencyService.getTemplateUrl('tracks', 'track-list-item');
        $scope.eventListItemUrl = qmSearchDependencyService.getTemplateUrl('events', 'event-list-item');
        $scope.authorListItemUrl = qmSearchDependencyService.getTemplateUrl('authors', 'author-list-item');
        // Get the data source
        $scope.dataSource = qmSearchService.getDataSource();

        var doSearch = function(searchTerm) {
            if (!$scope.dataSource.hasContent) {
                $scope.searching = true;
            }
            timerPromise = $timeout(function() {
                $scope.dataSource.search(searchTerm);
                var promises = $scope.dataSource.getPromises();
                qmLocalStorage.setItem('searchTerm', searchTerm);
                $q.all(promises).then(function() {
                    $scope.searching = false;
                });
            }, 400);
        };

        // Clear the search term when navigating away for the search component. Expectation is that
        // when searching for example attendees, you can navigate to the details page and go back to the same results
        // when you navigate back to search component
        $rootScope.$on('$stateChangeSuccess', function(event, toState, toParams, fromState) {
            if (toState.name.match(/^event\.(.+)(-V[0-9]+_[0-9]+)\.detail$/) ||
                toState.name.match(/^event\.tracks(-V[0-9]+_[0-9]+)\.events$/) ||
                toState.name.match(/^event\.messaging(-V[0-9]+_[0-9]+)\.compose$/) ||
                (fromState.name.match(/^event\.(.+)(-V[0-9]+_[0-9]+)\.detail$/) &&
                toState.name.match(/^event\.search(-V[0-9]+_[0-9]+)$/))) {
                return;
            } else {
                qmLocalStorage.removeItem('searchTerm');
            }
        });

        /**
         * Clear all  time promise
         */
        function clearTimePromise() {
            if (timerPromise !== null) {
                $timeout.cancel(timerPromise);
            }
        }

        $scope.setSearchActive = function() {
            $scope.isSearchActive = true;
        };

        $scope.cancelSearch = function() {
            $scope.dataSource.clearContent();
            clearTimePromise();
            $scope.searchTerms = '';
            $scope.isSearchActive = false;
            qmLocalStorage.removeItem('searchTerm');
        };

        $scope.$watch('searchTerms', function(newText, oldText) {
            if (newText === oldText) {
                return;
            }

            if (newText.length < 2) {
                $scope.dataSource.clearContent();
                clearTimePromise();
            } else {
                clearTimePromise();
                doSearch(newText);
            }
        });

        // If search term is cached, do a search on data source by the searchTerm
        // E.g. Navigating from the Search list results to a details page and then back to the Search via back
        // button should show the same results as before
        if (cachedSearchTerm && cachedSearchTerm.length > 1) {
            $scope.searchTerms = cachedSearchTerm;
            $scope.isSearchActive = true;
            doSearch(cachedSearchTerm);
        }
    }
]);

/**
 * @ngdoc controller
 * @name search-V2_0.controller:SearchListHeaderController
 * @description
 * Controller for search list header view
 */
search.controller('SearchListHeaderController', ['$scope', 'qmLocalization', function($scope, qmLocalization) {
    $scope.headerTitle = qmLocalization.getString('componentSearchTitle');
    $scope.headerTitleTestId = 'componentTitle';
}]);

var sessionqa = angular.module('sessionqa-V2_0');

sessionqa.config(['$stateProvider', function($stateProvider) {
    $stateProvider
        .state('event.sessionqa-V2_0', {
            url: '/SessionQA',
            views: {
                'component@event': {
                    templateUrl: '/asset/component/sessionqa/2.0/webapp/html/event-list.html',
                    controller: 'SessionQAListController'
                },
                'header@event': {
                    controller: 'SessionQAListHeaderController'
                }
            }
        })
        .state('event.sessionqa-V2_0.create', {
            url: '/:eventId/create',
            views: {
                'component@event': {
                    templateUrl: '/asset/component/sessionqa/2.0/webapp/html/sessionqa-create.html',
                    controller: 'SessionQACreateController'
                },
                'header@event': {
                    controller: 'SessionQACreateHeaderController'
                }
            }
        });
}]);

var sessionqa = angular.module('sessionqa-V2_0');

/**
 * @ngdoc service
 * @name sessionqa-V2_0.service:qmSessionQAService
 * @description
 * Manages REST API communication for sessionqa
 */
sessionqa.factory('qmSessionQAService',
['qmLogin', 'qmRpc', '$q', 'qmEventConfig', 'qmLocalization', 'qmSessionQADependencyService',
function(qmLogin, qmRpc, $q, qmEventConfig, qmLocalization, qmSessionQADependencyService) {
    return {
        /**
         * @ngdoc method
         * @name getList
         * @methodOf sessionqa-V2_0.service:qmSessionQAService
         * @description
         * Get the list of sessions
         *
         * @returns {promise} Promise that resolves when the sessions have been retrieved and filtered
         */
        getList: function() {
            var defer = $q.defer();
            var qmEventsService = qmSessionQADependencyService.getEventsService('qmEventsService');
            qmEventsService.getList().then(function(eventList) {
                var filteredEventList = eventList.filter(function(event) {
                    return (event.allowSessionQA === '1');
                });
                defer.resolve(filteredEventList);
            }, function(err) {
                defer.reject(err);
            });
            return defer.promise;
        },
        /**
         * @ngdoc method
         * @name create
         * @methodOf sessionqa-V2_0.service:qmSessionQAService
         * @description
         * Create a new Session Q&A question
         *
         * @param {string} question Session Question content
         * @param {string} eventId Session Event ID
         * @returns {promise} Promise that resolves when session question has successfully been created
         */
        create: function(question, eventId) {
            var createUrl = qmEventConfig.getRpcUrl('submitPost');
            var defer = $q.defer();
            var params = {
                method: 'submitPost',
                params: [
                    qmLogin.getUserToken(),
                    question,
                    eventId
                ]
            };
            qmRpc.post(createUrl, params).then(function(response) {
                defer.resolve(response);
            }, function(err) {
                defer.reject(err);
            });

            return defer.promise;
        }
    };
}]);

var sessionqa = angular.module('sessionqa-V2_0');

/**
 * @ngdoc service
 * @name sessionqa-V2_0.service:qmSessionQADependencyService
 * @description
 * Manages communication with dependency manager
 */
sessionqa.factory('qmSessionQADependencyService', ['qmDependencyManager', function(qmDependencyManager) {
    return {
        /**
         * @ngdoc method
         * @name getEventsService
         * @methodOf sessionqa-V2_0.service:qmSessionQADependencyService
         * @description
         * Get service from events component
         *
         * @param {string} service Service name
         * @returns {service} Angular service or null
         */
        getEventsService: function(service) {
            return qmDependencyManager.getService('events', '2.4', 'component', service);
        }
    };
}]);

var sessionqa = angular.module('sessionqa-V2_0');

/**
 * @ngdoc controller
 * @name sessionqa-V2_0.controller:SessionQAListController
 * @description
 * Controller for Session Q&A event list view
 */
sessionqa.controller('SessionQAListController', ['$scope', 'qmSessionQAService', 'qmList', 'qmLocalization', 'qmSessionQADependencyService',
function($scope, qmSessionQAService, qmList, qmLocalization, qmSessionQADependencyService) {
    $scope.loadEvents = qmSessionQAService.getList();
    $scope.loadEvents.then(function(events) {
        $scope.events = qmList.groupByGroup(events, 'eventDate', 'eventDateFormatted');

        var qmEventsDependencyService = qmSessionQADependencyService.getEventsService('qmEventsDependencyService');
        $scope.eventListItemUrl = qmEventsDependencyService.getTemplateUrl('event-list-item');

        var componentTitle = qmLocalization.getString('componentSessionQATitle');
        $scope.emptySubText = qmLocalization.getString('LABEL_EMPTY_SESSION_QA_MESSAGE', componentTitle);
    });
}]);

/**
 * @ngdoc controller
 * @name sessionqa-V2_0.controller:SessionQAListHeaderController
 * @description
 * Controller for Session Q&A event list header view
 */
sessionqa.controller('SessionQAListHeaderController', ['$scope', 'qmLocalization', function($scope, qmLocalization) {
    $scope.headerBackState = true;
    $scope.headerTitle = qmLocalization.getString('componentSessionQATitle');
    $scope.headerTitleTestId = 'componentTitle';
}]);

var sessionqa = angular.module('sessionqa-V2_0');

/**
 * @ngdoc controller
 * @name sessionqa-V2_0.controller:SessionQACreateController
 * @description
 * Controller for Session Q&A create view
 */
sessionqa.controller('SessionQACreateController', ['$scope', 'qmLocalization', 'qmSessionQAService', '$state', 'toastr', '$rootScope',
function($scope, qmLocalization, qmSessionQAService, $state, toastr, $rootScope) {
    $scope.confirm = {
        no: qmLocalization.getString('ALERT_NO'),
        yes: qmLocalization.getString('ALERT_YES'),
        title: qmLocalization.getString('ALERT_WARNING_TITLE'),
        content: qmLocalization.getString('ALERT_QA_CLOSE_WITHOUT_SAVING_MESSAGE'),
        isEmpty: function() {
            return !$scope.question;
        },
        isSubmitting: function() {
            return $scope.savingQuestion === true;
        }
    };
    $scope.$on('submitSessionQuestion', function() {
        if ($scope.question) {
            if ($scope.savingQuestion !== true) {
                $scope.savingQuestion = true;
                qmSessionQAService.create($scope.question, $state.params.eventId).then(function() {
                    toastr.success(qmLocalization.getString('ALERT_SESSION_QA_SUBMITTED_MESSAGE'), '', {
                        closeButton: true,
                        timeOut: 5000
                    });
                    $state.go('^');
                },
                function(err) {
                    $scope.savingQuestion = false;
                    if (err.error) {
                        toastr.error(qmLocalization.getString('ALERT_SESSION_QA_SUBMIT_FAILED'), '', {
                            closeButton: true,
                            timeOut: 5000
                        });
                    }
                });
            }
        } else {
            toastr.warning(qmLocalization.getString('ALERT_QA_EMPTY_TITLE'), '', {
                closeButton: true,
                timeOut: 5000
            });
        }
    });

    $scope.$watch('question', function(newValue) {
        if (newValue === '' || typeof newValue === 'undefined') {
            $rootScope.$broadcast('sessionQADisableSaveButton');
        } else {
            $rootScope.$broadcast('sessionQAEnableSaveButton');
        }
    });
}]);

/**
 * @ngdoc controller
 * @name sessionqa-V2_0.controller:SessionQACreateHeaderController
 * @description
 * Controller for Session Q&A create header view
 */
sessionqa.controller('SessionQACreateHeaderController', ['$scope', 'qmLocalization', '$rootScope', function($scope, qmLocalization, $rootScope) {
    $scope.headerTitle = qmLocalization.getString('LABEL_SESSION_QA_TITLE');
    $scope.headerTitleTestId = 'createTitle';
    $scope.headerBackState = true;

    $scope.headerRightElements = [];
    var saveElement = {};
    saveElement.type = 'text';
    saveElement.disabled = false;
    saveElement.text = qmLocalization.getString('BUTTON_SUBMIT');
    saveElement.clickHandler = function() {
        $rootScope.$broadcast('submitSessionQuestion');
    };
    $scope.headerRightElements.push(saveElement);
    $scope.$on('sessionQAEnableSaveButton', function() {
        $scope.headerRightElements[0].disabled = false;
    });
    $scope.$on('sessionQADisableSaveButton', function() {
        $scope.headerRightElements[0].disabled = true;
    });
}]);

/**
 * Created by Andrew Lachkovics on 2016-10-27
 */

var speakers = angular.module('speakers-V2_3');

speakers.config(['$stateProvider', function($stateProvider) {
    $stateProvider
        .state('event.speakers-V2_3', {
            url: '/Speakers',
            data: {
                componentKey: 'speakers',
                resourceKey: 'speaker',
                entityType: 'speakerId'
            },
            views: {
                'component@event': {
                    templateUrl: '/asset/component/speakers/2.1/webapp/html/speaker-list.html',
                    controller: 'SpeakerListController'
                },
                'header@event': {
                    controller: 'SpeakerListHeaderController'
                }
            }
        })
        .state('event.speakers-V2_3.detail', {
            url: '/:speakerId',
            views: {
                'component@event': {
                    templateUrl: '/asset/component/speakers/2.3/webapp/html/speaker-detail.html',
                    controller: 'SpeakerDetailController'
                },
                'header@event': {
                    controller: 'SpeakerDetailHeaderController'
                }
            }
        })
        .state('event.speakers-V2_3.likes', {
            url: '/Likes/:speakerId',
            views: {
                'component@event': {
                    templateUrl: '/asset/component/speakers/2.3/webapp/html/speaker-likes.html',
                    controller: 'SpeakerLikesController'
                },
                'header@event': {
                    controller: 'SpeakerLikesHeaderController'
                }
            }
        })
        .state('event.speakers-V2_3.comments', {
            url: '/Comments/:speakerId',
            views: {
                'component@event': {
                    templateUrl: '/asset/component/speakers/2.3/webapp/html/speaker-comments.html',
                    controller: 'SpeakerCommentsController'
                },
                'header@event': {
                    controller: 'SpeakerCommentsHeaderController'
                }
            }
        })
        .state('event.speakers-V2_3.comment-editor', {
            url: '/Comment/:speakerId/:commentId',
            views: {
                'component@event': {
                    templateUrl: '/asset/component/speakers/2.3/webapp/html/speaker-comment-editor.html',
                    controller: 'SpeakerCommentEditorController'
                },
                'header@event': {
                    controller: 'SpeakerCommentEditorHeaderController'
                }
            }
        })
        .state('event.speakers-V2_3.social-comments', {
            url: '/SocialComments/:speakerId',
            views: {
                'component@event': {
                    templateUrl: '/asset/component/social/2.5/webapp/html/social-comments.html',
                    controller: 'SocialCommentsController'
                },
                'header@event': {
                    controller: 'SocialCommentsHeaderController'
                }
            }
        })
        .state('event.speakers-V2_3.social-edit-comment', {
            url: '/SocialComment/:speakerId/:commentId',
            views: {
                'component@event': {
                    templateUrl: '/asset/component/social/2.5/webapp/html/social-edit-comment.html',
                    controller: 'SocialEditCommentController'
                },
                'header@event': {
                    controller: 'SocialEditCommentHeaderController'
                }
            }
        });
}]);

var speakers = angular.module('speakers-V2_3');

/**
 * @ngdoc service
 * @name speakers-V2_3.service:qmSpeakerDeepLinkingService
 * @description
 * Manages Speaker Deep Linking
 */
speakers.factory('qmSpeakerDeepLinkingService', ['$state', 'qmSpeakerService', 'qmSpeakersDependencyService', '$rootScope',
    function($state, qmSpeakerService, qmSpeakersDependencyService, $rootScope) {
        var init = {
            /**
             * @ngdoc method
             * @name handleDeeplinkState
             * @methodOf speakers-V2_3.service:qmSpeakerDeepLinkingService
             * @description
             * Get state for speaker component deep link
             *
             * @param {string} type Type of component
             * @param {string} entity Entity key
             */
            handleDeepLinkState: function(type, entity) {
                var qmDeeplinkingService = qmSpeakersDependencyService.getDeepLinkingService('qmDeepLinkingService');
                var destroyLoginListener = $rootScope.$on('onUserLogin', function() {
                    init.handleDeepLinkState(type, entity);
                    $rootScope.$broadcast('destroyLoginListener');
                });
                $rootScope.$on('destroyLoginListener', function() {
                    destroyLoginListener();
                });

                if (!entity || type === 'components') {
                    $state.go('event.speakers');
                } else {
                    qmSpeakerService.getDetail(entity).then(function(speakerResponse) {
                        if (!speakerResponse) {
                            qmDeeplinkingService.handleUnavailableDeeplink(type, entity);
                            return;
                        }
                        $state.go('event.speakers.detail', {speakerId: entity});
                    }, function(err) {
                        err.config.errorHandled = true;
                        if (err.status !== 401) {
                            qmDeeplinkingService.handleUnavailableDeeplink(type, entity);
                        }
                    });
                }
            }
        };

        return init;
    }]);

/**
 * Created by Andrew Lachkovics on 2016-10-31
 */

var speakers = angular.module('speakers-V2_3');

/**
 * @ngdoc service
 * @name speakers-V2_3.service:qmSpeakerService
 * @description
 * Manages REST API communication for Speakers
 */
speakers.factory('qmSpeakerService',
    ['qmRest', '$q', 'qmLocalization', 'qmSpeakersDependencyService', 'qmWebAppService',
        function(qmRest, $q, qmLocalization, qmSpeakersDependencyService, qmWebAppService) {
            return {
                /**
                 * @ngdoc method
                 * @name getCommentListState
                 * @methodOf speakers-V2_3.service:qmSpeakerService
                 * @description
                 * Get comments list page state for UI router
                 * * Get entity detail page state for UI router
                 * NEW function for V2_3
                 *
                 * @returns {string} UI router state
                 */
                getCommentListState: function() {
                    return 'event.speakers.comments';
                },
                /**
                 * @ngdoc method
                 * @name getDetailState
                 * @methodOf speakers-V2_3.service:qmSpeakerService
                 * @description
                 * Get entity detail page state for UI router
                 * NEW function for V2_3
                 *
                 * @returns {string} UI router state
                 */
                getDetailState: function() {
                    return 'event.speakers.detail';
                },
                /**
                 * @ngdoc method
                 * @name getList
                 * @methodOf speakers-V2_3.service:qmSpeakerService
                 * @description
                 * Get list of speakers
                 *
                 * @param {string} Search term
                 * @param {int}    Zero based offset
                 * @param {int}    Limit the response items
                 *
                 * @returns {promise} Promise that resolves when speaker list has been retrieved
                 */
                getList: function(searchTerm, offset, limit) {
                    searchTerm = (typeof searchTerm === 'undefined') ? '' : searchTerm;
                    offset = (typeof offset === 'undefined') ? 0 : offset;
                    limit = (typeof limit === 'undefined') ? 0 : limit;

                    var queryParams = [];
                    queryParams.push('searchTerm=' + encodeURIComponent(searchTerm));
                    queryParams.push('offset=' + offset);
                    queryParams.push('limit=' + limit);
                    queryParams.push('locale=' + qmLocalization.getLocale());

                    var url = qmWebAppService.getBaseRestUrl('component', 'speakers') + '/speakers?' + queryParams.join('&');
                    var defer = $q.defer();
                    // get speaker list data
                    qmRest.get(url).then(function(data) {
                        var speakerList = data;
                        // use qmRest service to get image data and store in imageSrc
                        for (var i = 0; i < speakerList.length; i++) {
                            speakerList[i].fullName = speakerList[i].firstName + ' ' + speakerList[i].lastName;
                            if (speakerList[i].smallImageUrl && speakerList[i].smallImageUrl !== '') {
                                speakerList[i].imageSrc = qmWebAppService.appendAuthHeader(speakerList[i].smallImageUrl);
                            }
                        }
                        defer.resolve(speakerList);
                    }, function(err) {
                        defer.reject(err);
                    });

                    return defer.promise;
                },
                /**
                 * @ngdoc method
                 * @name getDetail
                 * @methodOf speakers-V2_3.service:qmSpeakerService
                 * @description
                 * Get details of a speaker
                 *
                 * @param {string} speakerId Speaker Id
                 * @returns {promise} Promise that resolves when speaker details has been retrieved
                 */
                getDetail: function(speakerId, checkEvents) {

                    var url = qmWebAppService.getBaseRestUrl('component', 'speakers') + '/speakers/' + speakerId;
                    url += '?locale=' + qmLocalization.getLocale();
                    var defer = $q.defer();
                    // get speaker data
                    qmRest.get(url).then(function(data) {

                        data['events'] = [];

                        if (data.mediumImageUrl && data.mediumImageUrl !== '') {
                            data.imageSrc = qmWebAppService.appendAuthHeader(data.mediumImageUrl);
                        }

                        // Now get the events with these ids
                        if (checkEvents && data['eventIds'] && data['eventIds'].length > 0) {
                            var eventService = qmSpeakersDependencyService.getEventsService('qmEventsService');
                            var events = eventService.getList(data['eventIds']);
                            events.then(function(eventData) {

                                var eventList = eventData;
                                data['events'] = eventList;
                                defer.resolve(data);
                            });
                        } else {
                            defer.resolve(data);
                        }
                    }, function(err) {
                        defer.reject(err);
                    });

                    return defer.promise;
                },
                /**
                 * @ngdoc method
                 * @name getEventSpeakers
                 * @methodOf speakers-V2_3.service:qmSpeakerService
                 * @description
                 * Get list of speakers for an event
                 *
                 * @returns {promise} Promise that resolves when speaker list has been retrieved
                 */
                getEventSpeakers: function(eventId) {

                    var url = qmWebAppService.getBaseRestUrl('component', 'speakers') + '/events/' + eventId + '/speakers';
                    url += '?locale=' + qmLocalization.getLocale();
                    var defer = $q.defer();
                    // get speaker list data
                    qmRest.get(url).then(function(data) {
                        var speakerList = data;
                        // use qmRest service to get image data and store in imageSrc
                        for (var i = 0; i < speakerList.length; i++) {
                            if (speakerList[i].smallImageUrl && speakerList[i].smallImageUrl !== '') {
                                speakerList[i].imageSrc = qmWebAppService.appendAuthHeader(speakerList[i].smallImageUrl);
                            }
                        }
                        defer.resolve(speakerList);
                    }, function(err) {
                        defer.reject(err);
                    });

                    return defer.promise;
                },
                /**
                 * @ngdoc method
                 * @name getEventSpeakersLike
                 * @methodOf speakers-V2_3.service:qmSpeakerService
                 * @description
                 * Get likes data and add them to speaker list view
                 *
                 * @param {Array} speakers list of Speakers
                 * @returns {promise} Promise that resolves when likes data have successfully been added
                 */
                getEventSpeakersSocialData: function(speakers) {

                    var defer = $q.defer();
                    var qmSocialService = qmSpeakersDependencyService.getSocialService('qmSocialService');
                    if (qmSocialService) {

                        return qmSocialService.getObjectSocialData(speakers, 'speakerId', 'speakers');
                    } else {
                        defer.reject();
                    }
                    return defer.promise;
                },
                /**
                 * @ngdoc method
                 * @name getEventSpeakersLike
                 * @methodOf speakers-V2_3.service:qmSpeakerService
                 * @description
                 * Get likes data and add them to speaker list view
                 *
                 * @param {Array} speakers list of Speakers
                 * @returns {promise} Promise that resolves when likes data have successfully been added
                 */
                getEventSpeakersLike: function(speakers) {

                    var defer = $q.defer();
                    var qmSocialService = qmSpeakersDependencyService.getSocialService('qmSocialService');
                    if (qmSocialService) {

                        return qmSocialService.getObjectSocialData(speakers, 'speakerId', 'speakers');
                    } else {
                        defer.reject();
                    }
                    return defer.promise;
                }
            };
        }]);

/**
 * Created by Andrew Lachkovics on 2016-10-07.
 */

var speakers = angular.module('speakers-V2_3');

/**
 * @ngdoc service
 * @name speakers-V2_3.service:qmSpeakersDependencyService
 * @description
 * Manages communication with dependency manager
 */
speakers.factory('qmSpeakersDependencyService', ['qmDependencyManager', function(qmDependencyManager) {
    return {
        /**
         * @ngdoc method
         * @name getTemplateUrl
         * @methodOf speakers-V2_3.service:qmSpeakersDependencyService
         * @description
         * Get template url for speakers component
         *
         * @param {string} name Template name
         * @returns {string} Template url
         */
        getTemplateUrl: function(name) {
            if (name === 'speaker-list-item') {
                return '/asset/component/speakers/2.3/webapp/html/partials/speaker-list-item.html';
            }
            return null;
        },
        /**
         * @ngdoc method
         * @name getEventsService
         * @methodOf speakers-V2_3.service:qmSpeakersDependencyService
         * @description
         * Get service from events component
         *
         * @param {string} service Service name
         * @returns {service} Angular service or null
         */
        getEventsService: function(service) {
            return qmDependencyManager.getService('events', '2.4', 'component', service);
        },
        /**
         * @ngdoc method
         * @name getSocialService
         * @methodOf speakers-V2_3.service:qmSpeakersDependencyService
         * @description
         * Get service from social component
         *
         * @param {string} service Service name
         * @returns {service} Angular service or null
         */
        getSocialService: function(service) {
            return qmDependencyManager.getService('social', '2.1', 'component', service);
        },
        /**
         * @ngdoc method
         * @name getSocialCommentService
         * @methodOf speakers-V2_3.service:qmSpeakersDependencyService
         * @description
         * Get the social service for comments
         * NEW FUNCTION IN V2_3
         *
         * @param {string} service Service name
         * @returns {service} Angular service or null
         */
        getSocialCommentService: function(service) {
            return qmDependencyManager.getService('social', '2.5', 'component', service);
        },
        /**
         * @ngdoc method
         * @name getCommentListState
         * @methodOf speakers-V2_3.service:qmSpeakersDependencyService
         * @description
         * Get comments list page state for UI router
         * NEW FUNCTION IN V2_3
         *
         * @returns {string} UI router state
         */
        getCommentListState: function() {
            var qmSocialService = this.getSocialCommentService('qmSocialService');
            if (qmSocialService) {
                return qmSocialService.getCommentListState('speakers');
            }

            return 'event.speakers.comments';
        },
        /**
         * @ngdoc method
         * @name getNewCommentState
         * @methodOf speakers-V2_3.service:qmSpeakersDependencyService
         * @description
         * Get entity detail page state for UI router
         * NEW FUNCTION IN V2_3
         *
         * @returns {string} UI router state
         */
        getNewCommentState: function() {
            var qmSocialService = this.getSocialCommentService('qmSocialService');
            if (qmSocialService) {
                return qmSocialService.getNewCommentState('speakers');
            }

            return 'event.speakers.comment-editor';
        },
        /**
         * @ngdoc method
         * @name getEditCommentState
         * @methodOf speakers-V2_3.service:qmSpeakersDependencyService
         * @description
         * Get entity detail page state for UI router
         * NEW FUNCTION IN V2_3
         *
         * @returns {string} UI router state
         */
        getEditCommentState: function() {
            var qmSocialService = this.getSocialCommentService('qmSocialService');
            if (qmSocialService) {
                return qmSocialService.getEditCommentState('speakers');
            }

            return 'event.speakers.comment-editor';
        },
        /**
         * @ngdoc method
         * @name getDeepLinkingService
         * @methodOf speakers-V2_3.service:qmSpeakersDependencyService
         * @description
         * Gets the deep linking service
         * NEW FUNCTION IN V2_3
         *
         * @param {string} service Template name
         * @returns {string} Template url
         */
        getDeepLinkingService: function(service) {
            return qmDependencyManager.getService('deep-linking', '2.0', 'service', service);
        }
    };
}]);

/**
 * Created by Alexander Sugianto on 2016-06-21.
 */

var speakers = angular.module('speakers-V2_3');

/**
 * @ngdoc controller
 * @name speakers-V2_3.controller:SpeakerListController
 * @description
 * Controller for speaker list view
 */
speakers.controller('SpeakerListController', ['$scope', 'qmSpeakerService', 'qmEventConfig', 'qmLogin', 'qmList',
    'qmLocalization', 'qmSpeakersDependencyService',
    function($scope, qmSpeakerService, qmEventConfig, qmLogin, qmList, qmLocalization, qmSpeakersDependencyService) {
        var qmSocialCheck;
        var socialConfig;
        var likesEnabled;
        var commentsEnabled;
        $scope.speakerListItemUrl = qmSpeakersDependencyService.getTemplateUrl('speaker-list-item');
        $scope.loadSpeakers = qmSpeakerService.getList();
        qmSocialCheck = qmSpeakersDependencyService.getSocialService('qmSocialService');

        if (qmSocialCheck) {
            socialConfig = qmEventConfig.getComponentConfig('social');
            likesEnabled = socialConfig['@attributes'].speakerLikes === 'true' ? true : false;
            commentsEnabled = socialConfig['@attributes'].speakerComments === 'true' ? true : false;
            $scope.enableLikes = likesEnabled && qmLogin.isLoggedIn();
            $scope.enableComments = commentsEnabled && qmLogin.isLoggedIn();
        }

        $scope.loadSpeakers.then(function(speakers) {
            qmSpeakerService.getEventSpeakersSocialData(speakers);
            $scope.speakers = qmList.groupByLetter(speakers, 'lastName');
            var componentTitle = qmLocalization.getString('componentSpeakersTitle');
            $scope.emptyText = qmLocalization.getString('LABEL_EMPTY_SPEAKERS', componentTitle);
        });
    }
]);

/**
 * @ngdoc controller
 * @name speakers-V2_3.controller:SpeakerListHeaderController
 * @description
 * Controller for speaker list header view
 */
speakers.controller('SpeakerListHeaderController', ['$scope', 'qmLocalization', function($scope, qmLocalization) {
    $scope.headerTitle = qmLocalization.getString('componentSpeakersTitle');
    $scope.headerTitleTestId = 'componentTitle';
}]);

/**
 * Created by Andrew Lachkovics on 2016-10-07.
 */

var speakers = angular.module('speakers-V2_3');

/**
 * @ngdoc controller
 * @name speakers-V2_3.controller:SpeakerDetailController
 * @description
 * Controller for speaker detail view
 */
speakers.controller('SpeakerDetailController',
    ['$scope', '$state', 'qmList', 'qmEventConfig', 'qmSpeakerService', 'qmDependencyManager', 'qmLocalization', 'qmSpeakersDependencyService', 'qmLogin',
        function($scope, $state, qmList, qmEventConfig, qmSpeakerService, qmDependencyManager, qmLocalization, qmSpeakersDependencyService, qmLogin) {
            var isLoggedIn = qmLogin.isLoggedIn();
            var hasEvents = qmDependencyManager.hasComponent('events');
            var eventsIsConfigured = qmEventConfig.isComponentConfigured('events');
            var eventsRequiresLogin = qmEventConfig.isComponentLoginRequired('events');
            var eventsLoginRequired = eventsRequiresLogin && !isLoggedIn;
            var checkEvents;
            var qmEventsDependencyService;
            var qmSocialConstant;

            checkEvents = hasEvents && eventsIsConfigured && !eventsLoginRequired;
            $scope.showEvents = false;
            $scope.eventsLoginRequired = eventsLoginRequired;
            $scope.eventsTitle = qmLocalization.getString('componentEventsTitle');
            $scope.dataTestIdObj = {title: 'speakerDetailsSchedule', expander: 'speakerSessionExpander'};
            $scope.commentsState = qmSpeakersDependencyService.getCommentListState();
            $scope.newCommentState = qmSpeakersDependencyService.getNewCommentState();
            $scope.editCommentState = qmSpeakersDependencyService.getEditCommentState();
            $scope.whoLikesState = 'event.speakers.likes';
            $scope.stateAttrs = {speakerId: $state.params.speakerId};
            $scope.loadSpeaker = qmSpeakerService.getDetail($state.params.speakerId, checkEvents);
            $scope.loadSpeaker.then(function(speaker) {
                $scope.speaker = speaker;
                if (speaker['events'].length > 0) {
                    // Show Events if there are some to show
                    qmEventsDependencyService = qmSpeakersDependencyService.getEventsService('qmEventsDependencyService');
                    $scope.eventListItemUrl = qmEventsDependencyService.getTemplateUrl('event-list-item');

                    $scope.showEvents = true;
                    $scope.speaker.events = qmList.groupByGroup(speaker['events'], 'eventDate', 'eventDateFormatted');
                }
                if (eventsLoginRequired) {
                    $scope.showEvents = true;
                }
            });
            $scope.promptLogin = function() {
                qmLogin.checkLogin().then(function() {
                    $state.reload();
                });
            };

            qmSocialConstant = qmSpeakersDependencyService.getSocialService('SOCIAL');
            if (qmSocialConstant) {
                $scope.socialResourceKey = qmSocialConstant.resource.speakers;
            }
        }]);

/**
 * @ngdoc controller
 * @name speakers-V2_3.controller:SpeakerDetailHeaderController
 * @description
 * Controller for speaker detail header view
 */
speakers.controller('SpeakerDetailHeaderController', ['$scope', 'qmLocalization', function($scope, qmLocalization) {
    $scope.headerBackState = true;
    $scope.headerTitleTestId = 'detailTitle';
    $scope.headerTitle = qmLocalization.getString('LABEL_DETAILS');
}]);

/**
 * Created by Andrew Lachkovics on 2016-09-28.
 */

var speakers = angular.module('speakers-V2_3');

/**
 * @ngdoc controller
 * @name speakers-V2_3.controller:SpeakerLikesController
 * @description
 * Controller for speaker's who likes view
 */
speakers.controller('SpeakerLikesController', ['$scope', '$state', 'qmSpeakersDependencyService', 'qmEventsDependencyService', 'qmEventConfig',
    function($scope, $state, qmSpeakersDependencyService, qmEventsDependencyService, qmEventConfig) {
        var qmSocialConstant = qmEventsDependencyService.getSocialService('SOCIAL'),
            speakersConfig,
            socialConfig,
            likesEnabled;
        $scope.speakerId = $state.params.speakerId;
        $scope.newCommentState = qmSpeakersDependencyService.getNewCommentState();
        $scope.editCommentState = qmSpeakersDependencyService.getEditCommentState();
        $scope.stateAttrs = {speakerId: $state.params.speakerId};

        if (qmSocialConstant) {
            speakersConfig = qmEventConfig.getComponentConfig('speakers');
            socialConfig = qmEventConfig.getComponentConfig('social');
            likesEnabled = socialConfig['@attributes'].speakerLikes === 'true';
            $scope.socialResourceKey = qmSocialConstant.resource.speakers;
            if ($state.params.speakerId === '' || $state.params.speakerId === 'undefined' || !likesEnabled) {
                $state.go('event.speakers', {componentId: speakersConfig['@attributes'].id}, {reload: true});
            }
        }

        // go to the comment editor
        $scope.goToCommentEditor = function() {
            $state.go('event.speakers.comment-editor', {speakerId: $state.params.speakerId});
        };
    }]);

/**
 * @ngdoc controller
 * @name speakers-V2_3.controller:SpeakerLikesHeaderController
 * @description
 * Controller for speaker's who likes header view
 */
speakers.controller('SpeakerLikesHeaderController', ['$scope', 'qmLocalization', function($scope, qmLocalization) {
    $scope.headerBackState = true;
    $scope.headerTitle = qmLocalization.getString('LABEL_TITLE_LIKES');
    $scope.headerTitleTestId = 'detailTitle';
}]);

/**
 * Created by Andrew Lachkovics on 2016-10-17.
 */

var speakers = angular.module('speakers-V2_3');

/**
 * @ngdoc controller
 * @name speakers-V2_3.controller:SpeakerCommentsController
 * @description
 * Controller for speaker's comment list view
 */
speakers.controller('SpeakerCommentsController',
    ['$scope', '$state', 'qmSpeakersDependencyService', 'qmEventsDependencyService', 'qmEventConfig', 'qmLogin', 'qmNavigation',
    function($scope, $state, qmSpeakersDependencyService, qmEventsDependencyService, qmEventConfig, qmLogin, qmNavigation) {
        var qmSocialConstant = qmEventsDependencyService.getSocialService('SOCIAL');
        var qmSocialService = qmEventsDependencyService.getSocialService('qmSocialService');
        var socialConfig;
        var commentsEnabled;

        $scope.speakerId = $state.params.speakerId;
        $scope.socialResourceKey = qmSocialConstant ? qmSocialConstant.resource.speakers : null;
        $scope.newCommentState = qmSpeakersDependencyService.getNewCommentState();
        $scope.editCommentState = qmSpeakersDependencyService.getEditCommentState();
        $scope.stateAttrs = {speakerId: $state.params.speakerId};

        if (qmSocialService) {
            // redirect if comments are not enabled or not logged in
            socialConfig = qmEventConfig.getComponentConfig('social');
            commentsEnabled = socialConfig['@attributes'].speakerComments === 'true';
            $scope.enableComments = commentsEnabled && qmLogin.isLoggedIn();
            if (!$scope.enableComments) {
                qmNavigation.goBack();
            }
        }
    }]);

/**
 * @ngdoc controller
 * @name speakers-V2_3.controller:SpeakerCommentsHeaderController
 * @description
 * Controller for speaker's comment list header view
 */
speakers.controller('SpeakerCommentsHeaderController', ['$scope', '$state', 'qmLocalization',
    function($scope, $state, qmLocalization) {
        $scope.headerBackState = true;
        $scope.headerTitle = qmLocalization.getString('LABEL_TITLE_COMMENTS');
        $scope.headerTitleTestId = 'detailTitle';
    }]);


/**
 * Created by Andrew Lachkovics on 2016-010-18.
 */

var speakers = angular.module('speakers-V2_3');

/**
 * @ngdoc controller
 * @name speakers-V2_3.controller:SpeakerCommentEditorController
 * @description
 * Controller for speaker's comment editor view for adding or editing comments
 */
speakers.controller('SpeakerCommentEditorController', ['$scope', '$rootScope', '$state', 'qmEventsDependencyService',
    function($scope, $rootScope, $state, qmEventsDependencyService) {
        var qmSocialConstant = qmEventsDependencyService.getSocialService('SOCIAL');
        $scope.commentId = $state.params.commentId;
        $scope.socialResourceKey = qmSocialConstant.resource.speakers;
        $scope.entityId = $state.params.speakerId;
    }]);

/**
 * @ngdoc controller
 * @name speakers-V2_3.controller:SpeakerCommentEditorHeaderController
 * @description
 * Controller for speaker's comment editor header view for adding or editing comments
 */
speakers.controller('SpeakerCommentEditorHeaderController', ['$scope', '$rootScope', '$state', 'qmEventsDependencyService', 'qmLocalization',
    function($scope, $rootScope, $state, qmEventsDependencyService, qmLocalization) {
        var saveElement = {type: 'icon', disabled: false, class: 'comment-save-btn fa fa-check', dataTestId: 'commentsSave'};
        var deleteElement = {type: 'icon', class: 'comment-delete-btn fa fa-trash', dataTestId: 'commentDelete'};
        var isEditComment = $state.params.commentId ? true : false;

        $scope.headerTitle = qmLocalization.getString('LABEL_TITLE_COMMENTS');
        $scope.headerBackState = true;
        $scope.headerRightElements = [];
        $scope.commentCount = 0;

        // add save button to header
        saveElement.clickHandler = function() {
            $rootScope.$broadcast('saveMyComment');
        };
        $scope.headerRightElements.push(saveElement);

        // add delete button to header and set delete actions
        if (isEditComment) {
            deleteElement.clickHandler = function() {
                $rootScope.$broadcast('deleteMyComment', {commentCount: $scope.commentCount});
            };
            $scope.headerRightElements.push(deleteElement);
        }

        // listener functions for changes in comment content
        $scope.$on('saveButtonStatus', function(event, args) {
            $scope.headerRightElements[0].disabled = !args.enable;
        });
        $scope.$on('deleteButtonStatus', function(event, args) {
            if (typeof $scope.headerRightElements[1] !== 'undefined') {
                $scope.headerRightElements[1].disabled = !args.enable;
            }
        });

        // listener to comments data
        $scope.$on('commentsData', function(event, args) {
            $scope.commentCount = args.commentsData.commentsCount;
        });
    }]);


var speakouts = angular.module('speakouts-V2_0');

speakouts.config(['$stateProvider', function($stateProvider) {
    $stateProvider
        .state('event.speakouts-V2_0', {
            url: '/Speakouts',
            views: {
                'component@event': {
                    templateUrl: '/asset/component/speakouts/2.0/webapp/html/speakouts-list.html',
                    controller: 'SpeakoutsListController'
                },
                'header@event': {
                    controller: 'SpeakoutsListHeaderController'
                }
            }
        })
        .state('event.speakouts-V2_0.create', {
            url: '/create',
            views: {
                'component@event': {
                    templateUrl: '/asset/component/speakouts/2.0/webapp/html/speakouts-create.html',
                    controller: 'SpeakoutsCreateController'
                },
                'header@event': {
                    controller: 'SpeakoutsCreateHeaderController'
                }
            }
        })
        .state('event.speakouts-V2_0.flag', {
            url: '/edit',
            views: {
                'component@event': {
                    templateUrl: '/asset/component/speakouts/2.0/webapp/html/speakouts-list.html',
                    controller: 'SpeakoutsListController'
                },
                'header@event': {
                    controller: 'SpeakoutsListHeaderController'
                }
            }
        });
}]);

var speakouts = angular.module('speakouts-V2_0');

/**
 * @ngdoc service
 * @name speakouts-V2_0.service:qmSpeakoutsService
 * @description
 * Manages REST API communication for Speakouts
 */
speakouts.factory('qmSpeakoutsService',
['qmRest', 'qmRpc', '$q', 'qmWebAppService', 'qmEventConfig', 'qmLogin', 'qmLocalization', 'qmSpeakoutsDependencyService',
function(qmRest, qmRpc, $q, qmWebAppService, qmEventConfig, qmLogin, qmLocalization, qmSpeakoutsDependencyService) {

    return {
        /**
         * @ngdoc method
         * @name create
         * @methodOf speakouts-V2_0.service:qmSpeakoutsService
         * @description
         * Create a new speakout
         *
         * @param {string} speakout Speakout content
         * @returns {promise} Promise that resolves when note has successfully been created
         */
        create: function(speakout) {
            var createUrl = qmEventConfig.getRpcUrl('submitSpeakOut');
            var defer = $q.defer();
            var params = {
                method: 'submitSpeakOut',
                params: [
                    qmLogin.getUserToken(),
                    speakout
                ]
            };
            qmRpc.post(createUrl, params).then(function(response) {
                defer.resolve(response);
            }, function(err) {
                defer.reject(err);
            });

            return defer.promise;
        },
        /**
         * @ngdoc method
         * @name flag
         * @methodOf speakouts-V2_0.service:qmSpeakoutsService
         * @description
         * Flag a speakout
         *
         * @param {string} id Speakout id
         * @returns {promise} Promise that resolves when note has successfully been deleted
         */
        flag: function(id) {
            var flagUrl = qmEventConfig.getRpcUrl('flagContent');
            var defer = $q.defer();
            var params = {
                method: 'flagContent',
                params: [
                    qmLogin.getUserToken(),
                    'SpeakOuts',
                    id
                ]
            };
            qmRpc.post(flagUrl, params).then(function(response) {
                defer.resolve(response);
            }, function(err) {
                defer.reject(err);
            });

            return defer.promise;
        },
        /**
         * @ngdoc method
         * @name getList
         * @methodOf speakouts-V2_0.service:qmSpeakoutsService
         * @description
         * Get list of speakouts
         *
         * @returns {promise} Promise that resolves when list of notes have been retrieved
         */
        getList: function() {
            var getListUrl = qmWebAppService.getBaseRestUrl('component', 'speakouts') + '/speakouts';
            var defer = $q.defer();
            // Get Speakouts list data
            qmRest.get(getListUrl).then(function(data) {
                var speakoutsList = data;

                // Get Attendees
                var speakoutsAttendeeList = [];
                if (speakoutsList.length !== 0) {
                    // Get the Attendee Ids for Speakouts
                    var attendeeIdList = [];
                    angular.forEach(speakoutsList, function(speakoutsEntry) {
                        attendeeIdList.push(speakoutsEntry);
                    });

                    // Get the subset of Attendees for Speakouts
                    var attendeesList = [];
                    var qmAttendeeService = qmSpeakoutsDependencyService.getAttendeeService('qmAttendeeService');
                    qmAttendeeService.getList(attendeeIdList).then(function(attendeesResponse) {
                        angular.forEach(attendeesResponse, function(attendee) {
                            attendeesList[attendee.attendeeId] = attendee;
                        });

                        // Merge the Speakouts data with the Attendees data
                        angular.forEach(speakoutsList, function(speakoutsEntry) {
                            var attendee = attendeesList[speakoutsEntry.attendeeId];
                            if (angular.isDefined(attendee)) {
                                var speakoutAttendee = angular.extend(speakoutsEntry, attendee);
                                speakoutsAttendeeList.push(speakoutAttendee);
                            } else {
                                speakoutsAttendeeList.push(speakoutsEntry);
                            }
                        });
                        // Return
                        defer.resolve(speakoutsAttendeeList);
                    });
                } else {
                    defer.resolve(speakoutsAttendeeList);
                }
            }, function(err) {
                defer.reject(err);
            });

            return defer.promise;
        }
    };
}]);

var speakouts = angular.module('speakouts-V2_0');

/**
 * @ngdoc service
 * @name speakouts-V2_0.service:qmSpeakoutsDependencyService
 * @description
 * Manages communication with dependency manager
 */
speakouts.factory('qmSpeakoutsDependencyService', ['qmDependencyManager', function(qmDependencyManager) {
    return {
        /**
         * @ngdoc method
         * @name getAttendeeService
         * @methodOf speakouts-V2_0.service:qmSpeakoutsDependencyService
         * @description
         * Get service from attendees component
         *
         * @param {string} service Service name
         * @returns {service} Angular service or null
         */
        getAttendeeService: function(service) {
            return qmDependencyManager.getService('attendees', '2.2', 'component', service);
        },
        /**
         * @ngdoc method
         * @name getContentModeratorService
         * @methodOf speakouts-V2_0.service:qmSpeakoutsDependencyService
         * @description
         * Get service from content moderator service
         *
         * @param {string} service Service name
         * @returns {service} Angular service or null
         */
        getContentModeratorService: function(service) {
            return qmDependencyManager.getRequiredService('content-moderator', '2.1', 'service', service);
        }
    };
}]);

var speakouts = angular.module('speakouts-V2_0');

/**
 * @ngdoc controller
 * @name speakouts-V2_0.controller:SpeakoutsListController
 * @description
 * Controller for Speakouts list view
 */
speakouts.controller('SpeakoutsListController', ['$scope', 'qmSpeakoutsService', 'qmLocalization', 'qmList', '$rootScope', '$state', 'qmEventConfig', 'toastr',
    'qmSpeakoutsDependencyService',
function($scope, qmSpeakoutsService, qmLocalization, qmList, $rootScope, $state, qmEventConfig, toastr, qmSpeakoutsDependencyService) {
    // Create speakout button clicked
    $scope.$on('createSpeakout', function() {
        $state.go('event.speakouts.create');
    });
    // Refresh speakouts button clicked
    $scope.$on('refreshSpeakouts', function() {
        if ($scope.refreshingSpeakouts !== true) {
            $scope.refreshingSpeakouts = true;
            $scope.loadSpeakouts = qmSpeakoutsService.getList();
            $scope.loadSpeakouts.then(function(speakouts) {
                $scope.speakouts = qmList.transformList(speakouts);
                $scope.emptyText = qmLocalization.getString('LABEL_EMPTY_SPEAKOUT_TITLE');
                $scope.emptySubText = qmLocalization.getString('LABEL_EMPTY_SPEAKOUT_MESSAGE');
                $scope.refreshingSpeakouts = false;
            });
        }
    });

    // Load initial speakouts list
    $scope.$broadcast('refreshSpeakouts');

    $scope.flagStatus = {};
    var modService = null;

    modService = qmSpeakoutsDependencyService.getContentModeratorService('qmContentModeratorService');
    // Get the flags from local storage for Speakouts
    var ids = modService.getFlagsFromStorage('qmSpeakoutStorage');
    // for each element set the flag status
    for (var i = 0; i < ids.length; i++) {
        $scope.flagStatus[ids[i]] = true;
    }

    // Confirmation dialog for flag speakout
    $scope.confirmTitle = qmLocalization.getString('ALERT_FLAG_CONTENT_TITLE');
    $scope.confirmContent = qmLocalization.getString('ALERT_FLAG_CONTENT_MESSAGE');
    $scope.confirmNo = qmLocalization.getString('BUTTON_CANCEL');
    $scope.confirmYes = qmLocalization.getString('BUTTON_OK');
    $scope.flagSpeakout = function(speakOutId) {
        return qmSpeakoutsService.flag(speakOutId).then(function() {

            if (modService !== null) {
                // Save new speakOutId
                modService.storeFlagForKey('qmSpeakoutStorage', speakOutId);
            }

            // Update the array
            $scope.flagStatus[speakOutId] = true;

            // Show a success message
            toastr.success(qmLocalization.getString('ALERT_FLAG_CONTENT_MESSAGE'), '', {
                closeButton: true,
                timeOut: 5000
            });
        });
    };
}]);

/**
 * @ngdoc controller
 * @name speakouts-V2_0.controller:SpeakoutsListHeaderController
 * @description
 * Controller for Speakouts list header view
 */
speakouts.controller('SpeakoutsListHeaderController', ['$scope', 'qmLocalization', '$rootScope', function($scope, qmLocalization, $rootScope) {
    $scope.headerTitle = qmLocalization.getString('componentSpeakOutTitle');

    $scope.headerRightElements = [];
    var createElement = {};
    createElement.type = 'icon';
    createElement.class = 'fa fa-pencil-square-o';
    createElement.clickHandler = function() {
        $rootScope.$broadcast('createSpeakout');
    };
    $scope.headerRightElements.push(createElement);

    var refreshElement = {};
    refreshElement.type = 'icon';
    refreshElement.class = 'fa fa-refresh';
    refreshElement.clickHandler = function() {
        $rootScope.$broadcast('refreshSpeakouts');
    };
    $scope.headerRightElements.push(refreshElement);
}]);

var speakouts = angular.module('speakouts-V2_0');

/**
 * @ngdoc controller
 * @name speakouts-V2_0.controller:SpeakoutsCreateController
 * @description
 * Controller for Speakouts create view
 */
speakouts.controller('SpeakoutsCreateController', ['$scope', 'qmSpeakoutsService', 'qmNavigation', '$state', 'toastr', 'qmLocalization', '$rootScope',
function($scope, qmSpeakoutsService, qmNavigation, $state, toastr, qmLocalization, $rootScope) {

    $scope.speakoutStatus = 'empty';

    $scope.confirm = {
        no: qmLocalization.getString('ALERT_NO'),
        yes: qmLocalization.getString('ALERT_YES'),
        title: qmLocalization.getString('ALERT_WARNING_TITLE'),
        content: qmLocalization.getString('ALERT_SPEAKOUT_CLOSE_WITHOUT_SAVING_MESSAGE'),
        isEmpty: function() {
            if (!$scope.speakout) {
                $scope.speakout = '';
            }
            return ($scope.speakoutStatus === 'empty' || $scope.speakout.length === 0);
        }
    };

    $scope.$on('saveSpeakout', function() {
        if (angular.isDefined($scope.speakout) && $scope.speakout.length !== 0) {
            if ($scope.savingSpeakout !== true) {
                $scope.savingSpeakout = true;
                qmSpeakoutsService.create($scope.speakout).then(function() {
                    // Show a success message
                    toastr.success(qmLocalization.getString('ALERT_SPEAKOUT_POST_MESSAGE'), '', {
                        closeButton: true,
                        timeOut: 5000
                    });
                    $scope.speakoutStatus = 'empty';
                    qmNavigation.goBack();
                });
            }
        }
    });

    $scope.speakoutLength = 250;
    $scope.$watch('speakout', function(newValue) {
        if (newValue && newValue.length > 250) {
            $scope.speakout = newValue.substring(0, 250);
        }
        // Must be checked against undefined or you get problems when removing text
        if (newValue !== undefined) {
            $scope.speakoutLength = 250 - newValue.length;
        }

        if (newValue === '' || typeof newValue === 'undefined') {
            $rootScope.$broadcast('speakoutDisablePostButton');
        } else {
            $rootScope.$broadcast('speakoutEnablePostButton');
        }
    });
}]);

/**
 * @ngdoc controller
 * @name speakouts-V2_0.controller:SpeakoutsCreateHeaderController
 * @description
 * Controller for Speakouts create header view
 */
speakouts.controller('SpeakoutsCreateHeaderController', ['$scope', 'qmLocalization', '$rootScope', function($scope, qmLocalization, $rootScope) {
    $scope.headerTitle = qmLocalization.getString('LABEL_NEW_POST_TITLE');
    $scope.headerBackState = true;

    $scope.headerRightElements = [];
    var saveElement = {};
    saveElement.type = 'text';
    saveElement.disabled = false;
    saveElement.text = qmLocalization.getString('BUTTON_POST');
    saveElement.clickHandler = function() {
        $rootScope.$broadcast('saveSpeakout');
    };
    $scope.headerRightElements.push(saveElement);
    $scope.$on('speakoutEnablePostButton', function() {
        $scope.headerRightElements[0].disabled = false;
    });
    $scope.$on('speakoutDisablePostButton', function() {
        $scope.headerRightElements[0].disabled = true;
    });
}]);

var sponsors = angular.module('sponsors-V2_0');

sponsors.config(['$stateProvider', function($stateProvider) {
    $stateProvider
        .state('event.sponsors-V2_0', {
            url: '/Sponsors',
            views: {
                'component@event': {
                    templateUrl: '/asset/component/sponsors/2.0/webapp/html/sponsor-list.html',
                    controller: 'SponsorListController'
                },
                'header@event': {
                    controller: 'SponsorListHeaderController'
                }
            }
        })
        .state('event.sponsors-V2_0.detail', {
            url: '/:sponsorId',
            views: {
                'component@event': {
                    templateUrl: '/asset/component/sponsors/2.0/webapp/html/sponsor-detail.html',
                    controller: 'SponsorDetailController'
                },
                'header@event': {
                    controller: 'SponsorDetailHeaderController'
                }
            }
        });
}]);

var sponsors = angular.module('sponsors-V2_0');

/**
 * @ngdoc service
 * @name sponsors-V2_0.service:qmSponsorDeepLinkingService
 * @description
 * Manages Sponsor Deep Linking
 */
sponsors.factory('qmSponsorDeepLinkingService', ['$state', 'qmSponsorsService', 'qmSponsorsDependencyService', '$rootScope',
    function($state, qmSponsorsService, qmSponsorsDependencyService, $rootScope) {
        var init = {
            /**
             * @ngdoc method
             * @name handleDeeplinkState
             * @methodOf sponsors-V2_0.service:qmSponsorDeepLinkingService
             * @description
             * Get state for sponsor component deep link
             *
             * @param {string} type Type of component
             * @param {string} entity Entity key
             */
            handleDeepLinkState: function(type, entity) {
                var qmDeeplinkingService = qmSponsorsDependencyService.getDeepLinkingService('qmDeepLinkingService');
                var destroyLoginListener = $rootScope.$on('onUserLogin', function() {
                    init.handleDeepLinkState(type, entity);
                    $rootScope.$broadcast('destroyLoginListener');
                });
                $rootScope.$on('destroyLoginListener', function() {
                    destroyLoginListener();
                });

                if (!entity || type === 'components') {
                    $state.go('event.sponsors');
                } else {
                    qmSponsorsService.getDetail(entity).then(function(sponsorResponse) {
                        if (!sponsorResponse) {
                            qmDeeplinkingService.handleUnavailableDeeplink(type, entity);
                            return;
                        }
                        $state.go('event.sponsors.detail', {sponsorId: entity});
                    }, function(err) {
                        err.config.errorHandled = true;
                        if (err.status !== 401) {
                            qmDeeplinkingService.handleUnavailableDeeplink(type, entity);
                        }
                    });
                }
            }
        };

        return init;
    }]);

var sponsors = angular.module('sponsors-V2_0');

/**
 * @ngdoc service
 * @name sponsors-V2_0.service:qmSponsorsService
 * @description
 * Manages REST API communication for Sponsors
 */
sponsors.factory('qmSponsorsService', ['qmRest', '$q', 'qmLocalization', 'qmEventConfig', '$state', 'qmWebAppService',
    function(qmRest, $q, qmLocalization, qmEventConfig, $state, qmWebAppService) {

        /**
         * Sponsor decorator
         *
         * @param {object} sponsor
         *
         * @returns {object}
         */
        function decorator(sponsor) {

            if (sponsor.imageUrl && sponsor.imageUrl !== '') {
                sponsor.imageUrl = qmWebAppService.appendAuthHeader(sponsor.imageUrl);
            } else {
                var placeHolderIconUrl = qmEventConfig.getComponentArtifact(null, 'sponsors');
                sponsor.imageUrl = qmWebAppService.appendAuthHeader(placeHolderIconUrl);
            }

            return sponsor;
        }

        return {
            /**
             * @ngdoc method
             * @name getList
             * @methodOf sponsors-V2_0.service:qmSponsorsService
             * @description
             * Get list of sponsors
             *
             * @param {string} Search term
             * @param {int}    Zero based offset
             * @param {int}    Limit number of items to return
             *
             * @returns {promise} Promise that resolves when sponsors list has been retrieved
             */
            getList: function(searchTerm, offset, limit) {
                searchTerm = (typeof searchTerm === 'undefined') ? '' : searchTerm;
                offset = (typeof offset === 'undefined') ? 0 : offset;
                limit = (typeof limit === 'undefined') ? 0 : limit;

                var queryParams = [];
                queryParams.push('searchTerm=' + encodeURIComponent(searchTerm));
                queryParams.push('offset=' + offset);
                queryParams.push('limit=' + limit);
                queryParams.push('locale=' + qmLocalization.getLocale());

                var url = qmWebAppService.getBaseRestUrl('component', 'sponsors') + '/sponsors?' + queryParams.join('&');
                var defer = $q.defer();
                qmRest.get(url).then(function(response) {
                    var sponsorsList = response;
                    for (var i = 0; i < sponsorsList.length; i++) {
                        if (!sponsorsList[i].sponsorType) {
                            sponsorsList[i].groupValue = 'GROUP_GENERAL';
                            sponsorsList[i].groupLabel = qmLocalization.getString('LABEL_GENERAL');
                        } else {
                            sponsorsList[i].groupValue = sponsorsList[i].sponsorType;
                            sponsorsList[i].groupLabel = sponsorsList[i].sponsorType;
                        }
                        decorator(sponsorsList[i]);

                    }
                    defer.resolve(sponsorsList);
                }, function(err) {
                    defer.reject(err);
                });

                return defer.promise;
            },
            /**
             * @ngdoc method
             * @name getDetail
             * @methodOf sponsors-V2_0.service:qmSponsorsService
             * @description
             * Get details of a sponsor
             *
             * @returns {promise} Promise that resolves when details of a sponsor have been retrieved
             */
            getDetail: function(sponsorId) {
                var url = qmWebAppService.getBaseRestUrl('component', 'sponsors') + '/sponsors/' + sponsorId;
                url += '?locale=' + qmLocalization.getLocale();
                var defer = $q.defer();
                qmRest.get(url).then(function(data) {
                    decorator(data);
                    defer.resolve(data);
                }, function(err) {
                    defer.reject(err);
                });

                return defer.promise;
            }
        };
    }]);

var sponsors = angular.module('sponsors-V2_0');

/**
 * @ngdoc service
 * @name sponsors-V2_0.service:qmSponsorsDependencyService
 * @description
 * Manages communication with dependency manager
 */
sponsors.factory('qmSponsorsDependencyService', ['qmDependencyManager', function(qmDependencyManager) {
    return {
        /**
         * @ngdoc method
         * @name getTemplateUrl
         * @methodOf sponsors-V2_0.service:qmSponsorsDependencyService
         * @description
         * Get template url for sponsors component
         *
         * @param {string} name Template name
         * @returns {string} Template url
         */
        getTemplateUrl: function(name) {
            if (name === 'sponsor-list-item') {
                return '/asset/component/sponsors/2.0/webapp/html/partials/sponsor-list-item.html';
            }

            return null;
        },
        /**
         * @ngdoc method
         * @name getDeepLinkingService
         * @methodOf sponsors-V2_0.service:qmSponsorsDependencyService
         * @description
         * Gets the deep linking service
         * NEW FUNCTION IN V2_0
         *
         * @param {string} name Template name
         * @returns {string} Template url
         */
        getDeepLinkingService: function(service) {
            return qmDependencyManager.getService('deep-linking', '2.0', 'service', service);
        }
    };
}]);

var sponsors = angular.module('sponsors-V2_0');
/**
 * @ngdoc controller
 * @name sponsors-V2_0.controller:SponsorListController
 * @description
 * Controller for Sponsor list view
 */
sponsors.controller('SponsorListController', ['$scope', 'qmSponsorsService', 'qmList', 'qmLocalization', 'qmSponsorsDependencyService',
    function($scope, qmSponsorsService, qmList, qmLocalization, qmSponsorsDependencyService) {
        $scope.sponsorListItemUrl = qmSponsorsDependencyService.getTemplateUrl('sponsor-list-item');
        $scope.loadSponsors = qmSponsorsService.getList();
        $scope.loadSponsors.then(function(sponsors) {
            $scope.sponsors = qmList.groupByGroup(sponsors, 'groupValue', 'groupLabel');
            var componentTitle = qmLocalization.getString('componentSponsorsTitle');
            $scope.emptyText = qmLocalization.getString('LABEL_EMPTY_SPONSORS_TITLE', componentTitle);
        });
    }
]);

/**
 * @ngdoc controller
 * @name sponsors-V2_0.controller:SponsorListHeaderController
 * @description
 * Controller for Sponsor list header view
 */
sponsors.controller('SponsorListHeaderController', ['$scope', 'qmLocalization', function($scope, qmLocalization) {
    $scope.headerTitle = qmLocalization.getString('componentSponsorsTitle');
}]);

var sponsors = angular.module('sponsors-V2_0');

/**
 * @ngdoc controller
 * @name sponsors-V2_0.controller:SponsorDetailController
 * @description
 * Controller for sponsor detail view
 */
sponsors.controller('SponsorDetailController', ['$scope', '$state', 'qmSponsorsService', 'qmUtilities',
function($scope, $state, qmSponsorsService, qmUtilities) {
    $scope.loadSponsor = qmSponsorsService.getDetail($state.params.sponsorId);
    $scope.loadSponsor.then(function(sponsor) {
        $scope.sponsor = sponsor;
        if (sponsor.url) {
            sponsor.url = qmUtilities.generateValidUrl(sponsor.url);
        }
    });
}]);
/**
 * @ngdoc controller
 * @name sponsors-V2_0.controller:SponsorDetailHeaderController
 * @description
 * Controller for sponsor detail header view
 */
sponsors.controller('SponsorDetailHeaderController', ['$scope', 'qmLocalization', function($scope, qmLocalization) {
    $scope.headerBackState = true;
    $scope.headerTitle = qmLocalization.getString('LABEL_DETAILS');
}]);

var surveys = angular.module('surveys-V2_6');

surveys.config(['$stateProvider', function($stateProvider) {
    $stateProvider
        .state('event.surveys-V2_6', {
            url: '/Surveys',
            views: {
                'component@event': {
                    templateUrl: '/asset/component/surveys/2.2/webapp/html/survey-main-list.html',
                    controller: 'SurveysMainListController'
                },
                'header@event': {
                    controller: 'SurveysMainListHeaderController'
                }
            }
        })
        .state('event.surveys-V2_6.survey', {
            url: '/survey',
            views: {
                'component@event': {
                    templateUrl: '/asset/component/surveys/2.2/webapp/html/survey-list.html',
                    controller: 'SurveysListController'
                },
                'header@event': {
                    controller: 'SurveysListHeaderController'
                }
            }

        })
        .state('event.surveys-V2_6.quiz', {
            url: '/quiz',
            views: {
                'component@event': {
                    templateUrl: 'asset/component/surveys/2.2/webapp/html/quiz-list.html',
                    controller: 'QuizListController'
                },
                'header@event': {
                    controller: 'QuizListHeaderController'
                }
            }
        })
        .state('event.surveys-V2_6.survey.detail', {
            url: '/:surveySessionId',
            views: {
                'component@event': {
                    templateUrl: '/asset/component/surveys/2.2/webapp/html/survey-detail.html',
                    controller: 'SurveyDetailController'
                },
                'header@event': {
                    controller: 'SurveyDetailHeaderController'
                }
            }
        })
        .state('event.surveys-V2_6.quiz.detail', {
            url: '/:surveySessionId',
            views: {
                'component@event': {
                    templateUrl: '/asset/component/surveys/2.2/webapp/html/quiz-detail.html',
                    controller: 'QuizDetailController'
                },
                'header@event': {
                    controller: 'QuizDetailHeaderController'
                }
            }
        });
}]);

var attendees = angular.module('surveys-V2_6');

/**
 * @ngdoc service
 * @name surveys-V2_6.service:qmSurveyDeepLinkingService
 * @description
 * Manages Survey Deep Linking
 */
attendees.factory('qmSurveyDeepLinkingService', ['$state', 'qmSurveysService', 'qmSurveysDependencyService', '$rootScope',
    function($state, qmSurveysService, qmSurveysDependencyService, $rootScope) {
        var init = {
            /**
             * @ngdoc method
             * @name handleDeeplinkState
             * @methodOf surveys-V2_6.service:qmSurveyDeepLinkingService
             * @description
             * Get state for survey component deep link
             *
             * @param {string} type Type of component
             * @param {string} entity Entity key
             */
            handleDeepLinkState: function(type, entity) {
                var validTime;
                var qmDeeplinkingService = qmSurveysDependencyService.getDeepLinkingService('qmDeepLinkingService');
                var destroyLoginListener = $rootScope.$on('onUserLogin', function() {
                    init.handleDeepLinkState(type, entity);
                    $rootScope.$broadcast('destroyLoginListener');
                });
                $rootScope.$on('destroyLoginListener', function() {
                    destroyLoginListener();
                });

                if (!entity || type === 'components') {
                    $state.go('event.surveys.survey');
                } else {
                    qmSurveysService.getSurveyDetail(entity).then(function(surveyResponse) {
                        var now = moment();

                        validTime = moment(surveyResponse.startTime).isBefore(now) && moment(surveyResponse.endTime).isAfter(now);
                        if (!surveyResponse || !validTime) {
                            qmDeeplinkingService.handleUnavailableDeeplink(type, entity);
                            return;
                        }
                        if (surveyResponse['type'] === 'Survey') {
                            $state.go('event.surveys.survey.detail', {surveySessionId: entity});
                        } else {
                            $state.go('event.surveys.quiz.detail', {surveySessionId: entity});
                        }
                    }, function(err) {
                        err.config.errorHandled = true;
                        if (err.status !== 401) {
                            qmDeeplinkingService.handleUnavailableDeeplink(type, entity);
                        }
                    });
                }
            }
        };

        return init;
    }]);

var surveys = angular.module('surveys-V2_6');

/**
 * @ngdoc service
 * @name surveys-V2_6.service:qmSurveysService
 * @description
 * Manages REST API communication for Surveys
 */
surveys.factory('qmSurveysService', ['qmRest', '$q', '$filter', 'qmWebAppService', 'qmEventConfig', 'qmLocalization',
    'qmSurveysDependencyService', 'qmLogin', 'qmRpc', '$rootScope', 'qmLocalStorage', 'qmAnalytics', 'qmMoment',
    function(qmRest, $q, $filter, qmWebAppService, qmEventConfig, qmLocalization, qmSurveysDependencyService, qmLogin, qmRpc,
$rootScope, qmLocalStorage, qmAnalytics, qmMoment) {
        var dataSource = {
            'loaded': false,
            'surveys': {
                'general': [],
                'session': []
            },
            'quizzes': {
                'general': [],
                'session': []
            }
        };

        var labelQuestionsCount = qmLocalization.getString('LABEL_SURVEY_QUESTIONS_COUNT');
        var labelQuestionCount = qmLocalization.getString('LABEL_SURVEY_QUESTION_COUNT');

        return {

            /**
             * @ngdoc method
             * @name loadDataSource
             * @methodOf surveys-V2_6.service:qmSurveysService
             * @description
             * Generate the list of surveys and quizzes from Surveys REST API and Events REST API
             *
             * @param {boolean} reload Set to true to force reload of the data source.
             *
             * @returns {promise} Promise that resolves when the data source is loaded
             */
            loadDataSource: function(reload) {
                var defer = $q.defer();
                if (!dataSource.loaded || reload === true) {
                    var that = this;
                    dataSource = {
                        'loaded': false,
                        'surveys': {
                            'general': [],
                            'session': []
                        },
                        'quizzes': {
                            'general': [],
                            'session': []
                        }
                    };
                    var surveysListPromise = this.loadSurveys();
                    var quizzesListPromise = this.loadQuizzes();
                    $q.all([surveysListPromise, quizzesListPromise]).then(function(responseArray) {
                        var surveysList = responseArray[0];
                        var quizzesList = responseArray[1];
                        var eventIds = that.getEventIds(surveysList).concat(that.getEventIds(quizzesList));
                        that.loadEvents(eventIds).then(function(events) {
                            that.setSurveys(surveysList, events, dataSource.surveys);
                            that.setSurveys(quizzesList, events, dataSource.quizzes);
                            dataSource.loaded = true;
                            defer.resolve(dataSource);
                        });
                    });
                } else {
                    defer.resolve(dataSource);
                }

                return defer.promise;
            },

            /**
             * @ngdoc method
             * @name setSurveys
             * @methodOf surveys-V2_6.service:qmSurveysService
             * @description
             * Populate general and session lists in the data source
             *
             * @param {array} list Array of survey/quiz objects
             * @param {object} events Object with eventId as the key and event/session object as the value
             * @param {object} surveyDataSource dataSource.surveys or dataSource.quizzes object
             */
            setSurveys: function(list, events, surveyDataSource) {
                var surveySessions = {};
                for (var i = 0; i < list.length; i++) {
                    var eventId = list[i].eventId;
                    list[i].questionsCountLabel = this.getQuestionsCountLabel(list[i]);
                    list[i].startTimeFormatted = qmMoment(list[i].startTime).format('dddd, LL, h:mm');
                    list[i].endTimeFormatted = qmLocalization.getString('LABEL_EXPIRES').replace('{0}',
                        qmMoment(list[i].endTime).format('dddd, LL, h:mm'));
                    if (!eventId) {
                        list[i].groupValue = 'GROUP_GENERAL';
                        list[i].groupLabel = qmLocalization.getString('LABEL_GENERAL');
                        list[i].value = 'GROUP_GENERAL';
                        surveyDataSource.general.push(list[i]);
                    } else if (events[eventId]) {
                        if (!surveySessions[eventId]) {
                            surveySessions[eventId] = [];
                        }
                        surveySessions[eventId].push(list[i]);
                    }
                }
                angular.forEach(events, function(event, sessionId) {
                    if (surveySessions[sessionId]) {
                        for (var j = 0; j < surveySessions[sessionId].length; j++) {
                            var session = surveySessions[sessionId][j];
                            session.groupValue = event.title;
                            session.groupLabel = event.title;
                            session.eventName = event.title;
                            surveyDataSource.session.push(session);
                        }
                    }
                });
            },

            /**
             * @ngdoc method
             * @name loadSurveys
             * @methodOf surveys-V2_6.service:qmSurveysService
             * @description
             * Get a list of surveys from Surveys REST API
             *
             * @returns {promise} Promise that resolves when surveys list is loaded
             */
            loadSurveys: function() {
                var url = qmWebAppService.getBaseRestUrl('component', 'surveys') + '/surveys' + '?locale=' + qmLocalization.getLocale();
                var defer = $q.defer();
                qmRest.get(url).then(function(response) {
                    defer.resolve(response);
                },
                function(err) {
                    defer.reject(err);
                });

                return defer.promise;
            },

            /**
             * @ngdoc method
             * @name loadQuizzes
             * @methodOf surveys-V2_6.service:qmSurveysService
             * @description
             * Get a list of quizzes for Surveys REST API
             *
             * @returns {promise} Promise that resolves when quizzes list is loaded
             */
            loadQuizzes: function() {
                var url = qmWebAppService.getBaseRestUrl('component', 'surveys') + '/quizzes' + '?locale=' + qmLocalization.getLocale();
                var defer = $q.defer();
                qmRest.get(url).then(function(response) {
                    defer.resolve(response);
                },
                function(err) {
                    defer.reject(err);
                });

                return defer.promise;
            },

            /**
             * @ngdoc method
             * @name getEventIds
             * @methodOf surveys-V2_6.service:qmSurveysService
             * @description
             * Extract the eventIds from the rest response
             *
             * @param {array} Array of survey/quiz objects from the Surveys REST API for lists
             *
             * @returns {array} Array of eventIds that are assigned to surveys/quizzes
             */
            getEventIds: function(restResponse) {
                var ids = [];
                for (var i = 0; i < restResponse.length; i++) {
                    if (restResponse[i].eventId) {
                        ids.push({'eventId': restResponse[i].eventId});
                    }
                }

                return ids;
            },

            /**
             * @ngdoc method
             * @name loadEvents
             * @methodOf surveys-V2_6.service:qmSurveysService
             * @description
             * Get a list of events matching the list of eventIds
             *
             * @param {array} eventIds Array of eventId list we want the event/session data for
             *
             * @returns {promise} Promise that resolves when events list is loaded
             */
            loadEvents: function(eventIds) {
                var defer = $q.defer();
                var qmEventsService = qmSurveysDependencyService.getEventsService('qmEventsService');
                var events = {};
                if (eventIds.length > 0) {
                    qmEventsService.getList(eventIds).then(function(eventsList) {
                        for (var i = 0; i < eventsList.length; i++) {
                            events[eventsList[i].eventId] = eventsList[i];
                        }
                        defer.resolve(events);
                    });
                } else {
                    defer.resolve(events);
                }

                return defer.promise;
            },

            /**
             * @ngdoc method
             * @name getSurveyList
             * @methodOf surveys-V2_6.service:qmSurveysService
             * @description
             * Get list of surveys
             *
             * @returns {array} List of general and session surveys
             */
            getSurveyList: function() {
                return dataSource.surveys.general.concat(dataSource.surveys.session);
            },

            /**
             * @ngdoc method
             * @name getQuizList
             * @methodOf surveys-V2_6.service:qmSurveysService
             * @description
             * Get list of quizzes
             *
             * @returns {array} List of general and session quizzes
             */
            getQuizList: function() {
                return dataSource.quizzes.general.concat(dataSource.quizzes.session);
            },

            /**
             * @ngdoc method
             * @name getSessionSurveyList
             * @methodOf surveys-V2_6.service:qmSurveysService
             * @description
             * Get list of surveys/quizzes for an event/session
             *
             * @returns {promise} Promise that resolves when a list of session surveys/quizzes is fetched
             */
            getSessionSurveyList: function(eventId) {
                var url = qmWebAppService.getBaseRestUrl('component', 'surveys') + '/events/' + eventId + '/surveys' + '?locale=' + qmLocalization.getLocale();
                var defer = $q.defer();
                var that = this;
                var submittedSessionIds = this.getAnonymousSubmissionSurveySessionIds();

                qmRest.get(url).then(function(list) {
                    angular.forEach(list, function(item) {
                        item.questionsCountLabel = that.getQuestionsCountLabel(item);

                        if (!qmLogin.isLoggedIn()) {
                            item.accessAllowed = !(item.multipleSubmission === '0' && submittedSessionIds.indexOf(item.surveySessionId) > -1);
                        } else {
                            item.accessAllowed = !(item.multipleSubmission === '0' && item.isSubmitted);
                        }
                    });

                    defer.resolve(list);
                },
                function(err) {
                    defer.reject(err);
                });

                return defer.promise;
            },

            /**
             * @ngdoc method
             * @name getGameCenterQuizList
             * @methodOf surveys-V2_6.service:qmSurveysService
             * @description
             * Get list of general quizzes used for game center quiz game activity
             *
             * @returns {array} Promise that resolves when the list of general quizzes is fetched
             */
            getGameCenterQuizList: function() {
                var url = qmWebAppService.getBaseRestUrl('component', 'surveys') + '/game-quizzes' + '?locale=' + qmLocalization.getLocale();
                var defer = $q.defer();
                var that = this;
                qmRest.get(url).then(function(response) {
                    angular.forEach(response, function(item) {
                        item.questionsCountLabel = that.getQuestionsCountLabel(item);
                    });
                    defer.resolve(response);
                },
                function(err) {
                    defer.reject(err);
                });

                return defer.promise;
            },

            /**
             * @ngdoc method
             * @name stringFormat
             * @methodOf surveys-V2_6.service:qmSurveysService
             * @description
             * Replaces {0}, {1} occurrences in the string with parameters provide.
             * E.g. stringFormat("No {0} available now", "surveys") will return "No surveys available now"
             *
             * @returns {string} Formatted string
             */
            stringFormat: function() {
                var out = arguments[0];
                var pattern = '';
                for (var i = 1; i < arguments.length; i++) {
                    pattern = '\\{' + (i - 1) + '\\}';
                    out = out.replace(new RegExp(pattern, 'g'), arguments[i].toString());
                }

                return out;
            },

            /**
             * @ngdoc method
             * @name getQuestionsCountLabel
             * @methodOf surveys-V2_6.service:qmSurveysService
             * @description
             * Set the questions count label on a survey/quiz object
             *
             * @param {object} surveyItem Survey item
             * @returns {string} Questions count label
             */
            getQuestionsCountLabel: function(surveyItem) {
                var label = labelQuestionsCount;
                if (parseInt(surveyItem.questionsCount) === 1) {
                    label = labelQuestionCount;
                }

                return this.stringFormat(label, surveyItem.questionsCount);
            },

            /**
             * @ngdoc method
             * @name getSurveyDetail
             * @methodOf surveys-V2_6.service:qmSurveysService
             * @description
             * Get survey details
             *
             * @param {string} surveySessionId Survey session id
             * @param {string} type Survey type
             * @returns {array} Get survey details with survey title, question and answers
             */
            getSurveyDetail: function(surveySessionId, type) {
                var url = '';

                if (type === 'survey') {
                    url = qmWebAppService.getBaseRestUrl('component', 'surveys') + '/surveys/' + surveySessionId + '?locale=' + qmLocalization.getLocale();
                } else if (type === 'quiz') {
                    url = qmWebAppService.getBaseRestUrl('component', 'surveys') + '/quizzes/' + surveySessionId + '?locale=' + qmLocalization.getLocale();
                } else {
                    url = qmWebAppService.getBaseRestUrl('component', 'surveys') + '/survey-quiz/' + surveySessionId + '?locale=' + qmLocalization.getLocale();
                }

                var defer = $q.defer();
                qmRest.get(url).then(function(data) {
                    defer.resolve(data);
                }, function(err) {
                    defer.reject(err);
                });

                return defer.promise;
            },

            /**
             * @ngdoc method
             * @name validateSurveyForm
             * @methodOf surveys-V2_6.service:qmSurveysService
             * @description
             * Validates user input against required fields in the survey
             *
             * @param {object} survey Survey object
             * @param {object} userAnswers User answers object
             * @returns {boolean} Is valid submission
             */
            validateSurveyForm: function(survey, userAnswers) {

                var that = this;
                var validSubmission = true;

                angular.forEach(survey.questions, function(question) {
                    if (question.required === '1') {
                        if (question.type === 'Open Ended') {
                            if (!that.validateOpenEnded(question.surveyQuestionId, userAnswers)) {
                                validSubmission = false;
                            }
                        }
                        if (question.type === 'Radio Buttons') {
                            if (!that.validateRadioButtons(question.surveyQuestionId, userAnswers)) {
                                validSubmission = false;
                            }
                        }
                        if (question.type === 'Check Boxes') {
                            if (!that.validateCheckBoxes(question.surveyQuestionId, userAnswers)) {
                                validSubmission = false;
                            }
                        }
                    }
                });

                return validSubmission;
            },

            /**
             * @ngdoc method
             * @name validateOpenEnded
             * @methodOf surveys-V2_6.service:qmSurveysService
             * @description
             * Validates open ended question type against user input
             *
             * @param {string} surveyQuestionId Survey question id
             * @param {object} userAnswers User answers object
             * @returns {boolean} Is valid submission?
             */
            validateOpenEnded: function(surveyQuestionId, userAnswers) {
                var validSubmission = true;
                if (angular.isUndefined(userAnswers[surveyQuestionId])) {
                    validSubmission = false;
                }
                if (userAnswers[surveyQuestionId] === '') {
                    validSubmission = false;
                }

                return validSubmission;
            },

            /**
             * @ngdoc method
             * @name validateOpenEnded
             * @methodOf surveys-V2_6.service:qmSurveysService
             * @description
             * Validates radio buttons question type against user input
             *
             * @param {string} surveyQuestionId Survey question id
             * @param {object} userAnswers User answers object
             * @returns {boolean} Is valid submission?
             */
            validateRadioButtons: function(surveyQuestionId, userAnswers) {
                var validSubmission = true;
                if (angular.isUndefined(userAnswers[surveyQuestionId])) {
                    validSubmission = false;
                }

                return validSubmission;
            },

            /**
             * @ngdoc method
             * @name validateCheckBoxes
             * @methodOf surveys-V2_6.service:qmSurveysService
             * @description
             * Validates check boxes question type against user input
             *
             * @param {string} surveyQuestionId Survey question id
             * @param {object} userAnswers User answers object
             * @returns {boolean} Is valid submission?
             */
            validateCheckBoxes: function(surveyQuestionId, userAnswers) {
                var validSubmission = true;
                if (angular.isUndefined(userAnswers[surveyQuestionId])) {
                    validSubmission = false;
                }
                var atLeastOneCheckboxSelected = false;
                angular.forEach(userAnswers[surveyQuestionId], function(answer) {
                    if (answer === true) {
                        atLeastOneCheckboxSelected = true;
                    }
                });
                if (!atLeastOneCheckboxSelected) {
                    validSubmission = false;
                }

                return validSubmission;
            },

            /**
             * @ngdoc method
             * @name submitSurvey
             * @methodOf surveys-V2_6.service:qmSurveysService
             * @description
             * Submits a survey by calling Surveys RPC function submitSurvey
             *
             * @param {object} userAnswers User answers object
             * @param {string} surveyId Survey id
             * @param {string} surveySessionId Survey session id
             * @param {string} surveyType Survey type
             * @returns {promise} Promise that resolves when survey is submitted successfully
             */
            submitSurvey: function(userAnswers, surveyId, surveySessionId, surveyType) {
                var url = qmEventConfig.getRpcUrl('submitSurvey');
                var defer = $q.defer();
                var userAnswersFormatted = this.formatUserAnswers(userAnswers);
                var authToken = (qmLogin.isLoggedIn()) ? qmLogin.getUserToken() : '';
                var that = this;
                var params = {
                    method: 'submitSurvey',
                    params: [
                        authToken,
                        surveySessionId,
                        userAnswersFormatted
                    ]
                };
                qmRpc.post(url, params).then(function(response) {
                    qmAnalytics.markEvent('SurveySubmit', surveyId, surveySessionId);
                    defer.resolve(response);
                    if (qmLogin.isLoggedIn()) {
                        $rootScope.$broadcast('surveySubmitted', {surveySessionId: surveySessionId, surveyType: surveyType});
                    }
                    if (!qmLogin.isLoggedIn()) {
                        that.logAnonymousSubmissions(surveySessionId);
                    }
                }, function(err) {
                    defer.reject(err);
                });

                return defer.promise;
            },

            /**
             * @ngdoc method
             * @name formatUserAnswers
             * @methodOf surveys-V2_6.service:qmSurveysService
             * @description
             * Converts userAnswers JSON to an array which the submitSurvey rpc call understands.
             *
             * Example source: {"S60HEZR":"open ended reply","S60EAKR":{"S184JBDC":true,"S184VMER":false,"S184RVKY":true},"S60RQFO":"S184ABJP"}
             * Example output: [["S60HEZR","open ended reply"], ["S60EAKR","S184JBDC"], ["S60EAKR","S184RVKY"], ["S60RQFO","S184ABJP"]]
             *
             * @param {object} userAnswers User answers object
             * @returns {array} Array of formatted user answers
             */
            formatUserAnswers: function(userAnswers) {
                var formatted = [];
                for (var key in userAnswers) {
                    // if multiple choice, we loop through each answer and add to array
                    if (typeof userAnswers[key] === 'object') {
                        for (var keyMultiple in userAnswers[key]) {
                            if (userAnswers[key][keyMultiple] === true) {
                                formatted.push([key, keyMultiple]);
                            }
                        }
                    } else {
                        formatted.push([key, userAnswers[key]]);
                    }
                }

                return formatted;
            },

            /**
             * @ngdoc method
             * @name calcQuizScore
             * @methodOf surveys-V2_6.service:qmSurveysService
             * @description
             * Calculates the percentage of correct answers the user got for a given quiz
             *
             * @param {object} userAnswers User answers object
             *
             * @returns {number} Quiz score percentage
             */
            calcQuizScore: function(userAnswers) {
                var questions = userAnswers.questions.length;
                var correct = 0;
                angular.forEach(userAnswers.questions, function(questionObj) {
                    if (questionObj.answeredCorrectly) {
                        correct++;
                    }
                });
                var percentage = 0;
                if (correct !== 0) {
                    percentage = parseInt((correct / questions * 100).toFixed(0)) ;
                }

                return percentage;
            },

            /**
             * @ngdoc method
             * @name logAnonymousSubmissions
             * @methodOf surveys-V2_6.service:qmSurveysService
             * @description
             * Log the surveySessionIds for anonymous submissions in local storage
             *
             * @param {string} surveySessionId Survey session id
             */
            logAnonymousSubmissions: function(surveySessionId) {
                var submittedSessionIds = this.getAnonymousSubmissionSurveySessionIds();
                submittedSessionIds.push(surveySessionId);
                qmLocalStorage.setItem('submittedSurveySessionIds', submittedSessionIds);
            },

            /**
             * @ngdoc method
             * @name getAnonymousSubmissions
             * @methodOf surveys-V2_6.service:qmSurveysService
             * @description
             * Gets a list surveySessionIds for anonymous survey submissions
             *
             * @returns {Array} Array of submitted session ids
             */
            getAnonymousSubmissionSurveySessionIds: function() {
                var submittedSessionIds;
                submittedSessionIds = qmLocalStorage.getItem('submittedSurveySessionIds');
                if (!submittedSessionIds) {
                    submittedSessionIds = [];
                }

                return submittedSessionIds;
            }
        };
    }]);

var surveys = angular.module('surveys-V2_6');
/**
 * @ngdoc service
 * @name surveys-V2_6.service:qmSurveysDependencyService
 * @description
 * Manages communication with dependency manager
 */
surveys.factory('qmSurveysDependencyService', ['qmDependencyManager', function(qmDependencyManager) {
    return {
        /**
         * @ngdoc method
         * @name getEventsService
         * @methodOf surveys-V2_6.service:qmSurveysDependencyService
         * @description
         * Get service from events component
         *
         * @param {string} service Service name
         * @returns {service} Angular service or null
         */
        getEventsService: function(service) {
            return qmDependencyManager.getService('events', '2.4', 'component', service);
        },
        /**
         * @ngdoc method
         * @name getTemplateUrl
         * @methodOf surveys-V2_6.service:qmSurveysDependencyService
         * @description
         * Get template url for surveys component
         *
         * @param {string} name Template name
         * @returns {string} Template url
         */
        getTemplateUrl: function(name) {
            if (name === 'survey-list-item') {
                return '/asset/component/surveys/2.2/webapp/html/partials/survey-list-item.html';
            } else if (name === 'quiz-list-item') {
                return '/asset/component/surveys/2.2/webapp/html/partials/quiz-list-item.html';
            } else if (name === 'list-item') {
                return '/asset/component/surveys/2.2/webapp/html/partials/list-item.html';
            }

            return null;
        },
        /**
         * @ngdoc method
         * @name getDeepLinkingService
         * @methodOf surveys-V2_6.service:qmSurveysDependencyService
         * @description
         * Gets the deep linking service
         * NEW FUNCTION IN V2_6
         *
         * @param {string} name Template name
         * @returns {string} Template url
         */
        getDeepLinkingService: function(service) {
            return qmDependencyManager.getService('deep-linking', '2.0', 'service', service);
        }
    };
}]);

var surveys = angular.module('surveys-V2_4');

/**
 * @ngdoc controller
 * @name surveys-V2_4.controller:SurveyMainListController
 * @description
 * Controller for Surveys to check if we have regular surveys, quizzes, or both
 */
surveys.controller('SurveysMainListController', ['$scope', 'qmSurveysService', '$state', 'qmLocalization', 'qmList', 'qmLogin',
    function($scope, qmSurveysService, $state, qmLocalization, qmList, qmLogin) {
        $scope.loadDataSource = qmSurveysService.loadDataSource(true);
        $scope.loadDataSource.then(function() {
            var hasSurveys = qmSurveysService.getSurveyList().length > 0;
            var hasQuizzes = qmSurveysService.getQuizList().length > 0;
            var isLoggedIn = qmLogin.isLoggedIn();
            if (hasSurveys && hasQuizzes && isLoggedIn) {
                $scope.listItems = qmList.transformList([
                    {'state': 'event.surveys.survey', 'title': qmLocalization.getString('LABEL_SURVEYS')},
                    {'state': 'event.surveys.quiz', 'title': qmLocalization.getString('LABEL_QUIZZES')}
                ]);
            } else if (hasQuizzes && isLoggedIn) {
                $state.go('event.surveys.quiz');

            } else {
                $state.go('event.surveys.survey');
            }
        });
        var componentTitle = qmLocalization.getString('componentSurveysTitle');
        $scope.emptyText = qmLocalization.getString('LABEL_EMPTY_SURVEYS_TITLE', componentTitle);
    }]);

/**
 * @ngdoc controller
 * @name surveys-V2_4.controller:SurveysMainListController
 * @description
 * Controller for Surveys list header view
 */
surveys.controller('SurveysMainListHeaderController', ['$scope', 'qmLocalization',
    function($scope, qmLocalization) {
        $scope.headerTitle = qmLocalization.getString('componentSurveysTitle');
    }]);

var surveys = angular.module('surveys-V2_4');

/**
 * @ngdoc controller
 * @name surveys-V2_4.controller:SurveysListController
 * @description
 * Controller for Surveys to check if we have regular surveys, quizzes, or both
 */
surveys.controller('SurveysListController',
    ['$scope', 'qmSurveysService', 'qmList', 'qmLocalization', '$rootScope', '$state', 'qmLogin', 'qmSurveysDependencyService',
    function($scope, qmSurveysService, qmList, qmLocalization, $rootScope, $state, qmLogin, qmSurveysDependencyService) {
        $scope.surveyListItemUrl = qmSurveysDependencyService.getTemplateUrl('survey-list-item');
        var submittedSessionIds = qmSurveysService.getAnonymousSubmissionSurveySessionIds();
        $scope.loadDataSource = qmSurveysService.loadDataSource(true);
        $scope.loadDataSource.then(function() {
            var surveys = qmSurveysService.getSurveyList();
            for (var key in surveys) {
                if (surveys.hasOwnProperty(key)) {
                    var item = surveys[key];
                    var accessAllowed = true;
                    if (!qmLogin.isLoggedIn()) {
                        accessAllowed = !(item.multipleSubmission === '0' && submittedSessionIds.indexOf(item.surveySessionId) > -1);
                    } else {
                        accessAllowed = !(item.multipleSubmission === '0' && item.isSubmitted);
                    }
                    item.accessAllowed = accessAllowed;
                }
            }
            $scope.surveys = qmList.groupByGroup(surveys, 'groupValue', 'groupLabel');
            var hasQuizzes = qmSurveysService.getQuizList().length > 0;
            $rootScope.$broadcast('updateHeader', {'hasQuizzes': hasQuizzes});

            var componentTitle;
            if (hasQuizzes) {
                componentTitle = qmLocalization.getString('LABEL_SURVEYS');
            } else {
                componentTitle = qmLocalization.getString('componentSurveysTitle');
            }
            $scope.emptyText = qmLocalization.getString('LABEL_EMPTY_SURVEYS_TITLE', componentTitle);
        });

        $scope.gotoSurveyDetails = function(surveySessionId, clickable) {
            if (clickable) {
                $state.go('event.surveys.survey.detail', {'surveySessionId': surveySessionId});
            }
        };
    }
]);

/**
 * @ngdoc controller
 * @name surveys-V2_4.controller:SurveysListHeaderController
 * @description
 * Controller for Surveys list header view
 */
surveys.controller('SurveysListHeaderController', ['$scope', 'qmLocalization', function($scope, qmLocalization) {
    $scope.headerTitle = qmLocalization.getString('componentSurveysTitle');
    $scope.$on('updateHeader', function(event, args) {
        if (args.hasQuizzes) {
            $scope.headerBackState = '^';
            $scope.headerTitle = qmLocalization.getString('LABEL_SURVEYS');
        }
    });
}]);

var surveys = angular.module('surveys-V2_4');

/**
 * @ngdoc controller
 * @name surveys-V2_4.controller:QuizListController
 * @description
 * Controller for Survey quizzes
 */
surveys.controller('QuizListController', ['$scope', 'qmSurveysService', 'qmList', 'qmLocalization', '$rootScope', 'qmSurveysDependencyService',
    function($scope, qmSurveysService, qmList, qmLocalization, $rootScope, qmSurveysDependencyService) {
        $scope.quizListItemUrl = qmSurveysDependencyService.getTemplateUrl('quiz-list-item');
        $scope.loadDataSource = qmSurveysService.loadDataSource(true);
        $scope.loadDataSource.then(function() {
            var quizzes = qmSurveysService.getQuizList();
            $scope.quizzes = qmList.groupByGroup(quizzes, 'groupValue', 'groupLabel');
            var componentTitle = qmLocalization.getString('LABEL_QUIZZES');
            $scope.emptyText = qmLocalization.getString('LABEL_EMPTY_SURVEYS_TITLE', componentTitle);

            var hasSurveys = qmSurveysService.getSurveyList().length > 0;
            $rootScope.$broadcast('updateHeader', {'hasSurveys': hasSurveys});
        });
    }
]);

/**
 * @ngdoc controller
 * @name surveys-V2_4.controller:QuizListHeaderController
 * @description
 * Controller for Survey quizzes list header view
 */
surveys.controller('QuizListHeaderController', ['$scope', 'qmLocalization', function($scope, qmLocalization) {
    $scope.headerTitle = qmLocalization.getString('componentSurveysTitle');
    $scope.$on('updateHeader', function(event, args) {
        if (args.hasSurveys) {
            $scope.headerBackState = '^';
            $scope.headerTitle = qmLocalization.getString('LABEL_QUIZZES');
        }
    });
}]);

var surveys = angular.module('surveys-V2_5');

/**
 * @ngdoc controller
 * @name surveys-V2_5.controller:SurveyDetailController
 * @description
 * Controller for survey detail view
 */
surveys.controller('SurveyDetailController', ['$scope', '$state', 'qmSurveysService', 'toastr', 'qmLocalization', '$rootScope', 'qmNavigation',
    function($scope, $state, qmSurveysService, toastr, qmLocalization, $rootScope, qmNavigation) {

        $scope.loadSurveyDetails = qmSurveysService.getSurveyDetail($state.params.surveySessionId, 'survey');
        $scope.showSubmitButton = false;
        $scope.loadSurveyDetails.then(function(surveyDetails) {
            if (surveyDetails.isSubmitted && surveyDetails.multipleSubmission === '0') {
                $state.go('event.surveys.survey');
            }
            $scope.survey = surveyDetails;
            $scope.userAnswers = {};
            $scope.submitButtonDisabled = false;
            $scope.showSubmitButton = true;
        });

        $scope.submitSurvey = function() {
            var validSubmission = qmSurveysService.validateSurveyForm($scope.survey, $scope.userAnswers);
            if (validSubmission === true) {
                $scope.loadSubmitSurvey = qmSurveysService.submitSurvey($scope.userAnswers, $scope.survey.surveyId, $state.params.surveySessionId, 'survey');

                $scope.loadSubmitSurvey.then(function() {
                    $scope.submitButtonDisabled = true;
                    toastr.success(qmLocalization.getString('LABEL_SURVEY_COMPLETE'), '', {
                        closeButton: true,
                        timeOut: 3000,
                        onHidden: function() {
                            qmNavigation.goBack();
                        }
                    });
                }, function(reason) {
                    if (reason.error) {
                        // Show Failure Message
                        toastr.error(reason.error, '', {
                            closeButton: true,
                            timeOut: 5000
                        });
                    }
                });
            } else {
                toastr.warning(qmLocalization.getString('ALERT_SURVEY_ANSWER_ALL_QUESTIONS_MESSAGE'), '', {
                    closeButton: true,
                    timeOut: 3000
                });
            }
        };
    }]);

/**
 * @ngdoc controller
 * @name surveys-V2_5.controller:SurveyDetailHeaderController
 * @description
 * Controller for survey detail header view
 */
surveys.controller('SurveyDetailHeaderController', ['$scope', 'qmLocalization', function($scope, qmLocalization) {
    $scope.headerBackState = true;
    $scope.headerTitle = qmLocalization.getString('LABEL_DETAILS');
}]);

var surveys = angular.module('surveys-V2_4');

/**
 * @ngdoc controller
 * @name surveys-V2_4.controller:QuizDetailController
 * @description
 * Controller for quiz detail view
 */
surveys.controller('QuizDetailController', ['$scope', '$state', 'qmSurveysService', 'toastr', 'qmLocalization',
function($scope, $state, qmSurveysService, toastr, qmLocalization) {
    $scope.showSubmitButton = false;
    $scope.loadQuizDetails = qmSurveysService.getSurveyDetail($state.params.surveySessionId, 'quiz');
    $scope.loadQuizDetails.then(function(surveyDetails) {
        $scope.survey = surveyDetails;
        $scope.userAnswers = {};
        $scope.submitButtonDisabled = false;
        $scope.showSubmitButton = true;
        if (surveyDetails.isSubmitted) {
            $scope.quizScore = qmSurveysService.calcQuizScore(surveyDetails);
        }
    });

    $scope.submitSurvey = function() {
        var validSubmission = qmSurveysService.validateSurveyForm($scope.survey, $scope.userAnswers);
        if (validSubmission === true) {
            $scope.loadSubmitSurvey = qmSurveysService.submitSurvey($scope.userAnswers, $scope.survey.surveyId, $state.params.surveySessionId, 'quiz');
            $scope.loadSubmitSurvey.then(function() {
                $scope.submitButtonDisabled = true;
                toastr.success(qmLocalization.getString('LABEL_SURVEY_COMPLETE'), '', {
                    closeButton: true,
                    timeOut: 3000,
                    onHidden: function() {
                        $state.reload();
                    }
                });
            });
        } else {
            toastr.warning(qmLocalization.getString('ALERT_SURVEY_ANSWER_ALL_QUESTIONS_MESSAGE'), '', {
                closeButton: true,
                timeOut: 3000
            });
        }
    };
}]);

/**
 * @ngdoc controller
 * @name surveys-V2_4.controller:QuizDetailHeaderController
 * @description
 * Controller for quiz detail header view
 */
surveys.controller('QuizDetailHeaderController', ['$scope', 'qmLocalization', function($scope, qmLocalization) {
    $scope.headerBackState = true;
    $scope.headerTitle = qmLocalization.getString('LABEL_DETAILS');
}]);

var tellafriend = angular.module('tell-a-friend-V2_0');

tellafriend.config(['$stateProvider', function($stateProvider) {
    $stateProvider
        .state('event.tell-a-friend-V2_0', {
            url: '',
            resolve: {
                openTellAFriend: ['$stateParams', 'qmEventConfig', 'qmUtilities', 'qmLocalization',
                function($stateParams, qmEventConfig, qmUtilities, qmLocalization) {
                    var componentConfig = qmEventConfig.getComponentConfig('tell-a-friend', $stateParams.componentId);
                    var mailToLink = 'mailto:';
                    var shareAddressStringKey = componentConfig['@attributes'].shareAddressStringKey;
                    if (shareAddressStringKey) {
                        mailToLink += qmLocalization.getString(shareAddressStringKey);
                    }
                    var shareSubjectStringKey = componentConfig['@attributes'].shareSubjectStringKey;
                    var shareBodyStringKey = componentConfig['@attributes'].shareBodyStringKey;
                    if (shareSubjectStringKey || shareBodyStringKey) {
                        mailToLink += '?';
                        if (shareSubjectStringKey) {
                            mailToLink += 'subject=' + encodeURIComponent(qmLocalization.getString(shareSubjectStringKey));
                            if (shareBodyStringKey) {
                                mailToLink += '&';
                            }
                        }
                        if (shareBodyStringKey) {
                            mailToLink += 'body=' + encodeURIComponent(qmLocalization.getString(shareBodyStringKey));
                        }
                    }
                    qmUtilities.openMailtoUrl(mailToLink);
                }]
            }
        });
}]);

var tracks = angular.module('tracks-V2_0');

tracks.config(['$stateProvider', function($stateProvider) {
    $stateProvider
        .state('event.tracks-V2_0', {
            url: '/Tracks',
            views: {
                'component@event': {
                    templateUrl: '/asset/component/tracks/2.0/webapp/html/track-list.html',
                    controller: 'TracksListController'
                },
                'header@event': {
                    controller: 'TracksListHeaderController'
                }
            }
        })
        .state('event.tracks-V2_0.fullEvents', {
            url: '/FullSchedule',
            views: {
                'component@event': {
                    templateUrl: '/asset/component/events/2.9/webapp/html/event-list.html',
                    controller: 'FullEventListController'
                },
                'header@event': {
                    controller: 'FullEventListHeaderController'
                }
            }
        })
        .state('event.tracks-V2_0.events', {
            url: '/:trackId',
            params: {
                trackTitle: ''
            },
            views: {
                'component@event': {
                    templateUrl: '/asset/component/tracks/2.0/webapp/html/event-list.html',
                    controller: 'TrackEventsListController'
                },
                'header@event': {
                    controller: 'TrackEventListHeaderController'
                }
            }
        });
}]);

var tracks = angular.module('tracks-V2_0');

/**
 * @ngdoc service
 * @name tracks-V2_0.service:qmTracksService
 * @description
 * Manages REST API communication for Tracks
 */
tracks.factory('qmTracksService', ['qmRest', '$q', 'qmWebAppService', 'qmLocalization', 'qmTracksDependencyService',
function(qmRest, $q, qmWebAppService, qmLocalization, qmTracksDependencyService) {
    return {
        /**
         * @ngdoc method
         * @name getList
         * @methodOf tracks-V2_0.service:qmTracksService
         * @description
         * Get list of tracks
         *
         * @param {string} Search term
         * @param {int}    Zero based offset
         * @param {int}    Limit the response items
         *
         * @returns {promise} Promise that resolves when tracks list has been retrieved
         */
        getList: function(searchTerm, offset, limit) {
            searchTerm = (typeof searchTerm === 'undefined') ? '' : searchTerm;
            offset = (typeof offset === 'undefined') ? 0 : offset;
            limit = (typeof limit === 'undefined') ? 0 : limit;

            var queryParams = [];
            queryParams.push('searchTerm=' + encodeURIComponent(searchTerm));
            queryParams.push('offset=' + offset);
            queryParams.push('limit=' + limit);
            queryParams.push('locale=' + qmLocalization.getLocale());

            var url = qmWebAppService.getBaseRestUrl('component', 'tracks') + '/tracks?' + queryParams.join('&');

            var defer = $q.defer();
            // get tracks list data
            qmRest.get(url).then(function(data) {
                var tracksList = data;
                defer.resolve(tracksList);
            }, function(err) {
                defer.reject(err);
            });

            return defer.promise;
        },

        /**
         * @ngdoc method
         * @name getOne
         * @methodOf tracks-V2_0.service:qmTracksService
         * @description
         * Get list of eventIds within a track. This method makes two calls. First one to get the eventIds within the track
         * and the second one to get the events data passing the eventIds from the 1st call
         *
         * @param {string} trackId Track id
         *
         * @returns {promise} Promise that resolves when eventIds list has been retrieved
         */
        getOne: function(trackId) {

            var url = qmWebAppService.getBaseRestUrl('component', 'tracks') + '/tracks/' + trackId;
            url += '?locale=' + qmLocalization.getLocale();
            var defer = $q.defer();
            // get events within this track
            qmRest.get(url).then(function(data) {

                data['events'] = [];

                // Now get the events with these ids
                var eventService = qmTracksDependencyService.getEventsService('qmEventsService');
                var events = eventService.getList(data['eventIds']);
                events.then(function(eventData) {

                    data['events'] = eventData;
                    defer.resolve(data);
                });

            }, function(err) {
                defer.reject(err);
            });

            return defer.promise;
        }
    };
}]);

var tracks = angular.module('tracks-V2_0');

/**
 * @ngdoc service
 * @name tracks-V2_0.service:qmTrackDeepLinkingService
 * @description
 * Manages Track Deep Linking
 */
tracks.factory('qmTrackDeepLinkingService', ['$state', 'qmTracksService', 'qmTracksDependencyService', '$rootScope',
    function($state, qmTracksService, qmTracksDependencyService, $rootScope) {
        var init = {
            /**
             * @ngdoc method
             * @name handleDeeplinkState
             * @methodOf tracks-V2_0.service:qmTrackDeepLinkingService
             * @description
             * Get state for track component deep link
             *
             * @param {string} type Type of component
             * @param {string} entity Entity key
             */
            handleDeepLinkState: function(type, entity) {
                var qmDeeplinkingService = qmTracksDependencyService.getDeepLinkingService('qmDeepLinkingService');
                var destroyLoginListener = $rootScope.$on('onUserLogin', function() {
                    init.handleDeepLinkState(type, entity);
                    $rootScope.$broadcast('destroyLoginListener');
                });
                $rootScope.$on('destroyLoginListener', function() {
                    destroyLoginListener();
                });

                if (!entity || type === 'components') {
                    $state.go('event.tracks');
                } else {
                    qmTracksService.getOne(entity).then(function(trackResponse) {
                        if (!trackResponse) {
                            qmDeeplinkingService.handleUnavailableDeeplink(type, entity);
                            return;
                        }
                        $state.go('event.tracks.events', {trackId: entity});
                    }, function(err) {
                        err.config.errorHandled = true;
                        if (err.status !== 401) {
                            qmDeeplinkingService.handleUnavailableDeeplink(type, entity);
                        }
                    });
                }
            }
        };

        return init;
    }]);

var tracks = angular.module('tracks-V2_0');

/**
 * @ngdoc service
 * @name tracks-V2_0.service:qmTracksDependencyService
 * @description
 * Manages communication with dependency manager
 */
tracks.factory('qmTracksDependencyService', ['qmDependencyManager', function(qmDependencyManager) {
    return {
        /**
         * @ngdoc method
         * @name getEventsService
         * @methodOf tracks-V2_0.service:qmTracksDependencyService
         * @description
         * Get service from events component
         *
         * @param {string} service Service name
         * @returns {service} Angular service or null
         */
        getEventsService: function(service) {
            return qmDependencyManager.getRequiredService('events', '2.4', 'component', service);
        },

        /**
         * @ngdoc method
         * @name getTemplateUrl
         * @methodOf tracks-V2_0.service:qmTracksDependencyService
         * @description
         * Get template url for tracks component
         *
         * @param {string} name Template name
         * @returns {string} Template url
         */
        getTemplateUrl: function(name) {
            if (name === 'track-list-item') {
                return '/asset/component/tracks/2.0/webapp/html/partials/track-list-item.html';
            }

            return null;
        },

        /**
         * @ngdoc method
         * @name hasLikesOnEvent
         * @methodOf tracks-V2_0.service:qmTracksDependencyService
         * @description
         * Check if event 2.7 configurable for this project
         *
         * @returns {boolean} True or False
         */
        hasLikesOnEvent: function() {
            return qmDependencyManager.hasComponent('events', '2.7');
        },

        /**
         * @ngdoc method
         * @name getDeepLinkingService
         * @methodOf tracks-V2_0.service:qmTracksDependencyService
         * @description
         * Gets the deep linking service
         * NEW FUNCTION IN V2_0
         *
         * @param {string} service Service name
         * @returns {service} Angular service or null
         */
        getDeepLinkingService: function(service) {
            return qmDependencyManager.getService('deep-linking', '2.0', 'service', service);
        }
    };
}]);

var tracks = angular.module('tracks-V2_0');

/**
 * @ngdoc controller
 * @name tracks-V2_0.controller:TracksListController
 * @description
 * Controller for tracks list view
 */
tracks.controller('TracksListController', ['$scope', 'qmTracksService', 'qmList', 'qmLocalization', 'qmTracksDependencyService',
function($scope, qmTracksService, qmList, qmLocalization, qmTracksDependencyService) {
    $scope.trackListItemUrl = qmTracksDependencyService.getTemplateUrl('track-list-item');
    var qmEventsService = qmTracksDependencyService.getEventsService('qmEventsService');
    if (qmEventsService) {
        $scope.loadTracks = qmTracksService.getList();

        $scope.loadTracks.then(function(tracks) {
            $scope.tracks = qmList.transformList(tracks);
            if ($scope.tracks.length > 0) {
                $scope.hasTracks = true;
            } else {
                $scope.hasTracks = false;
            }
            var componentTitle = qmLocalization.getString('componentTracksTitle');
            $scope.emptyText = qmLocalization.getString('LABEL_EMPTY_TRACKS_TITLE', componentTitle);
        });
    }
}]);

/**
 * @ngdoc controller
 * @name tracks-V2_0.controller:TracksListHeaderController
 * @description
 * Controller for tracks list header view
 */
tracks.controller('TracksListHeaderController', ['$scope', 'qmLocalization', function($scope, qmLocalization) {
    $scope.headerTitle = qmLocalization.getString('componentTracksTitle');
}]);

/**
 * @ngdoc controller
 * @name tracks-V2_0.controller:FullEventListController
 * @description
 * Controller for tracks full event list view
 */
tracks.controller('FullEventListController', ['$scope', '$controller', '$timeout', 'qmTracksDependencyService',
    'qmList', 'qmLocalization', 'qmEventConfig', 'qmLogin', 'qmUtilities', 'qmMoment',
    function($scope, $controller, $timeout, qmTracksDependencyService,
             qmList, qmLocalization, qmEventConfig, qmLogin, qmUtilities, qmMoment) {
        // TODO: move full event list+search into it's own directive (this code is repeated in events)
        var qmEventsDependencyService = qmTracksDependencyService.getEventsService('qmEventsDependencyService');
        var qmEventsService = qmTracksDependencyService.getEventsService('qmEventsService');
        var qmSocialCheck = qmEventsDependencyService.getSocialService('qmSocialService');
        var commentsEnabled;
        var socialConfig;
        var likesEnabled;
        var timerPromise = null;
        var clearTimePromise = function() {
            return timerPromise ? $timeout.cancel(timerPromise) : false;
        };

        $scope.hasResults = false;
        $scope.isSearchActive = false;
        $scope.searching = false;
        $scope.searchTerms = '';
        $scope.eventListUrl = qmEventsDependencyService.getTemplateUrl('event-list');
        $scope.eventListItemUrl = qmEventsDependencyService.getTemplateUrl('event-list-item');
        $scope.loadEvents = qmEventsService.getList();
        $scope.cancelSearch = function() {
            clearTimePromise();
            $scope.hasResults = false;
            $scope.searchTerms = '';
            $scope.isSearchActive = false;
            $scope.searching = false;
        };

        $scope.$watch('searchTerms', function(newText, oldText) {
            if (newText === oldText) {
                return;
            }
            if (newText.length === 0) {
                $scope.cancelSearch();
            } else if (newText.length < 2) {
                clearTimePromise();
            } else {
                clearTimePromise();
                $scope.searching = true;
                $scope.isSearchActive = true;
                timerPromise = $timeout(function() {
                    $scope.searchResults = qmEventsService.doSearch(newText);
                    $scope.searchResults.then(function(searchResults) {
                        $scope.searching = false;
                        $scope.results = searchResults;
                        $scope.hasResults = searchResults.length > 0;
                        $scope.emptyText = qmLocalization.getString('LABEL_EMPTY_EVENTS_TITLE');
                    });
                }, 400);
            }
        });

        if (qmSocialCheck) {
            socialConfig = qmEventConfig.getComponentConfig('social');
            likesEnabled = socialConfig['@attributes'].sessionLikes === 'true';
            commentsEnabled = socialConfig['@attributes'].sessionComments === 'true';
            $scope.enableLikes = likesEnabled && qmLogin.isLoggedIn();
            $scope.enableComments = commentsEnabled && qmLogin.isLoggedIn();
        }

        $scope.loadEvents.then(function(eventList) {
            $scope.events = eventList;
            qmEventsService.getEventsLike($scope.events).then(function() {
                $scope.events = qmEventsService.handleEventList($scope.events, $scope.searchTerms);
                $scope.events = qmList.groupByGroup($scope.events, 'eventDate', 'eventDateFormatted');
            }, function() {
                $scope.events = qmEventsService.handleEventList($scope.events, $scope.searchTerms);
                $scope.events = qmList.groupByGroup($scope.events, 'eventDate', 'eventDateFormatted');
            }).finally(function() {
                //Check if event is happening soon so that the schedule would auto-scroll to day.
                var currentDate = new Date();
                currentDate.setHours(0, 0, 0, 0);
                var momentCurrentDate = qmMoment(currentDate).startOf('day');
                for (var i = 0; i < $scope.events.length; i++) {
                    var timeStringBeforeParse = $scope.events[i].value;
                    var timeStringSplit = timeStringBeforeParse.split("T");
                    // we only really care about the date
                    var tabDate = timeStringSplit[0];
                    var momentEventGroupDate = qmMoment(tabDate).startOf('day');

                    if (momentEventGroupDate.isAfter(momentCurrentDate) || momentEventGroupDate.isSame(momentCurrentDate)) {
                        var dayContainer = 'index' + $scope.events[i].value;
                        qmUtilities.scrollToLocation(dayContainer);
                        break;
                    }
                }
            });
            $scope.emptyText = qmLocalization.getString('LABEL_EMPTY_EVENTS_TITLE');
        });
    }]);

/**
 * @ngdoc controller
 * @name tracks-V2_0.controller:FullEventListHeaderController
 * @description
 * Controller for tracks full event list header view
 */
tracks.controller('FullEventListHeaderController', ['$scope', 'qmLocalization', function($scope, qmLocalization) {
    $scope.headerBackState = true;
    $scope.headerTitle = qmLocalization.getString('BUTTON_FULL_SCHEDULE');
}]);

var tracks = angular.module('tracks-V2_0');

/**
 * @ngdoc controller
 * @name tracks-V2_0.controller:TrackEventsListController
 * @description
 * Controller to show events within a track.
 */
tracks.controller('TrackEventsListController',
    ['$scope', '$rootScope', '$state', 'qmTracksService', 'qmList', 'qmLocalization', 'qmTracksDependencyService','qmUtilities', 'qmMoment',
        function($scope, $rootScope, $state, qmTracksService, qmList, qmLocalization, qmTracksDependencyService, qmUtilities, qmMoment) {
            $scope.loadEvents = qmTracksService.getOne($state.params.trackId);
            $scope.loadEvents.then(function(track) {
                var qmEventsDependencyService = qmTracksDependencyService.getEventsService('qmEventsDependencyService');
                var qmEventsService = qmTracksDependencyService.getEventsService('qmEventsService');

                if (qmEventsDependencyService) {

                    if (qmEventsService && qmTracksDependencyService.hasLikesOnEvent()) {
                        qmEventsService.getEventsLike(track['events']);
                    }

                    $scope.events = qmList.groupByGroup(track['events'], 'eventDate', 'eventDateFormatted');
                    $scope.eventListUrl = qmEventsDependencyService.getTemplateUrl('event-list');
                    $scope.eventListItemUrl = qmEventsDependencyService.getTemplateUrl('event-list-item');
                    $scope.emptyText = qmLocalization.getString('LABEL_EMPTY_EVENTS_TITLE');
                    $rootScope.$broadcast('trackLoaded', track.title);

                }
            }).finally(function() {
                //Check if event is happening soon so that the schedule would auto-scroll to day.
                var currentDate = new Date();
                currentDate.setHours(0, 0, 0, 0);
                var momentCurrentDate = qmMoment(currentDate).startOf('day');
                for (var i = 0; i < $scope.events.length; i++) {
                    var timeStringBeforeParse = $scope.events[i].value;
                    var timeStringSplit = timeStringBeforeParse.split("T");
                    // we only really care about the date
                    var tabDate = timeStringSplit[0];
                    var momentEventGroupDate = qmMoment(tabDate).startOf('day');

                    if (momentEventGroupDate.isAfter(momentCurrentDate) || momentEventGroupDate.isSame(momentCurrentDate)) {
                        var dayContainer = 'index' + $scope.events[i].value;
                        qmUtilities.scrollToLocation(dayContainer);
                        break;
                    }
                }
            });

        }]);

/**
 * @ngdoc controller
 * @name tracks-V2_0.controller:TrackEventListHeaderController
 * @description
 * Controller for track event list header view
 */
tracks.controller('TrackEventListHeaderController', ['$scope', function($scope) {
    $scope.headerBackState = true;
    $scope.$on('trackLoaded', function(event, trackTitle) {
        $scope.headerTitle = trackTitle;
    });
}]);

var venues = angular.module('venues-V2_0');

venues.config(['$stateProvider', function($stateProvider) {
    $stateProvider
        .state('event.venues-V2_0', {
            url: '/Venues',
            views: {
                'component@event': {
                    templateUrl: '/asset/component/venues/2.0/webapp/html/venue-list.html',
                    controller: 'VenueListController'
                },
                'header@event': {
                    controller: 'VenueListHeaderController'
                }
            }
        })
        .state('event.venues-V2_0.detail', {
            url: '/:venueId',
            views: {
                'component@event': {
                    templateUrl: '/asset/component/venues/2.0/webapp/html/venue-detail.html',
                    controller: 'VenueDetailController'
                },
                'header@event': {
                    controller: 'VenueDetailHeaderController'
                }
            }
        });
}]);

var venues = angular.module('venues-V2_0');

/**
 * @ngdoc service
 * @name venues-V2_0.service:qmVenuesService
 * @description
 * Manages REST API communication for Venues
 */
venues.factory('qmVenuesService', ['qmRest', '$q', 'qmLocalization', 'qmWebAppService',
function(qmRest, $q, qmLocalization, qmWebAppService) {

    return {
        /**
         * @ngdoc method
         * @name getList
         * @methodOf venues-V2_0.service:qmVenuesService
         * @description
         * Get list of venues
         *
         * @returns {promise} Promise that resolves when venues list has been retrieved
         */
        getList: function() {
            var url = qmWebAppService.getBaseRestUrl('component', 'venues') + '/venues';
            url += '?locale=' + qmLocalization.getLocale();
            var defer = $q.defer();
            qmRest.get(url).then(function(response) {
                var venuesList = response;
                for (var i = 0; i < venuesList.length; i++) {
                    if (!venuesList[i].venueType) {
                        venuesList[i].groupValue = 'GROUP_GENERAL';
                        venuesList[i].groupLabel = qmLocalization.getString('LABEL_GENERAL');
                    } else {
                        venuesList[i].groupValue = venuesList[i].venueType;
                        venuesList[i].groupLabel = venuesList[i].venueType;
                    }
                }
                defer.resolve(venuesList);
            }, function(err) {
                defer.reject(err);
            });

            return defer.promise;
        },
        /**
         * @ngdoc method
         * @name getDetail
         * @methodOf venues-V2_0.service:qmVenuesService
         * @description
         * Get details of a venue
         * @returns {promise} Promise that resolves when details of a venue have been retrieved
         */
        getDetail: function(venueId) {
            var url = qmWebAppService.getBaseRestUrl('component', 'venues') + '/venues/' + venueId;
            url += '?locale=' + qmLocalization.getLocale();
            var defer = $q.defer();
            qmRest.get(url).then(function(data) {
                if (data.mapFileUrl !== '') {
                    data.mapFileUrl = qmWebAppService.appendAuthHeader(data.mapFileUrl);
                }
                defer.resolve(data);
            }, function(err) {
                defer.reject(err);
            });

            return defer.promise;
        },
        /**
         * @ngdoc method
         * @name getEventVenues
         * @methodOf venues-V2_0.service:qmVenuesService
         * @description
         * Get list of venues for an event
         *
         * @param {string} eventId Event Id
         * @returns {promise} Promise that resolves when venues list has been retrieved
         */
        getEventVenues: function(eventId) {
            var url = qmWebAppService.getBaseRestUrl('component', 'venues') + '/events/' + eventId + '/venues';
            url += '?locale=' + qmLocalization.getLocale();
            var defer = $q.defer();
            qmRest.get(url).then(function(response) {
                var venuesList = response;

                defer.resolve(venuesList);
            }, function(err) {
                defer.reject(err);
            });

            return defer.promise;
        }
    };
}]);

var venues = angular.module('venues-V2_0');

/**
 * @ngdoc service
 * @name venues-V2_0.service:qmVenueDeepLinkingService
 * @description
 * Manages Venue Deep Linking
 */
venues.factory('qmVenueDeepLinkingService', ['$state', 'qmVenuesService', 'qmVenuesDependencyService', '$rootScope',
    function($state, qmVenuesService, qmVenuesDependencyService, $rootScope) {
        var init = {
            /**
             * @ngdoc method
             * @name handleDeeplinkState
             * @methodOf venues-V2_0.service:qmVenueDeepLinkingService
             * @description
             * Get state for venue component deep link
             *
             * @param {string} type Type of component
             * @param {string} entity Entity key
             */
            handleDeepLinkState: function(type, entity) {
                var qmDeeplinkingService = qmVenuesDependencyService.getDeepLinkingService('qmDeepLinkingService');
                var destroyLoginListener = $rootScope.$on('onUserLogin', function() {
                    init.handleDeepLinkState(type, entity);
                    $rootScope.$broadcast('destroyLoginListener');
                });
                $rootScope.$on('destroyLoginListener', function() {
                    destroyLoginListener();
                });

                if (!entity || type === 'components') {
                    $state.go('event.venues');
                } else {
                    qmVenuesService.getDetail(entity).then(function(venueResponse) {
                        if (!venueResponse) {
                            qmDeeplinkingService.handleUnavailableDeeplink(type, entity);
                            return;
                        }
                        $state.go('event.venues.detail', {venueId: entity});
                    }, function(err) {
                        err.config.errorHandled = true;
                        if (err.status !== 401) {
                            qmDeeplinkingService.handleUnavailableDeeplink(type, entity);
                        }
                    });
                }
            }
        };

        return init;
    }]);

var venues = angular.module('venues-V2_0');

/**
 * @ngdoc service
 * @name venues-V2_0.service:qmVenuesDependencyService
 * @description
 * Manages communication with dependency manager
 */
venues.factory('qmVenuesDependencyService', ['qmDependencyManager', function(qmDependencyManager) {
    return {
        /**
         * @ngdoc method
         * @name getDeepLinkingService
         * @methodOf venues-V2_0.service:qmVenuesDependencyService
         * @description
         * Gets the deep linking service
         * NEW FUNCTION IN V2_0
         *
         * @param {string} service Service name
         * @returns {service} Angular service or null
         */
        getDeepLinkingService: function(service) {
            return qmDependencyManager.getService('deep-linking', '2.0', 'service', service);
        }
    };
}]);

var venues = angular.module('venues-V2_0');
/**
 * @ngdoc controller
 * @name venues-V2_0.controller:VenueListController
 * @description
 * Controller for Venues list view
 */
venues.controller('VenueListController', ['$scope', 'qmVenuesService', 'qmList', 'qmLocalization',
    function($scope, qmVenuesService, qmList, qmLocalization) {
        $scope.loadVenues = qmVenuesService.getList();
        $scope.loadVenues.then(function(venues) {
            $scope.venues = qmList.groupByGroup(venues, 'groupValue', 'groupLabel');
            var componentTitle = qmLocalization.getString('componentVenuesTitle');
            $scope.emptyText = qmLocalization.getString('LABEL_EMPTY_VENUES', componentTitle);
        });
    }
]);

/**
 * @ngdoc controller
 * @name venues-V2_0.controller:VenueListHeaderController
 * @description
 * Controller for Venues list header view
 */
venues.controller('VenueListHeaderController', ['$scope', 'qmLocalization', function($scope, qmLocalization) {
    $scope.headerTitle = qmLocalization.getString('componentVenuesTitle');
    $scope.headerTitleTestId = 'componentTitle';
}]);

var venues = angular.module('venues-V2_0');

/**
 * @ngdoc controller
 * @name venues-V2_0.controller:VenueDetailController
 * @description
 * Controller for attendee detail view
 */
venues.controller('VenueDetailController', ['$scope', '$state', 'qmVenuesService', '$rootScope', 'qmUtilities',
function($scope, $state, qmVenuesService, $rootScope, qmUtilities) {
    $scope.loadVenue = qmVenuesService.getDetail($state.params.venueId);
    $scope.loadVenue.then(function(venue) {
        $scope.venue = venue;
        if (venue.mapFileUrl !== '') {
            $rootScope.$broadcast('hasImageMapUrl', venue.mapFileUrl);
        }
        if (venue.website) {
            venue.website = qmUtilities.generateValidUrl(venue.website);
        }
        venue.mapSrc = 'https://www.bing.com/maps/embed?h=370&w=370&cp=' + venue.latitude + '~' + venue.longitude + '&lvl=15&typ=s&sty=r&src=SHELL&FORM=MBEDV8';
    });
}]);
/**
 * @ngdoc controller
 * @name venues-V2_0.controller:VenueDetailHeaderController
 * @description
 * Controller for venue detail header view
 */
venues.controller('VenueDetailHeaderController', ['$scope', 'qmLocalization', 'qmUtilities', function($scope, qmLocalization, qmUtilities) {
    $scope.headerBackState = '^';
    $scope.headerTitle = qmLocalization.getString('LABEL_DETAILS');
    $scope.headerTitleTestId = 'detailTitle';

    $scope.$on('hasImageMapUrl', function(event, mapFileUrl) {
        $scope.headerRightElements = [];
        var mapElement = {};
        mapElement.type = 'icon';
        mapElement.class = 'fa fa-map-marker';
        mapElement.dataTestId = 'venueDetailMapIcon';
        mapElement.clickHandler = function() {
            qmUtilities.openUrl(mapFileUrl);
        };
        $scope.headerRightElements.push(mapElement);
    });
}]);

var videos = angular.module('videos-V2_0');

videos.config(['$stateProvider', function($stateProvider) {
    $stateProvider
        .state('event.videos-V2_0', {
            url: '/Videos',
            views: {
                'component@event': {
                    templateUrl: '/asset/component/videos/2.0/webapp/html/video-list.html',
                    controller: 'VideoListController'
                },
                'header@event': {
                    controller: 'VideoHeaderController'
                }
            }
        });
}]);

var videos = angular.module('videos-V2_0');

/**
 * @ngdoc service
 * @name videos-V2_0.service:qmVideoService
 * @description
 * Manages REST API communication for Videos
 */
videos.factory('qmVideoService', ['qmRest', '$q', 'qmLocalization', 'qmEventConfig', '$state', 'qmWebAppService',
    function(qmRest, $q, qmLocalization, qmEventConfig, $state, qmWebAppService) {

        return {
            /**
             * @ngdoc method
             * @name getList
             * @methodOf videos-V2_0.service:qmVideoService
             * @description
             * Get list of videos
             *
             * @returns {promise} Promise that resolves when list of videos have been retrieved
             */
            getList: function() {
                var getListUrl = qmWebAppService.getBaseRestUrl('component', 'videos') + '/videos?locale=' + qmLocalization.getLocale();

                var defer = $q.defer();

                qmRest.get(getListUrl).then(function(data) {

                    var videoList = data;

                    // use qmRest service to get image data and store in imageSrc
                    for (var i = 0; i < videoList.length; i++) {
                        if (videoList[i].imageUrl && videoList[i].imageUrl !== '') {
                            videoList[i].imageSrc = qmWebAppService.appendAuthHeader(videoList[i].imageUrl);
                        } else {
                            var placeHolderIconUrl = qmEventConfig.getComponentArtifact(null, 'videos');
                            videoList[i].imageSrc = qmWebAppService.appendAuthHeader(placeHolderIconUrl);
                        }
                    }
                    defer.resolve(videoList);
                }, function(err) {
                    defer.reject(err);
                });

                return defer.promise;
            }
        };
    }]);

var videos = angular.module('videos-V2_0');

/**
 * @ngdoc controller
 * @name videos-V2_0.controller:VideoListController
 * @description
 * Controller for Videos list view
 */
videos.controller('VideoListController', ['$scope', '$injector', 'qmVideoService', 'qmList', 'qmLocalization', 'qmUtilities',
    function($scope, $injector, qmVideoService, qmList, qmLocalization, qmUtilities) {
        $scope.loadVideos = qmVideoService.getList();

        $scope.loadVideos.then(function(videos) {
            videos.forEach(function(video) {
                if (video.WebSiteUrl) {
                    video.WebSiteUrl = qmUtilities.generateValidUrl(video.WebSiteUrl);
                }
            });
            $scope.videos = qmList.transformList(videos);
            var componentTitle = qmLocalization.getString('componentVideosTitle');
            $scope.emptyText = qmLocalization.getString('LABEL_EMPTY_VIDEOS', componentTitle);
        });

        $scope.gotoUrl = function(videoUrl) {
            if (videoUrl !== '') {
                qmUtilities.openUrl(videoUrl);
            }
        };
    }
]);

/**
 * @ngdoc controller
 * @name videos-V2_0.controller:VideoHeaderController
 * @description
 * Controller for Videos list header view
 */
videos.controller('VideoHeaderController', ['$scope', 'qmLocalization', function($scope, qmLocalization) {
    $scope.headerTitle = qmLocalization.getString('componentVideosTitle');
    $scope.headerTitleTestId = 'componentTitle';
}]);

var webview = angular.module('webview-V2_0');

webview.config(['$stateProvider', function($stateProvider) {
    $stateProvider
        .state('event.webview-V2_0', {
            views: {
            },
            resolve: {
                openWebview: ['$stateParams', 'qmEventConfig', 'qmUtilities', '$state', '$q', function($stateParams, qmEventConfig, qmUtilities, $state, $q) {
                    var componentConfig = qmEventConfig.getComponentConfig('webview', $stateParams.componentId);
                    var url = componentConfig['@attributes'].url;
                    var defer = $q.defer();
                    if (qmUtilities.openUrl(url)) {
                        $state.go('event', $state.params, {
                            reload: true,
                            priority: 10
                        });
                    } else {
                        defer.resolve();
                    }
                    return defer.promise;
                }]
            }
        });
}]);

var whatsonnow = angular.module('whats-on-now-V2_0');

whatsonnow.config(['$stateProvider', function($stateProvider) {
    $stateProvider
        .state('event.whats-on-now-V2_0', {
            url: '/WhatsOnNow',
            views: {
                'component@event': {
                    templateUrl: '/asset/component/whats-on-now/2.0/webapp/html/whatsonnow-list.html',
                    controller: 'WhatsonnowListController'
                },
                'header@event': {
                    controller: 'WhatsonnowListHeaderController'
                }
            }
        });
}]);

var whatsonnow = angular.module('whats-on-now-V2_0');

/**
 * @ngdoc controller
 * @name whats-on-now-V2_0.controller:WhatsonnowListController
 * @description
 * Controller for attendee list view
 */
whatsonnow.controller('WhatsonnowListController', ['$scope', 'qmWhatsOnNowDependencyService', 'qmList', 'qmLocalization',
    function($scope, qmWhatsOnNowDependencyService, qmList, qmLocalization) {
        var qmEventsService = qmWhatsOnNowDependencyService.getEventsService('qmEventsService');
        $scope.loadWhatsonnow = qmEventsService.getWhatsOnNow();
        $scope.loadWhatsonnow.then(function(whatsonnow) {
            $scope.whatsonnow = qmList.transformList(whatsonnow);
            $scope.emptyText = qmLocalization.getString('LABEL_EMPTY_WHATS_ON_TITLE');
            $scope.emptySubText = qmLocalization.getString('LABEL_EMPTY_WHATS_ON_MESSAGE');
        });
    }
]);

/**
 * @ngdoc controller
 * @name whats-on-now-V2_0.controller:WhatsonnowListHeaderController
 * @description
 * Controller for whatsonnow list header view
 */
whatsonnow.controller('WhatsonnowListHeaderController', ['$scope', 'qmLocalization', function($scope, qmLocalization) {
    $scope.headerTitle = qmLocalization.getString('componentWhatsonnowTitle');
}]);

var whatsonnow = angular.module('whats-on-now-V2_0');
/**
 * @ngdoc service
 * @name whats-on-now-V2_0.service:qmWhatsOnNowDependencyService
 * @description
 * Manages communication with dependency manager
 */
whatsonnow.factory('qmWhatsOnNowDependencyService', ['qmDependencyManager', function(qmDependencyManager) {
    return {
        /**
         * @ngdoc method
         * @name getEventsService
         * @methodOf whats-on-now-V2_0.service:qmWhatsOnNowDependencyService
         * @description
         * Get service from events component
         *
         * @param {string} service Service name
         * @returns {service} Angular service or null
         */
        getEventsService: function(service) {
            return qmDependencyManager.getRequiredService('events', '2.4', 'component', service);
        }
    };
}]);

