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

attendees.config(['$stateProvider', function($stateProvider) {
    $stateProvider
            .state('event.attendees-V2_5', {
                url: '/Attendees',
                views: {
                    'component@event': {
                        controller: 'AttendeesMainController'
                    }
                },
                resolve: ['qmAttendeeService', function(qmAttendeeService) {
                    return qmAttendeeService.loadCategories();
                }]
            })
            .state('event.attendees-V2_5.category', {
                url: '',
                views: {
                    'component@event': {
                        templateUrl: '/asset/component/attendees/2.3/webapp/html/attendee-main.html',
                        controller: 'AttendeesMainListController'
                    },
                    'header@event': {
                        controller: 'AttendeeCategoriesHeaderController'
                    }
                }
            })
            .state('event.attendees-V2_5.list', {
                url: '/:categoryId',
                views: {
                    'component@event': {
                        templateUrl: '/asset/component/attendees/2.3/webapp/html/attendee-main.html',
                        controller: 'AttendeesMainListController'
                    },
                    'header@event': {
                        controller: 'AttendeeListHeaderController'
                    }
                }
            })
            .state('event.attendees-V2_5.detail', {
                url: '/:categoryId/:attendeeId',
                views: {
                    'component@event': {
                        templateUrl: '/asset/component/attendees/2.4/webapp/html/attendee-detail.html',
                        controller: 'AttendeeDetailController'
                    },
                    'header@event': {
                        controller: 'AttendeeDetailHeaderController'
                    }
                }
            });
}]);

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

/**
 * @ngdoc service
 * @name attendees-V2_3.service:qmAttendeeService
 * @description
 * Manages REST API communication for Attendees
 */
attendees.factory('qmAttendeeService', ['qmRest', 'qmRpc', '$q', 'qmEventConfig', 'qmLocalization',
    'qmLogin', 'qmUtilities', 'qmAttendeeDependencyService', 'qmWebAppService',
    function(qmRest, qmRpc, $q, qmEventConfig, qmLocalization,
             qmLogin, qmUtilities, qmAttendeeDependencyService, qmWebAppService) {
        var categories = [];
        var currentTab = null;

        var init = {
            /**
             * @ngdoc method
             * @name setTab
             * @methodOf attendees-V2_3.service:qmAttendeeService
             * @description
             * Set current tab
             *
             * @param {string} tab Tab key
             */
            setTab: function(tab) {
                currentTab = tab;
            },
            /**
             * @ngdoc method
             * @name getTab
             * @methodOf attendees-V2_3.service:qmAttendeeService
             * @description
             * Get last active tab and erase it
             *
             * @returns {string} last active tab key
             */
            getTab: function() {
                var out = currentTab;
                currentTab = null;

                return out;
            },
            /**
             * @ngdoc method
             * @name decorator
             * @methodOf attendees-V2_3.service:qmAttendeeService
             * @description
             * Decorator for attendee object
             *
             * @returns {object} attendee
             */
            decorator: function(attendee) {

                if (attendee.imageUrl && attendee.imageUrl !== '') {
                    attendee.imageSrc = qmWebAppService.appendAuthHeader(attendee.imageUrl);
                }
                if ('firstName' in attendee && 'lastName' in attendee) {
                    attendee.fullName = attendee.firstName + ' ' + attendee.lastName;
                } else {
                    attendee.fullName = '';
                }
                if (attendee.website && attendee.website !== '') {

                    attendee.website = qmUtilities.generateValidUrl(attendee.website);
                }

                return attendee;
            },
            /**
             * @ngdoc method
             * @name loadCategories
             * @methodOf attendees-V2_3.service:qmAttendeeService
             * @description
             * Load list of categories
             *
             * @returns {promise} Promise that resolves when attendee category list has been retrieved
             */
            loadCategories: function() {
                var defer = $q.defer();

                var useCategories = init.isCategoriesAvailable();

                if (useCategories) {
                    var qmCategoriesService = qmAttendeeDependencyService.getCategoriesService('qmCategoriesService');
                    qmCategoriesService.getListCategoriesForEntities('attendee').then(function(response) {
                        categories = response;
                        defer.resolve({categories: response, success: true});
                    }, function(err) {
                        defer.resolve({categories: [], success: false, msg: err});
                    });
                } else {
                    defer.resolve({categories: [], success: true});
                }

                return defer.promise;
            },
            /**
             * @ngdoc method
             * @name getCategories
             * @methodOf attendees-V2_3.service:qmAttendeeService
             * @description
             * Get list of categories
             *
             * @returns {array} list of attendee categories
             */

            getCategories: function() {
                var allCategories = [{categoryId: '', title: qmLocalization.getString('LABEL_ALL')}];

                return allCategories.concat(categories);
            },
            /**
             * @ngdoc method
             * @name getCategoryTitle
             * @methodOf attendees-V2_3.service:qmAttendeeService
             * @description
             * Get category title by categoryId
             *
             * @returns {string} category title
             */
            getCategoryTitle: function(categoryId) {
                var title = '';
                angular.forEach(categories, function(category) {
                    if (category.categoryId === categoryId) {
                        title = category.title;

                        return false;
                    }
                });

                return title;
            },
            /**
             * @ngdoc method
             * @name removeAttendeePhoto
             * @methodOf attendees-V2_3.service:qmAttendeeService
             * @description
             * if set photo not show, remove attendee's imgSrc
             *
             * @param {Array} attendees List of attendees
             * @returns {Array} List of attendees
             */
            removeAttendeePhoto: function(attendees) {
                var returnAttendee = [];
                angular.forEach(attendees, function(attendee) {
                    if (attendee.imageSrc) {
                        delete attendee.imageSrc;
                    }
                    returnAttendee.push(attendee);
                });

                return returnAttendee;
            },
            /**
             * @ngdoc method
             * @name getListByCategory
             * @methodOf attendees-V2_3.service:qmAttendeeService
             * @description
             * Get list of attendees for category
             *
             * @returns {promise} Promise that resolves when attendee list has been retrieved
             */
            getListByCategory: function(categoryId, 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);

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

                var defer = $q.defer();
                // get attendee list data
                qmRest.get(url).then(function(data) {
                    var attendeeList = data;
                    // use qmRest service to get image data and store in imageSrc
                    for (var i = 0; i < attendeeList.length; i++) {
                        init.decorator(attendeeList[i]);
                    }
                    defer.resolve(attendeeList);
                }, function(err) {
                    defer.reject(err);
                });

                return defer.promise;
            },
            /**
             * @ngdoc method
             * @name getList
             * @methodOf attendees-V2_3.service:qmAttendeeService
             * @description
             * Get list of attendees
             *
             * @returns {promise} Promise that resolves when attendee list has been retrieved
             */
            getList: function(attendeeIds, searchTerm, offset, limit, allowMessage) {
                var queryParams = [];
                if (typeof searchTerm !== 'undefined') {
                    queryParams.push('searchTerm=' + encodeURIComponent(searchTerm));
                }
                if (typeof offset !== 'undefined') {
                    queryParams.push('offset=' + offset);
                }
                if (typeof limit !== 'undefined') {
                    queryParams.push('limit=' + limit);
                }
                if (typeof allowMessage !== 'undefined') {
                    queryParams.push('allowMessage=' + allowMessage);
                }

                if (attendeeIds !== undefined && attendeeIds.length) {
                    for (var i = 0; i < attendeeIds.length; i++) {
                        queryParams.push('attendeeIds[]=' + encodeURIComponent(attendeeIds[i].attendeeId));
                    }
                }
                var url = qmWebAppService.getBaseRestUrl('component', 'attendees') + '/attendees?' + queryParams.join('&');
                var defer = $q.defer();
                // get attendee list data
                qmRest.get(url).then(function(data) {
                    var attendeeList = data;
                    // use qmRest service to get image data and store in imageSrc
                    for (var i = 0; i < attendeeList.length; i++) {

                        // Angular needs a true/false value when using ng-show. O or 1 will not work
                        var attendee = attendeeList[i];
                        attendee.allowMessage = (attendee.allowMessage === '0') ? false : true;

                        attendeeList[i] = attendee;
                        init.decorator(attendeeList[i]);
                    }
                    defer.resolve(attendeeList);
                }, function(err) {
                    defer.reject(err);
                });

                return defer.promise;
            },
            /**
             * @ngdoc method
             * @name getListObject
             * @methodOf attendees-V2_3.service:qmAttendeeService
             * @description
             * Get attendees data for specified resources and decorate object with attendee properties
             *
             * @param {array} objects containing one property with attendeeId
             * @param {string} keyField is the name of the attendeeId property in objects
             * @returns {promise} Promise that resolves when attendee list has been retrieved
             */
            getListObject: function(objects, keyField) {
                keyField = angular.isDefined(keyField) ? keyField : 'attendeeId';
                var defer = $q.defer();
                var i;
                var max;
                var unknownAttendeeImgSrc = '/asset/service/web-app/2.0/images/image_attendee_default.png';
                var uniqueAttendeeIds = [];
                var formattedAttendeesIdList = [];
                var item;
                var attendeeId;
                var profile = qmLogin.getUserInfo();
                var assignAttendeeSettings = function(attendeeSrc, attendeeDest, unpublished) {
                    attendeeDest.imageSrc = angular.isDefined(attendeeSrc.imageSrc) && attendeeSrc.imageSrc;
                    attendeeDest.attendeeId = attendeeSrc.attendeeId;
                    attendeeDest.company = attendeeSrc.company;
                    attendeeDest.email = attendeeSrc.email;
                    attendeeDest.firstName = attendeeSrc.firstName;
                    attendeeDest.lastName = attendeeSrc.lastName;
                    attendeeDest.title = attendeeSrc.title;
                    attendeeDest.imageSrc = attendeeSrc.imageSrc;
                    attendeeDest.unpublished = unpublished;
                };

                if (objects.length < 1) {
                    defer.resolve();
                    return defer.promise;
                }

                for (i = 0, max = objects.length; i < max; i += 1) {
                    item = objects[i];
                    attendeeId = item[keyField];
                    if (angular.isUndefined(uniqueAttendeeIds[attendeeId])) {
                        formattedAttendeesIdList.push({attendeeId: attendeeId});
                        uniqueAttendeeIds[attendeeId] = {
                            unpublished: true,
                            firstName: qmLocalization.getString('LABEL_ANONYMOUS'),
                            lastName: ' ',
                            imageSrc: unknownAttendeeImgSrc
                        };
                    }
                    objects[i].attendee = uniqueAttendeeIds[attendeeId];
                }

                init.getList(formattedAttendeesIdList).then(function(attendeesList) {
                    for (i = 0, max = attendeesList.length; i < max; i += 1) {
                        assignAttendeeSettings(attendeesList[i], uniqueAttendeeIds[attendeesList[i].attendeeId], false);
                    }
                    if (angular.isDefined(profile) &&
                        angular.isDefined(uniqueAttendeeIds[profile.attendeeId]) &&
                        uniqueAttendeeIds[profile.attendeeId].unpublished) {
                        profile.imageSrc = qmWebAppService.appendAuthHeader(profile.smallImageUrl);
                        assignAttendeeSettings(profile, uniqueAttendeeIds[profile.attendeeId], true);
                    }
                    defer.resolve();
                }, function(err) {
                    defer.reject(err);
                });

                return defer.promise;
            },
            /**
             * @ngdoc method
             * @name getDetail
             * @methodOf attendees-V2_3.service:qmAttendeeService
             * @description
             * Get details of an attendee
             *
             * @param {string} attendeeId Attendee Id
             * @returns {promise} Promise that resolves when attendee details has been retrieved
             */
            getDetail: function(attendeeId) {
                var url = qmWebAppService.getBaseRestUrl('component', 'attendees') + '/attendees/' + attendeeId;
                var defer = $q.defer();
                // get attendee list data
                qmRest.get(url).then(function(data) {
                    init.decorator(data);
                    // attendeeId does not get sent because we already have this info
                    data.attendeeId = attendeeId;
                    // Angular needs a true/false value when using ng-show. O or 1 will not work
                    data.allowMessage = (data.allowMessage === '0') ? false : true;
                    defer.resolve(data);
                }, function(err) {
                    defer.reject(err);
                });

                return defer.promise;
            },
            /**
             * @ngdoc method
             * @name updateDetail
             * @methodOf attendees-V2_3.service:qmAttendeeService
             * @description
             * Update details of an attendee
             *
             * @param {string} attendeeId Attendee Id
             * @param {object} attendeeObj Attendee object
             * @returns {promise} Promise that resolves when attendee details has been updated
             */
            updateDetail: function(attendeeId, attendeeObj) {
                var defer = $q.defer();
                var updateAttendeeProfileUrl = qmEventConfig.getRpcUrl('updateAttendeeProfile');
                var params = [];
                params.push(qmLogin.getUserToken());
                params.push(attendeeObj);

                qmRpc.post(updateAttendeeProfileUrl, {method: 'updateAttendeeProfile', params: params}).then(function(response) {
                    defer.resolve(response);
                }, function(err) {
                    defer.reject(err);
                });

                return defer.promise;
            },
            /**
             * @ngdoc method
             * @name isCategoriesAvailable
             * @methodOf attendees-V2_3.service:qmAttendeeService
             * @description
             * Check if categories is available in for attendee component
             *
             * @returns {boolean} true if useCategories active
             */
            isCategoriesAvailable: function()
            {
                var config = qmEventConfig.getComponentConfig('attendees');

                if ('useCategories' in config['@attributes']) {

                    return config['@attributes']['useCategories'] === 'true';
                }

                return false;
            },
            /**
             * @ngdoc method
             * @name getSetting
             * @methodOf attendees-V2_3.service:qmAttendeeService
             * @description
             * Get settings for attendee component
             *
             * @returns {object} Attendee settings object
             */
            getSetting: function() {
                var config = qmEventConfig.getComponentConfig('attendees');
                var settingConfig = config.listItems[0].listItem;
                // convert values from config json into something more usable
                var returnObj = {};
                for (var i = 0; i < settingConfig.length; i++) {
                    returnObj[settingConfig[i]['@attributes'].name] = {};
                    returnObj[settingConfig[i]['@attributes'].name].isEditable = false;
                    returnObj[settingConfig[i]['@attributes'].name].isVisible = false;
                    if (settingConfig[i]['@attributes'].isEditable === 'true') {
                        returnObj[settingConfig[i]['@attributes'].name].isEditable = true;
                    }
                    if (settingConfig[i]['@attributes'].isVisible === 'true') {
                        returnObj[settingConfig[i]['@attributes'].name].isVisible = true;
                    }
                }

                return returnObj;
            }
        };

        return init;
    }]);

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

/**
 * @ngdoc controller
 * @name attendees-V2_3.controller:AttendeesMainController
 * @description
 * Controller for attendee main tab view
 */

attendees.controller('AttendeesMainController', ['qmAttendeeService', '$state',
    function(qmAttendeeService, $state) {
        var categoriesData = qmAttendeeService.getCategories();
        if (categoriesData.length === 1) {
            $state.go('event.attendees.list');
        } else {
            $state.go('event.attendees.category');
        }
    }]);

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

/**
 * @ngdoc controller
 * @name attendees-V2_3.controller:AttendeesMainListController
 * @description
 * Controller for attendee main tab view
 */

attendees.controller('AttendeesMainListController', ['$scope', '$state', 'qmEventConfig', 'qmAttendeeService', 'qmLocalization',
    'qmLogin', 'qmDependencyManager', 'qmAttendeeDependencyService', 'qmHistory',
    function($scope, $state, qmEventConfig, qmAttendeeService, qmLocalization,
             qmLogin, qmDependencyManager, qmAttendeeDependencyService, qmHistory) {
        $scope.panes = [];

        // ENG-13352, This bit of code is to detect the state on tab,
        // so user will go to the correct tabs when they click back button from a detail view
        var lastActiveTab = null;
        var prevState = qmHistory.getPrevState(2);

        // We leverage qmHistory to check the previous state and set the initial state to be
        // the leftmost 'attendees' if user navigate from previous components (eg: documents, profile, etc)
        if (prevState !== null && prevState.state.indexOf('event.attendees') > -1) {
            lastActiveTab = qmAttendeeService.getTab();
        } else {
            lastActiveTab = 'attendees';
        }
        // End of ENG-13352

        var isListAttendee = ('categoryId' in $state.params);
        var panes = [];
        if (!isListAttendee) {
            panes.push({
                title: qmLocalization.getString('componentAttendeesTitle'),
                partial: qmAttendeeDependencyService.getTemplateUrl('attendee-categories'),
                controller: 'AttendeeCategoryListController',
                isActive: (lastActiveTab === 'attendees')
            });
        } else {
            panes.push({
                title: qmLocalization.getString('componentAttendeesTitle'),
                partial: qmAttendeeDependencyService.getTemplateUrl('attendee-list'),
                controller: 'AttendeeListController',
                isActive: true
            });
        }

        // Check if we have Like Minded
        if (qmEventConfig.isComponentConfigured('like-minded') && qmDependencyManager.hasComponent('like-minded') && qmLogin.isLoggedIn()) {
            var qmLikeMindedService = qmAttendeeDependencyService.getLikeMindedService('qmLikeMindedService');
            var loadLikeMinded = qmLikeMindedService.getList();
            loadLikeMinded.then(function(likeMinded) {
                if (likeMinded.length) {
                    panes.push({
                        title: qmLocalization.getString('componentLikeMindedTitle'),
                        partial: qmAttendeeDependencyService.getTemplateUrl('like-minded-list'),
                        controller: 'AttendeeLikeMindedListController',
                        isActive: (lastActiveTab === 'like-minded')
                    });
                }
            });
        }

        $scope.panes = panes;
    }]);

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

/**
 * @ngdoc controller
 * @name attendees-V2_3.controller:AttendeeCategoryListController
 * @description
 * Controller for attendee categories main tab view
 */

attendees.controller('AttendeeCategoryListController', ['$scope', 'qmAttendeeService', 'qmList',
    function($scope, qmAttendeeService, qmList) {
        var categoriesData = qmAttendeeService.getCategories();
        $scope.categories = qmList.transformList(categoriesData);
    }]);

/**
 * @ngdoc controller
 * @name attendees-V2_3.controller:AttendeeCategoriesHeaderController
 * @description
 * Controller for attendee categories list header view
 */
attendees.controller('AttendeeCategoriesHeaderController', ['$scope', 'qmLocalization', function($scope, qmLocalization) {
    $scope.headerTitle = qmLocalization.getString('componentAttendeesTitle');
}]);

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

/**
 * @ngdoc controller
 * @name attendees-V2_3.controller:AttendeeListController
 * @description
 * Controller for attendee list view
 */
attendees.controller('AttendeeListController', ['$scope', 'qmAttendeeService', 'qmList', '$state',
    '$rootScope', 'qmLocalization', 'qmDependencyManager', '$q', 'qmAttendeeDependencyService',
    function($scope, qmAttendeeService, qmList, $state, $rootScope, qmLocalization, qmDependencyManager, $q, qmAttendeeDependencyService) {
        $scope.setting = qmAttendeeService.getSetting();
        $scope.categoryId = $state.params.categoryId;
        $scope.hasMessaging = qmDependencyManager.hasComponent('messaging');
        $scope.attendeeListItemUrl = qmAttendeeDependencyService.getTemplateUrl('attendee-list-item');
        $scope.attendeeListItemMoreUrl = qmAttendeeDependencyService.getTemplateUrl('attendee-list-item-more');
        $scope.showImage = $scope.setting.photo.isVisible;

        // Set attendee flag to the service for tab navigation, so user will go to the right tab when they
        // click back button from detail view
        // ENG-13352
        qmAttendeeService.setTab('attendees');

        var qmMessagingService = qmAttendeeDependencyService.getMessagingService('qmMessagingService');
        if (qmMessagingService) {
            $scope.messagingType = qmMessagingService.getMessagingType();
        }

        var getAttendeeList;
        if (!angular.isString($scope.categoryId) || $scope.categoryId === '') {
            getAttendeeList = function(filter, itemOffset, moreItemsNum) {
                return qmAttendeeService.getList([], filter, itemOffset, moreItemsNum);
            };
        } else {
            getAttendeeList = function(filter, itemOffset, moreItemsNum) {
                return qmAttendeeService.getListByCategory($scope.categoryId, filter, itemOffset, moreItemsNum);
            };
        }

        var loadDefer = $q.defer();
        $scope.loadAttendeesPromise = loadDefer.promise;
        $scope.loadAttendees = function(filter, itemOffset, moreItemsNum) {
            var defer = $q.defer();
            getAttendeeList(filter, itemOffset, moreItemsNum).then(function(data) {
                if (!$scope.setting.photo.isVisible) {
                    data = qmAttendeeService.removeAttendeePhoto(data);
                }
                var attendees = qmList.groupByLetter(data, 'lastName');
                defer.resolve(attendees);
                loadDefer.resolve();
            }, function() {
                defer.reject();
                loadDefer.reject();
            });

            return defer.promise;
        };

        var componentTitle = qmLocalization.getString('componentAttendeesTitle');
        $scope.emptyText = qmLocalization.getString('LABEL_EMPTY_ATTENDEES_TITLE', componentTitle);
        var showBack = qmAttendeeService.getCategories().length > 1 ? true : false;
        $rootScope.$broadcast('updateTitle', {showBackButton:showBack, categoryTitle: qmAttendeeService.getCategoryTitle($state.params.categoryId)});
    }]);

/**
 * @ngdoc controller
 * @name attendees-V2_3.controller:AttendeeListHeaderController
 * @description
 * Controller for attendee list header view
 */
attendees.controller('AttendeeListHeaderController', ['$scope', 'qmLocalization', function($scope, qmLocalization) {
    $scope.headerTitle = qmLocalization.getString('componentAttendeesTitle');
    $scope.$on('updateTitle', function(event, args) {
        if (args.categoryTitle !== '') {
            $scope.headerTitle = args.categoryTitle;
        }
        if (args.showBackButton) {
            $scope.headerBackState = '^';
        }
    });
}]);

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

/**
 * @ngdoc controller
 * @name attendees-V2_3.controller:AttendeeLikeMindedListController
 * @description
 * Controller for attendee like minded list view
 */
attendees.controller('AttendeeLikeMindedListController',
['$scope', 'qmAttendeeService', 'qmLikeMindedService', 'qmEventConfig', 'qmLocalization', 'qmList',
'qmLogin', '$state', 'qmWebAppService', 'qmAttendeeDependencyService', 'qmDependencyManager',
    function($scope, qmAttendeeService, qmLikeMindedService, qmEventConfig, qmLocalization, qmList,
    qmLogin, $state, qmWebAppService, qmAttendeeDependencyService, qmDependencyManager) {
        var isLoggedIn = qmLogin.checkLogin();
        $scope.setting = qmAttendeeService.getSetting();
        $scope.hasMessaging = qmDependencyManager.hasComponent('messaging');
        $scope.categoryId = $state.params.categoryId;
        $scope.attendeeListItemUrl = qmAttendeeDependencyService.getTemplateUrl('attendee-list-item');
        $scope.attendeeListItemMoreUrl = qmAttendeeDependencyService.getTemplateUrl('attendee-list-item-more');

        // Set like-minded flag to the service for tab navigation, so user will go to the right tab when they
        // click back button from detail view
        // ENG-13352
        qmAttendeeService.setTab('like-minded');

        isLoggedIn.then(function() {
            $scope.loadAttendees = qmLikeMindedService.getLikeMinded();
            $scope.loadAttendees.then(function(likeMindedAttendees) {
                $scope.attendees = qmList.groupByGroup(likeMindedAttendees, 'likeMindedness', 'likeMindednessPercentage');
            });

            var emptyIconUrl = qmEventConfig.getArtifact('icon_main_like_minded');
            $scope.emptyListIcon = qmWebAppService.appendAuthHeader(emptyIconUrl);

            var likeMindedTitle = qmLocalization.getString('componentLikeMindedTitle');
            var attendeesTitle = qmLocalization.getString('componentAttendeesTitle');
            var settingsTitle = qmLocalization.getString('componentSettingsTitle');
            $scope.emptyText = qmLocalization.getString('LABEL_EMPTY_LIKEMINDED_MESSAGE', likeMindedTitle, attendeesTitle, settingsTitle);
        });
    }
]);

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

/**
 * @ngdoc controller
 * @name attendees-V2_3.controller:AttendeeDetailController
 * @description
 * Controller for attendee detail view
 */
attendees.controller('AttendeeDetailController', ['$scope', '$state', 'qmAttendeeService', 'qmDependencyManager', 'qmAttendeeDependencyService',
    function($scope, $state, qmAttendeeService, qmDependencyManager, qmAttendeeDependencyService) {

        $scope.setting = qmAttendeeService.getSetting();
        $scope.loadAttendee = qmAttendeeService.getDetail($state.params.attendeeId);
        $scope.loadAttendee.then(function(attendee) {
            if (!$scope.setting.photo.isVisible) {
                attendee.imageSrc = '/asset/service/web-app/2.0/images/image_attendee_default.png';
                attendee.imageUrl = '/asset/service/web-app/2.0/images/image_attendee_default.png';
            } else {
                if (!attendee.imageSrc) {
                    attendee.imageSrc = '/asset/service/web-app/2.0/images/image_attendee_default.png';
                    attendee.imageUrl = '/asset/service/web-app/2.0/images/image_attendee_default.png';
                }
            }
            $scope.attendee = attendee;
        });
        $scope.hasQuickMeetings = qmDependencyManager.hasComponent('quick-meetings');
        $scope.hasMessaging = qmDependencyManager.hasComponent('messaging');

        $scope.createMeeting = function() {
            if ($scope.hasQuickMeetings) {
                var qmQuickMeetingsCreateDataService = qmAttendeeDependencyService.getQuickMeetingsService('qmQuickMeetingsCreateDataService');
                qmQuickMeetingsCreateDataService.init();
                qmQuickMeetingsCreateDataService.addAttendee($scope.attendee);
                $state.go('event.quick-meetings.create');
            }
        };

        $scope.mailClient = function() {

            window.location.href = 'mailto:' + $scope.attendee.email;
        };

    }]);

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

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

/**
 * @ngdoc service
 * @name attendees-V2_4.service:qmAttendeeDependencyService
 * @description
 * Manages requests from dependency manager
 */
attendees.factory('qmAttendeeDependencyService', ['qmDependencyManager', function(qmDependencyManager) {
    return {
        /**
         * @ngdoc method
         * @name getTemplateUrl
         * @methodOf attendees-V2_4.service:qmAttendeeDependencyService
         * @description
         * Get template url for attendee component
         *
         * @param {string} name Template name
         * @returns {string} Template url
         */
        getTemplateUrl: function(name) {
            if (name === 'attendee-list-item') {
                return '/asset/component/attendees/2.3/webapp/html/partials/attendee-list-item.html';
            } else if (name === 'attendee-list-item-more') {
                return '/asset/component/attendees/2.3/webapp/html/partials/attendee-list-item-more.html';
            } else if (name === 'attendee-list') {
                return '/asset/component/attendees/2.3/webapp/html/attendee-list.html';
            } else if (name === 'attendee-categories') {
                return '/asset/component/attendees/2.3/webapp/html/attendee-categories.html';
            } else if (name === 'like-minded-list') {
                return '/asset/component/attendees/2.3/webapp/html/like-minded-list.html';
            } else if (name === 'attendee-my-profile') {
                return '/asset/component/attendees/2.4/webapp/html/attendee-my-profile.html';
            }

            return null;
        },
        /**
         * @ngdoc method
         * @name getQuickMeetingsService
         * @methodOf attendees-V2_4.service:qmAttendeeDependencyService
         * @description
         * Get service from quick meetings component
         *
         * @param {string} service Service name
         * @returns {service} Angular service or null
         */
        getQuickMeetingsService: function(service) {
            return qmDependencyManager.getService('quick-meetings', '2.0', 'component', service);
        },
        /**
         * @ngdoc method
         * @name getCategoriesService
         * @methodOf attendees-V2_4.service:qmAttendeeDependencyService
         * @description
         * Get service from categories service
         *
         * @param {string} service Service name
         * @returns {service} Angular service or null
         */
        getCategoriesService: function(service) {
            return qmDependencyManager.getRequiredService('categories', '2.0', 'service', service);
        },
        /**
         * @ngdoc method
         * @name getLikeMindedService
         * @methodOf attendees-V2_4.service:qmAttendeeDependencyService
         * @description
         * Get service from like minded component
         *
         * @param {string} service Service name
         * @returns {service} Angular service or null
         */
        getLikeMindedService: function(service) {
            return qmDependencyManager.getService('like-minded', '2.1', 'component', service);
        },
        /**
         * @ngdoc method
         * @name getMessagingService
         * @methodOf attendees-V2_3.service:qmAttendeeDependencyService
         * @description
         * Get service from messaging component
         *
         * @param {string} service Service name
         * @returns {service} Angular service or null
         */
        getMessagingService: function(service) {
            return qmDependencyManager.getService('messaging', '2.0', 'component', service);
        }
    };
}]);

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

/**
 * @ngdoc service
 * @name categories.service:qmCategoriesService
 * @description
 * Manages REST API communication for Categories
 */
categories.factory('qmCategoriesService', ['qmRest', '$q', 'qmWebAppService', 'qmLocalization',
    function(qmRest, $q, qmWebAppService, qmLocalization) {

        return {
            /**
             * @ngdoc method
             * @name getListCategoriesForEntities
             * @methodOf categories.service:qmCategoriesService
             * @description
             * Get list of categories
             *
             * @returns {promise} Promise that resolves when categories list has been retrieved
             */
            getListCategoriesForEntities: function(entityType, entityIds) {
                var url = qmWebAppService.getBaseRestUrl('service', 'categories') + '/' + entityType + '-categories';
                var defer = $q.defer();
                var params = {
                    locale:qmLocalization.getLocale(),
                    entityIds: entityIds
                };

                qmRest.post(url, params).then(function(response) {
                    defer.resolve(response);
                }, function(err) {
                    defer.reject(err);
                });

                return defer.promise;
            }

        };
    }]);

var moderator = angular.module('content-moderator-V2_1');

/**
 * @ngdoc service
 * @name contentmoderator.service:qmContentModeratorService
 * @description
 * Manages Local Storage for flags
 */
moderator.factory('qmContentModeratorService', ['$q','qmLocalStorage', 'qmEventConfig', 'qmLogin', 'qmRpc',
    function($q, qmLocalStorage, qmEventConfig, qmLogin, qmRpc) {
        return {

            /**
             * @ngdoc method
             * @name setFlagFromStorage
             * @methodOf contentmoderator.service:qmContentModeratorService
             * @description
             * Get the flagIds saved in the storage for the key passed and converts string into an array
             *
             * @returns {Array} The flag ids
             */
            getFlagsFromStorage: function(storageKey) {

                // Let's get the flag status string from the local storage;
                var storage = qmLocalStorage.getItem(storageKey);
                var ids = [];
                // If we have something then convert it to an array
                if (storage && storage.length) {
                    ids = storage;
                }

                return ids;
            },
            /**
             * @ngdoc method
             * @name storeFlagForKey
             * @methodOf contentmoderator.service:qmContentModeratorService
             * @description
             * Saves the flags in storage as a string
             *
             */
            storeFlagForKey: function(storageKey, flagId) {
                var storage = qmLocalStorage.getItem(storageKey);
                var ids = [];
                if (storage && storage.length) {
                    ids = storage;
                }
                ids.push(flagId);
                // Save it in storage
                qmLocalStorage.setItem(storageKey, ids);
            },
            /**
             * @ngdoc method
             * @name flag
             * @methodOf contentmoderator.service:qmContentModeratorService
             * @description
             * Flag an entity
             *
             * @param {string} Entity id
             * @param {string} Entity name
             * @returns {promise} Promise that resolves when photo has successfully been flagged
             */
            flag: function(entityId, entityName) {
                var flagUrl = qmEventConfig.getRpcUrl('flagContent');
                var defer = $q.defer();
                var params = {
                    method: 'flagContent',
                    params: [
                        qmLogin.getUserToken(),
                        entityName,
                        entityId
                    ]
                };

                qmRpc.post(flagUrl, params).then(function(response) {
                    defer.resolve(response);
                }, function(err) {
                    defer.reject(err);
                });

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

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

logon.run(['qmSessionStorage', 'qmLocalStorage', '$window', function(qmSessionStorage, qmLocalStorage, $window) {
    var sessionStorageObj = qmSessionStorage.getStorage();
    var localStorageObj = qmLocalStorage.getStorage();
    if (sessionStorageObj && !sessionStorageObj.length) {
        localStorageObj.setItem('getQmSessionStorage', new Date().getTime());
    }
    $window.addEventListener('storage', function(event) {
        if (event.key === 'getQmSessionStorage') {
            // Another tab asked for session storage
            localStorageObj.setItem('qmSessionStorage', JSON.stringify(sessionStorageObj));
            // Remove the item from local storage after session storage gets populated
            localStorageObj.removeItem('qmSessionStorage');

        } else if (event.key === 'qmSessionStorage' && sessionStorageObj && !sessionStorageObj.length) {
            try {
                // Populate the empty session storage
                var data = JSON.parse(event.newValue);
                for (var key in data) {
                    if (data.hasOwnProperty(key)) {
                        sessionStorageObj.setItem(key, data[key]);
                    }
                }
            } catch (ex) {
            }
        }
    });
}]);

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

/**
 * @ngdoc controller
 * @name logon-V2_3.controller:LogonController
 * @description
 * Controller for component login modal view
 */
logon.controller('LogonController',
    ['$scope', 'qmEventConfig', '$modalInstance', '$q', 'qmRpc', 'qmLogonService', 'qmAnalytics', 'qmEventHelper',
        function($scope, qmEventConfig, $modalInstance, $q, qmRpc, qmLogonService, qmAnalytics, qmEventHelper) {
            $scope.isSubmitting = false;

            // Find out it this is a first time user
            $scope.firstTimeUser = qmLogonService.isFirstTimeUser();
            $scope.eventLogo = qmEventHelper.getEventLogo();
            $scope.showModal = true;
            $scope.hasPasswordReset = false;

            if (qmEventConfig.isEventPasswordResetAllowed()) {
                $scope.hasPasswordReset = true;
            }

            $scope.cancel = function() {
                $modalInstance.dismiss('cancel');
            };
            $scope.submit = function(username, password) {
                $scope.isSubmitting = true;
                var defer = $q.defer();
                var loginUrl = qmEventConfig.getRpcUrl('login');
                var bGetInfo = true;
                qmRpc.post(loginUrl, {method: 'login', params: [username, password, bGetInfo]}).then(function(response) {
                    if (response.token) {

                        $scope.incorrectLogin = false;
                        $scope.unknownError = false;
                        // User has logged in. Let's change the first time user flag
                        qmLogonService.markFirstTimeLogin();

                        if (response.needChangePassword) {

                            $scope.showModal = false;
                            qmLogonService.setUserName(username);
                            qmLogonService.newPassword(response.token, response.attendee).then(function(token) {

                                $modalInstance.close(token);
                                defer.resolve();
                            }, function() {
                                $scope.showModal = true;
                            });
                        } else {
                            // Log the user in and close modal.
                            qmAnalytics.markEvent('LoginSuccess', response.attendee.attendeeId);
                            qmAnalytics.startSession(response.attendee.attendeeId, true);
                            qmLogonService.setLoginInfo(response.token, response.attendee);
                            qmLogonService.setUserName(username);
                            $modalInstance.close(response.token);
                            defer.resolve();
                        }

                    } else {
                        qmAnalytics.markEvent('LoginFailure');
                        $scope.incorrectLogin = false;
                        $scope.unknownError = true;
                        defer.reject();
                    }
                }, function(err) {
                    qmAnalytics.markEvent('LoginFailure');
                    if (err.error) {

                        $scope.alertLoginIncorrectMessage = qmLogonService.getLocalizedStringFromErrorMsg(err.error);
                        $scope.incorrectLogin = true;
                        $scope.unknownError = false;
                        defer.reject();
                    } else {
                        $scope.incorrectLogin = false;
                        $scope.unknownError = true;
                        defer.reject();
                    }
                }).finally(function() {
                    $scope.isSubmitting = false;
                });
                return defer.promise;
            };

            $scope.createPassword = function() {

                $scope.showModal = false;
                qmLogonService.resetPassword('firstTime').then(function() {

                    $scope.showModal = true;
                    $scope.firstTimeUser = false;

                }, function() {

                    $scope.showModal = true;
                });
            };

            $scope.forgotPassword = function() {

                $scope.showModal = false;
                qmLogonService.resetPassword('forgot').then(function() {

                    $scope.showModal = true;
                    $scope.firstTimeUser = false;
                    $scope.showTempPassMsg = false;

                }, function() {

                    $scope.showModal = true;
                });
            };

        }]);

/**
 * @ngdoc controller
 * @name logon-V2_3.controller:ResetPasswordController
 * @description
 * Controller for component password-manager view
 */
logon.controller('ResetPasswordController',
    ['$scope', 'qmEventConfig', '$modalInstance', '$q', 'qmRpc', 'qmLogonService', 'qmAnalytics', 'toastr', 'qmLocalization','resetType',
        function($scope, qmEventConfig, $modalInstance, $q, qmRpc, qmLogonService, qmAnalytics, toastr, qmLocalization, resetType) {

            $scope.isSubmitting = false;
            // Default Labels for template
            $scope.headerLabel = qmLocalization.getString('LABEL_CREATE_PASSWORD_TITLE');
            $scope.labelEmail = qmLocalization.getString('LABEL_EMAIL_ADDRESS_WATERMARK');
            $scope.buttonCancel = qmLocalization.getString('BUTTON_CANCEL');
            $scope.buttonSubmit = qmLocalization.getString('BUTTON_SEND');
            $scope.instructionsText = qmLocalization.getString('LABEL_CREATE_PASSWORD_MESSAGE');
            // Message shown after sending password
            var toastMsg = qmLocalization.getString('ALERT_CREATE_PASSWORD_EMAIL_SENT_MESSAGE');
            var isPasswordReset = false;

            if (resetType === 'forgot') {
                // Change Labels
                $scope.headerLabel = qmLocalization.getString('LABEL_UPDATE_PASSWORD_TITLE');
                $scope.instructionsText = qmLocalization.getString('LABEL_UPDATE_PASSWORD_MESSAGE');
                // Change toastMsg
                toastMsg = qmLocalization.getString('ALERT_UPDATE_PASSWORD_EMAIL_SENT_MESSAGE');
                isPasswordReset = true;
            }

            toastMsg += '\r\n';
            toastMsg += qmLocalization.getString('ALERT_CHECK_EMAIL_INBOX_MESSAGE');

            $scope.cancel = function() {
                $modalInstance.dismiss('cancel');
            };

            $scope.submit = function(email) {

                $scope.isSubmitting = true;
                var defer = $q.defer();
                var url = qmEventConfig.getRpcUrl('login');
                var locale = qmLocalization.getLocale();
                qmRpc.post(url, {method: 'generateTempPassword', params: [email, locale, isPasswordReset]}).then(function() {

                    $scope.incorrectLogin = false;
                    $scope.unknownError = false;
                    $modalInstance.close();
                    qmLogonService.markFirstTimeLogin();

                }, function(err) {

                    qmAnalytics.markEvent('LoginFailure');

                    if (err.error) {
                        $scope.alertLoginIncorrectMessage = qmLogonService.getLocalizedStringFromErrorMsg(err.error);
                        $scope.incorrectLogin = true;
                        $scope.unknownError = false;
                        var title = '';

                        if (err.error === 'Duplicate email') {
                            // Let the user know that password reset has to be dealt by support
                            toastMsg = '\r\n';
                            toastMsg += qmLocalization.getString('LABEL_LOGON_UNIQUE_EMAIL_MESSAGE');

                            // Show error
                            toastr.error(toastMsg, title, {
                                closeButton: true,
                                timeOut: 1000000000
                            });

                        } else {
                            // For security reasons we show a success message here
                            title = qmLocalization.getString('ALERT_PASSWORD_EMAIL_SENT_TITLE');
                            toastr.success(toastMsg, title, {
                                closeButton: true,
                                timeOut: 1000000000
                            });
                        }

                        defer.reject();
                    } else {
                        $scope.unknownError = true;
                        $scope.incorrectLogin = false;
                        defer.reject();
                    }

                    $modalInstance.dismiss('cancel');

                }).finally(function() {
                    $scope.isSubmitting = false;
                });

                return defer.promise;
            };
        }]);

/**
 * @ngdoc controller
 * @name logon-V2_3.controller:NewPasswordController
 * @description
 * Controller for component new-password view
 */
logon.controller('NewPasswordController',
    ['$scope', 'qmEventConfig', '$modalInstance', '$q', 'qmRpc', 'qmLogonService', 'qmAnalytics', 'toastr',
        'qmLocalization', 'tempToken', 'attendee', 'rules','qmValidator',
        function($scope, qmEventConfig, $modalInstance, $q, qmRpc, qmLogonService, qmAnalytics, toastr,
                 qmLocalization, tempToken, attendee, rules, qmValidator) {

            $scope.isSubmitting = false;

            // Define constants
            var STATUS_NORMAL = 0;
            var STATUS_VALID = 1;
            var STATUS_INVALID = 2;

            // Default Labels for template
            $scope.headerLabel = qmLocalization.getString('LABEL_NEW_PASSWORD_TITLE');
            $scope.instructionsText = qmLocalization.getString('LABEL_NEW_PASSWORD_MESSAGE');
            $scope.labelNewPass = qmLocalization.getString('LABEL_NEW_PASSWORD_WATERMARK');
            $scope.labelConfirmNewPass = qmLocalization.getString('LABEL_REENTER_PASSWORD_WATERMARK');
            $scope.buttonCancel = qmLocalization.getString('BUTTON_CANCEL');
            $scope.buttonSave = qmLocalization.getString('BUTTON_SAVE');

            // Instructions String Keys
            $scope.passwordRulesTitle = qmLocalization.getString('LABEL_PASSWORD_RULE_MESSAGE');
            $scope.passwordRuleNumberCharacters = qmLocalization.getString('LABEL_PASSWORD_RULE_MIN_LENGTH').replace('{0}', rules.minLength);
            $scope.passwordRuleContainsNumber = qmLocalization.getString('LABEL_PASSWORD_RULE_MIN_NUMBERS');
            $scope.passwordRuleContainsUpper = qmLocalization.getString('LABEL_PASSWORD_RULE_MIN_UPPER_CHARS');
            $scope.passwordRuleContainsLower = qmLocalization.getString('LABEL_PASSWORD_RULE_MIN_LOWER_CHARS');
            $scope.passwordRuleContainsSpecial = qmLocalization.getString('LABEL_PASSWORD_RULE_MIN_SPECIAL_CHARS');
            $scope.labelPasswordRuleUserName = qmLocalization.getString('LABEL_PASSWORD_RULE_USERNAME');
            $scope.passwordRuleMustMatch = qmLocalization.getString('LABEL_PASSWORD_RULE_MATCH');

            // Set default values for the rules that should show
            $scope.showNumbersCharactersRule = true;
            $scope.showContainsUsernameRule = true;
            $scope.showContainsNumbersRule = false;
            $scope.showContainsUpperRule = false;
            $scope.showContainsLowerRule = false;
            $scope.showContainsSpecialRule = false;

            // Use qmValidator service to check for the password
            qmValidator.setMinLength(rules.minLength);
            qmValidator.addCheck('length');

            // Validate digits ?
            if (rules.oneDigit === 1) {
                $scope.showContainsNumbersRule = true;
                qmValidator.addCheck('number');
            }
            // Validate lower case ?
            if (rules.oneLowerCase === 1) {
                $scope.showContainsLowerRule = true;
                qmValidator.addCheck('lower');
            }
            // Validate upper case ?
            if (rules.oneUpperCase === 1) {
                $scope.showContainsUpperRule = true;
                qmValidator.addCheck('upper');
            }
            // Validate special characters ?
            if (rules.oneSpecialCharacter === 1) {
                $scope.showContainsSpecialRule = true;
                qmValidator.addCheck('special');
            }

            // Validation variables
            $scope.matchRule = STATUS_NORMAL;
            $scope.lengthRule = STATUS_NORMAL;
            $scope.numberRule = STATUS_NORMAL;
            $scope.upperRule = STATUS_NORMAL;
            $scope.lowerRule = STATUS_NORMAL;
            $scope.specialRule = STATUS_NORMAL;
            $scope.noEqualStrRule = STATUS_NORMAL;
            $scope.noInStrRule = STATUS_NORMAL;

            $scope.cancel = function() {
                $modalInstance.dismiss('cancel');
            };

            $scope.submit = function() {

                $scope.incorrectLogin = false;
                $scope.unknownError = false;
                $scope.isSubmitting = true;

                qmValidator.addMatch($scope.confirmPass);

                // From the rules find out what we need to validate
                if (rules.cannotContainUserName === 1) {
                    qmValidator.addNotIn(qmLogonService.getUserName());
                }

                // Ready to validate
                if (!qmValidator.validate($scope.newPass)) {
                    // Validation failed. Let's handle the errors to let the user know what went wrong
                    $scope.handleErrors(qmValidator.getErrors());
                    $scope.alertLoginIncorrectMessage = qmLocalization.getString('ALERT_PASSWORD_RULE_FAILURE_MESSAGE');
                    $scope.incorrectLogin = true;
                    $scope.unknownError = false;
                    $scope.isSubmitting = false;
                    $scope.newPass = '';
                    $scope.confirmPass = '';

                    return false;
                }

                // Validation pass.
                $scope.matchRule = STATUS_VALID;
                $scope.numberRule = STATUS_VALID;
                $scope.upperRule = STATUS_VALID;
                $scope.lowerRule = STATUS_VALID;
                $scope.specialRule = STATUS_VALID;
                $scope.lengthRule = STATUS_VALID;
                $scope.noEqualStrRule = STATUS_VALID;
                $scope.noInStrRule = STATUS_VALID;

                var defer = $q.defer();
                var url = qmEventConfig.getRpcUrl('login');

                qmRpc.post(url, {method: 'updatePassword', params: [tempToken, $scope.newPass]}).then(function(response) {

                    $scope.incorrectLogin = false;
                    $scope.unknownError = false;
                    // Show toast message
                    var toastTitle = qmLocalization.getString('ALERT_PASSWORD_UPDATED_TITLE');
                    var toastBody = qmLocalization.getString('ALERT_PASSWORD_UPDATED_MESSAGE');
                    toastr.success(toastBody, toastTitle, {
                        closeButton: true,
                        timeOut: 5000
                    });

                    // log user in with new token and close modal
                    qmAnalytics.markEvent('LoginSuccess', attendee.attendeeId);
                    qmAnalytics.startSession(attendee.attendeeId, true);
                    var userName = qmLogonService.getUserName();
                    qmLogonService.setLoginInfo(response.token, attendee);
                    // todo: Remove this when we have ability to specify app vs user storage
                    // For now we need this here because we need the username info under user level storage for session management
                    qmLogonService.setUserName(userName);

                    $modalInstance.close(response.token);
                    defer.resolve();

                }, function(err) {
                    qmAnalytics.markEvent('LoginFailure');
                    if (err.error) {
                        // Invalid password. Let's handle the errors to let the user know what went wrong
                        $scope.handleErrors(err.parameters);
                        $scope.alertLoginIncorrectMessage = qmLogonService.getLocalizedStringFromErrorMsg(err.error);
                        $scope.incorrectLogin = true;
                        $scope.unknownError = false;
                        defer.reject();
                    } else {

                        $scope.incorrectLogin = false;
                        $scope.unknownError = true;
                        defer.reject();
                    }

                }).finally(function() {
                    $scope.isSubmitting = false;
                });

                return defer.promise;
            };

            $scope.handleErrors = function(errors) {

                if (errors.indexOf('match') >= 0) {
                    $scope.matchRule = STATUS_INVALID;
                } else {
                    $scope.matchRule = STATUS_VALID;
                }

                if (errors.indexOf('length') >= 0 || errors.indexOf('minLength') >= 0) {
                    $scope.lengthRule = STATUS_INVALID;
                } else {
                    $scope.lengthRule = STATUS_VALID;
                }

                if (errors.indexOf('length') >= 0 || errors.indexOf('minLength') >= 0) {
                    $scope.lengthRule = STATUS_INVALID;
                } else {
                    $scope.lengthRule = STATUS_VALID;
                }

                if (errors.indexOf('number') >= 0 || errors.indexOf('oneDigit') >= 0) {
                    $scope.numberRule = STATUS_INVALID;
                } else {
                    $scope.numberRule = STATUS_VALID;
                }

                if (errors.indexOf('lower') >= 0 || errors.indexOf('oneLowerCase') >= 0) {
                    $scope.lowerRule = STATUS_INVALID;
                } else {
                    $scope.lowerRule = STATUS_VALID;
                }

                if (errors.indexOf('upper') >= 0 || errors.indexOf('oneUpperCase') >= 0) {
                    $scope.upperRule = STATUS_INVALID;
                } else {
                    $scope.upperRule = STATUS_VALID;
                }

                if (errors.indexOf('special') >= 0 || errors.indexOf('oneSpecialCharacter') >= 0) {
                    $scope.specialRule = STATUS_INVALID;
                } else {
                    $scope.specialRule = STATUS_VALID;
                }

                if (errors.indexOf('notin') >= 0 || errors.indexOf('cannotContainUserName') >= 0) {
                    $scope.noInStrRule = STATUS_INVALID;
                } else {
                    $scope.noInStrRule = STATUS_VALID;
                }
            };
        }]);

var logon = angular.module('logon-V2_2');

/**
 * @ngdoc service
 * @name logon-V2_2.service:qmLogonService
 * @description
 * Manages user login
 */
logon.factory('qmLogonService',
['$modal', '$q', 'qmLocalStorage', 'qmSessionStorage', 'qmContainerConfig', 'qmEventConfig', '$rootScope', 'qmLocalization',
'qmRpc', 'qmWebAppData',
function($modal, $q, qmLocalStorage, qmSessionStorage, qmContainerConfig, qmEventConfig, $rootScope, qmLocalization,
qmRpc, qmWebAppData) {
    return {
        /**
         * @ngdoc method
         * @name setUserName
         * @methodOf logon-V2_2.service:qmLogonService
         * @description
         * Sets username variable
         *
         * @param {string} str Username
         */
        setUserName: function(userName) {
            qmLocalStorage.setItem('userName', userName);
        },

        /**
         * @ngdoc method
         * @name getUserName
         * @methodOf logon-V2_2.service:qmLogonService
         * @description
         * Gets username variable
         *
         * @returns {string} Username
         */
        getUserName: function() {
            return qmLocalStorage.getItem('userName');
        },

        /**
         * @ngdoc method
         * @name reset
         * @methodOf logon-V2_2.service:qmLogonService
         * @description
         * Reset login info back to null
         *
         */
        reset: function() {
            qmWebAppData.resetUserId();

            qmLocalStorage.removeItem('userToken');
            qmLocalStorage.removeItem('userInfo');

            // storage user id info should be set after so current user info is removed from inside user specific storage
            qmLocalStorage.resetUserId();
            qmSessionStorage.resetUserId();

            // remove user id info from outside user specific settings
            qmLocalStorage.removeItem('userId');
            qmSessionStorage.removeItem('userId');
        },
        /**
         * @ngdoc method
         * @name isLoggedIn
         * @methodOf logon-V2_2.service:qmLogonService
         * @description
         * Is there a user logged in?
         *
         * @returns {boolean} Is user logged in?
         */
        isLoggedIn: function() {
            if (this.getUserToken() && this.getUserInfo()) {
                return true;
            }
            return false;
        },
        /**
         * @ngdoc method
         * @name setUserToken
         * @methodOf logon-V2_2.service:qmLogonService
         * @description
         * Set user token
         *
         * @param {string} token User token to be set
         */
        setUserToken: function(token) {
            qmLocalStorage.setItem('userToken', token);
        },
        /**
         * @ngdoc method
         * @name getUserToken
         * @methodOf logon-V2_2.service:qmLogonService
         * @description
         * Get user token
         *
         * @returns {string} User token
         */
        getUserToken: function() {
            return qmLocalStorage.getItem('userToken');
        },
        /**
         * @ngdoc method
         * @name setUserInfo
         * @methodOf logon-V2_2.service:qmLogonService
         * @description
         * Set user info
         *
         * @param {object} user User info to be set
         */
        setUserInfo: function(user) {
            qmLocalStorage.setItem('userInfo', user);
        },
        /**
         * @ngdoc method
         * @name getUserInfo
         * @methodOf logon-V2_2.service:qmLogonService
         * @description
         * Get user info
         *
         * @returns {object} User info
         */
        getUserInfo: function() {
            return qmLocalStorage.getItem('userInfo');
        },
        /**
         * @ngdoc method
         * @name setLoginInfo
         * @methodOf logon-V2_2.service:qmLogonService
         * @description
         * Set the login info
         *
         * @param {string} token User token to be set
         * @param {object} info User info object to be set
         */
        setLoginInfo: function(userToken, userInfo) {
            // store current userId outside user specific settings so we can set the storage user id correctly on app init
            qmLocalStorage.setItem('userId', userInfo.attendeeId);
            qmSessionStorage.setItem('userId', userInfo.attendeeId);

            qmWebAppData.setUserId(userInfo.attendeeId);

            // storage user id info should be set before so current user info is saved inside user specific storage
            qmLocalStorage.setUserId(userInfo.attendeeId);
            qmSessionStorage.setUserId(userInfo.attendeeId);

            this.setUserToken(userToken);
            this.setUserInfo(userInfo);
        },
        /**
         * @ngdoc method
         * @name setUserId
         * @methodOf logon-V2_2.service:qmLogonService
         * @description
         * Set user id to cache storage
         *
         * @param {string} userId User id
         */
        setUserId: function(userId) {
            return qmLocalStorage.setItem('userId', userId);
        },
        /**
         * @ngdoc method
         * @name getUserId
         * @methodOf logon-V2_2.service:qmLogonService
         * @description
         * Get user id from cache. This should only be called before userId is set in cache storage
         *
         * @returns {string} User Id
         */
        getUserId: function() {
            return qmLocalStorage.getItem('userId');
        },
        /**
         * @ngdoc method
         * @name checkLogin
         * @methodOf logon-V2_2.service:qmLogonService
         * @description
         * Checks if user is logged in and launches login dialog if not
         *
         * @returns {promise} Promise that resolves if already logged in or after user interaction with login modal
         */
        checkLogin: function() {
            var defer = $q.defer();
            if (!this.isLoggedIn()) {
                var scope = $rootScope.$new();
                scope.hasCancel = true;
                var openParams = {
                    templateUrl: '/asset/component/logon/2.1/webapp/html/login-modal.html',
                    controller: 'LogonController'
                };
                if (qmContainerConfig.isSingleEvent() && qmEventConfig.isEventLoginRequired()) {
                    openParams.backdrop = 'static'; // prevent modal from being closed when clicking on background
                    openParams.keyboard = false;    // prevent modal from being closed by pressing ESC
                    scope.hasCancel = false;
                }
                openParams.scope = scope;
                var loginModal = $modal.open(openParams);
                return loginModal.result;
            } else {
                defer.resolve();
            }
            return defer.promise;
        },
        /**
         * @ngdoc method
         * @name resetPassword
         * @methodOf logon-V2_2.service:qmLogonService
         * @description
         * Checks if user is logged in and launches reset password dialog if not
         *
         * @returns {promise} Promise that resolves if already logged in or after user interaction with reset password modal
         */
        resetPassword: function(type) {

            this.showLogin = false;
            var defer = $q.defer();

            if (!this.isLoggedIn()) {
                var scope = $rootScope.$new();
                scope.hasCancel = true;
                var openParams = {
                    templateUrl: '/asset/component/logon/2.1/webapp/html/password-manager.html',
                    controller: 'ResetPasswordController',
                    resolve: {
                        resetType: function() {
                            return type;
                        }
                    }
                };
                openParams.backdrop = 'static'; // prevent modal from being closed when clicking on background
                openParams.keyboard = false;    // prevent modal from being closed by pressing ESC
                scope.hasCancel = true;
                openParams.scope = scope;
                var loginModal = $modal.open(openParams);

                return loginModal.result;

            } else {
                defer.resolve();
            }
            return defer.promise;
        },

        /**
         * @ngdoc method
         * @name newPassword
         * @methodOf logon-V2_2.service:qmLogonService
         * @description
         * Checks if user is logged in and launches new password dialog if not
         *
         * @param {string} authToken The temporary auth token to use to create new password
         * @param {object} attendee The attendee object
         *
         * @returns {promise} Promise that resolves if already logged in or after user interaction with new password modal
         */
        newPassword: function(authToken, attendee) {

            this.showLogin = false;
            var defer = $q.defer();

            if (!this.isLoggedIn()) {

                // Let's get the password rules
                var url = qmEventConfig.getRpcUrl('login');
                var thisScope = this;
                var rules = [];

                qmRpc.post(url, {method: 'getPasswordRules', params: [authToken]}).then(function(response) {
                    rules = response;
                    var openParams = thisScope.configModalParams(authToken, attendee, rules);
                    var loginModal = $modal.open(openParams);
                    // Return the login modal
                    return loginModal.result;

                }, function() {
                    // Get the modal params
                    var openParams = thisScope.configModalParams(authToken, attendee, rules);
                    var loginModal = $modal.open(openParams);
                    // Return the login modal
                    return loginModal.result;

                }).finally(function() {
                    defer.resolve();
                });

            } else {
                defer.resolve();
            }
            return defer.promise;
        },

        /**
         * @ngdoc method
         * @name configModalParams
         * @methodOf logon-V2_2.service:qmLogonService
         * @description
         * Returns the login modal params
         *
         * @returns {boolean} Is this a first time user?
         */
        configModalParams: function(authToken, attendee, rules) {

            var scope = $rootScope.$new();
            scope.hasCancel = true;
            var openParams = {
                templateUrl: '/asset/component/logon/2.2/webapp/html/new-password.html',
                controller: 'NewPasswordController',
                resolve: {
                    tempToken: function() {
                        return authToken;
                    },
                    attendee: function() {
                        return attendee;
                    },
                    rules: function() {
                        return rules;
                    }
                }
            };
            openParams.backdrop = 'static'; // prevent modal from being closed when clicking on background
            openParams.keyboard = false;    // prevent modal from being closed by pressing ESC
            scope.hasCancel = true;
            openParams.scope = scope;

            return openParams;
        },

        /**
         * @ngdoc method
         * @name isFirstTimeUser
         * @methodOf logon-V2_2.service:qmLogonService
         * @description
         * Returns if this a first time user or not
         *
         * @returns {boolean} Is this a first time user?
         */
        isFirstTimeUser: function() {

            var hasLoggedIn = qmLocalStorage.getItem('QM_HAS_LOGGED_IN');

            if (!hasLoggedIn) {
                return true;
            } else {
                return false;
            }
        },

        /**
         * @ngdoc method
         * @name markFirstTimeLogin
         * @methodOf logon-V2_2.service:qmLogonService
         * @description
         * Stores first time login in the local storage
         */
        markFirstTimeLogin: function() {

            // User has performed first time login flow
            qmLocalStorage.setItem('QM_HAS_LOGGED_IN', true);
        },

        /**
         * @ngdoc method
         * @name getLocalizedStringFromErrorMsg
         * @methodOf logon-V2_2.service:qmLogonService
         * @description
         * Returns the localized string from the error message
         */
        getLocalizedStringFromErrorMsg: function(errorMsg) {

            var localizedString = '';

            switch (errorMsg) {

                case 'Authentication Failure' :
                    localizedString = qmLocalization.getString('ALERT_LOGIN_INCORRECT_MESSAGE');
                    break;

                case 'Missing username' :
                    localizedString = qmLocalization.getString('ALERT_MISSING_USERNAME');
                    break;

                case 'Missing password' :
                    localizedString = qmLocalization.getString('ALERT_MISSING_PASSWORD');
                    break;

                case 'Application error' :
                    localizedString = qmLocalization.getString('ALERT_APPLICATION_ERROR');
                    break;

                case 'Expired password' :
                    localizedString = qmLocalization.getString('ALERT_EXPIRED_PASSWORD_MESSAGE');
                    break;

                case 'Missing email' :
                    localizedString = qmLocalization.getString('ALERT_MISSING_EMAIL');
                    break;

                case 'Invalid email' :
                    localizedString = qmLocalization.getString('ALERT_INVALID_EMAIL');
                    break;

                case 'Invalid password' :
                    localizedString = qmLocalization.getString('ALERT_INVALID_PASSWORD_MESSAGE');
                    break;
            }

            return localizedString;
        },
        /**
         * @ngdoc method
         * @name logout
         * @methodOf logon-V2_2.service:qmLogonService
         * @description
         * Logout current user
         *
         */
        logout: function() {
            if (this.isLoggedIn()) {
                var logoutUrl = qmEventConfig.getRpcUrl('logout');
                var params = [];
                params.push(this.getUserToken());
                qmRpc.post(logoutUrl, {method: 'logout', params: params});
                // Reset internal login info
                this.reset();
            }
        }
    };
}]);

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

activityfeed.config(['$stateProvider', function($stateProvider) {
    $stateProvider
        .state('event.activityfeed-V2_0', {
            url: '/ActivityFeed',
            views: {
                'component@event': {
                    templateUrl: '/asset/component/activityfeed/2.0/webapp/html/activityfeed-list.html',
                    controller: 'ActivityFeedListController'
                },
                'header@event': {
                    controller: 'ActivityFeedListHeaderController'
                },
                'moreheader@event': {
                    templateUrl: '/asset/component/activityfeed/2.0/webapp/html/activityfeed-list-more-header.html',
                    controller: 'ActivityFeedListMoreHeaderController'
                }
            }
        });
}]);


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

/**
 * @ngdoc controller
 * @name activityfeed-V2_0.controller:ActivityFeedListController
 * @description
 * Controller for activity feed list view
 */

activityfeed.controller('ActivityFeedListController',
    ['$scope', 'qmLocalization', 'qmActivityFeedService', 'qmMoment', 'qmActivityFeedDependencyService',
        'qmEventHelper', 'qmEventConfig', 'qmUtilities', '$state', 'qmSessionStorage',
        function($scope, qmLocalization, qmActivityFeedService, qmMoment, qmActivityFeedDependencyService,
                 qmEventHelper, qmEventConfig, qmUtilities, $state, qmSessionStorage) {

            /* Filters the data for activity feed */
            $scope.filterItems = function(data) {
                if (!data || data.length === 0) {
                    $scope.hasItems = false;
                    return;
                }

                var sortedItems = [];
                var filter = [];

                if ($scope.dropdownItems && $scope.dropdownItems.length) {
                    filter = $scope.dropdownItems;
                }

                for (var component in data) {
                    if (data.hasOwnProperty(component)) {
                        var isSelected = false;
                        for (var j = 0; j < filter.length; j++) {
                            if (filter[j].isSelected && component === filter[j].value) {
                                isSelected = true;
                                break;
                            }
                        }
                        if (isSelected) {
                            sortedItems = sortedItems.concat(data[component]);
                        }
                    }
                }
                // Let's sort the items by dateTime DESC
                sortedItems.sort(function(a, b) {
                    if (a.type !== 'events') {

                        // Find out if the activityFeedDateTime of current item is after the one in the sorted array
                        if (qmMoment(a.activityFeedDateTime).isAfter(b.activityFeedDateTime)) {
                            return -1;
                        } else {
                            if (a.activityFeedDateTime === b.activityFeedDateTime && a.title < b.title) {
                                return -1;
                            } else {
                                return 1;
                            }
                        }
                    }

                });

                $scope.items = sortedItems;
                $scope.hasItems = ($scope.items.length > 0) ? true : false;
            };

            $scope.loadActivityFeedItems = qmActivityFeedService.getList();
            $scope.loadActivityFeedItems.then(function(data) {
                // Let's filter items
                $scope.initialData = angular.copy(data);
                $scope.filterItems(data);
            });

            $scope.showLogo = false;
            var config = qmEventConfig.getComponentConfig('activityfeed');
            if (config['@attributes']['showLogo'] === 'true') {
                $scope.showLogo = true;
                $scope.eventLogo = qmEventHelper.getEventLogo();
            }

            $scope.eventsTemplate = qmActivityFeedDependencyService.getTemplateUrl('activityfeed-events.html');
            $scope.announcementsTemplate = qmActivityFeedDependencyService.getTemplateUrl('activityfeed-announcements.html');
            $scope.galleriesTemplate = qmActivityFeedDependencyService.getTemplateUrl('activityfeed-galleries.html');

            /**
             * Makes url links within a string clickables
             */
            $scope.linkify = function(inputText) {

                return qmUtilities.linkify(inputText);
            };

            $scope.goToEventDetail = function(eventId) {
                $state.go('event.events.detail', {eventId: eventId});
            };

            $scope.goToSpeakerDetail = function(speakerId) {
                $state.go('event.speakers.detail', {speakerId: speakerId});
            };

            $scope.dropdownItems = qmSessionStorage.getItem('feedDropdownItems');
            $scope.$on('filterActivityFeed', function() {
                $scope.dropdownItems = qmSessionStorage.getItem('feedDropdownItems');
                $scope.filterItems($scope.initialData);
            });
        }
    ]);

/**
 * @ngdoc controller
 * @name activityfeed-V2_0.controller:ActivityFeedListHeaderController
 * @description
 * Controller for activity feed list header view
 */
activityfeed.controller('ActivityFeedListHeaderController', ['$scope', 'qmLocalization', '$rootScope', function($scope, qmLocalization, $rootScope) {

    $scope.headerBackState = false;
    $scope.headerTitle = qmLocalization.getString('componentActivityfeedTitle');
    $scope.headerTitleClickHandler = function() {
        $rootScope.$broadcast('dropDownToggle');
    };
    $scope.headerTitleTestId = 'componentTitle';
}]);

/**
 * @ngdoc controller
 * @name activityfeed-V2_0.controller:ActivityFeedListMoreHeaderController
 * @description
 * Controller for activity feed list more header view
 */
activityfeed.controller('ActivityFeedListMoreHeaderController',
    ['$scope', '$rootScope', 'qmLocalization', 'qmSessionStorage', 'qmEventConfig', 'qmLogin',
        'qmActivityFeedDependencyService', 'qmActivityFeedConfigService',
        function($scope, $rootScope, qmLocalization, qmSessionStorage, qmEventConfig, qmLogin,
                 qmActivityFeedDependencyService, qmActivityFeedConfigService) {

            $scope.isFilterOpen = false;

            $scope.$on('dropDownToggle', function() {
                $scope.toggleFilter();
            });

            var originalItems = qmSessionStorage.getItem('feedDropdownItems');
            if (originalItems && originalItems.length) {
                $scope.dropdownItems = angular.copy(originalItems);
            } else {
                // Default configuration for the dropdown items
                $scope.dropdownItems = [
                    {
                        title: qmLocalization.getString('componentAnnouncementTitle'),
                        value: 'announcements',
                        icon: 'fa fa-bullhorn',
                        isSelected: true
                    },
                    {
                        title: qmLocalization.getString('componentGalleryTitle'),
                        value: 'gallery',
                        icon: 'fa fa-camera',
                        isSelected: true
                    },
                    {
                        title: qmLocalization.getString('componentEventsTitle'),
                        value: 'events',
                        icon: 'fa fa-clock-o',
                        isSelected: true
                    }
                ];
            }

            $scope.checkServiceAvailable = function(componentKey) {

                var hasService = false;

                if (qmEventConfig.isComponentConfigured(componentKey) &&
                    (!qmEventConfig.isComponentLoginRequired(componentKey) || qmLogin.isLoggedIn())) {

                    switch (componentKey) {
                        case 'announcements':
                            var qmAnnouncementsService = qmActivityFeedDependencyService.getAnnouncementsService('qmAnnouncementsService');
                            if (qmAnnouncementsService) {
                                hasService = true;
                            }
                            break;
                        case 'gallery':
                            var qmGalleryService = qmActivityFeedDependencyService.getGalleryService('qmGalleryService');
                            if (qmGalleryService) {
                                hasService = true;
                            }
                            break;
                        case 'events':
                            var qmEventsService = qmActivityFeedDependencyService.getEventsService('qmEventsService');
                            if (qmEventsService) {
                                hasService = true;
                            }
                            break;
                        default:
                            hasService = false;
                            break;
                    }
                } else {
                    hasService = false;
                }

                return hasService;
            };

            $scope.setDisplayFlag = function(componentKey) {

                var itemDisplay = false;
                if (qmActivityFeedConfigService.checkConfig(componentKey) && $scope.checkServiceAvailable(componentKey)) {
                    itemDisplay = true;
                }

                return itemDisplay;
            };

            // Add isDisplayed property if component is configured and does not require login when not logged in
            for (var i = 0; i < $scope.dropdownItems.length; i++) {

                var componentKey = $scope.dropdownItems[i].value;
                $scope.dropdownItems[i].isDisplayed = $scope.setDisplayFlag(componentKey);

            }
            $scope.filterUpdate = function() {
                qmSessionStorage.setItem('feedDropdownItems', $scope.dropdownItems);
                $rootScope.$broadcast('filterActivityFeed');
            };

            $scope.filterContent = function() {
                $scope.toggleFilter();
                if (!angular.equals(originalItems, $scope.dropdownItems)) {
                    originalItems = angular.copy($scope.dropdownItems);
                    $scope.filterUpdate();
                }
            };

            // toggle showing of drop down menu
            $scope.toggleFilter = function() {
                $scope.isFilterOpen = !$scope.isFilterOpen;
            };

            // toggle component filter items
            $scope.toggleSelected = function(item) {
                item.isSelected = !item.isSelected;
            };

            $scope.filterUpdate();
        }]);

var activityfeed = angular.module('activityfeed-V2_0');
/**
 * @ngdoc service
 * @name activityfeed-V2_0.service:qmActivityFeedService
 * @description
 * Manages REST API communication for ActivityFeed
 */
activityfeed.factory('qmActivityFeedService',
    ['qmRest', '$rootScope', '$q', 'qmLocalization', 'qmWebAppService', 'qmActivityFeedDependencyService', 'qmEventConfig', 'qmLogin', 'qmMoment',
    function(qmRest, $rootScope, $q, qmLocalization, qmWebAppService, qmActivityFeedDependencyService, qmEventConfig, qmLogin, qmMoment) {
        return {
            /**
             * @ngdoc method
             * @name getList
             * @methodOf activityfeed-V2_0.service:qmActivityFeedService
             * @description
             * Gets the list of activity feed items
             *
             * @returns {promise} Promise that resolves when the activity feed items have been retrieved
             */
            getList: function() {

                var url = qmWebAppService.getBaseRestUrl('component', 'activityfeed') + '/activityfeed';
                var defer = $q.defer();
                var scope = this;
                var items = [];
                // get activity feed data
                qmRest.get(url).then(function(data) {

                    if (data.length === 0) {

                        defer.resolve(items);
                        return defer.promise;
                    }

                    var announcementsPromise = scope.getAnnouncements(data['announcements']);
                    var eventsPromise = scope.getEvents(data['events']);
                    var galleryIds = (data['gallery']) ? data['gallery'] : [];
                    var galleryPromise = scope.getGalleries(galleryIds);

                    $q.all({announcements: announcementsPromise, events: eventsPromise, gallery: galleryPromise}).
                    then(function(items) {
                        defer.resolve(items);
                    });

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

                return defer.promise;
            },

            /**
             * @ngdoc method
             * @name getAnnouncements
             * @methodOf activityfeed-V2_0.service:qmActivityFeedService
             * @description
             * Gets the list of announcements given an array of ids
             *
             * @returns {promise} Promise that resolves when the announcements items have been retrieved
             */
            getAnnouncements: function(ids) {

                var defer = $q.defer();
                var announcements = [];
                if (typeof ids === 'undefined' || !ids.length) {
                    // announcements are not configured to show under activity feed
                    defer.resolve(announcements);
                    return defer.promise;
                }

                var qmAnnouncementsService = qmActivityFeedDependencyService.getAnnouncementsService('qmAnnouncementsService');
                var announcementsRequiresLogin = qmEventConfig.isComponentLoginRequired('announcements');
                if (qmAnnouncementsService && (!announcementsRequiresLogin || qmLogin.isLoggedIn())) {
                    var promise = qmAnnouncementsService.getList(ids);
                    promise.then(function(data) {
                        for (var i = 0; i < data.length; i++) {

                            var item = data[i];
                            item.announcementImageUrl = qmWebAppService.appendAuthHeader(item.announcementImageUrl);
                            item.activityFeedDateTime = item.scheduledDateTime;
                            item.type = 'announcements';
                            announcements.push(item);
                        }
                        // Resolve promise
                        defer.resolve(announcements);
                    }, function(err) {
                        defer.reject(err);
                    });
                } else {
                    defer.resolve(announcements);
                }

                return defer.promise;
            },

            /**
             * @ngdoc method
             * @name getEvents
             * @methodOf activityfeed-V2_0.service:qmActivityFeedService
             * @description
             * Gets the list of events given an array of ids
             *
             * @returns {promise} Promise that resolves when the events items have been retrieved
             */
            getEvents: function(ids) {

                var mainDefer = $q.defer();
                var events = [];
                var that = this;

                if (typeof ids === 'undefined' || !ids.length) {
                    // events are not configured to show under activity feed
                    mainDefer.resolve(events);
                    return mainDefer.promise;
                }

                // Events' service expects objects so let's make them
                var eventIdList = [];
                for (var i = 0; i < ids.length; i++) {
                    var obj = {eventId: ids[i]};
                    eventIdList.push(obj);
                }

                var qmEventsService = qmActivityFeedDependencyService.getEventsService('qmEventsService');
                var eventsRequiresLogin = qmEventConfig.isComponentLoginRequired('events');
                if (qmEventsService && (!eventsRequiresLogin || qmLogin.isLoggedIn())) {

                    var promise = qmEventsService.getList(eventIdList, '', 0, 0, 1, 1);
                    var qmSpeakerService = qmActivityFeedDependencyService.getSpeakersService('qmSpeakerService');
                    var speakersRequiresLogin = qmEventConfig.isComponentLoginRequired('speakers');
                    var handleSpeakersPromise = function(speakerPromise, defer, eventObj) {
                        speakerPromise.then(function(speakers) {
                            if (speakers.length > 0) {
                                var filteredSpeakers = [];
                                // Filter out speakers that don't have any photos
                                for (var i = 0; i < speakers.length; i++) {
                                    if (speakers[i].smallImageUrl && speakers[i].smallImageUrl !== '') {
                                        filteredSpeakers.push(speakers[i]);
                                    }
                                    // Display max 5 speakers
                                    if (filteredSpeakers.length === 5) {
                                        break;
                                    }
                                }
                                eventObj.speakers = filteredSpeakers;
                            }
                            defer.resolve();
                        }, function() {
                            defer.reject();
                        });
                    };
                    var speakerPromises = [];
                    promise.then(function(sessions) {
                        var events = [];
                        for (var i = 0; i < sessions.length; i++) {

                            // Get time difference
                            var timeDiffMins = that.getTimeDiffInMinutes(sessions[i].startTimeFormatted);
                            var now = qmMoment();
                            var today = qmMoment().format('YYYY-MM-DD');
                            var eventDate = qmMoment(sessions[i].startDateTime).format('YYYY-MM-DD');
                            var isCurrentDate = qmMoment(eventDate).isSame(today);
                            sessions[i].happenningSoon = false;
                            sessions[i].happenningNow = false;
                            sessions[i].happenned = false;

                            // Today's events ?
                            if (isCurrentDate) {

                                if (timeDiffMins > 15) {
                                    // This event is too far away. Don't display
                                    continue;
                                } else if (timeDiffMins <= 15 && timeDiffMins > 0) {
                                    // This event is happening soon
                                    sessions[i].happenningSoon = true;
                                } else if (now.isAfter(qmMoment(sessions[i].startDateTime)) && qmMoment(sessions[i].endDateTime).isAfter(now)) {
                                    // This event is happening now
                                    sessions[i].happenningNow = true;
                                } else {
                                    // Already Happened
                                    sessions[i].happenned = true;
                                }
                            } else {
                                // Find out if it is in the past
                                if (now.isAfter(qmMoment(sessions[i].startDateTime))) {
                                    // Already Happened
                                    sessions[i].happenned = true;
                                } else {
                                    // This event is too far away. Don't display
                                    continue;
                                }
                            }

                            sessions[i].activityFeedDateTime = sessions[i].startDateTime;
                            sessions[i].type = 'events';
                            if (qmSpeakerService && (!speakersRequiresLogin || qmLogin.isLoggedIn())) {

                                var defer = $q.defer();
                                var speakerPromise = qmSpeakerService.getEventSpeakers(sessions[i].eventId);
                                handleSpeakersPromise(speakerPromise, defer, sessions[i]);
                                speakerPromises.push(defer.promise);
                            }

                            events.push(sessions[i]);
                        }

                        $q.all(speakerPromises).then(function() {
                            mainDefer.resolve(events);
                        }, function(err) {
                            mainDefer.reject(err);
                        });

                    }, function(err) {
                        mainDefer.reject(err);
                    });
                } else {
                    mainDefer.resolve(events);
                }
                return mainDefer.promise;

            },

            /**
             * @ngdoc method
             * @name getTimeDiffInMinutes
             * @methodOf activityfeed-V2_0.service:qmActivityFeedService
             * @description
             * Gets the difference from now in minutes
             *
             * @returns {Number} Time difference in minutes
             */
            getTimeDiffInMinutes: function(formattedTime) {

                var now = qmMoment();
                var startTime = qmMoment(formattedTime, 'hh:mm a');
                var timeDiffMins = startTime.diff(now) / 60000;

                return timeDiffMins;
            },

            /**
             * @ngdoc method
             * @name getGalleries
             * @methodOf activityfeed-V2_0.service:qmActivityFeedService
             * @description
             * Gets the list of galleries given an array of ids
             *
             * @returns {promise} Promise that resolves when the galleries items have been retrieved
             */
            getGalleries: function(ids) {

                var mainDefer = $q.defer();
                var galleries = [];

                if (typeof ids === 'undefined' || ids.length === 0) {
                    // galleries are not configured to show under activity feed
                    mainDefer.resolve(galleries);
                    return mainDefer.promise;
                }

                var qmGalleryService = qmActivityFeedDependencyService.getGalleryService('qmGalleryService');
                var galleryRequiresLogin = qmEventConfig.isComponentLoginRequired('gallery');
                if (qmGalleryService && (!galleryRequiresLogin || qmLogin.isLoggedIn())) {

                    var galleriesPromise = qmGalleryService.getList(ids);
                    galleriesPromise.then(function(galleriesData) {

                        var detailPromises = [];

                        // handle individual gallery detail promises
                        var handleDetailPromise = function(detailPromise, defer, gallery) {
                            detailPromise.then(function(photos) {
                                photos.sort(function(a,b) {return a.qmLastMod < b.qmLastMod;});
                                if (photos.length > 0) {
                                    gallery.activityFeedDateTime = photos[0].qmLastMod;
                                    gallery.photos = photos;
                                    galleries.push(gallery);
                                }
                                defer.resolve();
                            }, function() {
                                defer.reject();
                            });
                        };

                        for (var i = 0; i < galleriesData.length; i++) {
                            var gallery = galleriesData[i];
                            gallery.type = 'gallery';
                            var galleryId = gallery.galleryId;
                            var defer = $q.defer();
                            var detailPromise = qmGalleryService.getDetail(galleryId, 0, 4, '-qmLastMod');
                            // let function handle promise resolution so that the objects are properly resolved inside a closure
                            handleDetailPromise(detailPromise, defer, gallery);
                            detailPromises.push(defer.promise);
                        }

                        $q.all(detailPromises).then(function() {
                            mainDefer.resolve(galleries);
                        }, function(err) {
                            mainDefer.reject(err);
                        });

                    }, function(err) {
                        mainDefer.reject(err);
                    });
                } else {
                    mainDefer.resolve(galleries);
                }

                return mainDefer.promise;
            }
        };
    }
]);

var activityfeed = angular.module('activityfeed-V2_0');
/**
 * @ngdoc service
 * @name activityfeed-V2_0.service:qmActivityFeedConfigService
 * @description
 * Check config for ActivityFeed
 */
activityfeed.factory('qmActivityFeedConfigService',
    ['qmEventConfig', function(qmEventConfig) {
        return {
            /**
             * @ngdoc method
             * @name checkConfig
             * @methodOf activityfeed-V2_0.service:qmActivityFeedConfigService
             * @description
             * check if current component config within activityFeed feature config
             *
             * @param {string} componentKey component's key
             * @returns {boolean} Has component been configured?
             */
            checkConfig: function(componentKey) {

                var config = qmEventConfig.getComponentConfig('activityfeed');

                var checkValue = false;

                angular.forEach(config.listItems[0].listItem, function(componentItem) {
                    if (componentItem['@attributes']['name'].toLowerCase() === componentKey.toLowerCase()) {
                        checkValue = true;
                    }
                });

                return checkValue;
            }
        };
    }
]);

var activityfeed = angular.module('activityfeed-V2_0');
/**
 * @ngdoc service
 * @name activityfeed-V2_0.service:qmActivityFeedDependencyService
 * @description
 * Manages communication with dependency manager
 */
activityfeed.factory('qmActivityFeedDependencyService', ['qmDependencyManager', function(qmDependencyManager) {
    return {
        /**
         * @ngdoc method
         * @name getEventsService
         * @methodOf activityfeed-V2_0.service:qmActivityFeedDependencyService
         * @description
         * Get service from event's 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 getAnnouncementsService
         * @methodOf activityfeed-V2_0.service:qmActivityFeedDependencyService
         * @description
         * Get service from announcements's component
         *
         * @param {string} service Service name
         * @returns {service} Angular service or null
         */
        getAnnouncementsService: function(service) {
            return qmDependencyManager.getService('announcements', '2.0', 'component', service);
        },

        /**
         * @ngdoc method
         * @name getGalleryService
         * @methodOf activityfeed-V2_0.service:qmActivityFeedDependencyService
         * @description
         * Get service from gallery's component
         *
         * @param {string} service Service name
         * @returns {service} Angular service or null
         */
        getGalleryService: function(service) {
            return qmDependencyManager.getService('gallery', '2.1', 'component', service);
        },

        /**
         * @ngdoc method
         * @name getSpeakersService
         * @methodOf activityfeed-V2_0.service:qmActivityFeedDependencyService
         * @description
         * Get service from speakers component
         *
         * @param {string} service Service name
         * @returns {service} Angular service or null
         */
        getSpeakersService: function(service) {
            return qmDependencyManager.getService('speakers', '2.0', 'component', service);
        },

        getTemplateUrl: function(template) {

            return '/asset/component/activityfeed/2.0/webapp/html/' + template;
        }
    };
}]);

var announcements = angular.module('announcements-V2_0');
/**
 * @ngdoc service
 * @name announcements-V2_0.service:qmAnnouncementsService
 * @description
 * Manages REST API communication for Announcements
 */
announcements.factory('qmAnnouncementsService', ['qmRest', '$rootScope', '$q', 'qmLocalization', 'qmWebAppService', 'qmMoment',
    function(qmRest, $rootScope, $q, qmLocalization, qmWebAppService, qmMoment) {
        return {
            /**
             * @ngdoc method
             * @name getList
             * @methodOf announcements-V2_0.service:qmAnnouncementsService
             * @description
             * Get the list of announcements
             *
             * @returns {promise} Promise that resolves when the announcements have been retrieved
             */
            getList: function(announcementsIds) {

                var queryParams = [];
                queryParams.push('locale=' + qmLocalization.getLocale());

                if (angular.isDefined(announcementsIds)) {
                    for (var i = 0; i < announcementsIds.length; i++) {
                        queryParams.push('announcementId[]=' + encodeURIComponent(announcementsIds[i]));
                    }
                }

                var url = qmWebAppService.getBaseRestUrl('component', 'announcements') + '/announcements?' + queryParams.join('&');
                var defer = $q.defer();
                // get announcements' data
                qmRest.get(url).then(function(data) {

                    var announcementsList = data;
                    // Reformat dates and times for view
                    for (var i = 0; i < announcementsList.length; i++) {
                        announcementsList[i].scheduleDate = qmMoment(announcementsList[i].scheduledDateTime).format('ddd, MMM D, YYYY');
                        announcementsList[i].scheduleTime = qmMoment(announcementsList[i].scheduledDateTime).format('h:mm A');
                    }
                    defer.resolve(announcementsList);
                }, function(err) {
                    defer.reject(err);
                });

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

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

articles.config(['$stateProvider', function($stateProvider) {
    $stateProvider
        .state('event.articles-V2_0', {
            url: '/Articles',
            views: {
                'component@event': {
                    templateUrl: '/asset/component/articles/2.0/webapp/html/article-list.html',
                    controller: 'ArticlesListController'
                },
                'header@event': {
                    controller: 'ArticlesListHeaderController'
                }
            }
        });
}]);

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

/**
 * @ngdoc service
 * @name articles-V2_0.service:qmArticlesService
 * @description
 * Manages REST API communication for Articles
 */
articles.factory('qmArticlesService', ['qmRest', '$q', 'qmWebAppService',
function(qmRest, $q, qmWebAppService) {
    return {
        /**
         * @ngdoc method
         * @name getList
         * @methodOf articles-V2_0.service:qmArticlesService
         * @description
         * Get list of articles
         *
         * @returns {promise} Promise that resolves when article list has been retrieved
         */
        getList: function() {
            var url = qmWebAppService.getBaseRestUrl('component', 'articles') + '/articles';
            var defer = $q.defer();
            // get article list data
            qmRest.get(url).then(function(data) {
                var articleList = data;
                defer.resolve(articleList);
            }, function(err) {
                defer.reject(err);
            });

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

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

/**
 * @ngdoc service
 * @name articles-V2_0.service:qmArticleDeepLinkingService
 * @description
 * Manages Speaker Deep Linking
 */
articles.factory('qmArticleDeepLinkingService', ['$state', '$window', 'qmArticlesService', 'qmArticlesDependencyService', '$rootScope',
    function($state, $window, qmArticlesService, qmArticlesDependencyService, $rootScope) {
        var init = {
            /**
             * @ngdoc method
             * @name handleDeeplinkState
             * @methodOf articles-V2_0.service:qmArticleDeepLinkingService
             * @description
             * Get state for article component deep link
             *
             * @param {string} type Type of component
             * @param {string} entity Entity key
             */
            handleDeepLinkState: function(type, entity) {
                var qmDeeplinkingService = qmArticlesDependencyService.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.articles');
                } else {
                    qmArticlesService.getList(entity).then(function(articleResponse) {
                        var gotArticle = articleResponse.find(function(article) {
                            return article.articleId === entity;
                        });

                        if (!gotArticle) {
                            qmDeeplinkingService.handleUnavailableDeeplink(type, entity);
                            return;
                        }
                        $window.location.href = gotArticle.link;
                    }, function(err) {
                        err.config.errorHandled = true;
                        if (err.status !== 401) {
                            qmDeeplinkingService.handleUnavailableDeeplink(type, entity);
                        }
                    });
                }
            }
        };

        return init;
    }]);

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

/**
 * @ngdoc service
 * @name articles-V2_0.service:qmArticlesDependencyService
 * @description
 * Manages communication with dependency manager
 */
articles.factory('qmArticlesDependencyService', ['qmDependencyManager', function(qmDependencyManager) {
    return {
        /**
         * @ngdoc method
         * @name getDeepLinkingService
         * @methodOf articles-V2_0.service:qmArticlesDependencyService
         * @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 articles = angular.module('articles-V2_0');

/**
 * @ngdoc controller
 * @name articles-V2_0.controller:ArticlesListController
 * @description
 * Controller for article list view
 */
articles.controller('ArticlesListController', ['$scope', 'qmArticlesService', 'qmList', 'qmUtilities', 'qmLocalization',
function($scope, qmArticlesService, qmList, qmUtilities, qmLocalization) {
    $scope.loadArticles = qmArticlesService.getList();
    $scope.loadArticles.then(function(articles) {
        articles = articles.map(function(article) {
            article.link = qmUtilities.generateValidUrl(article.link);

            return article;
        });
        $scope.articles = qmList.transformList(articles);
        var componentTitle = qmLocalization.getString('componentArticlesTitle');
        $scope.emptyText = qmLocalization.getString('LABEL_EMPTY_ARTICLES', componentTitle);
    });

    $scope.openLink = function(link) {
        qmUtilities.openUrl(link);
    };
}]);

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

var bannerAds = angular.module('banner-ads-V2_0');

/**
 * @ngdoc directive
 * @name banner-ads-V2_0.directive:qmBannerAds
 * @scope
 * @restrict E
 *
 * @description
 * Displays and rotates banner ads with optional links
 *
 * @param {Array} source Array of banner ad objects
 */
bannerAds.directive('qmBannerAds', ['$interval', '$timeout', '$window', 'qmBannerAdsAnalyticsService',
function($interval, $timeout, $window, qmBannerAdsAnalyticsService) {
    return {
        restrict: 'E',
        scope: {
            source: '='
        },
        controller: function($scope) {
            var interval = null;

            var counter = 0;
            var nextBanner = function() {
                $interval.cancel(interval);
                var timeInSeconds = parseInt($scope.source[counter].interval, 10);
                $scope.banner = $scope.source[counter];

                counter++;
                if (counter === $scope.source.length) {
                    counter = 0;
                }
                // don't continue if it has been stopped
                if (interval) {
                    interval = $interval(nextBanner, timeInSeconds * 1000, 1);
                }
            };
            var stopInterval = function() {
                if (interval) {
                    $interval.cancel(interval);
                    interval = null;
                }
            };
            var startInterval = function() {
                // start interval loop only if there is a data source
                if ($scope.source && $scope.source.length) {
                    // if there is only 1 banner, there's no need to rotate
                    if ($scope.source.length === 1) {
                        $scope.banner = $scope.source[0];
                    } else {
                        interval = $interval(nextBanner, 0, 1);
                    }
                } else {
                    // if we detect that we don't have a data source, stop the interval
                    stopInterval();
                }
            };

            $scope.$watch('source', function() {
                startInterval();
            });
            // $interval will keep going so we need to cancel it when we are no longer in scope
            $scope.$on('$destroy', function() {
                stopInterval();
            });

            $scope.windowFocus = true;
            $window.addEventListener('focus', function() {
                $scope.windowFocus = true;
            });
            $window.addEventListener('blur', function() {
                $scope.windowFocus = false;
            });

            startInterval();
        },
        link: function(scope, element) {
            element.on('click', function() {
                var bannerAdId = scope.banner.bannerAdId;
                if (bannerAdId) {
                    qmBannerAdsAnalyticsService.markClickEvent(bannerAdId);
                }
            });
            scope.$watch('banner', function(newVal) {
                if (newVal) {
                    element.empty();
                    var imageElement = angular.element('<img class="img-responsive"/>');
                    if (newVal.imageUrl) {
                        imageElement.prop('src', newVal.imageUrl);
                    }

                    var registerAnalytics = function(scope, loadedElement) {
                        loadedElement.on('load', function() {
                            if (scope.windowFocus) {
                                // need to make sure the slide out menu animation has finished before checking position
                                $timeout(function() {
                                    // do not mark banner ad view if it is not on screen
                                    if (loadedElement.offset().left >= 0) {
                                        var bannerAdId = scope.banner.bannerAdId;
                                        if (bannerAdId) {
                                            qmBannerAdsAnalyticsService.markViewEvent(bannerAdId);
                                        }
                                    }
                                }, 1000);
                            }
                        });
                    };

                    if (newVal.imageUrl) {
                        registerAnalytics(scope, imageElement, newVal);
                    }

                    if (newVal.link) {
                        var hrefLink = newVal.link;
                        // if link does not have proper protocol, use a default protocol so link does not become relative to current WebApp url
                        if (!hrefLink.match(/^(http(s)?:\/\/)/)) {
                            hrefLink = 'http://' + hrefLink;
                        }
                        var anchorElement = angular.element('<a class="banner-link" href="' + hrefLink + '" target="_blank"></a>');
                        anchorElement.append(imageElement);
                        // create fullHeightElement to make entire banner ad area clickable to the anchor
                        var fullHeightElement = angular.element('<div class="full-height"></div>');
                        anchorElement.append(fullHeightElement);
                        element.append(anchorElement);
                    } else {
                        element.append(imageElement);
                    }
                }
            });
        }
    };
}]);

var bannerAds = angular.module('banner-ads-V2_0');

/**
 * @ngdoc service
 * @name banner-ads-V2_0.service:qmBannerAdsService
 * @description
 * Manages REST API communication for Banner Ads
 */
bannerAds.factory('qmBannerAdsService', ['qmRest', '$q', 'qmLocalization', 'qmWebAppService',
function(qmRest, $q, qmLocalization, qmWebAppService) {
    return {
        /**
         * @ngdoc method
         * @name getList
         * @methodOf banner-ads-V2_0.service:qmBannerAdsService
         * @description
         * Get list of banner ads
         *
         * @returns {promise} Promise that resolves when list of banner ads have been retrieved
         */
        getList: function() {
            var getListUrl = qmWebAppService.getBaseRestUrl('component', 'banner-ads') + '/banner-ads';

            var defer = $q.defer();

            qmRest.get(getListUrl).then(function(data) {
                var bannerAdsList = [];

                // use qmRest service to get image data and store in imageSrc
                for (var i = 0; i < data.length; i++) {
                    // Do not add banner ads if ad has interval of 0
                    if (parseInt(data[i].interval) > 0) {
                        if (data[i].mediumImageUrl && data[i].mediumImageUrl !== '') {
                            data[i].imageUrl = qmWebAppService.appendAuthHeader(data[i].mediumImageUrl);
                        } else if (data[i].smallImageUrl && data[i].smallImageUrl !== '') {
                            data[i].imageUrl = qmWebAppService.appendAuthHeader(data[i].smallImageUrl);
                        }
                        bannerAdsList.push(data[i]);
                    }
                }
                defer.resolve(bannerAdsList);
            }, function(err) {
                defer.reject(err);
            });

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

/**
 * @ngdoc service
 * @name banner-ads-V2_0.service:qmBannerAdsAnalyticsService
 * @description
 * Manages the sending of banner ad analytics
 */
bannerAds.factory('qmBannerAdsAnalyticsService', ['qmAnalytics', function(qmAnalytics) {
    var bannerAdViewId = null;

    return {
        /**
         * @ngdoc method
         * @name markViewEvent
         * @methodOf banner-ads-V2_0.service:qmBannerAdsAnalyticsService
         * @description
         * Send BannerAdView markEvent if banner ad view is different from the last one it sent
         *
         * @params {string} id Banner id
         */
        markViewEvent: function(id) {
            // make sure we do not send multiple banner ad views for the same banner ad
            if (id !== bannerAdViewId) {
                bannerAdViewId = id;
                qmAnalytics.markEvent('BannerAdView', id, 'HomeView');
            }
        },
        /**
         * @ngdoc method
         * @name markClickEvent
         * @methodOf banner-ads-V2_0.service:qmBannerAdsAnalyticsService
         * @description
         * Send BannerAdClick markEvent
         *
         * @params {string} id Banner id
         */
        markClickEvent: function(id) {
            qmAnalytics.markEvent('BannerAdClick', id, 'HomeView');
        }
    };
}]);

var cityguide = angular.module('city-guide-V2_0');

cityguide.config(['$stateProvider', function($stateProvider) {
    $stateProvider
        .state('event.city-guide-V2_0', {
            url: '',
            resolve: {
                openCityguide: ['$stateParams', 'qmEventConfig', 'qmUtilities', '$state', '$q', function($stateParams, qmEventConfig, qmUtilities, $state, $q) {
                    var componentConfig = qmEventConfig.getComponentConfig('city-guide', $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 community = angular.module('community-V2_1');

community.config(['$stateProvider', function($stateProvider) {
    $stateProvider
        .state('event.community-V2_1', {
            url: '/Community',
            views: {
                'component@event': {
                    templateUrl: '/asset/component/community/2.1/webapp/html/community-list.html',
                    controller: 'CommunityListController'
                },
                'header@event': {
                    controller: 'CommunityListHeaderController'
                }
            }
        });
}]);

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

/**
 * @ngdoc service
 * @name community-V2_1.service:qmCommunityService
 * @description
 * Manages REST API communication for Community
 */
community.factory('qmCommunityService', ['qmEventConfig', 'qmWebAppService', '$state', 'qmLocalization', 'toastr',
    function(qmEventConfig, qmWebAppService, $state, qmLocalization, toastr) {
        return {
            /**
             * @ngdoc method
             * @name getList
             * @methodOf community-V2_1.service:qmCommunityService
             * @description
             * Get list of community
             *
             * @returns {promise} Promise that resolves when community list has been retrieved
             */
            getList: function() {
                var communityItems = [];
                var config = qmEventConfig.getComponentConfig('community');
                var isFacebookConfigured = qmEventConfig.isComponentConfigured('facebook');
                var misconfigured = [];

                if (!isFacebookConfigured && config['@attributes']['Facebook'] === 'true') {
                    misconfigured.push(qmLocalization.getString('ALERT_COMMUNITY_NO_FACEBOOK'));
                }

                if (isFacebookConfigured && config['@attributes']['Facebook'] === 'true') {
                    var facebook = {};
                    facebook.key = 'facebook';

                    var facebookConfig = qmEventConfig.getComponentConfig('facebook');
                    if (facebookConfig['@attributes']['iconSrc']) {
                        facebook.hasImage = true;
                        facebook.icon = facebookConfig['@attributes']['iconSrc'];
                    } else {
                        facebook.hasImage = false;
                        facebook.iconclass = 'fa fa-facebook-square fa-5x facebook-hex';
                    }

                    facebook.title = qmLocalization.getString('componentFacebookTitle');
                    communityItems.push(facebook);
                }

                if (config['@attributes']['Weblink1'] === 'true') {
                    var weblink1 = {};
                    weblink1.key = 'weblink1';
                    weblink1.hasImage = true;
                    weblink1.title = qmLocalization.getString(config['@attributes']['weblinkOneTitleStringKey']);
                    weblink1.url = config['@attributes']['weblinkOneUrl'];
                    var weblink1IconUrl = qmEventConfig.getArtifact('icon_main_linkedin');
                    weblink1.icon = qmWebAppService.appendAuthHeader(weblink1IconUrl);
                    communityItems.push(weblink1);
                }

                if (config['@attributes']['Weblink2'] === 'true') {
                    var weblink2 = {};
                    weblink2.key = 'weblink2';
                    weblink2.hasImage = true;
                    weblink2.title = qmLocalization.getString(config['@attributes']['weblinkTwoTitleStringKey']);
                    weblink2.url = config['@attributes']['weblinkTwoUrl'];
                    var weblink2IconUrl = qmEventConfig.getArtifact('icon_main_weblink2');
                    weblink2.icon = qmWebAppService.appendAuthHeader(weblink2IconUrl);
                    communityItems.push(weblink2);
                }

                if (misconfigured.length > 0) {
                    for (var i = 0; i < misconfigured.length; i++) {
                        toastr.error(misconfigured[i], '', {
                            closeButton: true,
                            timeOut: 5000
                        });
                    }
                }

                return communityItems;
            }
        };
    }]);

/**
 * Created by Alexander Sugianto on 2015-06-12.
 */

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

community.constant('COMMUNITY', {
    twitterUrl: 'https://twitter.com'
});

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

/**
 * @ngdoc controller
 * @name community-V2_1.controller:CommunityListController
 * @description
 * Controller for community list view
 */
community.controller('CommunityListController', ['$scope', 'qmCommunityService', 'qmList', '$state', 'qmDependencyManager', 'qmUtilities',
    function($scope, qmCommunityService, qmList, $state, qmDependencyManager, qmUtilities) {
    $scope.communitySetting = qmCommunityService.getList();
    $scope.community = qmList.transformList($scope.communitySetting);
    $scope.goToComponent = function(item) {
        switch (item.key) {
            case 'facebook':
                var qmFacebookService = qmDependencyManager.getService('facebook', '2.0', 'component', 'qmFacebookService');
                var url = qmFacebookService.getUrl();
                qmUtilities.openUrl(url);
                break;
            case 'weblink1':
            case 'weblink2':
                qmUtilities.openUrl(item.url);
                break;
        }
    };
}]);

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

var contactexchange = angular.module('contact-exchange-V2_0');

contactexchange.config(['$stateProvider', function($stateProvider) {
    $stateProvider
        .state('event.contact-exchange-V2_0', {
            url: '/Contact-Exchange',
            params: {
                visible: true
            },
            views: {
                'component@event': {
                    templateUrl: '/asset/component/contact-exchange/2.0/webapp/html/contactexchange-list.html',
                    controller: 'ContactExchangeListController'
                },
                'header@event': {
                    controller: 'ContactExchangeListHeaderController'
                }
            }
        })
        .state('event.contact-exchange-V2_0.detail', {
            url: '/:attendeeId',
            params: {
                visible: true
            },
            views: {
                'component@event': {
                    templateUrl: '/asset/component/contact-exchange/2.0/webapp/html/contactexchange-detail.html',
                    controller: 'ContactExchangeDetailController'
                },
                'header@event': {
                    controller: 'ContactExchangeDetailHeaderController'
                }
            }
        });
}]);

var contactexchange = angular.module('contact-exchange-V2_0');
/**
 * @ngdoc service
 * @name contact-exchange-V2_0.service:qmContactExchangeService
 * @description
 * Manages REST API communication for Contact Exchange
 */
contactexchange.factory('qmContactExchangeService',
['qmRpc', '$q', 'qmLogin', 'qmEventConfig', 'qmContactExchangeDependencyService', '$rootScope', 'qmWebAppService',
    function(qmRpc, $q, qmLogin, qmEventConfig, qmContactExchangeDependencyService, $rootScope, qmWebAppService) {
        return {
            /**
             * @ngdoc method
             * @name create
             * @methodOf contact-exchange-V2_0.service:qmContactExchangeService
             * @description
             * Create a new general contact
             *
             * @param {string} pin Contact's pin
             * @returns {promise} Promise that resolves when note has successfully been created
             */
            create: function(pin) {
                var defer = $q.defer();
                var createContactUrl = qmEventConfig.getRpcUrl('contactGenerate');
                var params = [];
                params.push(qmLogin.getUserToken());
                params.push(pin);
                qmRpc.post(createContactUrl, {method: 'contactGenerate', params: params}).then(function(response) {
                    qmContactExchangeDependencyService.getAttendeeService('qmAttendeeService').getDetail(response.attendee).then(function(attendee) {
                        defer.resolve(attendee);
                    }, function(err) {
                        defer.reject(err);
                    });
                    $rootScope.$broadcast('gameActivity', {activity: 'contactExchange'});
                }, function(err) {
                    defer.reject(err);
                });
                return defer.promise;
            },
            /**
             * @ngdoc method
             * @name getList
             * @methodOf contact-exchange-V2_0.service:qmContactExchangeService
             * @description
             * Get list of Contacts
             *
             * @returns {promise} Promise that resolves when list of contacts have been retrieved
             */
            getList: function() {
                var defer = $q.defer();
                var createContactUrl = qmEventConfig.getRpcUrl('getContacts');
                var params = [];
                params.push(qmLogin.getUserToken());
                qmRpc.post(createContactUrl, {method: 'getContacts', params: params}).then(function(response) {
                    var contactExchangeList = response.contacts;
                    var attendeeIdList = [];
                    for (var i = 0; i < contactExchangeList.length; i++) {
                        if (contactExchangeList[i].qmActive !== '0') {
                            attendeeIdList.push({'attendeeId': contactExchangeList[i].requesteeIdentifier});
                        }
                    }
                    if (attendeeIdList.length) {
                        qmContactExchangeDependencyService.getAttendeeService('qmAttendeeService').getList(attendeeIdList).then(function(attendeeList) {
                            for (var i = 0; i < attendeeList.length; i++) {
                                for (var j = 0; j < contactExchangeList.length; j++) {
                                    if (attendeeList[i].attendeeId === contactExchangeList[j].requesteeIdentifier) {
                                        attendeeList[i].contactExchangeLogId = contactExchangeList[j].contactExchangeLogId;
                                        attendeeList[i].requesteeIdentifier = contactExchangeList[j].requesteeIdentifier;
                                        attendeeList[i].requesterIdentifier = contactExchangeList[j].requesterIdentifier;
                                        attendeeList[i].imageUrl = qmWebAppService.appendAuthHeader(attendeeList[i].imageUrl);
                                    }
                                }
                            }
                            defer.resolve(attendeeList);
                        }, function(err) {
                            defer.reject(err);
                        });
                    } else {
                        defer.resolve(attendeeIdList);
                    }
                }, function(err) {
                    defer.reject(err);
                });
                return defer.promise;
            }
        };
    }]);

var contactexchange = angular.module('contact-exchange-V2_0');

/**
 * @ngdoc controller
 * @name contact-exchange-V2_0.controller:ContactExchangeListController
 * @description
 * Controller for Contact Exchange list view
 */
contactexchange.controller('ContactExchangeListController',
['$scope', '$state', 'qmLogin', 'qmDependencyManager', 'qmContactExchangeService',
'qmContactExchangeDependencyService', 'qmLocalization', 'qmList', 'toastr', '$q',
function($scope, $state, qmLogin, qmDependencyManager, qmContactExchangeService,
qmContactExchangeDependencyService, qmLocalization, qmList, toastr, $q) {

    var contactsList = [];
    var profile = qmLogin.getUserInfo();
    if (profile.pinId === '' || (typeof profile.pinId === 'undefined')) {
        $scope.myPin = qmLocalization.getString('LABEL_YOUR_PINCODE').replace('{0}', qmLocalization.getString('LABEL_PIN_UNASSIGNED'));
    } else {
        $scope.myPin = qmLocalization.getString('LABEL_YOUR_PINCODE').replace('{0}', profile.pinId);
    }
    var qmAttendeeDependencyService = qmContactExchangeDependencyService.getAttendeeService('qmAttendeeDependencyService');
    $scope.attendeeListItemUrl = qmAttendeeDependencyService.getTemplateUrl('attendee-list-item');
    $scope.attendeeListItemMoreUrl = qmAttendeeDependencyService.getTemplateUrl('attendee-list-item-more');
    $scope.setting = qmContactExchangeDependencyService.getAttendeeService('qmAttendeeService').getSetting();
    $scope.showImage = $scope.setting.photo.isVisible;

    var loadContacts = function() {
        $scope.loadContacts = qmContactExchangeService.getList();
        $scope.loadContacts.then(function(contacts) {
            contactsList = contacts;
            $scope.contactexchange = qmList.transformList(contactsList);
            $scope.emptyText = qmLocalization.getString('LABEL_EMPTY_CONTACT_EXCHANGE_TITLE');
        });
    };

    loadContacts();

    $scope.hasMessaging = qmDependencyManager.hasComponent('messaging');
    $scope.hasAttendee = qmDependencyManager.hasComponent('attendees');
    $scope.submitPin = function() {
        var defer = $q.defer();
        if (profile.pinId !== $scope.pinCode) {
            qmContactExchangeService.create($scope.pinCode).then(function(attendee) {
                if (typeof attendee.attendeeId !== 'undefined') {
                    loadContacts();
                    toastr.success(qmLocalization.getString('LABEL_CONTACT_EXCHANGED').replace('{0}', attendee.firstName + ' ' + attendee.lastName), '', {
                        closeButton: true,
                        timeOut: 5000
                    });
                }
                defer.resolve();
            },
            function(error) {
                var errorMessage;
                if (error.error === 'Exchange number can not be empty') {
                    errorMessage = qmLocalization.getString('ALERT_EMPTY_CODE_SUBMIT');
                } else if (error.error === 'Exchange number does not exist') {
                    errorMessage = qmLocalization.getString('ALERT_INVALID_CODE');
                } else if (error.error === 'This contact has already been added') {
                    errorMessage = qmLocalization.getString('ALERT_DUPLICATE_CONTACT_MESSAGE');
                }
                // Show Failure Message
                toastr.error(errorMessage, '', {
                    closeButton: true,
                    timeOut: 5000
                });
                defer.reject();
            });
        } else {
            var errorMessage = qmLocalization.getString('ALERT_INVALID_SELF_CODE');
            // Show Failure Message
            toastr.error(errorMessage, '', {
                closeButton: true,
                timeOut: 5000
            });
            defer.reject();
        }
        return defer.promise;
    };
}]);

/**
 * @ngdoc controller
 * @name contact-exchange-V2_0.controller:ContactExchangeListHeaderController
 * @description
 * Controller for Contact Exchange list header view
 */
contactexchange.controller('ContactExchangeListHeaderController', ['$scope', 'qmLocalization', function($scope, qmLocalization) {
    $scope.headerTitle = qmLocalization.getString('componentContactExchangeTitle');
}]);

var contactexchange = angular.module('contact-exchange-V2_0');

/**
 * @ngdoc service
 * @name contact-exchange-V2_0.service:qmContactExchangeDependencyService
 * @description
 * Manages communication with dependency manager
 */
contactexchange.factory('qmContactExchangeDependencyService', ['qmDependencyManager', function(qmDependencyManager) {
    return {
        /**
         * @ngdoc method
         * @name getAttendeeService
         * @methodOf contact-exchange-V2_0.service:qmContactExchangeDependencyService
         * @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);
        }
    };
}]);

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

/**
 * @ngdoc controller
 * @name disclaimer-V2_0.controller:DisclaimerController
 * @description
 * Controller for disclaimer
 */
disclaimer.controller('DisclaimerController',
['$scope', 'qmDisclaimerService', 'qmLocalization', '$modalInstance', 'qmContainerConfig', 'qmWebAppData', '$modal',
    'qmAnalytics', 'qmEventConfig', 'qmStateChange',
function($scope, qmDisclaimerService, qmLocalization, $modalInstance, qmContainerConfig, qmWebAppData, $modal,
         qmAnalytics, qmEventConfig, qmStateChange) {

    var loadDisclaimer = function() {

        $scope.isSingleEvent = qmContainerConfig.isSingleEvent();
        $scope.isContainer = qmWebAppData.isContainer();
        $scope.continueText = qmLocalization.getString('BUTTON_DISCLAIMER_CONTINUE');
        $scope.backText = qmLocalization.getString('BUTTON_DISCLAIMER_BACK');
        $scope.acceptText = qmLocalization.getString('BUTTON_DISCLAIMER_ACCEPT');
        $scope.declineText = qmLocalization.getString('BUTTON_DISCLAIMER_DECLINE');

        if (qmStateChange.transitionLevel === 'container') {
            $scope.useTransparentView = qmContainerConfig.isTransparentView();
        } else {
            $scope.useTransparentView = qmEventConfig.isTransparentView();
        }

        $scope.loadDisclaimer = qmDisclaimerService.getDetail();
        $scope.loadDisclaimer.then(function(disclaimer) {
            $scope.disclaimer = disclaimer;
        }, function() {
            // If disclaimer REST fails, show the disclaimer with default values so user wont get stuck on a blank page
            $scope.disclaimer = {
                required: false,
                title: qmLocalization.getString('componentDisclaimerTitle'),
                body: qmLocalization.getString('CONTENT_DISCLAIMER_BODY_DEFAULT')
            };
        });

        $scope.accept = function() {
            if ($scope.disclaimer.required) {
                qmAnalytics.markEvent('DisclaimerAccepted');
            } else {
                qmAnalytics.markEvent('DisclaimerContinued');
            }
            $modalInstance.close();
        };
        $scope.decline = function() {
            if ($scope.disclaimer.required) {
                qmAnalytics.markEvent('DisclaimerDeclined');
            } else {
                qmAnalytics.markEvent('DisclaimerBack');
            }
            $modalInstance.dismiss('cancel');
        };
    };

    $scope.goToLanguageSelect = function() {
        var openParams = {
            templateUrl: '/asset/component/disclaimer/2.0/webapp/html/disclaimer-language-modal.html',
            controller: 'DisclaimerLanguageController',
            backdrop: 'static',
            keyboard: false
        };
        $modal.open(openParams).result.then(function() {
            loadDisclaimer();
        });
    };

    loadDisclaimer();
}]);

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

/**
 * @ngdoc controller
 * @name disclaimer-V2_0.controller:DisclaimerLanguageController
 * @description
 * Controller for disclaimer language select
 */
disclaimer.controller('DisclaimerLanguageController',
['$scope', '$modalInstance', 'qmConfigManager', 'qmLocalization', 'qmUtilities', 'qmWebAppData',
function($scope, $modalInstance, qmConfigManager, qmLocalization, qmUtilities, qmWebAppData) {
    var languages = qmConfigManager.getConfig().getLanguages();
    var currentLocale = qmLocalization.getLocale();
    for (var i = 0; i < languages.length; i++) {
        if (languages[i]['@attributes'].locale === currentLocale) {
            languages[i]['@attributes'].active = true;
        } else {
            languages[i]['@attributes'].active = false;
        }
    }
    $scope.languages = languages;

    $scope.updateLanguage = function(locale) {
        if ($scope.locale !== locale) {
            qmUtilities.addPageLoad();
            qmLocalization.updateStringKeys(qmWebAppData.getAppId(), locale).then(function() {
                $modalInstance.close();
            }).finally(function() {
                qmUtilities.removePageLoad();
            });
        }
    };

    $scope.cancel = function() {
        $modalInstance.dismiss('cancel');
    };
}]);

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

/**
 * @ngdoc service
 * @name disclaimer-V2_0.service:qmDisclaimerService
 * @description
 * Manages REST API communication for disclaimer
 */
disclaimer.factory('qmDisclaimerService', ['qmRest', '$q', 'qmWebAppService', 'qmLocalization', '$modal', 'qmLocalStorage',
function(qmRest, $q, qmWebAppService, qmLocalization, $modal, qmLocalStorage) {
    return {
        /**
         * @ngdoc method
         * @name checkDisclaimer
         * @methodOf disclaimer-V2_0.service:qmDisclaimerService
         * @description
         * Show disclaimer if not previously accepted
         *
         * @returns {promise} Promise that resolves when disclaimer has been accepted
         */
        checkDisclaimer: function() {
            var defer = $q.defer();
            var userId = qmLocalStorage.getUserId();
            if (userId) {
                qmLocalStorage.resetUserId();
            }
            var isDisclaimerAccepted = qmLocalStorage.getItem('isDisclaimerAccepted');
            if (!isDisclaimerAccepted) {
                var openParams = {
                    templateUrl: '/asset/component/disclaimer/2.0/webapp/html/disclaimer-modal.html',
                    controller: 'DisclaimerController',
                    backdrop: 'static',
                    keyboard: false
                };

                var disclaimerModal = $modal.open(openParams);
                disclaimerModal.result.then(function() {
                    qmLocalStorage.setItem('isDisclaimerAccepted', true);
                    defer.resolve();
                }, function() {
                    defer.reject();
                }).finally(function() {
                    if (userId) {
                        qmLocalStorage.setUserId(userId);
                    }
                });
            } else {
                if (userId) {
                    qmLocalStorage.setUserId(userId);
                }
                defer.resolve();
            }

            return defer.promise;
        },
        /**
         * @ngdoc method
         * @name getDetail
         * @methodOf disclaimer-V2_0.service:qmDisclaimerService
         * @description
         * Get detail of disclaimer
         *
         * @returns {promise} Promise that resolves when disclaimer detail has been retrieved
         */
        getDetail: function() {
            var url = qmWebAppService.getBaseRestUrl('component', 'disclaimer') + '/disclaimer';
            url += '?locale=' + qmLocalization.getLocale();
            var defer = $q.defer();
            // get disclaimer data
            qmRest.get(url).then(function(disclaimer) {
                defer.resolve(disclaimer);
            }, function(err) {
                defer.reject(err);
                err.config.errorHandled = true;
            });

            return defer.promise;
        },
        /**
         * @ngdoc method
         * @name getTemplateUrl
         * @methodOf disclaimer-V2_0.service:qmDisclaimerService
         * @description
         * Get template url for disclaimer component
         *
         * @param {string} name Template name
         * @returns {string} Template url
         */
        getTemplateUrl: function(name) {
            if (name === 'disclaimer-container-setting') {
                return '/asset/component/disclaimer/2.0/webapp/html/disclaimer-container-setting.html';
            }
            return null;
        }
    };
}]);

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

disclaimer.config(['$stateProvider', function($stateProvider) {
    $stateProvider
        .state('container.disclaimer-V2_0', {
            url: '',
            views: {
                'container@container': {
                    templateUrl: '/asset/component/disclaimer/2.0/webapp/html/disclaimer-container.html',
                    controller: 'DisclaimerContainerController'
                }
            }
        });
}]);

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

/**
 * @ngdoc controller
 * @name disclaimer-V2_0.controller:DisclaimerContainerController
 * @description
 * Controller for container disclaimer view
 */
disclaimer.controller('DisclaimerContainerController',
['$scope', 'qmDisclaimerService', 'qmLocalization', 'qmContainerConfig',
function($scope, qmDisclaimerService, qmLocalization, qmContainerConfig) {
    $scope.disclaimerTitle = qmLocalization.getString('componentDisclaimerTitle').toUpperCase();
    if (qmContainerConfig.isComponentConfigured('disclaimer')) {
        $scope.loadDisclaimer = qmDisclaimerService.getDetail();
        $scope.loadDisclaimer.then(function(disclaimer) {
            $scope.disclaimer = disclaimer;
        }, function() {
            // If disclaimer REST fails, show the disclaimer with default values so user wont get stuck on a blank page
            $scope.disclaimer = {
                required: false,
                title: qmLocalization.getString('componentDisclaimerTitle'),
                body: qmLocalization.getString('CONTENT_DISCLAIMER_BODY_DEFAULT')
            };
        });
    } else {
        $scope.disclaimer = {
            required: false,
            title: qmLocalization.getString('componentDisclaimerTitle'),
            body: qmLocalization.getString('CONTENT_DISCLAIMER_BODY_DEFAULT')
        };
    }

}]);

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

documents.config(['$stateProvider', function($stateProvider) {
    $stateProvider
        .state('event.documents-V2_3', {
            url: '/Documents',
            views: {
                'component@event': {
                    templateUrl: '/asset/component/documents/2.1/webapp/html/document-list.html',
                    controller: 'DocumentsListController'
                },
                'header@event': {
                    controller: 'DocumentsListHeaderController'
                }
            }
        })
        .state('event.documents-V2_3.detail', {
            url: '/:documentId',
            views: {
                'component@event': {
                    templateUrl: '/asset/component/documents/2.1/webapp/html/document-detail.html',
                    controller: 'DocumentDetailController'
                },
                'header@event': {
                    controller: 'DocumentDetailHeaderController'
                }
            }
        });
}]);

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

/**
 * @ngdoc service
 * @name documents-V2_1.service:qmDocumentsService
 * @description
 * Manages REST API communication for Documents
 */
documents.factory('qmDocumentsService', ['qmRest', '$q', 'qmLocalization', 'qmWebAppService',
    function(qmRest, $q, qmLocalization, qmWebAppService) {

        function decorator(document) {

            if (document.documentUrl && document.documentUrl !== '') {
                document.documentUrl = qmWebAppService.appendAuthHeader(document.documentUrl);
            }

            return document;
        }

        return {
            /**
             * @ngdoc method
             * @name getList
             * @methodOf documents-V2_1.service:qmDocumentsService
             * @description
             * Get list of documents
             *
             * @param {array} documentIds Document Ids
             *
             * @returns {promise} Promise that resolves when document list has been retrieved
             */
            getList: function(documentIds) {
                var url = qmWebAppService.getBaseRestUrl('component', 'documents') + '/documents';
                url += '?locale=' + qmLocalization.getLocale();
                if (documentIds !== undefined && documentIds.length) {
                    for (var i = 0; i < documentIds.length; i++) {
                        url += '&documentIds[]=' + documentIds[i].documentId;
                    }
                }

                var defer = $q.defer();
                // get document list data
                qmRest.get(url).then(function(response) {
                    var documentList = response;
                    for (var i = 0; i < documentList.length; i++) {
                        if (!documentList[i].categoryType) {
                            documentList[i].groupValue = 'GROUP_GENERAL';
                            documentList[i].groupLabel = qmLocalization.getString('LABEL_GENERAL');
                        } else {
                            documentList[i].groupValue = documentList[i].categoryType;
                            documentList[i].groupLabel = documentList[i].categoryType;
                        }
                    }
                    defer.resolve(documentList);
                }, function(err) {
                    defer.reject(err);
                });

                return defer.promise;
            },
            /**
             * @ngdoc method
             * @name getList
             * @methodOf documents-V2_1.service:qmDocumentsService
             * @description
             * Get list of author documents
             *
             * @param {array} documentIds Document Ids
             *
             * @returns {promise} Promise that resolves when document list has been retrieved
             */
            getAuthorDocumentsList: function(documentIds) {
                var url = qmWebAppService.getBaseRestUrl('component', 'documents') + '/author-documents';
                url += '?locale=' + qmLocalization.getLocale();
                if (documentIds !== undefined && documentIds.length) {
                    for (var i = 0; i < documentIds.length; i++) {
                        url += '&documentIds[]=' + documentIds[i].documentId;
                    }
                }

                var defer = $q.defer();
                // get document list data
                qmRest.get(url).then(function(response) {
                    var documentList = response;
                    for (var i = 0; i < documentList.length; i++) {
                        if (!documentList[i].categoryType) {
                            documentList[i].groupValue = 'GROUP_GENERAL';
                            documentList[i].groupLabel = qmLocalization.getString('LABEL_GENERAL');
                        } else {
                            documentList[i].groupValue = documentList[i].categoryType;
                            documentList[i].groupLabel = documentList[i].categoryType;
                        }
                    }
                    defer.resolve(documentList);
                }, function(err) {
                    defer.reject(err);
                });

                return defer.promise;
            },
            /**
             * @ngdoc method
             * @name getDetail
             * @methodOf documents-V2_1.service:qmDocumentsService
             * @description
             * Get a document
             *
             * @returns {promise} Promise that resolves when document has been retrieved
             */
            getDetail: function($documentId) {
                var url = qmWebAppService.getBaseRestUrl('component', 'documents') + '/documents/' + $documentId;
                url += '?locale=' + qmLocalization.getLocale();
                var defer = $q.defer();
                // get document detail data
                qmRest.get(url).then(function(response) {
                    var documentDetail = decorator(response);

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

                return defer.promise;
            },
            /**
             * @ngdoc method
             * @name getExhibitorDocuments
             * @methodOf documents-V2_1.service:qmDocumentsService
             * @description
             * Get list of documents
             *
             * @param {string} exhibitorId Exhibitor Id
             *
             * @returns {promise} Promise that resolves when document list has been retrieved
             */
            getExhibitorDocuments: function(exhibitorId) {
                var url = qmWebAppService.getBaseRestUrl('component', 'documents') +
                    '/exhibitors/' + exhibitorId + '/documents?locale=' + qmLocalization.getLocale();

                var defer = $q.defer();
                // get document list data
                qmRest.get(url).then(function(response) {
                    var documentList = [];
                    angular.forEach(response, function(document) {
                        documentList.push(decorator(document));

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

                return defer.promise;
            },
            /**
             * @ngdoc method
             * @name getEventDocuments
             * @methodOf documents-V2_1.service:qmDocumentsService
             * @description
             * Get list of documents
             *
             * @param {string} eventId Event Id
             *
             * @returns {promise} Promise that resolves when document list has been retrieved
             */
            getEventDocuments: function(eventId) {
                var url = qmWebAppService.getBaseRestUrl('component', 'documents') + '/events/' + eventId + '/documents?locale=' + qmLocalization.getLocale();

                var defer = $q.defer();
                // get document list data
                qmRest.get(url).then(function(response) {
                    var documentList = [];
                    angular.forEach(response, function(document) {
                        documentList.push(decorator(document));

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

                return defer.promise;
            },
            /**
             * @ngdoc method
             * @name getMyDocuments
             * @methodOf documents-V2_1.service:qmDocumentsService
             * @description
             * Get list of my documents
             *
             * @returns {promise} Promise that resolves when my documents list has been retrieved
             */
            getMyDocuments: function() {
                var url = qmWebAppService.getBaseRestUrl('component', 'documents') + '/my-documents?locale=' + qmLocalization.getLocale();

                var defer = $q.defer();
                // get document list data
                qmRest.get(url).then(function(response) {
                    var documentList = response;
                    for (var i = 0; i < documentList.length; i++) {
                        if (!documentList[i].categoryType) {
                            documentList[i].groupValue = 'GROUP_GENERAL';
                            documentList[i].groupLabel = qmLocalization.getString('LABEL_GENERAL');
                        } else {
                            documentList[i].groupValue = documentList[i].categoryType;
                            documentList[i].groupLabel = documentList[i].categoryType;
                        }
                    }
                    defer.resolve(documentList);
                }, function(err) {
                    defer.reject(err);
                });

                return defer.promise;
            },
            /**
             * @ngdoc method
             * @name addMyDocument
             * @methodOf documents-V2_1.service:qmDocumentsService
             * @description
             * Add a document to my documents list
             *
             * @param {string} $documentId Document Id
             * @returns {promise} Promise that resolves when my document has been added
             */
            addMyDocument: function($documentId) {
                var url = qmWebAppService.getBaseRestUrl('component', 'documents') + '/my-documents/' + $documentId;
                var defer = $q.defer();
                qmRest.put(url, {}).then(function(response) {
                    defer.resolve(response);
                }, function(err) {
                    defer.reject(err);
                });

                return defer.promise;
            },
            /**
             * @ngdoc method
             * @name deleteMyDocument
             * @methodOf documents-V2_1.service:qmDocumentsService
             * @description
             * Delete document from my documents list
             *
             * @param {string} $documentId Document Id
             * @returns {promise} Promise that resolves when my document is deleted
             */
            deleteMyDocument: function($documentId) {
                var url = qmWebAppService.getBaseRestUrl('component', 'documents') + '/my-documents/' + $documentId;
                var defer = $q.defer();
                qmRest.delete(url, {}).then(function(response) {
                    defer.resolve(response);
                }, function(err) {
                    defer.reject(err);
                });

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

/**
 * @ngdoc service
 * @name documents-V2_1.service:qmDocumentsHelperService
 * @description
 * Manages REST API communication for Documents
 */
documents.factory('qmDocumentsHelperService', ['$state', function($state) {
    return {
        /**
         * @ngdoc method
         * @name goToDocumentDetail
         * @methodOf documents-V2_1.service:qmDocumentsHelperService
         * @description
         * Go to document detail
         *
         * @param {object} documentObj Document object
         */
        goToDocumentDetail: function(documentObj) {
            $state.go('event.documents.detail', {documentId: documentObj.documentId});
        }
    };
}]);

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

/**
 * @ngdoc service
 * @name documents-V2_1.service:qmDocumentsDependencyService
 * @description
 * Manages communication with dependency manager
 */
documents.factory('qmDocumentsDependencyService', ['qmDependencyManager', function(qmDependencyManager) {
    return {
        /**
         * @ngdoc method
         * @name getTemplateUrl
         * @methodOf documents-V2_1.service:qmDocumentsDependencyService
         * @description
         * Get template url for documents component
         *
         * @param {string} name Template name
         * @returns {string} Template url
         */
        getTemplateUrl: function(name) {
            if (name === 'document-list-item') {
                return '/asset/component/documents/2.1/webapp/html/partials/document-list-item.html';
            }
            return null;
        },
        /**
         * @ngdoc method
         * @name getMyDocumentsService
         * @methodOf documents-V2_1.service:qmDocumentsDependencyService
         * @description
         * Get service from my documents component
         *
         * @param {string} service Service name
         * @returns {service} Angular service or null
         */
        getMyDocumentsService: function(service) {
            return qmDependencyManager.getService('my-documents', '2.0', 'component', service);
        }
    };
}]);

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

/**
 * @ngdoc controller
 * @name documents-V2_1.controller:DocumentsListController
 * @description
 * Controller for document list view
 */
documents.controller('DocumentsListController',
['$scope', 'qmDocumentsService', 'qmList', 'qmLocalization', 'qmUtilities', 'qmDocumentsHelperService', 'qmDocumentsDependencyService',
    function($scope, qmDocumentsService, qmList, qmLocalization, qmUtilities, qmDocumentsHelperService, qmDocumentsDependencyService) {
        $scope.documentListItemUrl = qmDocumentsDependencyService.getTemplateUrl('document-list-item');

        $scope.loadDocuments = qmDocumentsService.getList();
        $scope.loadDocuments.then(function(documents) {
            $scope.documents = qmList.groupByGroup(documents, 'groupValue', 'groupLabel');
            var componentTitle = qmLocalization.getString('componentDocumentsTitle');
            $scope.emptyText = qmLocalization.getString('LABEL_EMPTY_DOCUMENTS_MESSAGE', componentTitle);
        });
        $scope.goToDocument = function(documentObj) {
            qmDocumentsHelperService.goToDocumentDetail(documentObj);
        };
    }
]);

/**
 * @ngdoc controller
 * @name documents-V2_1.controller:DocumentListHeaderController
 * @description
 * Controller for document list header view
 */
documents.controller('DocumentsListHeaderController', ['$scope', 'qmLocalization', function($scope, qmLocalization) {
    $scope.headerTitle = qmLocalization.getString('componentDocumentsTitle');
}]);

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

/**
 * @ngdoc controller
 * @name documents-V2_1.controller:DocumentDetailController
 * @description
 * Controller for document detail view
 */
documents.controller('DocumentDetailController', ['$scope', '$state', 'qmDocumentsService', 'qmDocumentsDependencyService',
    'qmDependencyManager', 'qmEventConfig', 'qmLogin', 'qmUtilities',
    function($scope, $state, qmDocumentsService, qmDocumentsDependencyService, qmDependencyManager, qmEventConfig, qmLogin, qmUtilities) {
        $scope.loadDocument = qmDocumentsService.getDetail($state.params.documentId);
        $scope.loadDocument.then(function(documentIn) {
            $scope.documentObj = documentIn;
            $scope.isMyDocument = documentIn.isMyFavourite;
            // Display add/remove button
            var hasMyDocs = qmDependencyManager.hasComponent('my-documents');
            var showAddRemoveButton = hasMyDocs && qmLogin.isLoggedIn();

            $scope.showAddRemoveButton = showAddRemoveButton;

            if (showAddRemoveButton) {
                var qmMyDocumentsService = qmDocumentsDependencyService.getMyDocumentsService('qmMyDocumentsService');
                $scope.confirmAdd = function(documentId) {
                    return qmMyDocumentsService.addMyDocument(documentId).then(function() {
                        $scope.isMyDocument = true;
                    });
                };
                $scope.confirmRemove = function(documentId) {
                    return qmMyDocumentsService.deleteMyDocument(documentId).then(function() {
                        $scope.isMyDocument = false;
                    });
                };
            }

            if (documentIn.documentUrl) {
                documentIn.documentUrl = qmUtilities.generateValidUrl(documentIn.documentUrl);
            }
        });
    }
]);

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

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

events.config(['$stateProvider', function($stateProvider) {
    $stateProvider
        .state('event.events-V2_6', {
            url: '/Schedule',
            views: {
                'component@event': {
                    templateUrl: '/asset/component/events/2.6/webapp/html/event-list.html',
                    controller: 'EventListController'
                },
                'header@event': {
                    controller: 'EventListHeaderController'
                }
            }
        })
        .state('event.events-V2_6.detail', {
            url: '/:eventId',
            views: {
                'component@event': {
                    templateUrl: '/asset/component/events/2.6/webapp/html/event-detail.html',
                    controller: 'EventDetailController'
                },
                'header@event': {
                    controller: 'EventDetailHeaderController'
                }
            }
        });
}]);

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

/**
 * @ngdoc service
 * @name events-V2_4.service:qmEventsService
 * @description
 * Manages REST API communication for events
 */
events.factory('qmEventsService', ['qmRest', '$q', 'qmWebAppService', 'qmLocalization', 'qmEventsDependencyService', 'qmMoment',
function(qmRest, $q, qmWebAppService, qmLocalization, qmEventsDependencyService, qmMoment) {
    return {
        /**
         * @ngdoc method
         * @name getList
         * @methodOf events-V2_4.service:qmEventsService
         * @description
         * Get the list of sessions
         *
         * @param {Array} eventIds Event Ids
         * @param {string} searchTerm Search term
         * @param {number}    offset     Zero based offset
         * @param {number}    limit      Limit the response items
         *
         * @returns {promise} Promise that resolves when the sessions have been retrieved
         */
        getList: function(eventIds, searchTerm, offset, limit, isCheckInList, useSortOrder) {
            searchTerm = (typeof searchTerm === 'undefined') ? '' : searchTerm;
            offset = (typeof offset === 'undefined') ? 0 : offset;
            limit = (typeof limit === 'undefined') ? 0 : limit;
            isCheckInList = (typeof isCheckInList === 'undefined') ? 0 : 1;
            useSortOrder = (typeof useSortOrder === 'undefined') ? 0 : 1;

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

            if (angular.isDefined(eventIds)) {
                for (var i = 0; i < eventIds.length; i++) {
                    queryParams.push('eventId[]=' + encodeURIComponent(eventIds[i].eventId));
                }
            }

            var url = qmWebAppService.getBaseRestUrl('component', 'events') + '/events?' + queryParams.join('&');
            var defer = $q.defer();
            // get session check in list data
            qmRest.get(url).then(function(data) {
                var eventList = data;
                // Reformat dates and times for view
                for (var i = 0; i < eventList.length; i++) {
                    eventList[i].eventDateFormatted = qmMoment.parseZone(eventList[i].eventDate).format('dddd, MMMM D, YYYY');
                    eventList[i].startTimeFormatted = qmMoment.parseZone(eventList[i].startDateTime).format('h:mm A');
                    eventList[i].endTimeFormatted = qmMoment.parseZone(eventList[i].endDateTime).format('h:mm A');
                }

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

            return defer.promise;
        },

        /**
         * @ngdoc method
         * @name getDetail
         * @methodOf events-V2_4.service:qmEventsService
         * @description
         * Get details of an event
         *
         * @param {string} eventId Event Id
         * @returns {promise} Promise that resolves when event details has been retrieved
         */
        getDetail: function(eventId) {
            var url = qmWebAppService.getBaseRestUrl('component', 'events') + '/events/' + eventId;
            url += '?locale=' + qmLocalization.getLocale();
            var defer = $q.defer();
            // get event data
            qmRest.get(url).then(function(data) {
                // Decorate date and times
                data.eventDateFormatted = qmMoment.parseZone(data.eventDate).format('dddd, MMMM D, YYYY');
                data.startTimeFormatted = qmMoment.parseZone(data.startDateTime).format('h:mm A');
                data.endTimeFormatted = qmMoment.parseZone(data.endDateTime).format('h:mm A');
                // Resolve now
                defer.resolve(data);
            }, function(err) {
                defer.reject(err);
            });

            return defer.promise;
        },
        /**
         * @ngdoc method
         * @name getWhatsOnNow
         * @methodOf events-V2_4.service:qmEventsService
         * @description
         * Get list of Events
         *
         * @returns {promise} Promise that resolves when events list has been retrieved
         */
        getWhatsOnNow: function() {
            var url = qmWebAppService.getBaseRestUrl('component', 'events') + '/whats-on-now';
            url += '?locale=' + qmLocalization.getLocale();
            var defer = $q.defer();
            // get whatsonnow list data
            qmRest.get(url).then(function(data) {
                var whatsonnowList = data;
                // Reformat dates and times for view
                for (var i = 0; i < whatsonnowList.length; i++) {
                    whatsonnowList[i].eventDateFormatted = qmMoment.parseZone(whatsonnowList[i].eventDate).format('dddd, MMMM D, YYYY');
                    whatsonnowList[i].startTimeFormatted = qmMoment.parseZone(whatsonnowList[i].startDateTime).format('h:mm A');
                    whatsonnowList[i].endTimeFormatted = qmMoment.parseZone(whatsonnowList[i].endDateTime).format('h:mm A');
                }
                defer.resolve(whatsonnowList);
            }, function(err) {
                defer.reject(err);
            });

            return defer.promise;
        },
        /**
         * @ngdoc method
         * @name getDocumentsList
         * @methodOf events-V2_4.service:qmEventsService
         * @description
         * Get the list of documents for an event
         *
         * @param {string} eventId Event Id
         * @returns {promise} Promise that resolves when the sessions have been retrieved
         */
        getEventDocuments: function(eventId) {
            var url = qmWebAppService.getBaseRestUrl('component', 'events') + '/events/' + eventId + '/documents';

            var defer = $q.defer();
            // get event document ids
            qmRest.get(url).then(function(data) {
                var documentIds = data;

                // Get Documents
                var documentList = [];
                if (documentIds.length !== 0) {
                    var qmDocumentsService = qmEventsDependencyService.getDocumentsService('qmDocumentsService');
                    documentList = qmDocumentsService.getEventDocuments(documentIds);
                }

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

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

var events = angular.module('events-V2_5');
/**
 * @ngdoc service
 * @name events-V2_5.service:qmEventsDependencyService
 * @description
 * Manages communication with dependency manager
 */
events.factory('qmEventsDependencyService', ['qmDependencyManager', function(qmDependencyManager) {
    return {
        /**
         * @ngdoc method
         * @name getTemplateUrl
         * @methodOf events-V2_5.service:qmEventsDependencyService
         * @description
         * Get template url for events component
         *
         * @param {string} name Template name
         * @returns {string} Template url
         */
        getTemplateUrl: function(name) {
            if (name === 'event-list-item') {
                return '/asset/component/events/2.4/webapp/html/partials/event-list-item.html';
            } else if (name === 'event-list') {
                return '/asset/component/events/2.4/webapp/html/event-list.html';
            }

            return null;
        },
        /**
         * @ngdoc method
         * @name getDocumentsService
         * @methodOf events-V2_5.service:qmEventsDependencyService
         * @description
         * Get service from documents component
         *
         * @param {string} service Service name
         * @returns {service} Angular service or null
         */
        getDocumentsService: function(service) {
            return qmDependencyManager.getService('documents', '2.1', 'component', service);
        },
        /**
         * @ngdoc method
         * @name getSpeakersService
         * @methodOf events-V2_5.service:qmEventsDependencyService
         * @description
         * Get service from speakers component
         *
         * @param {string} service Service name
         * @returns {service} Angular service or null
         */
        getSpeakersService: function(service) {
            return qmDependencyManager.getService('speakers', '2.0', 'component', service);
        },
        /**
         * @ngdoc method
         * @name getSessionQAService
         * @methodOf events-V2_5.service:qmEventsDependencyService
         * @description
         * Get service from session qa component
         *
         * @param {string} service Service name
         * @returns {service} Angular service or null
         */
        getSessionQAService: function(service) {
            return qmDependencyManager.getService('sessionqa', '2.0', 'component', service);
        },
        /**
         * @ngdoc method
         * @name getVenuesService
         * @methodOf events-V2_5.service:qmEventsDependencyService
         * @description
         * Get service from venues component
         *
         * @param {string} service Service name
         * @returns {service} Angular service or null
         */
        getVenuesService: function(service) {
            return qmDependencyManager.getService('venues', '2.0', 'component', service);
        },
        /**
         * @ngdoc method
         * @name getSurveysService
         * @methodOf events-V2_5.service:qmEventsDependencyService
         * @description
         * Get service from surveys component
         *
         * @param {string} service Service name
         * @returns {service} Angular service or null
         */
        getSurveysService: function(service) {
            return qmDependencyManager.getService('surveys', '2.2', 'component', service);
        },
        /**
         * @ngdoc method
         * @name getMyNotesService
         * @methodOf events-V2_5.service:qmEventsDependencyService
         * @description
         * Get service from my notes component
         *
         * @param {string} service Service name
         * @returns {service} Angular service or null
         */
        getMyNotesService: function(service) {
            return qmDependencyManager.getService('my-notes', '2.0', 'component', service);
        },
        /**
         * @ngdoc method
         * @name getMyScheduleService
         * @methodOf events-V2_5.service:qmEventsDependencyService
         * @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 getInteractiveMapsService
         * @methodOf events-V2_5.service:qmEventsDependencyService
         * @description
         * Get service from my interactive maps component
         *
         * @param {string} service Service name
         * @returns {service} Angular service or null
         */
        getInteractiveMapsService: function(service) {
            return qmDependencyManager.getService('interactive-maps', '2.1', 'component', service);
        },
        /**
         * @ngdoc method
         * @name getLivePollingService
         * @methodOf events-V2_5.service:qmEventsDependencyService
         * @description
         * Get service from my live polling component
         *
         * @param {string} service Service name
         * @returns {service} Angular service or null
         */
        getLivePollingService: function(service) {
            return qmDependencyManager.getService('live-polling', '2.2', 'component', service);
        }
    };
}]);

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

/**
 * @ngdoc controller
 * @name events-V2_4.controller:EventListController
 * @description
 * Controller for event list view
 */
events.controller('EventListController', ['$scope', 'qmEventsService', 'qmList', 'qmLocalization', 'qmEventsDependencyService',
    function($scope, qmEventsService, qmList, qmLocalization, qmEventsDependencyService) {
        $scope.eventListItemUrl = qmEventsDependencyService.getTemplateUrl('event-list-item');
        $scope.loadEvents = qmEventsService.getList();
        $scope.loadEvents.then(function(events) {
            $scope.events = qmList.groupByGroup(events, 'eventDate', 'eventDateFormatted');
            $scope.emptyText = qmLocalization.getString('LABEL_EMPTY_EVENTS_TITLE');
        });
    }
]);

/**
 * @ngdoc controller
 * @name events-V2_4.controller:EventListHeaderController
 * @description
 * Controller for event list header view
 */
events.controller('EventListHeaderController', ['$scope', 'qmLocalization', function($scope, qmLocalization) {
    $scope.headerBackState = false;
    $scope.headerTitle = qmLocalization.getString('componentEventsTitle');
    $scope.headerTitleTestId = 'componentTitle';
}]);

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

/**
 * @ngdoc controller
 * @name events-V2_6.controller:EventDetailController
 * @description
 * Controller for event detail view
 */
events.controller('EventDetailController',
['$scope', '$state', '$location', 'qmList', 'qmEventConfig', 'qmEventsService', 'qmLocalization', 'qmAnalytics',
'toastr', 'ical', 'qmLogin', 'qmEventsDependencyService', 'qmUtilities', '$controller', '$q', 'qmLocalStorage', '$window',
function($scope, $state, $location, qmList, qmEventConfig, qmEventsService, qmLocalization, qmAnalytics,
toastr, ical, qmLogin, qmEventsDependencyService, qmUtilities, $controller, $q, qmLocalStorage, $window) {
    $scope.loadEvent = qmEventsService.getDetail($state.params.eventId);
    $scope.showEvent = false;
    var isLoggedIn = qmLogin.isLoggedIn();

    // Additional Fields Section 1
    $scope.additionalLabel1 = qmLocalization.getString('LABEL_EVENT_ADDITIONAL_SECTION1');
    $scope.additionalDataTestIdObj1 = {expander: 'sessionAdditionalHeaderExpander1'};
    // Additional Fields Section 2
    $scope.additionalLabel2 = qmLocalization.getString('LABEL_EVENT_ADDITIONAL_SECTION2');
    $scope.additionalDataTestIdObj2 = {expander: 'sessionAdditionalHeaderExpander2'};

    $scope.descriptionLabel = qmLocalization.getString('LABEL_DESCRIPTION');
    $scope.descriptionDataTestIdObj = {expander: 'sessionDetailsHeaderExpander'};

    var venuesIsConfigured = qmEventConfig.isComponentConfigured('venues');
    var venuesRequiresLogin = qmEventConfig.isComponentLoginRequired('venues');

    var speakersIsConfigured = qmEventConfig.isComponentConfigured('speakers');
    var speakersRequiresLogin = qmEventConfig.isComponentLoginRequired('speakers');
    $scope.showSpeakers = false;
    $scope.speakersTitle = qmLocalization.getString('componentSpeakersTitle');
    $scope.speakersDataTestIdObj = {expander: 'sessionSpeakersExpander'};

    var sessionQAIsConfigured = qmEventConfig.isComponentConfigured('sessionqa');
    var sessionQARequiresLogin = qmEventConfig.isComponentLoginRequired('sessionqa');
    $scope.showSessionQA = false;
    $scope.sessionQATitle = qmLocalization.getString('componentSessionQATitle');
    $scope.sessionQAInput = qmLocalization.getString('LABEL_TYPE_A_QUESTION');
    $scope.withoutSavingSessionQA = qmLocalization.getString('ALERT_QA_CLOSE_WITHOUT_SAVING_MESSAGE');
    $scope.sessionQADataTestIdObj = {expander: 'sessionQAExpander'};

    var surveysIsConfigured = qmEventConfig.isComponentConfigured('surveys');
    var surveysRequiresLogin = qmEventConfig.isComponentLoginRequired('surveys');
    $scope.showSurveys = false;
    $scope.surveysTitle = qmLocalization.getString('componentSurveysTitle');
    $scope.surveysDataTestIdObj = {expander: 'sessionSurveysExpander'};

    var documentsIsConfigured = qmEventConfig.isComponentConfigured('documents');
    var documentsRequiresLogin = qmEventConfig.isComponentLoginRequired('documents');
    $scope.showDocuments = false;
    $scope.documentsTitle = qmLocalization.getString('componentDocumentsTitle');
    $scope.documentsDataTestIdObj = {expander: 'sessionDocumentsExpander'};

    var myNotesIsConfigured = qmEventConfig.isComponentConfigured('my-notes');
    var myNotesRequiresLogin = qmEventConfig.isComponentLoginRequired('my-notes');
    $scope.myNotesTitle = qmLocalization.getString('componentMynotesTitle');
    $scope.newNoteTitle = qmLocalization.getString('LABEL_NEW_NOTE');
    $scope.myNotesInput = qmLocalization.getString('LABEL_TYPE_A_NOTE');
    $scope.withoutSavingNote = qmLocalization.getString('ALERT_MY_NOTES_CLOSE_WITHOUT_SAVING_MESSAGE');
    $scope.showMyNotes = false;
    $scope.myNotesAllowEmptySubmit = false;
    $scope.myNotesDataTestIdObj = {expander: 'sessionNotesExpander'};

    var myScheduleIsConfigured = qmEventConfig.isComponentConfigured('myschedule');
    $scope.showMySchedule = false;

    var interactiveMapsIsConfigured = qmEventConfig.isComponentConfigured('interactive-maps');
    var interactiveMapsRequiresLogin = qmEventConfig.isComponentLoginRequired('interactive-maps');
    $scope.showInteractiveMaps = false;
    $scope.showAttendeeSeating = false;

    var livePollingIsConfigured = qmEventConfig.isComponentConfigured('live-polling');
    $scope.showLivePolling = false;

    if (venuesIsConfigured && (!venuesRequiresLogin || isLoggedIn)) {
        var qmVenuesService = qmEventsDependencyService.getVenuesService('qmVenuesService');
        $scope.loadVenues = qmVenuesService.getEventVenues($state.params.eventId);
    }
    if (speakersIsConfigured && (!speakersRequiresLogin || isLoggedIn)) {
        var qmSpeakerService = qmEventsDependencyService.getSpeakersService('qmSpeakerService');
        $scope.loadSpeakers = qmSpeakerService.getEventSpeakers($state.params.eventId);
    }
    if (surveysIsConfigured && (!surveysRequiresLogin || isLoggedIn)) {
        var qmSurveysService = qmEventsDependencyService.getSurveysService('qmSurveysService');
        $scope.loadSurveys = qmSurveysService.getSessionSurveyList($state.params.eventId);
    }
    if (documentsIsConfigured && (!documentsRequiresLogin || isLoggedIn)) {
        var qmDocumentsService = qmEventsDependencyService.getDocumentsService('qmDocumentsService');
        $scope.loadDocuments = qmDocumentsService.getEventDocuments($state.params.eventId);
    }
    if (myNotesIsConfigured && isLoggedIn) {
        var qmMyNotesService = qmEventsDependencyService.getMyNotesService('qmMyNotesService');
        $scope.loadMyNotes = qmMyNotesService.getEventList($state.params.eventId);
    }
    if (interactiveMapsIsConfigured && (!interactiveMapsRequiresLogin || isLoggedIn)) {
        var qmInteractiveMapsService = qmEventsDependencyService.getInteractiveMapsService('qmInteractiveMapsService');
        $scope.loadLandmarks = qmInteractiveMapsService.getListEventLandmarks($state.params.eventId);
        if (interactiveMapsIsConfigured && myScheduleIsConfigured && isLoggedIn) {
            $scope.loadAttendeeLandmarks = qmInteractiveMapsService.getListAttendeeEventLandmarks($state.params.eventId);
        }
    }
    if (livePollingIsConfigured) {
        var qmLumiService = qmEventsDependencyService.getLivePollingService('qmLumiService');
    }

    $scope.promptLogin = function() {
        qmLogin.checkLogin().then(function() {
            $state.reload();
        });
    };

    $scope.newSessionQA = '';
    $scope.sessionQAAllowEmptySubmit = false;
    $scope.createSessionQA = function() {
        if ($scope.newSessionQA) {
            if ($scope.savingQuestion !== true) {
                $scope.savingQuestion = true;
                var qmSessionQAService = qmEventsDependencyService.getSessionQAService('qmSessionQAService');
                qmSessionQAService.create($scope.newSessionQA, $state.params.eventId).then(function() {
                    $scope.savingQuestion = false;
                    $scope.newSessionQA = '';
                    toastr.success(qmLocalization.getString('ALERT_SESSION_QA_SUBMITTED_MESSAGE'), '', {
                        closeButton: true,
                        timeOut: 10000
                    });
                },
                function(err) {
                    $scope.savingQuestion = false;
                    if (err.error) {
                        toastr.error(qmLocalization.getString('ALERT_SESSION_QA_SUBMIT_FAILED'), '', {
                            closeButton: true,
                            timeOut: 10000
                        });
                    }
                });
            }
        } else {
            toastr.warning(qmLocalization.getString('ALERT_QA_EMPTY_TITLE'), '', {
                closeButton: true,
                timeOut: 10000
            });
        }
    };

    $scope.newNote = '';
    $scope.createNote = function() {
        var qmMyNotesService = qmEventsDependencyService.getMyNotesService('qmMyNotesService');
        qmMyNotesService.create($scope.newNote, 'Events', $state.params.eventId).then(function() {
            qmAnalytics.markEvent('EventsNoteAdd', $state.params.eventId);
            $scope.newNote = '';
            $scope.loadMyNotes = qmMyNotesService.getEventList($state.params.eventId);
            $scope.loadMyNotes.then(function(notes) {
                $scope.event.notes = qmList.transformList(notes);
            });
        });
    };

    $scope.confirmAddMySchedule = function(eventId) {
        var qmMyScheduleService = qmEventsDependencyService.getMyScheduleService('qmMyScheduleService');

        return qmMyScheduleService.addMySchedule(eventId).then(function() {
            $scope.isInMySchedule = true;
            $scope.myScheduleAllowDelete = true;
        });
    };

    $scope.confirmRemoveMySchedule = function(eventId) {
        var qmMyScheduleService = qmEventsDependencyService.getMyScheduleService('qmMyScheduleService');

        if ($scope.myScheduleAllowDelete) {
            // We are allowed to remove this from our schedule
            return qmMyScheduleService.deleteMySchedule(eventId).then(function() {
                $scope.isInMySchedule = false;
            });
        }
    };

    $scope.addToCalendar = function(event) {
        // Setup the Calendar link
        var comp = new ical.Component(['vcalendar', [], []]);
        comp.updatePropertyWithValue('prodid', '-//' + qmEventConfig.getEventName());
        comp.updatePropertyWithValue('version', '2.0');

        var vevent = new ical.Component('vevent');
        var calendarEvent = new ical.Event(vevent);

        calendarEvent.uid = event.eventId;
        calendarEvent.startDate = ical.Time.fromDateTimeString(event.startDateTime);
        calendarEvent.endDate = ical.Time.fromDateTimeString(event.endDateTime);
        calendarEvent.summary = event.title;
        if (event.description !== null) {
            calendarEvent.description = event.description + '\n\n' + $location.absUrl();
        } else {
            calendarEvent.description = $location.absUrl();
        }

        // Include the Venue name and location if there is one
        if (angular.isDefined(event.venue) && angular.isDefined(event.venue.name)) {
            if (angular.isDefined(event.location) && event.location !== null) {
                calendarEvent.location = event.location + ' - ' + event.venue.name;
            } else {
                calendarEvent.location = event.venue.name;
            }
        } else if (angular.isDefined(event.location) && event.location !== null) {
            calendarEvent.location = event.location;
        }

        comp.addSubcomponent(vevent);

        // Open the ics attachment
        var userAgent = new UAParser().getResult();
        if (userAgent.browser.name === 'IE') {
            var blob = new Blob(comp.toString());
            window.navigator.msSaveOrOpenBlob(blob, 'Calendar.ics');
        } else {
            $window.open('data:text/calendar;charset=utf8,' + encodeURIComponent(comp.toString()));
        }
    };

    var isJoiningLivePoll = false;
    $scope.joinLivePoll = function() {
        if (qmLumiService && !isJoiningLivePoll) {
            isJoiningLivePoll = true;
            qmUtilities.addPageLoad();
            qmLumiService.joinPoll($scope.event.lumi.sessionId, $scope.event.lumi.isAnonymous).then(function() {
                var storedLumiSessionId = qmLocalStorage.getItem('lumiSessionId');
                // If we are not navigating away triggered by logic in LivePollingPollSyncBaseController
                if (!$scope.hasPollStarted && (storedLumiSessionId !== $scope.event.lumi.sessionId)) {
                    var modalScope = {
                        title: qmLocalization.getString('ALERT_LUMI_JOIN_TITLE'),
                        content: qmLocalization.getString('ALERT_LUMI_JOIN_MESSAGE'),
                        yesLabel: qmLocalization.getString('BUTTON_OK')
                    };

                    qmUtilities.openConfirmModal(modalScope);
                }
                $scope.isPollJoined = true;
                // Make sure leaving confirmation modal will show if user tries to leave this page
                $scope.leavingPollModalScope.isSubmitting = false;
                qmLocalStorage.setItem('lumiSessionId', $scope.event.lumi.sessionId);
            }, function() {
                var modalScope = {
                    title: qmLocalization.getString('ALERT_CONNECTION_ERROR_TITLE'),
                    content: qmLocalization.getString('ALERT_CONNECTION_ERROR_MESSAGE'),
                    yesLabel: qmLocalization.getString('BUTTON_OK')
                };
                qmUtilities.openConfirmModal(modalScope);
                $scope.isPollJoined = false;
            }).finally(function() {
                isJoiningLivePoll = false;
                qmUtilities.removePageLoad();
            });
        }
    };

    var isLeavingLivePoll = false;
    $scope.leaveLivePoll = function() {
        if (qmLumiService) {
            isLeavingLivePoll = true;
            qmUtilities.addPageLoad();
            qmLumiService.leavePoll().then(function() {
                // Since user cannot trigger this in a discussion, we can leave a session here
                return qmLumiService.leaveSession($scope.event.lumi.sessionId);
            }).then(function() {
                $scope.isPollJoined = false;
                // Prevent leaving confirmation modal from showing if we're not connected to a poll
                $scope.leavingPollModalScope.isSubmitting = true;
                qmLocalStorage.removeItem('lumiSessionId');
            }).finally(function() {
                isLeavingLivePoll = false;
                qmUtilities.removePageLoad();
            });
        }
    };

    $scope.goToLiveDiscussions = function() {
        qmUtilities.addPageLoad();
        // Join poll if polls are enabled otherwise resolve the promise right away
        var liveDiscussionJoinPoll = function() {
            var defer = $q.defer();
            if ($scope.event.lumi && $scope.event.lumi.hasPolls) {
                qmLumiService.joinPoll($scope.event.lumi.sessionId, $scope.event.lumi.isAnonymous).then(function() {
                    defer.resolve();
                }, function() {
                    defer.reject();
                });
            } else {
                defer.resolve();
            }
            return defer.promise;
        };
        // If polls are enabled, make sure we also join the poll so user can be taken to poll inside a discussion
        // We need to make sure joinPoll is first because PollState event gets fired after joining a session
        // where MessageBoardList is fired after viewSession is called
        liveDiscussionJoinPoll().then(function() {
            return qmLumiService.joinMessageBoards($scope.event.lumi.sessionId, $scope.event.lumi.isAnonymous);
        }).then(function() {
            if ($scope.event.lumi && $scope.event.lumi.hasPolls) {
                // Prevent confirmation dialog from showing when navigating to live discussion
                $scope.leavingPollModalScope.isSubmitting = function() {
                    return true;
                };
            }
            $state.go('event.live-polling.discussion.list', $state.params, {
                location: false
            });
        }, function() {
            var modalScope = {
                title: qmLocalization.getString('ALERT_CONNECTION_ERROR_TITLE'),
                content: qmLocalization.getString('ALERT_CONNECTION_ERROR_MESSAGE'),
                yesLabel: qmLocalization.getString('BUTTON_OK')
            };
            qmUtilities.openConfirmModal(modalScope);
        }).finally(function() {
            qmUtilities.removePageLoad();
        });
    };

    $scope.loadEvent.then(function(event) {

        $scope.event = event;
        $scope.showEvent = true;
        $scope.allowCheckIn = event.allowSessionCheckIn && isLoggedIn;
        $scope.isInMySchedule = event.isMyFavourite;

        if (venuesIsConfigured && (!venuesRequiresLogin || isLoggedIn)) {
            $scope.loadVenues.then(function(venues) {
                $scope.event.venues = qmList.transformList(venues);

                // Save only the first Venue for display, currently a work around due to Product Managements design
                if ($scope.event.venues.length > 0) {
                    $scope.event.venue = venues[0];
                }
            });
        }

        if (speakersIsConfigured) {
            if (!speakersRequiresLogin || isLoggedIn) {
                $scope.speakersLoginRequired = false;
                $scope.loadSpeakers.then(function(speakers) {
                    $scope.event.speakers = qmList.transformList(speakers);

                    var qmSpeakersDependencyService = qmEventsDependencyService.getSpeakersService('qmSpeakersDependencyService');
                    $scope.speakerListItemUrl = qmSpeakersDependencyService.getTemplateUrl('speaker-list-item');

                    // Show Speakers if there are some to show
                    if ($scope.event.speakers.length > 0) {
                        $scope.showSpeakers = true;
                    }
                });
            } else {
                $scope.showSpeakers = true;
                $scope.speakersLoginRequired = true;
            }
        }

        if (sessionQAIsConfigured && event.allowSessionQA === '1' && (!sessionQARequiresLogin || isLoggedIn)) {
            $scope.showSessionQA = true;
        }

        if (surveysIsConfigured) {
            if (!surveysRequiresLogin || isLoggedIn) {
                $scope.surveysLoginRequired = false;
                var qmSurveysDependencyService = qmEventsDependencyService.getSurveysService('qmSurveysDependencyService');
                $scope.surveyListItemUrl = qmSurveysDependencyService.getTemplateUrl('list-item');
                $scope.loadSurveys.then(function(surveys) {
                    $scope.event.surveys = qmList.transformList(surveys);

                    // Show Surveys if there are some to show
                    if ($scope.event.surveys.length > 0) {
                        $scope.showSurveys = true;
                    }
                });
            } else {
                $scope.showSurveys = true;
                $scope.surveysLoginRequired = true;
            }
        }

        if (documentsIsConfigured && (!documentsRequiresLogin || isLoggedIn)) {
            $scope.loadDocuments.then(function(documents) {
                $scope.event.documents = qmList.transformList(documents);

                var qmDocumentsDependencyService = qmEventsDependencyService.getDocumentsService('qmDocumentsDependencyService');
                $scope.documentListItemUrl = qmDocumentsDependencyService.getTemplateUrl('document-list-item');

                // Show Documents if there are some to show
                if ($scope.event.documents.length > 0) {
                    $scope.showDocuments = true;
                }
            });
        }

        if (myNotesIsConfigured) {
            if (!myNotesRequiresLogin || isLoggedIn) {
                $scope.myNotesLoginRequired = false;
                $scope.loadMyNotes.then(function(notes) {
                    $scope.event.notes = qmList.transformList(notes);

                    var qmMyNotesDependencyService = qmEventsDependencyService.getMyNotesService('qmMyNotesDependencyService');
                    $scope.myNotesListItemUrl = qmMyNotesDependencyService.getTemplateUrl('mynotes-list-item');
                });
            } else {
                $scope.myNotesLoginRequired = true;
            }
            $scope.showMyNotes = true;
        }

        if (myScheduleIsConfigured && isLoggedIn) {

            $scope.showMySchedule = true;

            if (event.isMyFavourite) {

                $scope.confirmTitle = qmLocalization.getString('ALERT_MY_SCHEDULE_IS_READ_ONLY_TITLE');
                $scope.confirmContent = qmLocalization.getString('ALERT_MY_SCHEDULE_IS_READ_ONLY_MESSAGE');
                $scope.confirmYes = qmLocalization.getString('BUTTON_OK');

                var qmMyScheduleService = qmEventsDependencyService.getMyScheduleService('qmMyScheduleService');
                var loadAllowDelete = qmMyScheduleService.allowDeleteMySchedule(event.createdBy, event.limitedVisibility);
                loadAllowDelete.then(function(response) {
                    $scope.myScheduleAllowDelete = response;
                });
            }
        }

        if (interactiveMapsIsConfigured && (!interactiveMapsRequiresLogin || isLoggedIn)) {
            $scope.loadLandmarks.then(function(landmarks) {

                // Save only the first Landmark for display, currently a work around due to Product Managements design
                if (landmarks.length > 0) {
                    $scope.event.landmark = landmarks[0];
                    $scope.showInteractiveMaps = true;
                }
            });
        }

        if (interactiveMapsIsConfigured && myScheduleIsConfigured && isLoggedIn) {
            $scope.loadAttendeeLandmarks.then(function(landmarks) {

                // Save only the first Landmark for display
                if (landmarks.length > 0) {
                    $scope.event.attendeeLandmark = landmarks[0];
                    $scope.showAttendeeSeating = true;
                }
            });
        }

        if (livePollingIsConfigured && $scope.event.lumi && $scope.event.lumi.hasPolls) {
            $scope.showLivePolling = true;

            // inherit logic for leaving poll that has been joined
            $state.params.lumiSessionId = $scope.event.lumi.sessionId;
            $controller('LivePollingLeavingPollBaseController', {
                $scope: $scope
            });
            $controller('LivePollingPollSyncBaseController', {
                $scope: $scope
            });

            var qmLumiService = qmEventsDependencyService.getLivePollingService('qmLumiService');
            $scope.isPollJoined = qmLumiService.getStatus().isPollConnected;
            if ($scope.isPollJoined) {
                $scope.leavingPollModalScope.isSubmitting = false;
            } else {
                // Prevent leaving confirmation modal from showing if we're not connected to a poll
                $scope.leavingPollModalScope.isSubmitting = true;

                // Check if we need to reconnect user to live poll
                var storedLumiSession = qmLocalStorage.getItem('lumiSessionId');
                if (storedLumiSession && storedLumiSession === $scope.event.lumi.sessionId) {
                    $scope.joinLivePoll();
                }
            }
        }

        if (livePollingIsConfigured && $scope.event.lumi && $scope.event.lumi.hasDiscussions) {
            if ($scope.event.lumi.hasPolls) {
                $scope.liveDiscussionsTitle = qmLocalization.getString('LABEL_LUMI_DISCUSSIONS_AND_POLLS_HEADER');
            } else {
                $scope.liveDiscussionsTitle = qmLocalization.getString('LABEL_LUMI_DISCUSSIONS_ONLY_HEADER');
            }
            $scope.showLiveDiscussions = true;
            $scope.liveDiscussionsDataTestIdObj = {expander: 'liveDiscussionsExpander'};
        }
    });
}]);

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

var exhibitors = angular.module('exhibitors-V2_2');

exhibitors.config(['$stateProvider', function($stateProvider) {
    $stateProvider
        .state('event.exhibitors-V2_2', {
            url: '/Exhibitors',
            views: {
                'component@event': {
                    controller: 'ExhibitorMainListController'
                }
            },
            resolve: ['qmExhibitorsService', function(qmExhibitorsService) {
                return qmExhibitorsService.preLoadData(true);
            }]
        })
        .state('event.exhibitors-V2_2.category', {
            url: '',
            views: {
                'component@event': {
                    templateUrl: '/asset/component/exhibitors/2.2/webapp/html/exhibitor-categories.html',
                    controller: 'ExhibitorCategoryListController'
                },
                'header@event': {
                    controller: 'ExhibitorCategoryListHeaderController'
                }
            }
        })
        .state('event.exhibitors-V2_2.list', {
            url: '/:categoryId',
            views: {
                'component@event': {
                    templateUrl: '/asset/component/exhibitors/2.2/webapp/html/exhibitor-list.html',
                    controller: 'ExhibitorListController'
                },
                'header@event': {
                    controller: 'ExhibitorListHeaderController'
                }
            }
        })
        .state('event.exhibitors-V2_2.detail', {
            url: '/:categoryId/:exhibitorId',
            views: {
                'component@event': {
                    templateUrl: '/asset/component/exhibitors/2.2/webapp/html/exhibitor-detail.html',
                    controller: 'ExhibitorDetailController'
                },
                'header@event': {
                    controller: 'ExhibitorDetailHeaderController'
                }
            }
        });
}]);

var exhibitors = angular.module('exhibitors-V2_2');

/**
 * @ngdoc service
 * @name exhibitors-V2_2.service:qmExhibitorsService
 * @description
 * Manages REST API communication for Exhibitors
 */
exhibitors.factory('qmExhibitorsService',
    ['qmRest', '$q', 'qmLocalization', 'qmEventConfig', 'qmDependencyManager', 'qmLogin',
    'qmRpc', 'qmExhibitorsDependencyService', 'qmWebAppService',
    function(qmRest, $q, qmLocalization, qmEventConfig, qmDependencyManager, qmLogin,
    qmRpc, qmExhibitorsDependencyService, qmWebAppService) {

        var config = qmEventConfig.getComponentConfig('exhibitors');
        var dataSource = {exhibitors: [], exhibitorsIndex: {}, categories: [], loaded: false};

        function decorator(exhibitor) {

            if (exhibitor.imageUrl && exhibitor.imageUrl !== '') {
                exhibitor.imageSrc = qmWebAppService.appendAuthHeader(exhibitor.imageUrl);
            } else {
                var placeHolderIconUrl = qmEventConfig.getComponentArtifact(null, 'exhibitors');
                exhibitor.imageSrc = qmWebAppService.appendAuthHeader(placeHolderIconUrl);
            }
            if (exhibitor.mainContactPhone && exhibitor.mainContactPhone !== '') {
                exhibitor.phone = exhibitor.mainContactPhone;
            }
            if (exhibitor.mainContactEmail && exhibitor.mainContactEmail !== '') {
                exhibitor.email = exhibitor.mainContactEmail;
            }

            return exhibitor;
        }

        function copyDetailData(data, exhibitorId) {

            if (exhibitorId in dataSource.exhibitorsIndex) {
                var index = dataSource.exhibitorsIndex[exhibitorId];

                dataSource.exhibitors[index] = data;
            }
        }

        var init = {
            /**
             * @ngdoc method
             * @name preLoadData
             * @methodOf exhibitors-V2_2.service:qmExhibitorsService
             * @description
             * Load list of exhibitors and categories
             *
             * @returns {promise} Promise that resolves when exhibitors list has been retrieved
             */
            preLoadData: function(reLoad) {
                var defer = $q.defer();
                if (reLoad === true || !dataSource.loaded) {
                    init.loadExhibitors().then(function(response) {
                        dataSource.exhibitors = response.exhibitors;
                        dataSource.exhibitorsIndex = {};
                        var exhibitorIds = [];
                        angular.forEach(dataSource.exhibitors, function(exhibitor, key) {
                            exhibitorIds.push(exhibitor.exhibitorId);
                            exhibitor = decorator(exhibitor);
                            dataSource.exhibitorsIndex[exhibitor.exhibitorId] = key;
                        });
                        init.loadCategoriesForExhibitors(exhibitorIds).then(function(response) {
                            dataSource.categories = response.categories;
                            dataSource.loaded = true;
                            defer.resolve(dataSource);
                        });
                    });
                } else {
                    defer.resolve(dataSource);
                }

                return defer.promise;
            },
            /**
             * @ngdoc method
             * @name loadExhibitors
             * @methodOf exhibitors-V2_2.service:qmExhibitorsService
             * @description
             * Get list of exhibitors
             *
             * @param {Array} exhibitorIds Exhibitor Ids
             * @returns {promise} Promise that resolves when exhibitors list has been retrieved
             */
            loadExhibitors: function(exhibitorIds) {
                var defer = $q.defer();
                var url = qmWebAppService.getBaseRestUrl('component', 'exhibitors') + '/exhibitors' + '?locale=' + qmLocalization.getLocale();

                if (angular.isDefined(exhibitorIds)) {
                    for (var i = 0; i < exhibitorIds.length; i++) {
                        url += '&exhibitorIds[]=' + exhibitorIds[i].exhibitorId;
                    }
                }

                qmRest.get(url).then(function(response) {
                    defer.resolve({exhibitors: response, success: true});
                }, function(err) {
                    defer.resolve({exhibitors: [], success: false, msg: err});
                });

                return defer.promise;
            },
            /**
             * @ngdoc method
             * @name getList
             * @methodOf exhibitors-V2_2.service:qmExhibitorsService
             * @description
             * Get list of exhibitors
             *
             * @param {Array} exhibitorIds List of exhibitorIds to return. If empty, return everything
             * @param {Number} searchTerm Zero based offset
             * @param {Number} offset Limit number of items to return
             *
             * @returns {promise} Promise that resolves when exhibitors list has been retrieved
             */
            getList: function(exhibitorIds, searchTerm, offset, limit) {
                var defer = $q.defer();

                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());

                if (angular.isDefined(exhibitorIds)) {
                    for (var i = 0; i < exhibitorIds.length; i++) {
                        queryParams.push('exhibitorIds[]=' + encodeURIComponent(exhibitorIds[i].exhibitorId));
                    }
                }

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

                qmRest.get(url).then(function(response) {
                    var exhibitors = [];
                    angular.forEach(response, function(exhibitor) {
                        exhibitor = decorator(exhibitor);
                        exhibitors.push(exhibitor);
                    });

                    defer.resolve({exhibitors: exhibitors, success: true});
                }, function(err) {
                    defer.resolve({exhibitors: [], success: false, msg: err});
                });

                return defer.promise;
            },
            /**
             * @ngdoc method
             * @name loadCategoriesForExhibitors
             * @methodOf exhibitors-V2_2.service:qmExhibitorsService
             * @description
             * Get list of exhibitor categories
             *
             * @returns {promise} Promise that resolves when exhibitor categories list has been retrieved
             */
            loadCategoriesForExhibitors: function(exhibitorIds) {
                var defer = $q.defer();
                var useCategories = init.isCategoriesAvailable();
                if (exhibitorIds.length > 0 && useCategories) {
                    var qmCategoriesService = qmExhibitorsDependencyService.getCategoriesService('qmCategoriesService');
                    qmCategoriesService.getListCategoriesForEntities('exhibitor', exhibitorIds).then(function(categories) {
                        defer.resolve({categories: categories, success: true});
                    }, function(err) {
                        defer.resolve({categories: [], success: false, msg: err});
                    });
                } else {
                    defer.resolve({categories: [], success: true});
                }

                return defer.promise;
            },
            /**
             * @ngdoc method
             * @name getCategories
             * @methodOf exhibitors-V2_2.service:qmExhibitorsService
             * @description
             * Get list of exhibitor categories form preloaded data
             *
             * @returns {Array} List of exhibitor categories
             */
            getCategories: function() {

                var allCategories = [{categoryId: '', title: qmLocalization.getString('LABEL_ALL')}];

                return allCategories.concat(dataSource.categories);
            },
            /**
             * @ngdoc method
             * @name getCategoryTitle
             * @methodOf exhibitors-V2_2.service:qmExhibitorsService
             * @description
             * Get category title by categoryId
             *
             * @param {string} categoryId Category Id
             * @returns {string} Category title
             */
            getCategoryTitle: function(categoryId) {
                var title = '';
                angular.forEach(dataSource.categories, function(category) {
                    if (category.categoryId === categoryId) {
                        title = category.title;

                        return false;
                    }
                });

                return title;
            },
            /**
             * @ngdoc method
             * @name getExhibitors
             * @methodOf exhibitors-V2_2.service:qmExhibitorsService
             * @description
             * Get list of exhibitor form preloaded data
             *
             * @param {string} categoryId Category Id
             * @returns {Array} List of exhibitors
             */
            getExhibitors: function(categoryId) {
                if (categoryId === '') {

                    return dataSource.exhibitors;
                } else {
                    var exhibitorIds = [];
                    angular.forEach(dataSource.categories, function(category) {
                        if (category.categoryId === categoryId) {
                            exhibitorIds = category.entities;

                            return false;
                        }
                    });
                    var out = [];
                    angular.forEach(dataSource.exhibitors, function(exhibitor) {
                        if (exhibitorIds.indexOf(exhibitor.exhibitorId) !== -1) {
                            exhibitor.categoryId = categoryId;
                            out.push(exhibitor);
                        }
                    });

                    return out;
                }
            },
            /**
             * @ngdoc method
             * @name getDetail
             * @methodOf exhibitors-V2_2.service:qmExhibitorsService
             * @description
             * Get details of a exhibitor
             *
             * @param {string} exhibitorId Exhibitor Id
             * @returns {promise} Promise that resolves when details of a exhibitor have been retrieved
             */
            getDetail: function(exhibitorId) {
                var url = qmWebAppService.getBaseRestUrl('component', 'exhibitors') + '/exhibitors/' + exhibitorId;
                url += '?locale=' + qmLocalization.getLocale();
                var defer = $q.defer();
                qmRest.get(url).then(function(data) {
                    data = decorator(data);
                    copyDetailData(data, exhibitorId);
                    init.getLandmarkRedirect(exhibitorId).then(function(landmarkRedirect) {
                        init.getExhibitorDocuments(exhibitorId).then(function(filesResponse) {
                            defer.resolve(
                                    {
                                        exhibitor: data,
                                        landmarkRedirect: landmarkRedirect.redirect,
                                        documents: filesResponse.documents,
                                        canUnlock: filesResponse.canUnlock
                                    }
                            );
                        });
                    });
                }, function(err) {
                    defer.reject(err);
                });

                return defer.promise;
            },
            /**
             * @ngdoc method
             * @name deleteMyExhibitor
             * @methodOf exhibitors-V2_2.service:qmExhibitorsService
             * @description
             * Delete attendee exhibitor link
             *
             * @param {string} exhibitorId Exhibitor Id
             * @returns {promise} Promise that resolves when my exhibitor has been removed
             */
            deleteMyExhibitor: function(exhibitorId) {
                var deleteUrl = qmWebAppService.getBaseRestUrl('component', 'exhibitors') + '/my-exhibitors/' + exhibitorId;
                var defer = $q.defer();
                qmRest.delete(deleteUrl).then(function(response) {
                    defer.resolve(response);
                }, function(err) {
                    defer.reject(err);
                });

                return defer.promise;
            },
            /**
             * @ngdoc method
             * @name addMyExhibitor
             * @methodOf exhibitors-V2_2.service:qmExhibitorsService
             * @description
             * Add attendee exhibitor link
             *
             * @param {string} exhibitorId Exhibitor Id
             * @returns {promise} Promise that resolves when exhibitor is added
             */
            addMyExhibitor: function(exhibitorId) {
                var url = qmWebAppService.getBaseRestUrl('component', 'exhibitors') + '/my-exhibitors/' + exhibitorId;
                var defer = $q.defer();
                qmRest.put(url).then(function(response) {
                    defer.resolve(response);
                }, function(err) {
                    defer.reject(err);
                });

                return defer.promise;
            },
            /**
             * @ngdoc method
             * @name getLandmarkRedirect
             * @methodOf exhibitors-V2_2.service:qmExhibitorsService
             * @description
             * Get landmark redirect
             *
             * @param {string} exhibitorId Exhibitor Id
             * @returns {promise} Promise that resolves when landmarks for this exhibitor have been retrieved
             */
            getLandmarkRedirect: function(exhibitorId) {
                var out = false;
                var defer = $q.defer();

                var isLoggedIn = qmLogin.isLoggedIn();
                var interactiveMapsRequiresLogin = qmEventConfig.isComponentLoginRequired('interactive-maps');

                if (qmDependencyManager.hasComponent('interactive-maps') && (!interactiveMapsRequiresLogin || isLoggedIn)) {
                    var qmInteractiveMaps = qmExhibitorsDependencyService.getInteractiveMapsService('qmInteractiveMapsService');
                    qmInteractiveMaps.getListExhibitorLandmarks(exhibitorId).then(function(landmarks) {
                        if (landmarks.length > 0) {
                            out = qmInteractiveMaps.getRedirect(landmarks[0]);
                        }
                        defer.resolve({redirect: out, success: true});
                    }, function(err) {
                        defer.resolve({redirect: out, success: false, msg: err});
                    });
                } else {
                    defer.resolve({redirect: out, success: true});
                }

                return defer.promise;

            },
            /**
             * @ngdoc method
             * @name getExhibitorFiles
             * @methodOf exhibitors-V2_2.service:qmExhibitorsService
             * @description
             * Get landmark redirect
             *
             * @param {string} exhibitorId Exhibitor Id
             * @returns {promise} Promise that resolves when exhibitor files for this exhibitor have been retrieved
             */
            getExhibitorDocuments: function(exhibitorId) {
                var defer = $q.defer();
                var useDocuments = init.isExhibitorFilesAvailable();

                var isLoggedIn = qmLogin.isLoggedIn();
                var documentsRequiresLogin = qmEventConfig.isComponentLoginRequired('documents');

                if (useDocuments && qmDependencyManager.hasComponent('documents') && (!documentsRequiresLogin || isLoggedIn)) {
                    var qmDocumentsService = qmExhibitorsDependencyService.getDocumentsService('qmDocumentsService');

                    init.getAllExhibitorDocumentIds(exhibitorId).then(function(allDocuments) {
                        if (allDocuments.ids.length > 0) {
                            qmDocumentsService.getExhibitorDocuments(exhibitorId).then(function(documents) {
                                var canUnlock = false;
                                if (angular.isArray(documents)) {
                                    var unlockedIds = [];
                                    angular.forEach(documents, function(document) {
                                        unlockedIds.push(document.documentId);
                                    });
                                    canUnlock = !angular.equals(unlockedIds.sort(), allDocuments.ids.sort());
                                }

                                defer.resolve({documents: documents, canUnlock: canUnlock, success: true});
                            });
                        } else {
                            defer.resolve({documents: false, canUnlock: false, success: true});
                        }
                    });
                } else {
                    defer.resolve({documents: false, canUnlock: false, success: true});
                }

                return defer.promise;
            },
            /**
             * @ngdoc method
             * @name getAllExhibitorDocumentIds
             * @methodOf exhibitors-V2_2.service:qmExhibitorsService
             * @description
             * Get list all documents ids
             *
             * @param {string} exhibitorId Exhibitor Id
             * @returns {promise} Promise that resolves when document ids for this exhibitor have been retrieved
             */
            getAllExhibitorDocumentIds: function(exhibitorId) {
                var url = qmWebAppService.getBaseRestUrl('component', 'exhibitors') + '/exhibitors/' + exhibitorId + '/documents';
                var defer = $q.defer();
                qmRest.get(url).then(function(data) {
                    var ids = [];
                    if (angular.isArray(data)) {
                        angular.forEach(data, function(item) {
                            ids.push(item.documentId);
                        });
                    }
                    defer.resolve({ids: ids, success: true});

                }, function(err) {
                    defer.resolve({ids: [], success: false, err: err});
                });

                return defer.promise;
            },
            /**
             * @ngdoc method
             * @name unlockExhibitorDocuments
             * @methodOf exhibitors-V2_2.service:qmExhibitorsService
             * @description
             * Get list all documents ids
             *
             * @param {string} exhibitorId Exhibitor Id
             * @param {string} pin Document pin
             * @returns {promise} Promise that resolves response from unlockExhibitorDocuments RPC request has been retrieved
             */
            unlockExhibitorDocuments: function(exhibitorId, pin) {
                var defer = $q.defer();
                if (init.isExhibitorFilesAvailable() && qmLogin.isLoggedIn()) {
                    var unlockExhibitorDocumentsUrl = qmEventConfig.getRpcUrl('unlockExhibitorDocuments');
                    var params = [];
                    params.push(qmLogin.getUserToken());
                    params.push(exhibitorId);
                    params.push(pin);

                    qmRpc.post(unlockExhibitorDocumentsUrl, {method: 'unlockExhibitorDocuments', params: params}).then(function(response) {
                        response.success = (typeof response.success) === 'undefined' ? true : (response.success === 'true');
                        defer.resolve(response);
                    }, function(err) {
                        err.success = false;
                        defer.resolve(err);

                    });
                } else {
                    defer.resolve({documents: [], success: true});
                }

                return defer.promise;
            },
            /**
             * @ngdoc method
             * @name isCategoriesAvailable
             * @methodOf exhibitors-V2_2.service:qmExhibitorsService
             * @description
             * Check if categories is available
             *
             * @returns {boolean} Is categories available?
             */
            isCategoriesAvailable: function()
            {
                return (init.getSetting('useCategories').toLowerCase() === 'true');
            },
            /**
             * @ngdoc method
             * @name isExhibitorFilesAvailable
             * @methodOf exhibitors-V2_2.service:qmExhibitorsService
             * @description
             * Check if exhibitor files is available
             *
             * @returns {boolean} Is exhibitor files available?
             */
            isExhibitorFilesAvailable: function() {
                return (init.getSetting('exhibitorFiles').toLowerCase() === 'true');
            },
            /**
             * @ngdoc method
             * @name getSetting
             * @methodOf exhibitors-V2_2.service:qmExhibitorsService
             * @description
             * Get settings for exhibitors component
             *
             * @returns {object} Exhibitor settings object
             */
            getSetting: function(keyName) {
                if (keyName in config['@attributes']) {

                    return config['@attributes'][keyName];
                } else {

                    return null;
                }
            },
            /**
             * @ngdoc method
             * @name stringFormat
             * @methodOf exhibitors-V2_2.service:qmExhibitorsService
             * @description
             * Formats string
             *
             * @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;
            }

        };

        return init;
    }]);

var exhibitors = angular.module('exhibitors-V2_2');

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

            return null;
        },
        /**
         * @ngdoc method
         * @name getMyExhibitorsService
         * @methodOf exhibitors-V2_2.service:qmExhibitorsDependencyService
         * @description
         * Get service from my exhibitors component
         *
         * @param {string} service Service name
         * @returns {service} Angular service or null
         */
        getMyExhibitorsService: function(service) {
            return qmDependencyManager.getService('my-exhibitors', '2.0', 'component', service);
        },
        /**
         * @ngdoc method
         * @name getInteractiveMapsService
         * @methodOf exhibitors-V2_2.service:qmExhibitorsDependencyService
         * @description
         * Get service from interactive maps component
         *
         * @param {string} service Service name
         * @returns {service} Angular service or null
         */
        getInteractiveMapsService: function(service) {
            return qmDependencyManager.getService('interactive-maps', '2.1', 'component', service);
        },
        /**
         * @ngdoc method
         * @name getDocumentsService
         * @methodOf exhibitors-V2_2.service:qmExhibitorsDependencyService
         * @description
         * Get service from documents component
         *
         * @param {string} service Service name
         * @returns {service} Angular service or null
         */
        getDocumentsService: function(service) {
            return qmDependencyManager.getService('documents', '2.1', 'component', service);
        },
        /**
         * @ngdoc method
         * @name getCategoriesService
         * @methodOf exhibitors-V2_2.service:qmExhibitorsDependencyService
         * @description
         * Get service from categories service
         *
         * @param {string} service Service name
         * @returns {service} Angular service or null
         */
        getCategoriesService: function(service) {
            return qmDependencyManager.getRequiredService('categories', '2.0', 'service', service);
        }
    };
}]);

var exhibitors = angular.module('exhibitors-V2_2');

/**
 * @ngdoc controller
 * @name exhibitors-V2_2.controller:ExhibitorMainListController
 * @description
 * Controller for Exhibitors check if we have categories
 */
exhibitors.controller('ExhibitorMainListController', ['qmExhibitorsService', '$state',
    function(qmExhibitorsService, $state) {

        var categoriesData = qmExhibitorsService.getCategories();
        if (categoriesData.length === 1) {
            $state.go('event.exhibitors.list');
        } else {
            $state.go('event.exhibitors.category');
        }
    }]);

var exhibitors = angular.module('exhibitors-V2_2');

/**
 * @ngdoc controller
 * @name exhibitors-V2_2.controller:ExhibitorListController
 * @description
 * Controller for Exhibitors list view
 */
exhibitors.controller('ExhibitorListController',
['$scope', 'qmExhibitorsService', 'qmList', '$state', '$rootScope', 'qmLocalization', 'qmExhibitorsDependencyService',
function($scope, qmExhibitorsService, qmList, $state, $rootScope, qmLocalization, qmExhibitorsDependencyService) {
    $scope.exhibitorListItemUrl = qmExhibitorsDependencyService.getTemplateUrl('exhibitor-list-item');
    $scope.loadExhibitors = qmExhibitorsService.preLoadData();
    $scope.loadExhibitors.then(function() {
        $scope.exhibitors = qmList.groupByLetter(qmExhibitorsService.getExhibitors($state.params.categoryId), 'company');
        var componentTitle = qmLocalization.getString('componentExhibitorsTitle');
        $scope.emptyText = qmLocalization.getString('LABEL_EMPTY_EXHIBITORS', componentTitle);
        var showBack = qmExhibitorsService.getCategories().length > 1 ? true : false;
        $rootScope.$broadcast('updateTitle', {showBackButton:showBack, categoryTitle: qmExhibitorsService.getCategoryTitle($state.params.categoryId)});
    });
    $scope.categoryId = $state.params.categoryId;
}]);

/**
 * @ngdoc controller
 * @name exhibitors-V2_2.controller:ExhibitorListHeaderController
 * @description
 * Controller for Exhibitors list header view
 */
exhibitors.controller('ExhibitorListHeaderController', ['$scope', 'qmLocalization', function($scope, qmLocalization) {
    $scope.headerTitle = qmLocalization.getString('componentExhibitorsTitle');
    $scope.$on('updateTitle', function(event, args) {
        if (args.categoryTitle !== '') {
            $scope.headerTitle = args.categoryTitle;
        }
        if (args.showBackButton) {
            $scope.headerBackState = '^';
        }
    });
}]);

var exhibitors = angular.module('exhibitors-V2_2');

/**
 * @ngdoc controller
 * @name exhibitors-V2_2.controller:ExhibitorCategoryListController
 * @description
 * Controller for Exhibitor category list view
 */
exhibitors.controller('ExhibitorCategoryListController', ['$scope', 'qmExhibitorsService', 'qmList', 'qmLocalization',
function($scope, qmExhibitorsService, qmList, qmLocalization) {

    var categoriesData = qmExhibitorsService.getCategories();
    $scope.categories = qmList.transformList(categoriesData);

    var componentTitle = qmLocalization.getString('componentExhibitorsTitle');
    $scope.emptyText = qmLocalization.getString('LABEL_EMPTY_EXHIBITORS', componentTitle);
}]);

/**
 * @ngdoc controller
 * @name exhibitors-V2_2.controller:ExhibitorCategoryListHeaderController
 * @description
 * Controller for Exhibitor Categories list header view
 */
exhibitors.controller('ExhibitorCategoryListHeaderController', ['$scope', 'qmLocalization', function($scope, qmLocalization) {
    $scope.headerTitle = qmLocalization.getString('componentExhibitorsTitle');
}]);

var exhibitors = angular.module('exhibitors-V2_2');

/**
 * @ngdoc controller
 * @name exhibitors-V2_2.controller:ExhibitorDetailController
 * @description
 * Controller for exhibitor detail view
 */
exhibitors.controller('ExhibitorDetailController', ['$scope', '$state', 'qmExhibitorsService', 'qmExhibitorsDependencyService', '$rootScope', 'qmLocalization',
    'qmDependencyManager', 'qmLogin', 'toastr', 'qmList', 'qmUtilities', '$q',
    function($scope, $state, qmExhibitorsService, qmExhibitorsDependencyService, $rootScope, qmLocalization,
    qmDependencyManager, qmLogin, toastr, qmList, qmUtilities, $q) {
        $scope.documents = false;
        $scope.canUnlockDocuments = false;

        $scope.documentPin = {};
        $scope.documentPin.pinCode = '';
        $scope.documentsTitle = qmLocalization.getString('componentDocumentsTitle');
        $scope.loadExhibitor = qmExhibitorsService.getDetail($state.params.exhibitorId);
        $scope.loadExhibitor.then(function(data) {
            $scope.showMyExhibitor = qmDependencyManager.hasComponent('my-exhibitors') && qmLogin.isLoggedIn();
            $scope.exhibitor = data;
            $scope.canUnlockDocuments = data.canUnlock;
            $scope.documents = data.documents !== false ? qmList.transformList(data.documents) : data.documents;

            if (qmDependencyManager.hasComponent('documents')) {
                var qmDocumentsDependencyService = qmExhibitorsDependencyService.getDocumentsService('qmDocumentsDependencyService');
                $scope.documentListItemUrl = qmDocumentsDependencyService.getTemplateUrl('document-list-item');
            } else {
                $scope.documentListItemUrl = '';
            }
            if (data.landmarkRedirect !== false) {
                $rootScope.$broadcast('hasLandmark', data.landmarkRedirect);
            }

            if ($scope.exhibitor.exhibitor.url) {
                $scope.exhibitor.exhibitor.url = qmUtilities.generateValidUrl($scope.exhibitor.exhibitor.url);
            }

            if ($scope.showMyExhibitor) {
                var qmMyExhibitorsService = qmExhibitorsDependencyService.getMyExhibitorsService('qmMyExhibitorsService');
                $scope.confirmAdd = function(exhibitorId) {
                    var defer = $q.defer();
                    $scope.loadExhibitor = defer.promise;

                    return qmMyExhibitorsService.addMyExhibitor(exhibitorId).then(function() {
                        $scope.isMyExhibitor = true;
                        data.exhibitor.isMyFavourite = true;
                        defer.resolve();
                    });
                };
                $scope.confirmRemove = function(exhibitorId) {
                    var defer = $q.defer();
                    $scope.loadExhibitor = defer.promise;

                    return qmMyExhibitorsService.deleteMyExhibitor(exhibitorId).then(function() {
                        $scope.isMyExhibitor = false;
                        data.exhibitor.isMyFavourite = false;
                        defer.resolve();
                    });
                };
            }
        });

        $scope.goToDocument = function(documentObj) {
            var qmDocumentsHelperService = qmExhibitorsDependencyService.getDocumentsService('qmDocumentsHelperService');
            qmDocumentsHelperService.goToDocumentDetail(documentObj);
        };

        $scope.unlockExhibitorDocuments = function() {
            var defer = $q.defer();
            qmExhibitorsService.unlockExhibitorDocuments($state.params.exhibitorId, $scope.documentPin.pinCode).then(function(response) {
                if (!response.success && response.error) {
                    toastr.warning(qmLocalization.getString(response.error), '', {
                        closeButton: true,
                        timeOut: 3000
                    });
                    defer.reject();
                } else if (response.success && response.message) {
                    qmExhibitorsService.getExhibitorDocuments($state.params.exhibitorId).then(function(response) {
                        $scope.documents = response.documents !== false ? qmList.transformList(response.documents) : response.documents;
                        $scope.canUnlockDocuments = response.canUnlock;

                    });
                    toastr.success(qmLocalization.getString('ALERT_EXHIBITOR_FILE_UNLOCKED'), '', {
                        closeButton: true,
                        timeOut: 3000
                    });
                    defer.resolve();
                } else {
                    defer.reject();
                }
            }, function() {
                defer.reject();
            });

            return defer.promise;
        };
    }
]);

/**
 * @ngdoc controller
 * @name exhibitors-V2_2.controller:ExhibitorDetailHeaderController
 * @description
 * Controller for exhibitor detail header view
 */
exhibitors.controller('ExhibitorDetailHeaderController', ['$scope', 'qmLocalization', '$state', function($scope, qmLocalization, $state) {
    $scope.headerBackState = '^';
    $scope.headerTitle = qmLocalization.getString('LABEL_DETAILS');
    $scope.$on('hasLandmark', function(event, landmarkRedirect) {
        $scope.headerRightElements = [];
        var mapElement = {};
        mapElement.type = 'icon';
        mapElement.class = 'fa fa-map-marker';
        mapElement.clickHandler = function() {
            $state.go(landmarkRedirect.state, landmarkRedirect.params);
        };
        $scope.headerRightElements.push(mapElement);
    });
}]);

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

facebook.config(['$stateProvider', function($stateProvider) {
    $stateProvider
        .state('event.facebook-V2_0', {
            url: '',
            resolve: {
                openFacebook: ['qmFacebookService', 'qmUtilities', '$state', '$q', function(qmFacebookService, qmUtilities, $state, $q) {
                    var url = qmFacebookService.getUrl();
                    var defer = $q.defer();
                    if (qmUtilities.openUrl(url)) {
                        $state.go('event', $state.params, {
                            reload: true,
                            priority: 10
                        });
                    } else {
                        defer.resolve();
                    }
                    return defer.promise;
                }]
            }
        });
}]);

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

/**
 * @ngdoc service
 * @name facebook-V2_0.service:qmFacebookService
 * @description
 * Manages REST API communication for Facebook
 */
facebook.factory('qmFacebookService', ['qmEventConfig',
    function(qmEventConfig) {
        return {
            getUrl: function() {
                var componentConfig = qmEventConfig.getComponentConfig('facebook');

                return componentConfig['@attributes'].url;
            }
        };
    }]);

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

social.constant('SOCIAL', {
    resource: {
        photos: 'photo',
        speakers: 'speaker',
        events: 'session',
        exhibitors: 'exhibitor'
    }
});

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

/**
 * @ngdoc service
 * @name social-V2_0.service:qmSocialDependencyService
 * @description
 * Manages communication with dependency manager
 */
social.factory('qmSocialDependencyService', ['qmDependencyManager', function(qmDependencyManager) {
    return {
        /**
         * @ngdoc method
         * @name getAnalyticsService
         * @methodOf social-V2_0.service:qmSocialDependencyService
         * @description
         * Get service from analytics service
         *
         * @param {string} service Service name
         * @returns {service} Angular service or null
         */
        getAnalyticsService: function(service) {
            return qmDependencyManager.getService('analytics', '2.0', 'service', service);
        }
    };
}]);

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

/**
 * @ngdoc service
 * @name social-V2_0.service:qmSocialService
 * @description
 * Manages REST API communication for Social
 */
social.factory('qmSocialService',
    ['qmRest', 'qmWebAppService', '$q', 'qmSocialDependencyService', 'qmBaseTempStorage', '$timeout', 'qmLocalization', 'qmEventConfig', 'qmLogin', 'SOCIAL',
        function(qmRest, qmWebAppService, $q, qmSocialDependencyService, qmBaseTempStorage, $timeout, qmLocalization, qmEventConfig, qmLogin, SOCIAL) {

            var likesCache = new qmBaseTempStorage('socialLikes', '2.0');
            // The server currently has a 15 second processing delay. We will wait a minute before clearing our cache and respecting server values
            var cleanLikesCache = function(resource, resourceId) {
                $timeout(function() {
                    var cachedResourceLikes = likesCache.getUserItem(resource);
                    if (cachedResourceLikes && angular.isDefined(cachedResourceLikes[resourceId])) {
                        delete cachedResourceLikes[resourceId];
                    }
                    likesCache.setUserItem(resource, cachedResourceLikes);
                }, 60000);
            };

            var init = {
                /**
                 * @ngdoc method
                 * @name getLikes
                 * @methodOf social-V2_0.service:qmSocialService
                 * @description
                 * Get likes for specified resource and ids
                 *
                 * @param {string} resource Resource the ids are associated with
                 * @param {Array} ids Array of ids to get likes for
                 * @returns {promise} Promise that resolves after getting likes response
                 */
                getLikes: function(resource, ids) {
                    var url = qmWebAppService.getBaseRestUrl('component', 'social') + '/' + resource + '/likes?';
                    url += 'ids=' + ids.join();

                    var defer = $q.defer();
                    // get likes data
                    qmRest.get(url).then(function(responseData) {
                        var likesData = responseData.data;
                        var cachedResourceLikes = likesCache.getUserItem(resource);
                        for (var i = 0; i < likesData.length; i++) {
                            likesData[i].likeCount = parseInt(likesData[i].likes);
                            likesData[i].isLiked = likesData[i].status === '1';
                            if (cachedResourceLikes) {
                                if (likesData[i].isLiked) {
                                    // If local cache tells us resource is unliked but server status is liked, we decrement the count by 1
                                    if (cachedResourceLikes[likesData[i].id] === false) {
                                        likesData[i].likeCount--;
                                        likesData[i].isLiked = false;
                                    }
                                } else {
                                    // If local cache tells us the resource is liked but server status is unliked, we increment the count by 1
                                    if (cachedResourceLikes[likesData[i].id] === true) {
                                        likesData[i].likeCount++;
                                        likesData[i].isLiked = true;
                                    }
                                }
                            }
                        }
                        // For the case where we liked a photo locally that was not previously like, but backend has not updated
                        if (cachedResourceLikes) {
                            for (var j in cachedResourceLikes) {
                                if (cachedResourceLikes.hasOwnProperty(j)) {
                                    var resourceIdFound = false;
                                    for (var k = 0; k < likesData.length; k++) {
                                        if (j === likesData[k].id) {
                                            resourceIdFound = true;
                                            break;
                                        }
                                    }
                                    if (resourceIdFound === false && ids.indexOf(j) >= 0) {
                                        likesData.push({
                                            id: j,
                                            likes: '1',
                                            status: '1',
                                            likeCount: 1,
                                            isLiked: true
                                        });
                                    }
                                }
                            }
                        }
                        defer.resolve(likesData);
                    }, function(err) {
                        defer.reject(err);
                    });

                    return defer.promise;
                },
                /**
                 * @ngdoc method
                 * @name getResourceKey
                 * @methodOf social-V2_0.service:qmSocialService
                 * @description
                 * Get resource key based on component key
                 *
                 * @param {string} componentKey component key
                 * @returns {string} resource name
                 */
                getResourceKey: function(componentKey) {
                    var resource = null;

                    if (componentKey in SOCIAL.resource) {
                        resource = SOCIAL.resource[componentKey];
                    }

                    return resource;
                },
                /**
                 * @ngdoc method
                 * @name allowLikes
                 * @methodOf social-V2_0.service:qmSocialService
                 * @description
                 * Check if likes allows for component
                 *
                 * @param {string} componentKey component key
                 * @returns {boolean} true if like allows for this component
                 */
                allowLikes: function(componentKey) {

                    var resource = init.getResourceKey(componentKey);

                    var socialConfig = qmEventConfig.getComponentConfig('social');
                    var likesEnabled = false;
                    if (socialConfig && resource !== null) {
                        likesEnabled = socialConfig['@attributes'][resource + 'Likes'] === 'true';
                    }

                    return likesEnabled;
                },
                /**
                 * @ngdoc method
                 * @name getObjectSocialData
                 * @methodOf social-V2_0.service:qmSocialService
                 * @description
                 * Get likes for specified resources and decorate object with like properties
                 *
                 * @param {Array} objects Array of objects to get likes for
                 * @param {string} keyField id field of object
                 * @param {string} componentKey component key the ids are associated with
                 * @returns {promise} Promise that resolves after getting likes response
                 */
                getObjectSocialData: function(objects, keyField, componentKey) {

                    var resource = init.getResourceKey(componentKey);
                    var likesEnabled = init.allowLikes(componentKey);

                    var defer = $q.defer();
                    if (likesEnabled && resource !== null && qmLogin.isLoggedIn()) {
                        var ids = [];
                        for (var index = 0; index < objects.length; index++) {
                            objects[index].enableLikes = likesEnabled;
                            ids.push(objects[index][keyField]);
                        }
                        // if id string is longer than 1600 chars, then request all of them
                        ids = ids.join().length > 1600 ? [] : ids;
                        init.getLikes(resource, ids).then(function(likesData) {
                            if (likesData.length > 0) {
                                var likeIndexes = [];
                                angular.forEach(likesData, function(item) {
                                    likeIndexes[item.id] = item;
                                });

                                for (var k = 0; k < objects.length; k++) {
                                    var id = objects[k][keyField];
                                    if (!angular.isDefined(likeIndexes[id])) {
                                        objects[k].likeCount = 0;
                                    } else {
                                        objects[k].likeCount = likeIndexes[id].likeCount;
                                        objects[k].isLiked = likeIndexes[id].isLiked;
                                    }
                                    if (objects[k].likeCount === 0) {
                                        objects[k].likeCountLabel = '';
                                    } else if (objects[k].likeCount === 1) {
                                        objects[k].likeCountLabel = qmLocalization.getString('LABEL_LIKE', objects[k].likeCount);
                                    } else {
                                        objects[k].likeCountLabel = qmLocalization.getString('LABEL_LIKES', objects[k].likeCount);
                                    }
                                    objects[k].socialDataLoaded = true;

                                }

                            }
                            defer.resolve();
                        }, function() {
                            defer.resolve();
                        });
                    } else {
                        defer.resolve();
                    }
                    return defer.promise;
                },

                /**
                 * @ngdoc method
                 * @name like
                 * @methodOf social-V2_0.service:qmSocialService
                 * @description
                 * Send like data to analytics
                 *
                 * @param {string} resource Resource name
                 * @param {string} resourceId Resource id
                 * @param {string} sourceElement Source element
                 * @param {string} condition Event condition
                 */
                like: function(resource, resourceId, sourceElement, condition) {
                    var qmAnalyticsService = qmSocialDependencyService.getAnalyticsService('qmAnalyticsService');
                    var defer = $q.defer();

                    if (qmAnalyticsService) {
                        qmAnalyticsService.socialEvent('Like', resourceId, resource, sourceElement, condition).then(function() {
                                var cachedResourceLikes = likesCache.getUserItem(resource);
                                if (!cachedResourceLikes) {
                                    cachedResourceLikes = {};
                                }
                                cachedResourceLikes[resourceId] = true;
                                likesCache.setUserItem(resource, cachedResourceLikes);
                                // in case user logs into another device and unlikes the photo, we want to clean our cache
                                cleanLikesCache(resource, resourceId);
                                defer.resolve();
                            },
                            function() {
                                defer.reject();
                            });
                    }

                    return defer.promise;
                },
                /**
                 * @ngdoc method
                 * @name unlike
                 * @methodOf social-V2_0.service:qmSocialService
                 * @description
                 * Send unlike data to analytics
                 *
                 * @param {string} resource Resource name
                 * @param {string} resourceId Resource id
                 * @param {string} sourceElement Source element
                 * @param {string} condition Event condition
                 */
                unlike: function(resource, resourceId, sourceElement, condition) {
                    var qmAnalyticsService = qmSocialDependencyService.getAnalyticsService('qmAnalyticsService');
                    var defer = $q.defer();

                    if (qmAnalyticsService) {
                        qmAnalyticsService.socialEvent('Unlike', resourceId, resource, sourceElement, condition).then(function() {
                                var cachedResourceLikes = likesCache.getUserItem(resource);
                                if (!cachedResourceLikes) {
                                    cachedResourceLikes = {};
                                }
                                cachedResourceLikes[resourceId] = false;
                                likesCache.setUserItem(resource, cachedResourceLikes);
                                // in case user logs into another device and unlikes the photo, we want to clean our cache
                                cleanLikesCache(resource, resourceId);
                                defer.resolve();
                            },
                            function() {
                                defer.reject();
                            });
                    }

                    return defer.promise;
                }
            };

            return init;
        }]);


var gallery = angular.module('gallery-V2_2');

gallery.config(['$stateProvider', function($stateProvider) {
    $stateProvider
        .state('event.gallery-V2_2', {
            url: '/Gallery',
            views: {
                'component@event': {
                    templateUrl: '/asset/component/gallery/2.1/webapp/html/gallery-list.html',
                    controller: 'GalleryListController'
                },
                'header@event': {
                    controller: 'GalleryListHeaderController'
                }
            }
        })
        .state('event.gallery-V2_2.preview', {
            url: '/:galleryId',
            views: {
                'component@event': {
                    templateUrl: '/asset/component/gallery/2.2/webapp/html/gallery-preview.html',
                    controller: 'GalleryPreviewController'
                },
                'header@event': {
                    controller: 'GalleryPreviewHeaderController'
                }
            }
        })
        .state('event.gallery-V2_2.preview.detail', {
            url: '/:photoId',
            views: {
                'component@event': {
                    templateUrl: '/asset/component/gallery/2.2/webapp/html/gallery-detail.html',
                    controller: 'GalleryDetailController'
                },
                'header@event': {
                    controller: 'GalleryDetailHeaderController'
                }
            }
        });
}]);

var gallery = angular.module('gallery-V2_2');

/**
 * @ngdoc service
 * @name gallery-V2_2.service:qmGalleryService
 * @description
 * Manages REST API communication for Gallery
 */
gallery.factory('qmGalleryService',
['qmRest', '$q', 'qmLocalization', 'qmEventConfig', 'qmLogin', 'qmRpc', 'qmWebAppService', 'qmGalleryDependencyService',
function(qmRest, $q, qmLocalization, qmEventConfig, qmLogin, qmRpc, qmWebAppService, qmGalleryDependencyService) {
    return {
        /**
         * @ngdoc method
         * @name getList
         * @methodOf gallery-V2_2.service:qmGalleryService
         * @description
         * Get list of galleries
         *
         * @returns {promise} Promise that resolves when list of galleries have been retrieved
         */
        getList: function(galleryIds) {
            var queryParams = [];
            queryParams.push('locale=' + qmLocalization.getLocale());
            if (angular.isDefined(galleryIds)) {
                for (var i = 0; i < galleryIds.length; i++) {
                    queryParams.push('galleryId[]=' + encodeURIComponent(galleryIds[i]));
                }
            }
            var getListUrl = qmWebAppService.getBaseRestUrl('component', 'gallery') + '/galleries?' + queryParams.join('&');
            var defer = $q.defer();

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

                var galleryList = data;

                // use qmRest service to get image data and store in imageSrc
                for (var i = 0; i < galleryList.length; i++) {

                    if (galleryList[i].smallImageUrl && galleryList[i].smallImageUrl !== '') {
                        galleryList[i].imageSrc = qmWebAppService.appendAuthHeader(galleryList[i].smallImageUrl);
                    }
                }
                defer.resolve(galleryList);
            }, function(err) {
                defer.reject(err);
            });

            return defer.promise;
        },

        /**
         * @ngdoc method
         * @name getDetail
         * @methodOf gallery-V2_2.service:qmGalleryService
         * @description
         * Get details of an gallery
         *
         * @param {string} galleryId Gallery Id
         * @returns {promise} Promise that resolves when gallery details has been retrieved
         */
        getDetail: function(galleryId, offset, limit, sort) {
            var queryParams = [];
            queryParams.push('locale=' + qmLocalization.getLocale());
            if (angular.isDefined(offset)) {
                queryParams.push('offset=' + offset);
            }
            if (angular.isDefined(limit)) {
                queryParams.push('limit=' + limit);
            }
            if (angular.isDefined(sort)) {
                queryParams.push('sort=' + sort);
            }
            var getDetailUrl = qmWebAppService.getBaseRestUrl('component', 'gallery') + '/galleries/' + galleryId +
                '/photos?' + queryParams.join('&');
            var defer = $q.defer();
            qmRest.get(getDetailUrl).then(function(data) {

                var photoList = data;
                var pics = [];

                for (var i = 0; i < photoList.length; i++) {

                    var hasPic = false;

                    if (photoList[i].smallImageUrl && photoList[i].smallImageUrl !== '') {
                        photoList[i].smallImageUrl = qmWebAppService.appendAuthHeader(photoList[i].smallImageUrl);
                        hasPic = true;
                    }
                    if (photoList[i].mediumImageUrl && photoList[i].mediumImageUrl !== '') {
                        photoList[i].mediumImageUrl = qmWebAppService.appendAuthHeader(photoList[i].mediumImageUrl);
                        hasPic = true;
                    }
                    if (photoList[i].largeImageUrl && photoList[i].largeImageUrl !== '') {
                        photoList[i].largeImageUrl = qmWebAppService.appendAuthHeader(photoList[i].largeImageUrl);
                        hasPic = true;
                    }

                    if (hasPic) {
                        pics.push(photoList[i]);
                    }
                }

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

            return defer.promise;
        },
        /**
         * @ngdoc method
         * @name flag
         * @methodOf gallery-V2_2.service:qmGalleryService
         * @description
         * Flag a photo
         *
         * @param {string} Photo 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(),
                    'Photos',
                    id
                ]
            };

            qmRpc.post(flagUrl, params).then(function(response) {
                defer.resolve(response);
            }, function(err) {
                defer.reject(err);
            });

            return defer.promise;
        },
        /**
         * @ngdoc method
         * @name getPhotoLikes
         * @methodOf gallery-V2_2.service:qmGalleryService
         * @description
         * Get likes data and add them to photos
         *
         * @param {Array} List of photos
         * @returns {promise} Promise that resolves when likes data have successfully been added
         */
        getPhotoLikes: function(photos) {
            var defer = $q.defer();
            var qmSocialService = qmGalleryDependencyService.getSocialService('qmSocialService');
            if (qmSocialService) {
                var photoIds = [];
                for (var index = 0; index < photos.length; index++) {
                    photoIds.push(photos[index].photoId);
                }
                qmSocialService.getLikes('photo', photoIds).then(function(likesData) {
                    for (var i = 0; i < likesData.length; i++) {
                        for (var j = 0; j < photos.length; j++) {
                            if (likesData[i].id === photos[j].photoId) {
                                photos[j].likeCount = likesData[i].likeCount;
                                photos[j].isLiked = likesData[i].isLiked;
                            }
                        }
                    }
                    for (var k = 0; k < photos.length; k++) {
                        // If like count is not defined, set it to 0 so it increments properly
                        if (!angular.isDefined(photos[k].likeCount)) {
                            photos[k].likeCount = 0;
                        }
                        photos[k].likeDataLoaded = true;
                    }
                    defer.resolve();
                }, function() {
                    defer.reject();
                });
            } else {
                defer.reject();
            }
            return defer.promise;
        },
        /**
         * @ngdoc method
         * @name likePhoto
         * @methodOf gallery-V2_2.service:qmGalleryService
         * @description
         * Send like photo data to analytics
         *
         * @param {Object} photo Photo object
         * @param {string} sourceElement Source element
         * @param {string} condition Event condition
         */
        likePhoto: function(photo, sourceElement, condition) {
            var qmSocialService = qmGalleryDependencyService.getSocialService('qmSocialService');
            var qmSocialConstant = qmGalleryDependencyService.getSocialService('SOCIAL');
            if (qmSocialService && qmSocialConstant) {
                qmSocialService.like(qmSocialConstant.resource.photos, photo.photoId, sourceElement, condition);
            }
        },
        /**
         * @ngdoc method
         * @name unlikePhoto
         * @methodOf gallery-V2_2.service:qmGalleryService
         * @description
         * Send unlike photo data to analytics
         *
         * @param {Object} photo Photo object
         * @param {string} sourceElement Source element
         * @param {string} condition Event condition
         */
        unlikePhoto: function(photo, sourceElement, condition) {
            var qmSocialService = qmGalleryDependencyService.getSocialService('qmSocialService');
            var qmSocialConstant = qmGalleryDependencyService.getSocialService('SOCIAL');
            if (qmSocialService && qmSocialConstant) {
                qmSocialService.unlike(qmSocialConstant.resource.photos, photo.photoId, sourceElement, condition);
            }
        }
    };
}]);

var gallery = angular.module('gallery-V2_2');

/**
 * @ngdoc service
 * @name gallery-V2_2.service:qmGalleryDependencyService
 * @description
 * Manages communication with dependency manager
 */
gallery.factory('qmGalleryDependencyService', ['qmDependencyManager', function(qmDependencyManager) {
    return {
        /**
         * @ngdoc method
         * @name getContentModeratorService
         * @methodOf gallery-V2_2.service:qmGalleryDependencyService
         * @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);
        },
        /**
         * @ngdoc method
         * @name getSocialService
         * @methodOf gallery-V2_2.service:qmGalleryDependencyService
         * @description
         * Get service from social service
         *
         * @param {string} service Service name
         * @returns {service} Angular service or null
         */
        getSocialService: function(service) {
            return qmDependencyManager.getService('social', '2.0', 'component', service);
        }
    };
}]);

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

/**
 * @ngdoc controller
 * @name gallery-V2_1.controller:GalleryListController
 * @description
 * Controller for Gallery list view
 */
gallery.controller('GalleryListController', ['$scope', 'qmGalleryService', 'qmList', 'qmLocalization', 'qmEventConfig', 'qmWebAppService',
    function($scope, qmGalleryService, qmList, qmLocalization, qmEventConfig, qmWebAppService) {
        $scope.loadGalleries = qmGalleryService.getList();
        var defaultIconUrl = qmWebAppService.appendAuthHeader(qmEventConfig.getArtifact('icon_main_gallery'));

        $scope.loadGalleries.then(function(galleries) {
            angular.forEach(galleries, function(gallery) {
                if (!gallery.imageSrc) {
                    gallery.imageSrc = defaultIconUrl;
                }
            });
            $scope.galleries = qmList.transformList(galleries);
            var componentTitle = qmLocalization.getString('componentGalleryTitle');
            $scope.emptyText = qmLocalization.getString('LABEL_EMPTY_GALLERY', componentTitle);
        });
    }
]);

/**
 * @ngdoc controller
 * @name gallery-V2_1.controller:GalleryListHeaderController
 * @description
 * Controller for Gallery list header view
 */
gallery.controller('GalleryListHeaderController', ['$scope', 'qmLocalization', function($scope, qmLocalization) {
    $scope.headerTitle = qmLocalization.getString('componentGalleryTitle');
    $scope.headerTitleTestId = 'componentTitle';
}]);

var gallery = angular.module('gallery-V2_2');

/**
 * @ngdoc controller
 * @name gallery-V2_2.controller:GalleryDetailController
 * @description
 * Controller for gallery detail view
 */
gallery.controller('GalleryDetailController',
['$scope', '$state', 'qmGalleryService', '$rootScope', 'qmLocalization', 'toastr', 'qmAnalytics',
'qmGalleryDependencyService', 'qmEventConfig', 'qmWebAppService', '$q', 'qmLogin',
function($scope, $state, qmGalleryService, $rootScope, qmLocalization, toastr, qmAnalytics,
qmGalleryDependencyService, qmEventConfig, qmWebAppService, $q, qmLogin) {
    $scope.itemOffset = 0;
    $scope.initialItems = 50;
    $scope.moreItems = 25;
    $scope.loadingOffset = 5;

    $scope.isLastItem = false;
    $scope.galleryInterval = -1;
    $scope.galleryWrap = false;
    $scope.slides = [];
    $scope.photoCount = 0;
    $scope.currentSlide = null;
    $scope.currentSlideLikeCount = '';

    var emptyIconUrl = qmEventConfig.getArtifact('icon_main_gallery');
    $scope.emptyListIcon = qmWebAppService.appendAuthHeader(emptyIconUrl);
    $scope.emptyText = qmLocalization.getString('LABEL_EMPTY_GALLERY_MESSAGE');

    $scope.flagLabel = qmLocalization.getString('BUTTON_FLAG');
    $scope.likeLabel = qmLocalization.getString('BUTTON_LIKE');

    // Confirmation dialog for photo flag
    $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');

    var qmSocialService = qmGalleryDependencyService.getSocialService('qmSocialService');
    if (qmSocialService) {
        var socialConfig = qmEventConfig.getComponentConfig('social');
        var likesEnabled = socialConfig['@attributes'].photoLikes === 'true' ? true : false;
        $scope.enableLikes = likesEnabled && qmLogin.isLoggedIn();

        // Get likes REST has 2000 character limit. Ids can be a maximum 45 characters
        // Base url is about 100 characters. We'll be conservative and try for 1500 characters
        $scope.initialItems = 30;
        $scope.moreItems = 30;
    }

    // Add like data to photo data
    $scope.addLikeData = function(photos) {
        return qmGalleryService.getPhotoLikes(photos);
    };

    $scope.updateLikeCount = function(likeCount) {
        if (angular.isDefined(likeCount)) {
            if (likeCount === 1) {
                $scope.currentSlideLikeCount = qmLocalization.getString('LABEL_LIKE', likeCount);
            } else {
                $scope.currentSlideLikeCount = qmLocalization.getString('LABEL_LIKES', likeCount);
            }
        }
    };

    $scope.addToSlideshow = function(photos) {
        $scope.photoCount += photos.length;
        for (var i = 0; i < photos.length; i++) {
            if (photos[i].photoId === $state.params.photoId) {
                photos[i].active = true;
                $scope.currentSlide = photos[i];
            }
            $scope.slides.push(photos[i]);
        }
        $scope.addLikeData(photos).then(function() {
            if ($scope.currentSlide) {
                $scope.updateLikeCount($scope.currentSlide.likeCount);
            }
        });
    };

    $scope.loadMorePhotos = function() {
        // Load more images when we get to the last image
        return qmGalleryService.getDetail($state.params.galleryId, $scope.itemOffset, $scope.moreItems).then(function(photos) {
            if (photos && photos.length) {
                $scope.addToSlideshow(photos);
                $scope.itemOffset += $scope.moreItems;
            } else {
                $scope.isLastItem = true;
            }
        });
    };

    // Load initial gallery items.
    // todo: We need a way to specify id in the paginated request.
    // If we can't find photoId in the first batch, we have to recursively load more until we find it.
    $scope.initGallery = function() {
        var defer = $q.defer();
        qmGalleryService.getDetail($state.params.galleryId, $scope.itemOffset, $scope.initialItems).then(function(photos) {
            $scope.addToSlideshow(photos);
            $scope.itemOffset = $scope.initialItems;
            if (!$scope.currentSlide) {
                var findPhoto = function() {
                    $scope.loadMorePhotos().finally(function() {
                        if ($scope.isLastItem) {
                            defer.reject();
                            return;
                        }
                        if (!$scope.currentSlide) {
                            findPhoto();
                        } else {
                            // In case current slide index is past the $scope.loadingOffset, lets just load the next photos
                            // We can probably be smarter and load based off current index and loadingOffset
                            $scope.loadMorePhotos();
                            defer.resolve();
                            return;
                        }
                    });
                };
                findPhoto();
            } else {
                // In case current slide index is past the $scope.loadingOffset, lets just load the next photos
                // We can probably be smarter and load based off current index and loadingOffset
                $scope.loadMorePhotos();
                defer.resolve();
            }
        });
        return defer.promise;
    };

    $scope.loadGallery = $scope.initGallery();
    $scope.isLoading = true;
    $scope.loadGallery.finally(function() {
        $scope.isLoading = false;
    });

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

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

    $scope.flagPhoto = function(photoId) {

        return qmGalleryService.flag(photoId).then(function() {

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

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

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

    $scope.likePhoto = function(photo) {
        // Just in case don't like a photo again if its already been liked
        if (!photo.isLiked) {
            photo.isLiked = true;
            photo.likeCount++;
            $scope.updateLikeCount(photo.likeCount);
            //todo: LikeButton source element should come from html
            qmGalleryService.likePhoto(photo, 'LikeButton', 'Click');
        }
    };

    $scope.unlikePhoto = function(photo) {
        // Just in case don't unlike a photo if its not liked already
        if (photo.isLiked) {
            photo.isLiked = false;
            photo.likeCount--;
            $scope.updateLikeCount(photo.likeCount);
            //todo: LikeButton source element should come from html
            qmGalleryService.unlikePhoto(photo, 'LikeButton', 'Click');
        }
    };

    $scope.nextSlideHandler = function(slide, direction, index) {
        // Gallery slide index starts at -1
        if (!$scope.isLastItem && index === $scope.slides.length - $scope.loadingOffset - 2) {
            // nextSlideHandler gets called when leaving the gallery slideshow for some weird reason so make sure galleryId state param exists
            if ($state.params && $state.params.galleryId) {
                $scope.loadMorePhotos();
            }
        }

        $scope.currentSlide = slide.$parent.slide;
        $scope.updateLikeCount($scope.currentSlide.likeCount);
        var photoId = slide.$parent.slide.photoId;
        var galleryId = $state.params.galleryId;
        qmAnalytics.markEvent('PhotoDetailsView', photoId, galleryId);
    };

}]);

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

var gallery = angular.module('gallery-V2_2');

/**
 * @ngdoc controller
 * @name gallery-V2_2.controller:GalleryPreviewController
 * @description
 * Controller for gallery preview view
 */
gallery.controller('GalleryPreviewController',
['$scope', '$state', 'qmGalleryService', 'qmLocalization', 'qmEventConfig', 'qmWebAppService',
'qmGalleryDependencyService', 'LIST', 'qmListDataService', 'qmLogin',
function($scope, $state, qmGalleryService, qmLocalization, qmEventConfig, qmWebAppService,
qmGalleryDependencyService, LIST, qmListDataService, qmLogin) {
    $scope.itemOffset = 0;
    $scope.initialItems = 50;
    $scope.moreItems = 25;

    qmListDataService.setInfiniteScroll(true);
    $scope.$on('$destroy', function() {
        qmListDataService.setInfiniteScroll(false);
    });

    var qmSocialService = qmGalleryDependencyService.getSocialService('qmSocialService');
    if (qmSocialService) {
        var socialConfig = qmEventConfig.getComponentConfig('social');
        var likesEnabled = socialConfig['@attributes'].photoLikes === 'true' ? true : false;
        $scope.enableLikes = likesEnabled && qmLogin.isLoggedIn();

        // Get likes REST has 2000 character limit. Ids can be a maximum 45 characters
        // Base url is about 100 characters. We'll be conservative and try for 1500 characters
        $scope.initialItems = 30;
        $scope.moreItems = 30;
    }

    // Add like data to photo data
    $scope.addLikeData = function(photos) {
        qmGalleryService.getPhotoLikes(photos);
    };

    $scope.loadGallery = qmGalleryService.getDetail($state.params.galleryId, $scope.itemOffset, $scope.initialItems);
    $scope.loadGallery.then(function(photos) {
        $scope.addLikeData(photos);
        $scope.photos = photos;
        $scope.isEmptyList = $scope.photos.length === 0;
        $scope.itemOffset = $scope.initialItems;

        if ($scope.enableLikes) {
            // Get likes REST has a url limit of 2000 characters. Since an id can be 45 characters, the safest choice
            // was to only allow requests for 30 photos at a time. On initialization, do another request
            // so we get 60 photos initially so user can have a scrollbar that they use to activate more requests.
            $scope.$broadcast('componentInfiniteScroll');
        }
    });
    $scope.emptyText = qmLocalization.getString('LABEL_EMPTY_GALLERY_MESSAGE');
    var emptyIconUrl = qmEventConfig.getComponentArtifact($state.params.componentId);
    var emptyIconSrc = qmWebAppService.appendAuthHeader(emptyIconUrl);
    $scope.emptyListIcon = emptyIconSrc;

    $scope.$on('componentInfiniteScroll', function(event, callback) {
        qmGalleryService.getDetail($state.params.galleryId, $scope.itemOffset, $scope.moreItems).then(function(moreItems) {
            if (moreItems && moreItems.length) {
                $scope.addLikeData(moreItems);
                $scope.photos = $scope.photos.concat(moreItems);
                $scope.itemOffset += $scope.moreItems;
            } else {
                // we reached the end of the list
                qmListDataService.setInfiniteScroll(false);
            }
        }).finally(function() {
            callback();
        });
    });
}]);

/**
 * @ngdoc controller
 * @name gallery-V2_2.controller:GalleryPreviewHeaderController
 * @description
 * Controller for gallery preview header view
 */
gallery.controller('GalleryPreviewHeaderController', ['$scope', 'qmLocalization', function($scope, qmLocalization) {
    $scope.headerBackState = true;
    $scope.headerTitle = qmLocalization.getString('LABEL_DETAILS');
    $scope.headerTitleTestId = 'detailTitle';
}]);

var gamification = angular.module('gamification-V2_2');

gamification.config(['$stateProvider', function($stateProvider) {
    $stateProvider
            .state('event.gamification-V2_2', {
                url: '/Gamification',
                views: {
                    'component@event': {
                        templateUrl: '/asset/component/gamification/2.2/webapp/html/gamification-home.html',
                        controller: 'GamificationMainController'
                    },
                    'header@event': {
                        controller: 'GamificationMainHeaderController'
                    }
                }
            });
}]);

var gamification = angular.module('gamification-V2_2');

gamification.run(['qmGamificationService', '$rootScope', function(qmGamificationService) {
    qmGamificationService.addEventListener();
}]);

/**
 * @ngdoc service
 * @name gamification-V2_2.service:qmGamificationService
 * @description
 * Manages REST API communication for gamification
 */
gamification.factory('qmGamificationService', ['qmRest', '$q', 'qmWebAppService', 'qmLocalization', 'qmEventConfig', 'qmLogin', 'qmRpc', 'toastr', '$rootScope',
'qmDependencyManager', 'qmGamificationDependencyService',
function(qmRest, $q, qmWebAppService, qmLocalization, qmEventConfig, qmLogin, qmRpc, toastr, $rootScope,
qmDependencyManager, qmGamificationDependencyService) {

    var config = null;
    var availabilities = {instructions: true, qrZone: false, quizzes: false, leaderboard: true};
    var instructions = [];
    var searchDefer = null;
    var currentTab = null;

    var init = {
        /**
         * @ngdoc method
         * @name setTab
         * @methodOf gamification-V2_2.service:qmGamificationService
         * @description
         * Set current tab
         *
         * @param {string} tab Tab key
         */
        setTab: function(tab) {
            currentTab = tab;
        },
        /**
         * @ngdoc method
         * @name getTab
         * @methodOf gamification-V2_2.service:qmGamificationService
         * @description
         * Get last active tab and erase it
         *
         * @returns {string} last active tab key
         */
        getTab: function() {
            var out = currentTab;
            currentTab = null;

            return out;
        },
        /**
         * @ngdoc method
         * @name getAvailabilities
         * @methodOf gamification-V2_2.service:qmGamificationService
         * @description
         * Load availabilities feature for gamification
         *
         * @returns {promise} Promise that resolves when availabilities information has been retrieved
         */
        getAvailabilities: function() {
            var defer = $q.defer();
            var url = qmWebAppService.getBaseRestUrl('component', 'gamification') + '/available-features';
            qmRest.get(url).then(function(response) {

                availabilities.qrZone = response.qrZone;
                availabilities.quizzes = response.quizzes && qmDependencyManager.hasComponent('surveys');
                defer.resolve(availabilities);
            }, function(err) {
                defer.reject(err);
            });

            return defer.promise;
        },
        /**
         * @ngdoc method
         * @name getInstructions
         * @methodOf gamification-V2_2.service:qmGamificationService
         * @description
         * Load list of instructions for gamification
         *
         * @returns {promise} Promise that resolves when instruction list has been retrieved
         */
        getInstructions: function() {
            var componentsRequired = {
                tweet: ['twitter'],
                photo: ['gallery'],
                mySchedule: ['myschedule'],
                myBriefcase: ['my-documents'],
                myExhibitor: ['my-exhibitors'],
                allSurvey: ['surveys'],
                quiz: ['surveys'],
                message: ['messaging'],
                myNote: ['my-notes'],
                contactExchange: ['contact-exchange'],
                sessionAttendance: ['session-check-in']
            };
            var defer = $q.defer();
            if (instructions.length === 0) {
                var url = qmWebAppService.getBaseRestUrl('component', 'gamification') + '/instructions?locale=' + qmLocalization.getLocale();
                qmRest.get(url).then(function(response) {
                    var allInstructions = response;
                    var out = [];
                    //filters
                    angular.forEach(allInstructions, function(instruction) {
                        var valid = true;

                        if (instruction.gameInstructionKey in componentsRequired) {
                            angular.forEach(componentsRequired[instruction.gameInstructionKey], function(component) {
                                if (!qmEventConfig.isComponentConfigured(component)) {
                                    valid = false;
                                }
                            });
                        }

                        if (angular.equals(instruction.gameInstructionKey, 'tweet')) {
                            valid = false;
                        }
                        if (valid) {
                            out.push(instruction);
                        }
                    });
                    instructions = out;

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

            return defer.promise;
        },
        /**
         * @ngdoc method
         * @name clearInstructions
         * @methodOf gamification-V2_2.service:qmGamificationService
         * @description
         * Clear all instructions for force reload
         *
         */
        clearInstructions: function() {
            instructions = [];
        },
        /**
         * @ngdoc method
         * @name gameSubmit
         * @methodOf gamification-V2_2.service:qmGamificationService
         * @description
         * Submit game activity
         *
         * @returns {promise} Promise that resolves when game activity response been retrieved
         */
        gameSubmit: function(gameActivityId, code) {
            var defer = $q.defer();

            var gameSubmitUrl = qmEventConfig.getRpcUrl('gameSubmit');
            var params = [];
            params.push(qmLogin.getUserToken());
            params.push(gameActivityId);
            params.push(code);
            params.push(qmLocalization.getLocale());

            qmRpc.post(gameSubmitUrl, {method: 'gameSubmit', params: params}).then(function(response) {
                if (response.display) {
                    toastr.info(response.text, response.headerNote, {
                        closeButton: true,
                        timeOut: 3000,
                        preventDuplicates: true
                    });
                }
                defer.resolve(response);
            }, function(err) {
                toastr.error(err.error, '', {
                    closeButton: true,
                    timeOut: 3000,
                    preventDuplicates: true
                });
                defer.resolve(err);

            });

            return defer.promise;
        },
        /**
         * @ngdoc method
         * @name getLeaderboard
         * @methodOf gamification-V2_2.service:qmGamificationService
         * @description
         * Get leaderboard
         *
         * @returns {promise} Promise that resolves when leaderboard list has been retrieved
         */
        getLeaderboard: function() {
            var defer = $q.defer();

            qmLogin.checkLogin().then(function() {
                var getGameLeaderboardUrl = qmEventConfig.getRpcUrl('getGameLeaderboard');
                var params = [];
                params.push(qmLogin.getUserToken());
                params.push(500);
                params.push(qmLocalization.getLocale());

                qmRpc.post(getGameLeaderboardUrl, {method: 'getGameLeaderboard', params: params}).then(function(response) {
                    defer.resolve(response);
                }, function(err) {
                    defer.reject(err);

                });
            });

            return defer.promise;
        },
        /**
         * @ngdoc method
         * @name getSearchLeaderboard
         * @methodOf gamification-V2_2.service:qmGamificationService
         * @description
         * Search attendees on leaderboard
         *
         * @returns {promise} Promise that resolves when leaderboard list has been retrieved
         */
        getSearchLeaderboard: function(terms, timeout) {
            init.clearSearchLeaderboardPromise();
            searchDefer = $q.defer();

            qmLogin.checkLogin().then(function() {
                var searchGameLeaderboardUrl = qmEventConfig.getRpcUrl('searchGameLeaderboard');
                var params = [];
                params.push(qmLogin.getUserToken());
                params.push(terms);
                params.push(500);
                params.push(qmLocalization.getLocale());

                qmRpc.post(searchGameLeaderboardUrl, {method: 'searchGameLeaderboard', params: params}, timeout).then(function(response) {
                    searchDefer.resolve(response);
                }, function(err) {
                    searchDefer.reject(err);

                });
            });

            return searchDefer.promise;
        },
        /**
         * @ngdoc method
         * @name clearSearchLeaderboardPromise
         * @methodOf gamification-V2_2.service:qmGamificationService
         * @description
         * Clear promise search leader board rpc
         *
         */
        clearSearchLeaderboardPromise: function() {
            if (searchDefer) {
                searchDefer.reject();
            }
        },
        /**
         * @ngdoc method
         * @name getQuizList
         * @methodOf gamification-V2_2.service:qmGamificationService
         * @description
         * Get list of quizzes for gamification
         *
         * @returns {promise} Promise that resolves when quizzes list has been retrieved
         */
        getQuizList: function() {
            var defer = $q.defer();
            if (qmDependencyManager.hasComponent('surveys')) {
                qmGamificationDependencyService.getSurveysService('qmSurveysService').getGameCenterQuizList().then(function(response) {
                    defer.resolve({quizzes: response, success: true});
                }, function(err) {
                    defer.resolve({quizzes: [], success: false, msg: err});
                });
            } else {
                defer.resolve({quizzes: [], success: false});
            }

            return defer.promise;
        },
        /**
         * @ngdoc method
         * @name getSetting
         * @methodOf gamification-V2_2.service:qmGamificationService
         * @description
         * Get settings for this component
         *
         * @returns {object} Settings object
         */
        getSetting: function(keyName) {
            if (config === null) {
                config = qmEventConfig.getComponentConfig('gamification');
            }
            if (keyName in config['@attributes']) {

                return config['@attributes'][keyName];
            } else {

                return null;
            }
        },
        /**
         * @ngdoc method
         * @name stringFormat
         * @methodOf gamification-V2_2.service:qmGamificationService
         * @description
         * Get format string
         *
         * @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 checkGameQuiz
         * @methodOf gamification-V2_2.service:qmGamificationService
         * @description
         * Check if quiz linked to gamification
         *
         * @returns {promise} success promise if quiz linked to gamification
         */
        checkGameQuiz: function(surveySessionId) {
            var defer = $q.defer();
            var url = qmWebAppService.getBaseRestUrl('component', 'gamification') + '/surveys/' + surveySessionId + '/quiz-status';
            qmRest.get(url).then(function(response) {
                if (response.gameQuiz) {
                    defer.resolve(true);
                } else {
                    defer.reject(false);
                }

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

            return defer.promise;
        },
        /**
         * @ngdoc method
         * @name checkGameActivity
         * @methodOf gamification-V2_2.service:qmGamificationService
         * @description
         * Check if game activity is valid
         *
         * @returns {promise} success promise if game activity is valid
         */
        checkGameActivity: function(gameActivity) {
            var defer = $q.defer();
            var url = qmWebAppService.getBaseRestUrl('component', 'gamification') + '/activity-types';
            qmRest.get(url).then(function(response) {
                if (response.indexOf(gameActivity) !== -1) {
                    defer.resolve(gameActivity);
                } else {
                    defer.reject(gameActivity);
                }

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

            return defer.promise;
        },
        /**
         * @ngdoc method
         * @name checkGameActivityAndSubmit
         * @methodOf gamification-V2_2.service:qmGamificationService
         * @description
         * Check if game activity is valid and submit game activity
         */
        checkGameActivityAndSubmit: function(args) {
            init.checkGameActivity(args.activity).then(function() {
                if (args && 'code' in args) {
                    init.gameSubmit(args.activity, args.code);
                } else {
                    init.gameSubmit(args.activity, null);
                }
            });
        },
        /**
         * @ngdoc method
         * @name checkSurveyTypeAndSubmit
         * @methodOf gamification-V2_2.service:qmGamificationService
         * @description
         * Check if quiz is a game quiz
         */
        checkSurveyTypeAndSubmit: function(args) {
            if ('surveyType' in args && 'surveySessionId' in args) {
                var sentArgs = {activity: 'quiz', code: args.surveySessionId};
                if (args.surveyType === 'quiz') {
                    // check if quiz is game quiz
                    init.checkGameQuiz(args.surveySessionId).then(function() {
                        init.checkGameActivityAndSubmit(sentArgs);
                    });
                } else if (args.surveyType === 'survey') {
                    sentArgs.activity = 'allSurvey';
                    sentArgs.code = args.surveySessionId;
                    init.checkGameActivityAndSubmit(sentArgs);
                }
            }
        },
        /**
         * @ngdoc method
         * @name addEventListener
         * @methodOf gamification-V2_2.service:qmGamificationService
         * @description
         * Add game event listeners
         */
        addEventListener: function() {
            $rootScope.$on('gameActivity', function(event, args) {
                if (args && 'activity' in args && qmEventConfig.isComponentConfigured('gamification') && qmLogin.isLoggedIn()) {
                    init.checkGameActivityAndSubmit(args);
                }
            });
            // Survey submit
            $rootScope.$on('surveySubmitted', function(event, args) {
                if (args && qmEventConfig.isComponentConfigured('gamification') && qmLogin.isLoggedIn()) {
                    init.checkSurveyTypeAndSubmit(args);
                }
            });

        }
    };

    return init;
}]);

var gamification = angular.module('gamification-V2_2');

/**
 * @ngdoc service
 * @name gamification-V2_2.service:qmGamificationDependencyService
 * @description
 * Manages communication with dependency manager
 */
gamification.factory('qmGamificationDependencyService', ['qmDependencyManager', function(qmDependencyManager) {
    return {
        /**
         * @ngdoc method
         * @name getSurveysService
         * @methodOf gamification-V2_2.service:qmGamificationDependencyService
         * @description
         * Get service from surveys component
         *
         * @param {string} service Service name
         * @returns {service} Angular service or null
         */
        getSurveysService: function(service) {
            return qmDependencyManager.getService('surveys', '2.2', 'component', service);
        }
    };
}]);

var gamification = angular.module('gamification-V2_2');

/**
 * @ngdoc controller
 * @name gamification-V2_2.controller:GamificationMainController
 * @description
 * Controller for Gamification home view
 */
gamification.controller('GamificationMainController', ['$scope', '$rootScope', 'qmLocalization', 'qmGamificationService',
    function($scope, $rootScope, qmLocalization, qmGamificationService) {
        $scope.panes = [];
        qmGamificationService.clearInstructions();
        $scope.loadAvailabilities = qmGamificationService.getAvailabilities();
        $scope.loadAvailabilities.then(function(availabilities) {
            var lastActiveTab = qmGamificationService.getTab();
            var panes = [];
            if (availabilities.instructions) {
                panes.push({
                    title: qmLocalization.getString('LABEL_GAME_INSTRUCTIONS'),
                    partial: '/asset/component/gamification/2.2/webapp/html/gamification-instructions.html',
                    controller: 'GamificationInstructionsController',
                    isActive: true
                });
            }
            if (availabilities.qrZone) {
                panes.push({
                    title: qmLocalization.getString('LABEL_GAME_QR_ZONE'),
                    partial: '/asset/component/gamification/2.2/webapp/html/gamification-qr-zone.html',
                    controller: 'GamificationQrZoneController',
                    isActive: false
                });
            }
            if (availabilities.quizzes) {
                panes.push({
                    title: qmLocalization.getString('LABEL_QUIZZES'),
                    partial: '/asset/component/gamification/2.2/webapp/html/gamification-quizzes.html',
                    controller: 'GamificationQuizzesController',
                    isActive: (lastActiveTab === 'quizzes')
                });
            }
            if (availabilities.leaderboard) {
                panes.push({
                    title: qmLocalization.getString('LABEL_GAME_LEADERBOARD'),
                    partial: '/asset/component/gamification/2.2/webapp/html/gamification-leaderboard.html',
                    controller: 'GamificationLeaderboardController',
                    isActive: false
                });
            }
            $scope.panes = panes;

        });

        $scope.changeTitle = function(paneIndex) {
            $rootScope.$broadcast('updateTitle', {title: $scope.panes[paneIndex].title});

        }
        ;
    }]);

/**
 * @ngdoc controller
 * @name gamification-V2_2.controller:GamificationMainHeaderController
 * @description
 * Controller for Gamification home title view
 */
gamification.controller('GamificationMainHeaderController', ['$scope', 'qmLocalization', function($scope, qmLocalization) {
    $scope.headerTitle = qmLocalization.getString('componentGamificationTitle');
    $scope.$on('updateTitle', function(event, args) {
        if (args.title !== '') {
            $scope.headerTitle = args.title;
        }
    });
}]);

var gamification = angular.module('gamification-V2_2');

/**
 * @ngdoc controller
 * @name gamification-V2_2.controller:GamificationInstructionsController
 * @description
 * Controller for Gamification  instructions view
 */
gamification.controller('GamificationInstructionsController', ['$scope', 'qmGamificationService', 'qmList',
    function($scope, qmGamificationService, qmList) {
        $scope.instructions = [];
        $scope.loadInstructions = qmGamificationService.getInstructions();
        $scope.loadInstructions.then(function(instructions) {
            $scope.instructions = qmList.transformList(instructions);
        });
    }]);

