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

/**
 * @ngdoc service
 * @name exhibitors-V2_3.service:qmExhibitorDeepLinkingService
 * @description
 * Manages Exhibitor Deep Linking
 */
exhibitors.factory('qmExhibitorDeepLinkingService', ['$state', 'qmExhibitorsService', 'qmExhibitorsDependencyService', '$rootScope',
    function($state, qmExhibitorsService, qmExhibitorsDependencyService, $rootScope) {
        var init = {
            /**
             * @ngdoc method
             * @name handleDeeplinkState
             * @methodOf exhibitors-V2_3.service:qmExhibitorDeepLinkingService
             * @description
             * Get state for exhibitor component deep link
             *
             * @param {string} type Type of component
             * @param {string} entity Entity key
             */
            handleDeepLinkState: function(type, entity) {
                var qmDeeplinkingService = qmExhibitorsDependencyService.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.exhibitors');
                } else {
                    qmExhibitorsService.getDetail(entity).then(function(exhibitorResponse) {
                        if (!exhibitorResponse) {
                            qmDeeplinkingService.handleUnavailableDeeplink(type, entity);
                            return;
                        }
                        $state.go('event.exhibitors.detail', {categoryId: null, exhibitorId: entity});
                    }, function(err) {
                        err.config.errorHandled = true;
                        if (err.status !== 401) {
                            qmDeeplinkingService.handleUnavailableDeeplink(type, entity);
                        }
                    });
                }
            }
        };

        return init;
    }]);

/**
 * Created by Arben Hajrizaj on 2019-05-10.
 */

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

/**
 * @ngdoc service
 * @name exhibitors-V2_4.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_4.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_4.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() {
                    var defer = $q.defer();
                    var url = qmWebAppService.getBaseRestUrl('component', 'exhibitors') + '/exhibitors' + '?locale=' + qmLocalization.getLocale();

                    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_4.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 url = qmWebAppService.getBaseRestUrl('component', 'exhibitors') + '/exhibitors';
                    var defer = $q.defer();

                    searchTerm = (typeof searchTerm === 'undefined') ? '' : searchTerm;
                    offset = (typeof offset === 'undefined') ? 0 : offset;
                    limit = (typeof limit === 'undefined') ? 0 : limit;

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

                    var params = {
                        locale:qmLocalization.getLocale(),
                        exhibitorIds: exhibitorIdList,
                        searchTerm:searchTerm,
                        offset:offset,
                        limit:limit
                    };

                    qmRest.post(url, params).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_4.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_4.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_4.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_4.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_4.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_4.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_4.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_4.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_4.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();

                    if (useDocuments && qmDependencyManager.hasComponent('documents')) {
                        var qmDocumentsService = qmExhibitorsDependencyService.getDocumentsService('qmDocumentsService');
                        // get all document ids (documents of type 'exhibitor') that are assigned to this exhibitor
                        // this will include the ids of documents that are currently locked for this exhibitor
                        init.getAllExhibitorDocumentIds(exhibitorId).then(function(allDocuments) {
                            // now get all the exhibitor documents that are assigned to this exhibitor including the
                            // ones that were unlocked (will not include locked documents)
                            qmDocumentsService.getExhibitorDocuments(exhibitorId).then(function(documents) {
                                var canUnlock = false;
                                var unlockedIds = [];
                                if (angular.isArray(documents)) {
                                    angular.forEach(documents, function(document) {
                                        unlockedIds.push(document.documentId);
                                    });
                                }
                                if ((allDocuments.ids.length > 0) && isLoggedIn) {
                                    canUnlock = !allDocuments.ids.every(function(value) {
                                        return unlockedIds.indexOf(value) !== -1;
                                    });

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

                    return defer.promise;
                },
                /**
                 * @ngdoc method
                 * @name getAllExhibitorDocumentIds
                 * @methodOf exhibitors-V2_4.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 documentsIds = [];
                        if (angular.isArray(data)) {
                            angular.forEach(data, function(item) {
                                documentsIds.push(item.documentId);
                            });
                        }
                        defer.resolve({ids: documentsIds, success: true});

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

                    return defer.promise;
                },
                /**
                 * @ngdoc method
                 * @name unlockExhibitorDocuments
                 * @methodOf exhibitors-V2_4.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_4.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_4.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_4.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_4.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;
                },
                /**
                 * @ngdoc method
                 * @name getExhibitorLikes
                 * @methodOf exhibitors-V2_4.service:qmExhibitorsService
                 * @description
                 * Get likes data and add them to exhibitor list view
                 *
                 * @param {Array} exhibitors list of Exhibitor
                 * @returns {promise} Promise that resolves when likes data have successfully been added
                 */
                getExhibitorLikes: function(exhibitors) {

                    var defer = $q.defer();
                    var qmSocialService = qmExhibitorsDependencyService.getSocialService('qmSocialService');
                    if (qmSocialService) {
                        return qmSocialService.getObjectSocialData(exhibitors, 'exhibitorId', 'exhibitors');
                    } else {
                        defer.reject();
                    }

                    return defer.promise;
                }

            };

            return init;
        }]);

/**
 * Created by Andrew Lachkovics on 2017-07-19
 */

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

/**
 * @ngdoc service
 * @name exhibitors-V2_4.service:qmExhibitorsDependencyService
 * @description
 * Manages communication with dependency manager
 */
exhibitors.factory('qmExhibitorsDependencyService', ['qmDependencyManager', function(qmDependencyManager) {
    return {
        /**
         * @ngdoc method
         * @name getTemplateUrl
         * @methodOf exhibitors-V2_4.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.3/webapp/html/partials/exhibitor-list-item.html';
            }

            return null;
        },
        /**
         * @ngdoc method
         * @name getMyExhibitorsService
         * @methodOf exhibitors-V2_4.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_4.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_4.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_4.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);
        },
        /**
         * @ngdoc method
         * @name getSocialService
         * @methodOf exhibitors-V2_4.service:qmExhibitorsDependencyService
         * @description
         * Get service from social component
         *
         * @param {string} service Service name
         * @returns {service} Angular service or null
         */
        getSocialService: function(service) {
            return qmDependencyManager.getService('social', '2.1', 'component', service);
        },
        /**
         * @ngdoc method
         * @name getSocialService
         * @methodOf exhibitors-V2_4.service:qmExhibitorsDependencyService
         * @description
         * Gets the deep linking service
         * NEW FUNCTION IN V2_4
         *
         * @param {string} name Template name
         * @returns {string} Template url
         */
        getDeepLinkingService: function(service) {
            return qmDependencyManager.getService('deep-linking', '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');
        }
    }]);

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

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

/**
 * @ngdoc controller
 * @name exhibitors-V2_4.controller:ExhibitorListController
 * @description
 * Controller for Exhibitors list view
 */
exhibitors.controller('ExhibitorListController',
    ['$scope', 'qmExhibitorsService', 'qmList', '$state', '$rootScope', 'qmLocalization', 'qmEventConfig', 'qmLogin', 'qmExhibitorsDependencyService',
        function($scope, qmExhibitorsService, qmList, $state, $rootScope, qmLocalization, qmEventConfig, qmLogin, qmExhibitorsDependencyService) {
            var qmSocialCheck;
            var socialConfig;
            var likesEnabled;

            $scope.exhibitorListItemUrl = qmExhibitorsDependencyService.getTemplateUrl('exhibitor-list-item');
            $scope.loadExhibitors = qmExhibitorsService.preLoadData();

            // Check to enable/disable qmSocialWidget within detail view
            qmSocialCheck = qmExhibitorsDependencyService.getSocialService('qmSocialService');
            if (qmSocialCheck) {
                socialConfig = qmEventConfig.getComponentConfig('social');
                likesEnabled = socialConfig['@attributes'].exhibitorLikes === 'true' ? true : false;
                $scope.enableLikes = likesEnabled && qmLogin.isLoggedIn();
            }

            $scope.loadExhibitors.then(function() {
                var exhibitorList = qmExhibitorsService.getExhibitors($state.params.categoryId);
                qmExhibitorsService.getExhibitorLikes(exhibitorList);

                $scope.exhibitors = qmList.groupByLetter(exhibitorList, '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_4.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');
}]);

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

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

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

        // Check to enable/disable qmSocialWidget within detail view
        var qmSocialCheck = qmExhibitorsDependencyService.getSocialService('qmSocialService');
        if (qmSocialCheck) {
            var socialConfig = qmEventConfig.getComponentConfig('social');
            var likesEnabled = socialConfig['@attributes'].exhibitorLikes === 'true' ? true : false;
            $scope.enableLikes = likesEnabled && qmLogin.isLoggedIn();
        }

        $scope.documentPin = {};
        $scope.documentPin.pinCode = '';
        $scope.documentsTitle = qmLocalization.getString('componentDocumentsTitle');
        $scope.loadExhibitor = qmExhibitorsService.getDetail($state.params.exhibitorId);
        $scope.whoLikesState = 'event.exhibitors.likes';
        $scope.stateAttrs = {resourceKey: $scope.socialResourceKey, exhibitorId: $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();
                    });
                };
            }

            var qmSocialConstant = qmExhibitorsDependencyService.getSocialService('SOCIAL');
            if (qmSocialConstant) {
                $scope.socialResourceKey = qmSocialConstant.resource.exhibitors;
            }
        });

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


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

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

/**
 * @ngdoc controller
 * @name exhibitors-V2_4.controller:ExhibitorLikesController
 * @description
 * Controller for exhibitor's who likes view
 */
exhibitors.controller('ExhibitorLikesController', ['$scope', '$state', 'qmExhibitorsDependencyService', 'qmEventConfig',
    function($scope, $state, qmExhibitorsDependencyService, qmEventConfig) {
        var qmSocialConstant = qmExhibitorsDependencyService.getSocialService('SOCIAL'),
            exhibitorsConfig,
            socialConfig,
            likesEnabled;
        $scope.exhibitorId = $state.params.exhibitorId;

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

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

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 gallery = angular.module('gallery-V2_5');

gallery.config(['$stateProvider', function($stateProvider) {
    $stateProvider
        .state('event.gallery-V2_5', {
            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_5.preview', {
            url: '/:galleryId',
            views: {
                'component@event': {
                    templateUrl: '/asset/component/gallery/2.5/webapp/html/gallery-preview.html',
                    controller: 'GalleryPreviewController'
                },
                'header@event': {
                    controller: 'GalleryPreviewHeaderController'
                }
            }
        })
        .state('event.gallery-V2_5.preview.detail', {
            url: '/:photoId',
            views: {
                'component@event': {
                    templateUrl: '/asset/component/gallery/2.5/webapp/html/gallery-detail.html',
                    controller: 'GalleryDetailController'
                },
                'header@event': {
                    controller: 'GalleryDetailHeaderController'
                }
            }
        })
        .state('event.gallery-V2_5.preview.likes', {
            url: '/Likes/:photoId',
            views: {
                'component@event': {
                    templateUrl: '/asset/component/gallery/2.5/webapp/html/gallery-likes.html',
                    controller: 'GalleryLikesController'
                },
                'header@event': {
                    controller: 'GalleryLikesHeaderController'
                }
            }
        })
        .state('event.gallery-V2_5.preview.comments', {
            url: '/Comments/:photoId',
            views: {
                'component@event': {
                    templateUrl: '/asset/component/gallery/2.5/webapp/html/gallery-comments.html',
                    controller: 'GalleryCommentsController'
                },
                'header@event': {
                    controller: 'GalleryCommentsHeaderController'
                }
            }
        })
        .state('event.gallery-V2_5.preview.comment-editor', {
            url: '/Comment/:photoId/:commentId',
            views: {
                'component@event': {
                    templateUrl: '/asset/component/gallery/2.5/webapp/html/gallery-comment-editor.html',
                    controller: 'GalleryCommentEditorController'
                },
                'header@event': {
                    controller: 'GalleryCommentEditorHeaderController'
                }
            }
        });
}]);

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

/**
 * @ngdoc service
 * @name gallery-V2_5.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 {
        unknownAttendeeImgSrc: '/asset/service/web-app/2.0/images/image_attendee_default.png',
        /**
         * @ngdoc method
         * @name getCommentListState
         * @methodOf gallery-V2_5.service:qmGalleryService
         * @description
         * Get comments list page state for UI router
         * * Get entity detail page state for UI router
         * NEW function for V2_5
         *
         * @returns {string} UI router state
         */
        getCommentListState: function() {
            return qmGalleryDependencyService.getCommentListState();
        },
        /**
         * @ngdoc method
         * @name getNewCommentState
         * @methodOf gallery-V2_5.service:qmGalleryService
         * @description
         * Get comments list page state for UI router
         * * Get entity detail page state for UI router
         * NEW function for V2_5
         *
         * @returns {string} UI router state
         */
        getNewCommentState: function() {
            return qmGalleryDependencyService.getNewCommentState();
        },
        /**
         * @ngdoc method
         * @name getDetailState
         * @methodOf gallery-V2_5.service:qmGalleryService
         * @description
         * Get entity detail page state for UI router
         * NEW function for V2_5
         *
         * @returns {string} UI router state
         */
        getDetailState: function() {
            return 'event.gallery.preview.detail';
        },
        /**
         * @ngdoc method
         * @name getPhotoSocialData
         * @methodOf gallery-V2_5.service:qmGalleryService
         * @description
         * Get likes & comments data and add them to photos
         * NEW function for V2_5
         *
         * @param {Array} List of photos
         * @returns {promise} Promise that resolves when likes & comments data have successfully been added
         */
        getPhotoSocialData: function(photos) {
            var defer = $q.defer();
            var qmSocialService = qmGalleryDependencyService.getSocialService('qmSocialService');
            if (qmSocialService) {

                return qmSocialService.getObjectSocialData(photos, 'photoId', 'photos');
            } else {
                defer.reject();
            }
            return defer.promise;
        },
        /**
         * @ngdoc method
         * @name getList
         * @methodOf gallery-V2_5.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_5.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 = [];
            var $this = this;
            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 currentPhoto = {};  // lets manually add props to this temp var so we remember to add auth tokens to photos
                    var hasPic = false;
                    var isAnon = !photoList[i].attendeePublish;
                    currentPhoto.photoId = angular.isDefined(photoList[i].photoId) ? photoList[i].photoId : '';
                    currentPhoto.title = angular.isDefined(photoList[i].title) ? photoList[i].title : '';
                    currentPhoto.comment = angular.isDefined(photoList[i].comment) ? photoList[i].comment : '';
                    currentPhoto.qmLastMod = angular.isDefined(photoList[i].qmLastMod) ? photoList[i].qmLastMod : '';
                    currentPhoto.attendeeId = angular.isDefined(photoList[i].attendeeId) ? photoList[i].attendeeId : '';
                    currentPhoto.attendeeFirstName = angular.isDefined(photoList[i].attendeeFirstName) ? photoList[i].attendeeFirstName : '';
                    currentPhoto.attendeeLastName = angular.isDefined(photoList[i].attendeeLastName) ? photoList[i].attendeeLastName : '';

                    // add auth tokens to all photos
                    currentPhoto.attendeeImageUrl = qmWebAppService.appendAuthHeader(photoList[i].attendeeImageUrl);
                    if (photoList[i].smallImageUrl && photoList[i].smallImageUrl !== '') {
                        currentPhoto.smallImageUrl = qmWebAppService.appendAuthHeader(photoList[i].smallImageUrl);
                        hasPic = true;
                    }
                    if (photoList[i].mediumImageUrl && photoList[i].mediumImageUrl !== '') {
                        currentPhoto.mediumImageUrl = qmWebAppService.appendAuthHeader(photoList[i].mediumImageUrl);
                        hasPic = true;
                    }
                    if (photoList[i].largeImageUrl && photoList[i].largeImageUrl !== '') {
                        currentPhoto.largeImageUrl = qmWebAppService.appendAuthHeader(photoList[i].largeImageUrl);
                        hasPic = true;
                    }
                    if (photoList[i].xlargeImageUrl && photoList[i].xlargeImageUrl !== '') {
                        currentPhoto.xlargeImageUrl = qmWebAppService.appendAuthHeader(photoList[i].xlargeImageUrl);
                        hasPic = true;
                    }

                    if (isAnon) {
                        currentPhoto.attendeeFirstName = 'Anonymous';
                        currentPhoto.attendeeLastName = '';
                        currentPhoto.attendeeImageUrl = qmWebAppService.appendAuthHeader($this.unknownAttendeeImgSrc);
                    }

                    if (hasPic) {
                        pics.push(currentPhoto);
                    }
                }
                defer.resolve(pics);
            }, function(err) {
                defer.reject(err);
            });

            return defer.promise;
        },
        /**
         * @ngdoc method
         * @name getPhotoDetail
         * @methodOf gallery-V2_5.service:qmGalleryService
         * @description
         * Get details of a photo
         *
         * @param {string} photoId Photo Id
         * @returns {promise} Promise that resolves when gallery details has been retrieved
         */
        getPhotoDetail: function(photoId) {
            var queryParams = [];
            queryParams.push('locale=' + qmLocalization.getLocale());
            var getDetailUrl = qmWebAppService.getBaseRestUrl('component', 'gallery') +
                '/photos/' + photoId +
                '?' + queryParams.join('&');
            var defer = $q.defer();

            qmRest.get(getDetailUrl).then(function(photoDetails) {
                defer.resolve(photoDetails);
            }, function(err) {
                defer.reject(err);
            });

            return defer.promise;
        },
        /**
         * @ngdoc method
         * @name flag
         * @methodOf gallery-V2_5.service:qmGalleryService
         * @description
         * Flag a photo
         *
         * @param {string} Photo id
         * @returns {promise} Promise that resolves when photo has successfully been flagged
         */
        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 likePhoto
         * @methodOf gallery-V2_5.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_5.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);
            }
        },
        /**
         * @ngdoc method
         * @name getPhotoLikes
         * @methodOf gallery-V2_5.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;
        }
    };
}]);

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

/**
 * @ngdoc service
 * @name gallery-V2_5.service:qmGalleryDependencyService
 * @description
 * Manages communication with dependency manager
 */
gallery.factory('qmGalleryDependencyService', ['qmDependencyManager', function(qmDependencyManager) {
    return {
        /**
         * @ngdoc method
         * @name getContentModeratorService
         * @methodOf gallery-V2_5.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_5.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);
        },
        /**
         * @ngdoc method
         * @name getCommentListState
         * @methodOf gallery-V2_5.service:qmGalleryDependencyService
         * @description
         * Get comments list page state for UI router
         * * Get entity detail page state for UI router
         * NEW function for V2_5
         *
         * @returns {string} UI router state
         */
        getCommentListState: function() {
            return 'event.gallery.preview.comments';
        },
        /**
         * @ngdoc method
         * @name getNewCommentState
         * @methodOf gallery-V2_5.service:qmGalleryDependencyService
         * @description
         * Get comments list page state for UI router
         * * Get entity detail page state for UI router
         * NEW function for V2_5
         *
         * @returns {string} UI router state
         */
        getNewCommentState: function() {
            return 'event.gallery.preview.comment-editor';
        },
        /**
         * @ngdoc method
         * @name getEditCommentState
         * @methodOf gallery-V2_5.service:qmGalleryDependencyService
         * @description
         * Get entity detail page state for UI router
         *
         * @returns {string} UI router state
         */
        getEditCommentState: function() {
            return 'event.gallery.preview.comment-editor';
        }
    };
}]);

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

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

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

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

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

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

/**
 * @ngdoc controller
 * @name gallery-V2_5.controller:GalleryDetailController
 * @description
 * Controller for gallery detail view
 */
gallery.controller('GalleryDetailController',
['$rootScope', '$scope', '$state', 'qmGalleryService', 'qmLocalization', 'toastr', 'qmAnalytics',
'qmGalleryDependencyService', 'qmEventConfig', 'qmWebAppService', 'qmLogin', '$q',
function($rootScope, $scope, $state, qmGalleryService, qmLocalization, toastr, qmAnalytics,
qmGalleryDependencyService, qmEventConfig, qmWebAppService, qmLogin, $q) {
    $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 = {};
    $scope.currentSlideLikeCount = '';
    $scope.galleryId = $state.params.galleryId;

    var emptyIconUrl = qmEventConfig.getArtifact('icon_main_gallery');
    var loginRequiredForAttendees = qmEventConfig.getComponentConfig('attendees')['@attributes']['requireLogin'] === 'true';

    $scope.showAttendeeInfo = loginRequiredForAttendees ? qmLogin.isLoggedIn() : true;
    $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');

    $scope.commentsState = qmGalleryDependencyService.getCommentListState();
    $scope.newCommentState = qmGalleryDependencyService.getNewCommentState();
    $scope.editCommentState = qmGalleryDependencyService.getEditCommentState();

    $scope.whoLikesState = 'event.gallery.preview.likes';
    $scope.stateAttrs = {photoId: $scope.currentSlide.photoId};

    var qmSocialService = qmGalleryDependencyService.getSocialService('qmSocialService');
    if (qmSocialService) {
        // 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;
    }

    var qmSocialConstant = qmGalleryDependencyService.getSocialService('SOCIAL');
    if (qmSocialConstant) {
        $scope.socialResourceKey = qmSocialConstant.resource.photos;
    }

    // Add like data to photo data
    $scope.addLikeData = function(photos) {
        return qmGalleryService.getPhotoSocialData(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) {
            var isCurrentSlideEmpty = Object.getOwnPropertyNames($scope.currentSlide).length === 0;
            $scope.addToSlideshow(photos);
            $scope.itemOffset = $scope.initialItems;

            if (isCurrentSlideEmpty) {
                var findPhoto = function() {
                    $scope.loadMorePhotos().finally(function() {
                        isCurrentSlideEmpty = Object.getOwnPropertyNames($scope.currentSlide).length === 0;
                        if ($scope.isLastItem) {
                            defer.resolve();
                            return;
                        }
                        if (isCurrentSlideEmpty) {
                            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.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.currentSlide.photoId = slide.$parent.slide.photoId;
        $scope.updateLikeCount($scope.currentSlide.likeCount);
        var photoId = slide.$parent.slide.photoId;
        var galleryId = $state.params.galleryId;
        qmAnalytics.markEvent('PhotoDetailsView', photoId, galleryId);
        $scope.stateAttrs = {photoId: $scope.currentSlide.photoId};
    };
}]);

/**
 * @ngdoc controller
 * @name gallery-V2_5.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_5');

/**
 * @ngdoc controller
 * @name gallery-V2_5.controller:GalleryPreviewController
 * @description
 * Controller for gallery preview view
 */
gallery.controller('GalleryPreviewController',
['$scope', '$state', 'qmGalleryService', 'qmLocalization', 'qmEventConfig', 'qmWebAppService',
'qmGalleryDependencyService', 'LIST', 'qmListDataService', 'qmLogin', '$document', '$window',
function($scope, $state, qmGalleryService, qmLocalization, qmEventConfig, qmWebAppService,
qmGalleryDependencyService, LIST, qmListDataService, qmLogin, $document, $window) {
    var qmSocialService;
    var socialConfig;
    var likesEnabled;
    var commentsEnabled;
    var photosPerLine = 7;
    var firstWideScreenWidth = 1200;
    var viewportWidth = Math.max($document[0].documentElement.clientWidth, $window.innerWidth || 0);
    var photoNum;
    var initialItems;
    var moreItems;
    var roundPhotosToFit = function(x, photosPerLine) {
        return Math.ceil(x / photosPerLine) * photosPerLine;
    };
    var addLikeDataToChunk = function(photos) {
        var photosToChunk = [].concat(photos);
        var chunkedPhotos = [];
        var photosPerChunk = 30;    // the safe amount for the social REST call
        var photosWithSocial;

        // split the list of photos into chunks so that we don't overwhelm the social REST call with too many ids
        $scope.chunkedPhotos = chunkedPhotos;
        while (photosToChunk.length > 0) {
            chunkedPhotos.push(photosToChunk.splice(0, photosPerChunk));
        }
        $scope.chunkedPhotos.map(function(chunk) {
            $scope.addLikeData(chunk);
        });
        if ($scope.chunkedPhotos.length > 0) {
            photosWithSocial = $scope.chunkedPhotos.reduce(function(acc, val) {
                return acc.concat(val);
            });
        } else {
            photosWithSocial = [];
        }

        return photosWithSocial;
    };

    // adjust number of initial and more photos for larger viewports
    if (viewportWidth >= firstWideScreenWidth) {
        var maxPhotoListWidth = $document[0].getElementsByClassName('list-view')[0].clientWidth;
        var viewportHeight = Math.max($document[0].documentElement.clientHeight, $window.innerHeight || 0);
        var gridCellWidth = maxPhotoListWidth / photosPerLine;
        var gridCellHeight = gridCellWidth;
        var gridCellArea = gridCellHeight * gridCellWidth;
        var photoListArea = viewportHeight * maxPhotoListWidth;

        photoNum = roundPhotosToFit(Math.round(photoListArea / gridCellArea), photosPerLine);
        moreItems = roundPhotosToFit(Math.round(photoNum), photosPerLine);
        initialItems = photoNum * 2;
    } else {
        moreItems = 25;
        initialItems = 50;
    }

    $scope.itemOffset = 0;
    $scope.initialItems = initialItems;
    $scope.moreItems = moreItems;

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

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

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

    $scope.loadGallery = qmGalleryService.getDetail($state.params.galleryId, $scope.itemOffset, $scope.initialItems);
    $scope.loadGallery.then(function(photos) {
        var photosWithSocial = addLikeDataToChunk(photos);

        $scope.photos = photosWithSocial;
        $scope.isEmptyList = $scope.photos.length === 0;
        $scope.itemOffset = $scope.initialItems;
        $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) {
                var morePhotosWithSocial = addLikeDataToChunk(moreItems);
                $scope.photos = $scope.photos.concat(morePhotosWithSocial);
                $scope.itemOffset += $scope.moreItems;
            } else {
                // we reached the end of the list
                qmListDataService.setInfiniteScroll(false);
            }
        }).finally(function() {
            callback();
        });
    });
}]);

/**
 * @ngdoc controller
 * @name gallery-V2_5.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';
}]);

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

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

/**
 * @ngdoc controller
 * @name gallery-V2_5.controller:GalleryLikesController
 * @description
 * Controller for a photo's who likes view
 */
gallery.controller('GalleryLikesController', ['$scope', '$state', 'qmGalleryDependencyService', 'qmEventConfig',
    function($scope, $state, qmGalleryDependencyService, qmEventConfig) {
        var qmSocialConstant = qmGalleryDependencyService.getSocialService('SOCIAL'),
            galleriesConfig,
            socialConfig,
            likesEnabled;
        $scope.photoId = $state.params.photoId;
        $scope.newCommentState = qmGalleryDependencyService.getNewCommentState();
        $scope.editCommentState = qmGalleryDependencyService.getEditCommentState();

        $scope.stateAttrs = {photoId: $state.params.photoId};
        if (qmSocialConstant) {
            galleriesConfig = qmEventConfig.getComponentConfig('gallery');
            socialConfig = qmEventConfig.getComponentConfig('social');
            likesEnabled = socialConfig['@attributes'].photoLikes === 'true';
            $scope.socialResourceKey = qmSocialConstant.resource.photos;
            if ($state.params.photoId === '' || $state.params.photoId === 'undefined' || !likesEnabled) {
                $state.go('event.gallery', {componentId: galleriesConfig['@attributes'].id}, {reload: true});
            }
        }

        // go to the comment editor to edit a new comment
        $scope.goToCommentEditor = function() {
            $state.go($scope.newCommentState, {photoId: $scope.photoId});
        };
    }]);

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

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

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

/**
 * @ngdoc controller
 * @name gallery-V2_5.controller:GalleryCommentsController
 * @description
 * Controller for event's comment list view
 */
gallery.controller('GalleryCommentsController', ['$scope', '$state', 'qmGalleryDependencyService', 'qmEventConfig', 'qmLogin', 'qmNavigation',
    function($scope, $state, qmGalleryDependencyService, qmEventConfig, qmLogin, qmNavigation) {
        var qmSocialConstant = qmGalleryDependencyService.getSocialService('SOCIAL');
        var qmSocialCheck = qmGalleryDependencyService.getSocialService('qmSocialService');
        var socialConfig;
        var commentsEnabled;
        $scope.photoId = $state.params.photoId;
        $scope.socialResourceKey = qmSocialConstant ? qmSocialConstant.resource.photos : null;
        $scope.newCommentState = qmGalleryDependencyService.getNewCommentState();
        $scope.editCommentState = qmGalleryDependencyService.getEditCommentState();
        $scope.stateAttrs = {photoId: $state.params.photoId};

        if (qmSocialCheck) {
            socialConfig = qmEventConfig.getComponentConfig('social');
            commentsEnabled = socialConfig['@attributes'].photoComments === 'true';
            $scope.enableComments = commentsEnabled && qmLogin.isLoggedIn();
            if (!$scope.enableComments) {
                qmNavigation.goBack();
            }
        }
    }]);

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

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

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

/**
 * @ngdoc controller
 * @name gallery-V2_5.controller:GalleryCommentEditorController
 * @description
 * Controller for gallery's comment editor view for adding or editing comments
 */
gallery.controller('GalleryCommentEditorController', ['$scope', '$rootScope', '$state', 'qmGalleryDependencyService',
    function($scope, $rootScope, $state, qmGalleryDependencyService) {
        var qmSocialConstant = qmGalleryDependencyService.getSocialService('SOCIAL');
        $scope.commentId = $state.params.commentId;
        $scope.socialResourceKey = qmSocialConstant.resource.photos;
        $scope.entityId = $state.params.photoId;
    }]);

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

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

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

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

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

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

var htmlpageview = angular.module('html-page-view-V2_0');

htmlpageview.config(['$stateProvider', function($stateProvider) {
    $stateProvider
        .state('event.html-page-view-V2_0', {
            url: '/HtmlPageView',
            views: {
                'component@event': {
                    templateUrl: '/asset/component/html-page-view/2.0/webapp/html/html-page-view-detail.html',
                    controller: 'HtmlPageViewDetailController'
                },
                'header@event': {
                    controller: 'HtmlPageViewDetailHeaderController'
                }
            }
        });
}]);

var htmlpageview = angular.module('html-page-view-V2_0');

/**
 * @ngdoc service
 * @name html-page-view-V2_0.service:qmHtmlPageViewService
 * @description
 * Manages REST API communication for Htmlpageview
 */
htmlpageview.factory('qmHtmlPageViewService', ['qmRest', '$q', 'qmWebAppService',
function(qmRest, $q, qmWebAppService) {
    return {
        /**
         * @ngdoc method
         * @name getDetail
         * @methodOf html-page-view-V2_0.service:qmHtmlPageViewService
         * @description
         * Get the body of the htmlpageview
         *
         * @returns {promise} Promise that resolves when htmlpageview body has been retrieved
         */
        getDetail: function() {
            var url = qmWebAppService.getBaseRestUrl('component', 'html-page-view') + '/html-page-view';
            var defer = $q.defer();
            // get htmlpageview body
            qmRest.get(url).then(function(data) {
                defer.resolve(data);
            }, function(err) {
                defer.reject(err);
            });

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

var htmlpageview = angular.module('html-page-view-V2_0');

/**
 * @ngdoc controller
 * @name html-page-view-V2_0.controller:HtmlPageViewDetailController
 * @description
 * Controller for htmlpageview detail view
 */
htmlpageview.controller('HtmlPageViewDetailController', ['$scope', 'qmHtmlPageViewService',
function($scope, qmHtmlPageViewService) {
    $scope.loadHtmlPageView = qmHtmlPageViewService.getDetail();
    $scope.loadHtmlPageView.then(function(htmlPageView) {
        $scope.htmlPageView = htmlPageView;
    });
}]);

/**
 * @ngdoc controller
 * @name html-page-view-V2_0.controller:HtmlPageViewDetailHeaderController
 * @description
 * Controller for htmlpageview detail header view
 */
htmlpageview.controller('HtmlPageViewDetailHeaderController', ['$scope', 'qmLocalization', function($scope, qmLocalization) {
    $scope.headerBackState = false;
    $scope.headerTitle = qmLocalization.getString('componentHtmlpageviewTitle');
}]);

var infobooths = angular.module('info-booths-V2_0');

infobooths.config(['$stateProvider', function($stateProvider) {
    $stateProvider
        .state('event.info-booths-V2_0', {
            url: '/InfoBooths',
            views: {
                'component@event': {
                    templateUrl: '/asset/component/info-booths/2.0/webapp/html/infobooth-list.html',
                    controller: 'InfoBoothListController'
                },
                'header@event': {
                    controller: 'InfoBoothListHeaderController'
                }
            }
        })
        .state('event.info-booths-V2_0.detail', {
            url: '/:infoBoothId',
            views: {
                'component@event': {
                    templateUrl: '/asset/component/info-booths/2.0/webapp/html/infobooth-detail.html',
                    controller: 'InfoBoothDetailController'
                },
                'header@event': {
                    controller: 'InfoBoothDetailHeaderController'
                }
            }
        });
}]);

var infobooths = angular.module('info-booths-V2_0');

/**
 * @ngdoc service
 * @name info-booths-V2_0.service:qmInfoBoothService
 * @description
 * Manages REST API communication for InfoBooths
 */
infobooths.factory('qmInfoBoothService', ['qmRest', '$q', 'qmWebAppService', 'qmLocalization',
function(qmRest, $q, qmWebAppService, qmLocalization) {

    return {
        /**
         * @ngdoc method
         * @name getList
         * @methodOf info-booths-V2_0.service:qmInfoBoothService
         * @description
         * Get list of infobooths
         *
         * @returns {promise} Promise that resolves when infobooth list has been retrieved
         */
        getList: function() {
            var url = qmWebAppService.getBaseRestUrl('component', 'info-booths') + '/info-booths?locale=' + qmLocalization.getLocale();
            var defer = $q.defer();
            // get infobooth list data
            qmRest.get(url).then(function(data) {
                var infoboothList = data;
                defer.resolve(infoboothList);
            }, function(err) {
                defer.reject(err);
            });

            return defer.promise;
        },
        /**
         * @ngdoc method
         * @name getDetail
         * @methodOf info-booths-V2_0.service:qmInfoBoothService
         * @description
         * Get details of an infobooth
         *
         * @param {string} infoBoothId InfoBooth Id
         * @returns {promise} Promise that resolves when infobooth details has been retrieved
         */
        getDetail: function(infoBoothId) {
            var url = qmWebAppService.getBaseRestUrl('component', 'info-booths') + '/info-booths/' + infoBoothId + '?locale=' + qmLocalization.getLocale();
            var defer = $q.defer();
            // get infobooth list data
            qmRest.get(url).then(function(data) {
                defer.resolve(data);
            }, function(err) {
                defer.reject(err);
            });

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

var infobooths = angular.module('info-booths-V2_0');

/**
 * @ngdoc controller
 * @name info-booths-V2_0.controller:InfoBoothListController
 * @description
 * Controller for infobooth list view
 */
infobooths.controller('InfoBoothListController', ['$scope', '$state', 'qmInfoBoothService', 'qmList', 'qmUtilities', 'qmLocalization',
    function($scope, $state, qmInfoBoothService, qmList, qmUtilities, qmLocalization) {
        $scope.loadInfoBooths = qmInfoBoothService.getList();
        $scope.loadInfoBooths.then(function(infobooths) {
            $scope.infobooths = qmList.transformList(infobooths);
            var componentTitle = qmLocalization.getString('componentInfoboothsTitle');
            $scope.emptyText = qmLocalization.getString('LABEL_EMPTY_INFO_BOOTH', componentTitle);
        });

        $scope.checkLink = function(item) {
            if (item.infoBooth.WebSiteUrl) {
                // Redirect to defined website URL
                qmUtilities.openUrl(qmUtilities.generateValidUrl(item.infoBooth.WebSiteUrl));
            } else if (item.infoBooth.defaultUrl) {
                // Redirect to default URL
                qmUtilities.openUrl(qmUtilities.generateValidUrl(item.infoBooth.defaultUrl));
            } else {
                // Redirect to InfoBooth detail
                $state.go('event.info-booths.detail', {'infoBoothId': item.infoBooth.infoBoothId});
            }
        };
    }
]);

/**
 * @ngdoc controller
 * @name info-booths-V2_0.controller:InfoBoothListHeaderController
 * @description
 * Controller for infobooth list header view
 */
infobooths.controller('InfoBoothListHeaderController', ['$scope', 'qmLocalization', function($scope, qmLocalization) {
    $scope.headerTitle = qmLocalization.getString('componentInfoboothsTitle');
    $scope.headerTitleTestId = 'componentTitle';
}]);

var infobooths = angular.module('info-booths-V2_0');

/**
 * @ngdoc controller
 * @name info-booths-V2_0.controller:InfoBoothDetailController
 * @description
 * Controller for infobooth detail view
 */
infobooths.controller('InfoBoothDetailController', ['$scope', '$state', 'qmInfoBoothService',
function($scope, $state, qmInfoBoothService) {
    $scope.loadInfoBooth = qmInfoBoothService.getDetail($state.params.infoBoothId);
    $scope.loadInfoBooth.then(function(infobooth) {
        $scope.infobooth = infobooth;
    });
}]);

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

var interactiveMaps = angular.module('interactive-maps-V2_1');

interactiveMaps.config(['$stateProvider', function($stateProvider) {
    $stateProvider
        .state('event.interactive-maps-V2_1', {
            url: '/Maps',
            views: {
                'component@event': {
                    controller: 'InteractiveMapMainListController'
                }
            }
        })
        .state('event.interactive-maps-V2_1.list', {
            url: '',
            views: {
                'component@event': {
                    templateUrl: '/asset/component/interactive-maps/2.1/webapp/html/interactive-maps-list.html',
                    controller: 'InteractiveMapListController'
                },
                'header@event': {
                    controller: 'InteractiveMapListHeaderController'
                }
            }
        })
        .state('event.interactive-maps-V2_1.detail', {
            url: '/:mapId/:landmarkId/:eventId/:exhibitorId',
            views: {
                'component@event': {
                    templateUrl: '/asset/component/interactive-maps/2.1/webapp/html/interactive-maps-detail.html',
                    controller: 'InteractiveMapDetailController'
                },
                'header@event': {
                    controller: 'InteractiveMapDetailHeaderController'
                }
            }
        })
        .state('event.interactive-maps-V2_1.detail.search', {
            url: '/Search',
            views: {
                'component@event': {
                    templateUrl: '/asset/component/interactive-maps/2.1/webapp/html/interactive-maps-search.html',
                    controller: 'InteractiveMapSearchController'
                },
                'header@event': {
                    controller: 'InteractiveMapSearchHeaderController'
                }
            }
        })
        .state('event.interactive-maps-V2_1.detail.landmark', {
            url: '',
            params: {
                listLandmarkId: null
            },
            views: {
                'component@event': {
                    controller: 'InteractiveMapLandmarkController'
                }
            }
        })
        .state('event.interactive-maps-V2_1.detail.landmark.list', {
            url: '/Landmark/:listLandmarkId',
            views: {
                'component@event': {
                    templateUrl: '/asset/component/interactive-maps/2.1/webapp/html/interactive-maps-landmark-list.html',
                    controller: 'InteractiveMapLandmarkListController'
                },
                'header@event': {
                    controller: 'InteractiveMapLandmarkListHeaderController'
                }
            }
        });
}]);

var interactiveMaps = angular.module('interactive-maps-V2_1');

/**
 * @ngdoc service
 * @name interactive-maps-V2_1.service:qmInteractiveMapsService
 * @description
 * Manages REST API communication for InteractiveMaps
 */
interactiveMaps.factory('qmInteractiveMapsService',
['qmRest', '$q', 'qmLogin', 'qmInteractiveMapsDependencyService', 'qmWebAppService',
function(qmRest, $q, qmLogin, qmInteractiveMapsDependencyService, qmWebAppService) {

    return {
        /**
         * @ngdoc method
         * @name getList
         * @methodOf interactive-maps-V2_1.service:qmInteractiveMapsService
         * @description
         * Get list of maps
         *
         * @returns {promise} Promise that resolves when map list has been retrieved
         */
        getList: function() {
            var url = qmWebAppService.getBaseRestUrl('component', 'interactive-maps') + '/maps';
            var defer = $q.defer();
            // get map list data
            qmRest.get(url).then(function(data) {
                var maps = [];

                // Add the token to the maps image url
                angular.forEach(data, function(map) {
                    if (map.imageURL && map.imageURL !== '') {
                        map.imageSrc = qmWebAppService.appendAuthHeader(map.imageURL);
                    }

                    if (map.type !== 'SEATING') {
                        maps.push(map);
                    }
                });

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

            return defer.promise;
        },
        /**
         * @ngdoc method
         * @name getDetail
         * @methodOf interactive-maps-V2_1.service:qmInteractiveMapsService
         * @description
         * Get details of a map
         *
         * @param {string} mapId Map Id
         * @returns {promise} Promise that resolves when map details has been retrieved
         */
        getDetail: function(mapId) {
            var url = qmWebAppService.getBaseRestUrl('component', 'interactive-maps') + '/maps/' + mapId;
            var defer = $q.defer();
            // get map detail data
            qmRest.get(url).then(function(data) {
                if (data.imageURL && data.imageURL !== '') {
                    data.imageSrc = qmWebAppService.appendAuthHeader(data.imageURL);
                }

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

            return defer.promise;
        },
        /**
         * @ngdoc method
         * @name getLandmarks
         * @methodOf interactive-maps-V2_1.service:qmInteractiveMapsService
         * @description
         * Get list of landmarks for a map
         *
         * @param {string} mapId Map Id
         * @returns {promise} Promise that resolves when landmark list has been retrieved
         */
        getLandmarks: function(mapId) {
            var url = qmWebAppService.getBaseRestUrl('component', 'interactive-maps') + '/maps/' + mapId + '/landmarks';
            var defer = $q.defer();
            // get landmarks
            qmRest.get(url).then(function(data) {
                defer.resolve(data);
            }, function(err) {
                defer.reject(err);
            });

            return defer.promise;
        },
        /**
         * @ngdoc method
         * @name getLandmark
         * @methodOf interactive-maps-V2_1.service:qmInteractiveMapsService
         * @description
         * Get a landmarks for a map
         *
         * @param {string} mapId Map Id
         * @param {string} landmarkId Landmark Id
         * @returns {promise} Promise that resolves when landmark has been retrieved
         */
        getLandmark: function(mapId, landmarkId) {
            var url = qmWebAppService.getBaseRestUrl('component', 'interactive-maps') + '/maps/' + mapId + '/landmarks/' + landmarkId;
            var defer = $q.defer();
            // get landmark
            qmRest.get(url).then(function(data) {
                if (!angular.isArray(data)) {
                    data = [data];
                }
                defer.resolve(data);
            }, function(err) {
                defer.reject(err);
            });

            return defer.promise;
        },
        /**
         * @ngdoc method
         * @name getMapEvents
         * @methodOf interactive-maps-V2_1.service:qmInteractiveMapsService
         * @description
         * Get the list of events for a map
         *
         * @param {string} mapId Map Id
         * @param {string} landmarkId Landmark Id
         * @returns {promise} Promise that resolves when the sessions have been retrieved
         */
        getMapEvents: function(mapId, landmarkId) {
            var url;

            if (angular.isDefined(landmarkId)) {
                url = qmWebAppService.getBaseRestUrl('component', 'interactive-maps') + '/maps/' + mapId + '/landmarks/' + landmarkId + '/events';
            } else {
                url = qmWebAppService.getBaseRestUrl('component', 'interactive-maps') + '/maps/' + mapId + '/events';
            }

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

                var eventIds = [];
                if (angular.isDefined(data)) {
                    for (var i = 0; i < data.length; i++) {
                        eventIds.push(encodeURIComponent(data[i].eventId));
                    }
                }
                // Get Events
                var eventList = [];
                if (eventIds.length !== 0) {
                    var qmEventsService = qmInteractiveMapsDependencyService.getEventsService('qmEventsService');
                    eventList = qmEventsService.getList(eventIds);
                }

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

            return defer.promise;
        },
        /**
         * @ngdoc method
         * @name getMapExhibitors
         * @methodOf interactive-maps-V2_1.service:qmInteractiveMapsService
         * @description
         * Get the list of exhibitors for a map
         *
         * @param {string} mapId Map Id
         * @param {string} landmarkId Landmark Id
         * @returns {promise} Promise that resolves when the sessions have been retrieved
         */
        getMapExhibitors: function(mapId, landmarkId) {
            var url;

            if (angular.isDefined(landmarkId)) {
                url = qmWebAppService.getBaseRestUrl('component', 'interactive-maps') + '/maps/' + mapId + '/landmarks/' + landmarkId + '/exhibitors';
            } else {
                url = qmWebAppService.getBaseRestUrl('component', 'interactive-maps') + '/maps/' + mapId + '/exhibitors';
            }

            var defer = $q.defer();
            // get exhibitor ids
            qmRest.get(url).then(function(data) {
                var exhibitorIds = data;

                // Get Exhibitors
                var exhibitorList = [];
                if (exhibitorIds.length !== 0) {
                    var qmExhibitorsService = qmInteractiveMapsDependencyService.getExhibitorsService('qmExhibitorsService');
                    exhibitorList = qmExhibitorsService.getList(exhibitorIds);
                }

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

            return defer.promise;
        },
        /**
         * @ngdoc method
         * @name getListEventLandmarks
         * @methodOf interactive-maps-V2_1.service:qmInteractiveMapsService
         * @description
         * Get list of landmarks for an event
         *
         * @param {string} eventId Event Id
         * @returns {promise} Promise that resolves when landmark list has been retrieved
         */
        getListEventLandmarks: function(eventId) {
            var url = qmWebAppService.getBaseRestUrl('component', 'interactive-maps') + '/events/' + eventId + '/landmarks';

            var defer = $q.defer();
            qmRest.get(url).then(function(response) {
                defer.resolve(response);

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

            return defer.promise;
        },
        /**
         * @ngdoc method
         * @name getListExhibitorLandmarks
         * @methodOf interactive-maps-V2_1.service:qmInteractiveMapsService
         * @description
         * Get list of landmarks for an exhibitor
         *
         * @param {string} exhibitorId Exhibitor Id
         * @returns {promise} Promise that resolves when landmark list has been retrieved
         */
        getListExhibitorLandmarks: function(exhibitorId) {
            var url = qmWebAppService.getBaseRestUrl('component', 'interactive-maps') + '/exhibitors/' + exhibitorId + '/landmarks';

            var defer = $q.defer();
            qmRest.get(url).then(function(response) {
                defer.resolve(response);

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

            return defer.promise;
        },
        /**
         * @ngdoc method
         * @name getListAttendeeEventLandmarks
         * @methodOf interactive-maps-V2_1.service:qmInteractiveMapsService
         * @description
         * Get list of landmarks for an attendee at an event
         *
         * @param {string} eventId Event Id
         * @returns {promise} Promise that resolves when landmark list has been retrieved
         */
        getListAttendeeEventLandmarks: function(eventId) {
            var url = qmWebAppService.getBaseRestUrl('component', 'interactive-maps') + '/attendee-event-landmarks/' + eventId;

            var defer = $q.defer();
            qmRest.get(url).then(function(response) {
                defer.resolve(response);

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

            return defer.promise;
        },
        /**
         * @ngdoc method
         * @name getRedirect
         * @methodOf interactive-maps-V2_1.service:qmInteractiveMapsService
         * @description
         * Get an object specifying the redirection state & parameters for a landmark
         *
         * @param {string} landmark Landmark object
         * @returns {object} An object containing the state and state parameters
         */
        getRedirect: function(landmark) {

            return {
                state: 'event.interactive-maps.detail',
                params: {mapId: landmark.mapId, landmarkId: landmark.landmarkId, eventId: null, exhibitorId: null}
            };
        }
    };
}]);

var interactiveMaps = angular.module('interactive-maps-V2_1');

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

var interactiveMaps = angular.module('interactive-maps-V2_1');

/**
 * @ngdoc controller
 * @name interactive-maps-V2_1.controller:InteractiveMapMainListController
 * @description
 * Main list controller for interactive maps
 */
interactiveMaps.controller('InteractiveMapMainListController', ['qmInteractiveMapsService', '$state',
    function(qmInteractiveMapsService, $state) {

        var mapList = qmInteractiveMapsService.getList();
        mapList.then(function(maps) {
            if (maps.length === 1 && maps[0].type === 'AREA') {
                $state.go('event.interactive-maps.detail', {'mapId': maps[0].mapId});
            } else {
                $state.go('event.interactive-maps.list');
            }
        });
    }]);

var interactiveMaps = angular.module('interactive-maps-V2_1');

/**
 * @ngdoc controller
 * @name interactive-maps-V2_1.controller:InteractiveMapListController
 * @description
 * Controller for map list view
 */
interactiveMaps.controller('InteractiveMapListController', ['$scope', 'qmInteractiveMapsService', 'qmList', 'qmLocalization',
    function($scope, qmInteractiveMapsService, qmList, qmLocalization) {
        $scope.loadMaps = qmInteractiveMapsService.getList();
        $scope.loadMaps.then(function(maps) {
            $scope.maps = qmList.transformList(maps);
        });
        var componentTitle = qmLocalization.getString('componentInteractiveMapsTitle');
        $scope.emptyText = qmLocalization.getString('LABEL_EMPTY_MAPS_TITLE', componentTitle);
    }
]);

/**
 * @ngdoc controller
 * @name interactive-maps-V2_1.controller:InteractiveMapListHeaderController
 * @description
 * Controller for map list header view
 */
interactiveMaps.controller('InteractiveMapListHeaderController', ['$scope', 'qmLocalization', function($scope, qmLocalization) {
    $scope.headerBackState = false;
    $scope.headerTitle = qmLocalization.getString('componentInteractiveMapsTitle');
}]);

var interactiveMaps = angular.module('interactive-maps-V2_1');

/**
 * @ngdoc controller
 * @name interactive-maps-V2_1.controller:InteractiveMapDetailController
 * @description
 * Controller for map list view
 */
interactiveMaps.controller('InteractiveMapDetailController', ['$scope', '$state', '$rootScope', 'qmInteractiveMapsService',
function($scope, $state, $rootScope, qmInteractiveMapsService) {
    $scope.loadMap = qmInteractiveMapsService.getDetail($state.params.mapId);
    // Setup the list of Landmarks
    if ($state.params.landmarkId) {
        $scope.landmarkId = $state.params.landmarkId;
        $scope.showLandmarks = true;
        $scope.loadLandmarks = qmInteractiveMapsService.getLandmark($state.params.mapId, $state.params.landmarkId);
    } else if ($state.params.eventId) {
        $scope.showLandmarks = true;
        $scope.loadLandmarks = qmInteractiveMapsService.getListEventLandmarks($state.params.eventId);
    } else if ($state.params.exhibitorId) {
        $scope.showLandmarks = true;
        $scope.loadLandmarks = qmInteractiveMapsService.getListExhibitorLandmarks($state.params.exhibitorId);
    } else {
        $scope.showLandmarks = false;
        $scope.loadLandmarks = qmInteractiveMapsService.getLandmarks($state.params.mapId);
    }

    // Once we have loaded the map, broadcast an event to the header controller to update the name
    $scope.loadMap.then(function(qmMap) {
        $scope.qmMap = qmMap;
        $rootScope.$broadcast('mapLoaded', qmMap);
    });

    // Callback for once the qmMap is loaded
    $scope.loadMap.then(function(qmMap) {
        // Create an image
        var img = new Image();
        // Callback for once the image is loaded to find the image dimensions
        img.onload = function() {
            $scope.$apply(function() {
                // Store the image dimensions
                $scope.qmMap.width = img.width;
                $scope.qmMap.height = img.height;

                // Create the markers for each landmark
                $scope.loadLandmarks.then(function(landmarks) {
                    if ($scope.showLandmarks) {
                        var canvas = $('#mapCanvas');
                        var context = canvas[0].getContext('2d');
                        context.clearRect(0, 0, canvas[0].width, canvas[0].height);

                        // Add pins
                        var pinIcon = new Image();
                        pinIcon.src = '/asset/component/interactive-maps/2.1/webapp/images/pin_map.png';
                        pinIcon.onload = function() {
                            angular.forEach(landmarks, function(landmark) {
                                if (qmMap && qmMap.mapId === landmark.mapId) {
                                    context.drawImage(pinIcon, landmark.x - 8, landmark.y - 25);
                                }
                            });
                        };
                    }
                });
            });
        };
        img.src = qmMap.imageSrc;
    });

    //Cross Browser Calculation of X & Y mouse position
    function relativeCoords(event) {
        var bounds = event.target.getBoundingClientRect();
        var x = event.clientX - bounds.left;
        var y = event.clientY - bounds.top;

        return {x: x, y: y};
    }

    // Callback for when the user clicks on the map
    $scope.click = function(event) {
        var coords = relativeCoords(event);
        $scope.loadMap.then(function(qmMap) {
            if (qmMap.type !== 'SEATING') {
                $scope.loadLandmarks.then(function(landmarks) {
                    angular.forEach(landmarks, function(landmark) {
                        if ($scope.isInCircle(landmark.x, landmark.y, coords.x, coords.y)) {
                            $state.go('event.interactive-maps.detail.landmark', {mapId: qmMap.mapId, listLandmarkId: landmark.landmarkId});
                        }
                    });
                });
            }
        });
    };

    $scope.isInCircle = function(centerX, centerY, x, y) {
        var radius = 15;
        var squareDist = Math.pow((centerX - x), 2) + Math.pow((centerY - y), 2);

        return squareDist < Math.pow(radius, 2);
    };

    var mapList = qmInteractiveMapsService.getList();
    mapList.then(function(maps) {
        if (maps.length > 1 || $state.params.landmarkId) {
            $rootScope.$broadcast('updateHeaderBackState', {headerBackState: true});
        }
    });

}]);

/**
 * @ngdoc controller
 * @name interactive-maps-V2_1.controller:InteractiveMapDetailHeaderController
 * @description
 * Controller for map detail header view
 */
interactiveMaps.controller('InteractiveMapDetailHeaderController', ['$scope', '$state', 'qmEventConfig', function($scope, $state, qmEventConfig) {

    $scope.$on('updateHeaderBackState', function(event, args) {
        if (args.headerBackState) {
            $scope.headerBackState = true;
        }
    });

    var eventsIsConfigured = qmEventConfig.isComponentConfigured('events');
    var exhibitorsIsConfigured = qmEventConfig.isComponentConfigured('exhibitors');

    $scope.$on('mapLoaded', function(event, map) {
        // Set the title to the name of the map
        $scope.headerTitle = map.name;

        // Don't allow search on SEATING maps and check we have Events/Exhibitors to search for
        if (map.type !== 'SEATING' && (eventsIsConfigured || exhibitorsIsConfigured)) {
            $scope.headerRightElements = [];
            var searchElement = {};
            searchElement.type = 'icon';
            searchElement.class = 'fa fa-search';
            searchElement.clickHandler = function() {
                $state.go('event.interactive-maps.detail.search', {mapId: $state.params.mapId});
            };
            $scope.headerRightElements.push(searchElement);
        }
    });
}]);

var interactiveMaps = angular.module('interactive-maps-V2_1');

/**
 * @ngdoc controller
 * @name interactive-maps-V2_1.controller:InteractiveMapSearchController
 * @description
 * Controller for map search view
 */
interactiveMaps.controller('InteractiveMapSearchController',
['$scope', '$state', 'qmLocalization', 'qmInteractiveMapsService', 'qmList', 'qmEventConfig', 'qmLogin', 'qmInteractiveMapsDependencyService',
function($scope, $state, qmLocalization, qmInteractiveMapsService, qmList, qmEventConfig, qmLogin, qmInteractiveMapsDependencyService) {
    $scope.map = {
        mapId: $state.params.mapId
    };

    var eventsIsConfigured = qmEventConfig.isComponentConfigured('events');
    var eventsRequiresLogin = qmEventConfig.isComponentLoginRequired('events');
    $scope.showEvents = false;
    $scope.eventsTitle = qmLocalization.getString('componentEventsTitle');
    $scope.events = $scope.filteredEvents = [];

    var exhibitorsIsConfigured = qmEventConfig.isComponentConfigured('exhibitors');
    var exhibitorsRequiresLogin = qmEventConfig.isComponentLoginRequired('exhibitors');
    $scope.showExhibitors = false;
    $scope.exhibitorsTitle = qmLocalization.getString('componentExhibitorsTitle');
    $scope.exhibitors = $scope.filteredExhibitors = [];

    if (eventsRequiresLogin || exhibitorsRequiresLogin) {
        qmLogin.checkLogin().then(function() {
            fetchEvents();
            fetchExhibitors();
        });
    } else {
        fetchEvents();
        fetchExhibitors();
    }

    function fetchEvents() {
        if (eventsIsConfigured) {
            $scope.loadEvents = qmInteractiveMapsService.getMapEvents($state.params.mapId);

            $scope.loadEvents.then(function(events) {
                $scope.events = qmList.groupByGroup(events, 'eventDate', 'eventDateFormatted');
                $scope.filteredEvents = [];

                var qmEventsDependencyService = qmInteractiveMapsDependencyService.getEventsService('qmEventsDependencyService');
                $scope.eventListItemUrl = qmEventsDependencyService.getTemplateUrl('event-list-item');
                // Show Events if there are some to show
                if ($scope.filteredEvents.length > 0) {
                    $scope.showEvents = true;
                } else {
                    $scope.showEvents = false;
                }
            });
        }
    }

    function fetchExhibitors() {
        if (exhibitorsIsConfigured) {
            $scope.loadExhibitors = qmInteractiveMapsService.getMapExhibitors($state.params.mapId);

            $scope.loadExhibitors.then(function(exhibitorResponse) {
                if (angular.isDefined(exhibitorResponse.exhibitors)) {
                    $scope.exhibitors = qmList.transformList(exhibitorResponse.exhibitors);
                    $scope.filteredExhibitors = [];
                } else {
                    $scope.exhibitors = $scope.filteredExhibitors = [];
                }

                var qmExhibitorsDependencyService = qmInteractiveMapsDependencyService.getExhibitorsService('qmExhibitorsDependencyService');
                $scope.exhibitorListItemUrl = qmExhibitorsDependencyService.getTemplateUrl('exhibitor-list-item');
                // Show Exhibitors if there are some to show
                if ($scope.filteredExhibitors.length > 0) {
                    $scope.showExhibitors = true;
                } else {
                    $scope.showExhibitors = false;
                }
            });
        }
    }

    $scope.dataFilter = '';

    $scope.$watch('dataFilter', function(value) {
        if (value) {
            if (eventsIsConfigured) {
                $scope.filteredEvents = qmList.filterList($scope.events, value, ['title']);
            }
            if (exhibitorsIsConfigured) {
                $scope.filteredExhibitors = qmList.filterList($scope.exhibitors, value, ['company', 'boothNumber']);
            }
        } else {
            $scope.filteredEvents = [];
            $scope.filteredExhibitors = [];
        }

        // Show Events if there are some to show
        if ($scope.filteredEvents.length > 0) {
            $scope.showEvents = true;
        } else {
            $scope.showEvents = false;
        }

        // Show Exhibitors if there are some to show
        if ($scope.filteredExhibitors.length > 0) {
            $scope.showExhibitors = true;
        } else {
            $scope.showExhibitors = false;
        }
    });

}]);

/**
 * @ngdoc controller
 * @name interactive-maps-V2_1.controller:InteractiveMapSearchHeaderController
 * @description
 * Controller for map search header view
 */
interactiveMaps.controller('InteractiveMapSearchHeaderController', ['$scope', 'qmLocalization', function($scope, qmLocalization) {
    $scope.headerBackState = true;
    $scope.headerTitle = qmLocalization.getString('LABEL_SEARCH');
}]);

var interactiveMaps = angular.module('interactive-maps-V2_1');

/**
 * @ngdoc controller
 * @name interactive-maps-V2_1.controller:InteractiveMapLandmarkController
 * @description
 * Controller for Maps to see if there are multiple Events or Exhibitors at a Landmark
 */
interactiveMaps.controller('InteractiveMapLandmarkController', ['$q', '$scope', '$state', 'qmInteractiveMapsService', 'qmEventConfig',
function($q, $scope, $state, qmInteractiveMapsService, qmEventConfig) {

    var eventsList = [];
    var exhibitorsList = [];
    var loadEvents = qmInteractiveMapsService.getMapEvents($state.params.mapId, $state.params.listLandmarkId);
    var loadExhibitors = qmInteractiveMapsService.getMapExhibitors($state.params.mapId, $state.params.listLandmarkId);

    $scope.countEvents = function() {
        var defer = $q.defer();
        if (qmEventConfig.isComponentConfigured('events')) {
            loadEvents.then(function(events) {
                eventsList = events;
                defer.resolve(eventsList.length);
            });
        } else {
            defer.resolve(0);
        }

        return defer.promise;
    };

    $scope.countExhibitors = function() {
        var defer = $q.defer();
        if (qmEventConfig.isComponentConfigured('exhibitors')) {
            loadExhibitors.then(function(exhibitorResponse) {
                if (angular.isDefined(exhibitorResponse.exhibitors)) {
                    exhibitorsList = exhibitorResponse.exhibitors;
                } else {
                    exhibitorsList = [];
                }

                defer.resolve(exhibitorsList.length);
            });
        } else {
            defer.resolve(0);
        }

        return defer.promise;
    };

    $scope.eventPromise = $scope.countEvents();
    $scope.exhibitorPromise = $scope.countExhibitors();

    $q.all({eventCount: $scope.eventPromise, exhibitorCount: $scope.exhibitorPromise}).then(function(values) {

        if (values.eventCount === 1 && values.exhibitorCount === 0) {
            // Go to the Event at this Landmark
            $state.go('event.events.detail', {eventId: eventsList[0].eventId});
        } else if (values.eventCount === 0 && values.exhibitorCount === 1) {
            // Go to the Exhibitor at this Landmark
            $state.go('event.exhibitors.detail', {exhibitorId: exhibitorsList[0].exhibitorId});
        } else {
            // Go to the list of Events & Exhibitors at this Landmark
            $state.go('event.interactive-maps.detail.landmark.list', {mapId: $state.params.mapId, listLandmarkId: $state.params.listLandmarkId});
        }
    });
}]);

var interactiveMaps = angular.module('interactive-maps-V2_1');

/**
 * @ngdoc controller
 * @name interactive-maps-V2_1.controller:InteractiveMapLandmarkListController
 * @description
 * Controller for landmark list view
 */
interactiveMaps.controller('InteractiveMapLandmarkListController',
['$scope', '$state', 'qmLocalization', 'qmInteractiveMapsService', 'qmList', 'qmEventConfig', 'qmInteractiveMapsDependencyService',
function($scope, $state, qmLocalization, qmInteractiveMapsService, qmList, qmEventConfig, qmInteractiveMapsDependencyService) {
    $scope.map = {
        mapId: $state.params.mapId
    };

    $scope.loadEvents = qmInteractiveMapsService.getMapEvents($state.params.mapId, $state.params.listLandmarkId);
    $scope.loadExhibitors = qmInteractiveMapsService.getMapExhibitors($state.params.mapId, $state.params.listLandmarkId);

    var eventsIsConfigured = qmEventConfig.isComponentConfigured('events');
    $scope.showEvents = false;
    $scope.eventsTitle = qmLocalization.getString('componentEventsTitle');

    var exhibitorsIsConfigured = qmEventConfig.isComponentConfigured('exhibitors');
    $scope.showExhibitors = false;
    $scope.exhibitorsTitle = qmLocalization.getString('componentExhibitorsTitle');

    if (eventsIsConfigured) {
        $scope.loadEvents.then(function(events) {
            $scope.events = $scope.filteredEvents = qmList.groupByGroup(events, 'eventDate', 'eventDateFormatted');

            var qmEventsDependencyService = qmInteractiveMapsDependencyService.getEventsService('qmEventsDependencyService');
            $scope.eventListItemUrl = qmEventsDependencyService.getTemplateUrl('event-list-item');
            // Show Events if there are some to show
            if ($scope.events.length > 0) {
                $scope.showEvents = true;
            }
        });
    }

    if (exhibitorsIsConfigured) {
        $scope.loadExhibitors.then(function(exhibitorResponse) {
            if (angular.isDefined(exhibitorResponse.exhibitors)) {
                $scope.exhibitors = $scope.filteredExhibitors = qmList.transformList(exhibitorResponse.exhibitors);
            } else {
                $scope.exhibitors = $scope.filteredExhibitors = [];
            }

            var qmExhibitorsDependencyService = qmInteractiveMapsDependencyService.getExhibitorsService('qmExhibitorsDependencyService');
            $scope.exhibitorListItemUrl = qmExhibitorsDependencyService.getTemplateUrl('exhibitor-list-item');
            // Show Exhibitors if there are some to show
            if ($scope.exhibitors.length > 0) {
                $scope.showExhibitors = true;
            }
        });
    }

    $scope.dataFilter = '';

    $scope.$watch('dataFilter', function(value) {
        if (value) {
            $scope.filteredEvents = qmList.qmList.filterList($scope.events, value, ['title']);
            $scope.filteredExhibitors = qmList.filterList($scope.exhibitors, value, ['company', 'boothNumber']);
        } else {
            $scope.filteredEvents = $scope.events;
            $scope.filteredExhibitors = $scope.exhibitors;
        }
    });

}]);

/**
 * @ngdoc controller
 * @name interactive-maps-V2_1.controller:InteractiveMapLandmarkListHeaderController
 * @description
 * Controller for map search header view
 */
interactiveMaps.controller('InteractiveMapLandmarkListHeaderController', ['$scope', 'qmLocalization', function($scope, qmLocalization) {
    $scope.headerBackState = true;
    $scope.headerTitle = qmLocalization.getString('LABEL_SEARCH');
}]);

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

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

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_3');

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

            var likesCache = new qmBaseTempStorage('socialLikes', '2.0'),
                commentsAddCache = new qmBaseTempStorage('socialCommentsAdd', '2.0'),
                commentsEditCache = new qmBaseTempStorage('socialCommentsEdit', '2.0'),
                commentsDeleteCache = new qmBaseTempStorage('socialCommentsDelete', '2.0'),
                // strings used for operations in analytics rpc calls
                commentActions = {add: 'add', edit: 'edit', delete: 'delete', socialData: 'socialData'},
                // how many seconds we should keep an operation cached
                cacheLifetime = 5,
                // font awesome replacement classes for qmojis
                qmojis = {
                    QM_ZZZ_0002: 'fa fa-bed',
                    QM_YEAH_0002: 'fa fa-smile-o',
                    QM_WOW_0002: 'fa fa-exclamation-circle',
                    QM_HMMM_0002: 'fa fa-question-circle'
                },
                // the server currently has a 15 second processing delay; we will wait a minute before clearing our cache and respecting server values
                cleanCache = function(resource, resourceId, cachedResource) {
                    $timeout(function() {
                        var cachedResourceItem = cachedResource.getUserItem(resource);
                        if (cachedResourceItem && angular.isDefined(cachedResourceItem[resourceId])) {
                            delete cachedResourceItem[resourceId];
                        }
                        cachedResource.setUserItem(resource, cachedResourceItem);
                    }, 60000);
                },
                cleanLikesCache = function(resource, resourceId) {
                    cleanCache(resource, resourceId, likesCache);
                },
                queueCommentCallToCache = function(cacheStorageObj, resource, resourceId, commentId, commentOperation, commentContent, callTimestamp) {
                    var cachedResource = cacheStorageObj.getUserItem(resource);

                    if (!cachedResource) {
                        cachedResource = {};
                    }
                    cachedResource[commentId] = {
                        commentId: commentId,
                        resourceId: resourceId,
                        commentOperation: commentOperation,
                        commentContent: commentContent,
                        callTimestamp: callTimestamp
                    };
                    cacheStorageObj.setUserItem(resource, cachedResource);
                };

            var init = {
                /**
                 * @ngdoc method
                 * @name buildSocialObj
                 * @methodOf social-V2_3.service:qmSocialService
                 * @description
                 * Build a social data object
                 * NEW function for V2_3
                 *
                 * @param {integer} likeCount number of likes
                 * @param {integer} commentCount number of comments
                 * @param {string} id resource id
                 * @param {string} status resource status
                 * @param {Object} obj if you are changing a referenced object, send it here
                 */
                buildSocialObj: function(likeCount, commentCount, isLiked, id, status, obj) {
                    var socialObj = angular.isDefined(obj) && obj || {};
                    var localLikeStringKey = '';
                    var localCommentStringKey = '';

                    socialObj.id = id || undefined;
                    socialObj.likeCount = angular.isDefined(likeCount) ? likeCount : undefined;
                    socialObj.isLiked = angular.isDefined(isLiked) ? isLiked : false;
                    socialObj.commentCount = angular.isDefined(commentCount) ? commentCount : undefined;
                    socialObj.hasComments = angular.isDefined(commentCount) ? commentCount > 0 : undefined;
                    socialObj.status = status || undefined;
                    socialObj.socialDataLoaded = true;

                    if (likeCount === 1) {
                        localLikeStringKey = 'LABEL_LIKE';
                    }
                    if (likeCount > 1) {
                        localLikeStringKey = 'LABEL_LIKES';
                    }
                    socialObj.likeCountLabel = localLikeStringKey ? qmLocalization.getString(localLikeStringKey, socialObj.likeCount) : '';

                    if (commentCount === 1) {
                        localCommentStringKey = 'LABEL_COUNT_COMMENT';
                    }
                    if (commentCount > 1) {
                        localCommentStringKey = 'LABEL_COUNT_COMMENTS';
                    }
                    socialObj.commentCountLabel = localCommentStringKey ? qmLocalization.getString(localCommentStringKey, socialObj.commentCount) : '';

                    return socialObj;
                },
                /**
                 * @ngdoc method
                 * @name getQmojiComment
                 * @methodOf social-V2_3.service:qmSocialService
                 * @description
                 * Determine if comment is a qMoji, and if so return the css class it corresponds to
                 * NEW function for V2_3
                 *
                 * @param {string} str Some string to look for qMoji
                 */
                getQmojiComment: function(str) {
                    str = str.trim();

                    // check that this is a qmoji
                    if (str.indexOf(' ') === -1 && str.substring(0, 3) === 'QM_') {
                        return qmojis[str];
                    } else {
                        return false;
                    }
                },
                /**
                 * @ngdoc method
                 * @name getComments
                 * @methodOf social-V2_3.service:qmSocialService
                 * @description
                 * Get comments corresponding resource and id & get single comment if commentId specified
                 * NEW function for V2_3
                 *
                 * @param {string} resource Resource the ids are associated with
                 * @param {Array} id to get comments for
                 * @param {string} commentId is an optional parameter to get a specific comment
                 * @returns {promise} Promise that resolves after getting likes response
                 */
                getComments: function(resource, id, commentId) {
                    var url = qmWebAppService.getBaseRestUrl('component', 'social') + '/comments/' + resource + '/' + id,
                        defer,
                        commentsData = {},
                        cachedAddComments,
                        cachedEditComments,
                        cachedDeleteComments,
                        commentsLimit = 100,
                        commentsCount;

                    defer = $q.defer();
                    qmRest.get(url).then(function(responseData) {
                        commentsData = responseData.data;
                        // if only one comment is being requested
                        if (typeof commentId !== 'undefined') {
                            commentsCount = commentsData.length;
                            commentsData = commentsData.find(function(comment) {
                                return comment.id === commentId;
                            });
                            if (typeof commentsData === 'undefined') {
                                defer.resolve(false);
                            } else {
                                commentsData.firstName = null;
                                commentsData.lastName = null;
                                commentsData.imgSrc = null;
                                commentsData.goToCommentLink = angular.noop;
                                commentsData.commentsCount = commentsCount;
                            }
                        // otherwise return all comments
                        } else {
                            // create copy of cached add, edit, delete op lists
                            cachedAddComments = commentsAddCache.getUserItem(resource);
                            cachedEditComments = commentsEditCache.getUserItem(resource);
                            cachedDeleteComments = commentsDeleteCache.getUserItem(resource);
                            // walk through retrieved comment and adjust according to cache contents
                            commentsData.length = commentsLimit;
                            commentsData = commentsData.filter(function(comment) {
                                var commentTime = new Date(comment.time);

                                // if comment response is a cached add, remove cached
                                if (cachedAddComments &&
                                    typeof cachedAddComments[comment.id] !== 'undefined') {
                                    delete cachedAddComments[comment.id];
                                }
                                // if cached edit comment & comment response are the same, remove cached
                                if (cachedEditComments &&
                                    typeof cachedEditComments[comment.id] !== 'undefined' &&
                                    comment.comment === cachedEditComments[comment.id].commentContent) {
                                    delete cachedEditComments[comment.id];
                                }
                                // if cached edit comment & comment response are different, and cached comment is old, remove cached
                                if (cachedEditComments &&
                                    typeof cachedEditComments[comment.id] !== 'undefined' &&
                                    comment.comment !== cachedEditComments[comment.id].commentContent &&
                                   (commentTime - cachedEditComments[comment.id].callTimestamp > cacheLifetime * 1000)) {
                                    delete cachedEditComments[comment.id];
                                }
                                // if cached edit comment & comment response are different, change comment content response to cached version
                                if (cachedEditComments &&
                                    typeof cachedEditComments[comment.id] !== 'undefined' &&
                                    comment.comment !== cachedEditComments[comment.id].commentContent) {
                                    comment.comment = cachedEditComments[comment.id].commentContent;
                                }
                                // if comment response is a cached delete, remove cached
                                if (cachedDeleteComments &&
                                    typeof cachedDeleteComments[comment.id] !== 'undefined') {
                                    if (commentTime - cachedDeleteComments[comment.id].callTimestamp > cacheLifetime * 1000) {
                                        delete cachedDeleteComments[comment.id];
                                    }
                                    return false;
                                }
                                return true;
                            });

                            // walk through unaccounted for cached add comments & add those to the top of the comment list
                            angular.forEach(cachedAddComments, function(cachedComment) {
                                commentsData.unshift({
                                    attendeeId: qmWebAppData.getUserId(),
                                    comment: cachedComment.commentContent,
                                    id: cachedComment.commentId,
                                    time: cachedComment.callTimestamp
                                });
                            });

                            // reset add, edit, delete cache lists with the versions we just adjusted
                            commentsAddCache.setUserItem(resource, cachedAddComments);
                            commentsEditCache.setUserItem(resource, cachedEditComments);
                            commentsDeleteCache.setUserItem(resource, cachedDeleteComments);
                        }
                        defer.resolve(commentsData);
                    }, function(err) {
                        defer.reject(err);
                    });

                    return defer.promise;
                },
                /**
                 * @ngdoc method
                 * @name addComment
                 * @methodOf social-V2_3.service:qmSocialService
                 * @description
                 * Send add comment data to analytics
                 * NEW function for V2_3
                 *
                 * @param {string} resource Resource name
                 * @param {string} id Resource id
                 * @param {string} sourceElement Source element
                 * @param {string} condition Event condition
                 * @param {string} comment comment text
                 * @returns {promise} Promise that resolves after getting social data response
                 */
                addComment: function(resource, id, sourceElement, condition, commentContent) {
                    var qmAnalyticsService = qmSocialDependencyService.getAnalyticsService('qmAnalyticsService'),
                        eventValue,
                        commentId = qmUtilities.generateUUID(),
                        callTimestamp = new Date(),
                        defer = $q.defer();

                    if (qmAnalyticsService) {
                        eventValue = {Action: commentActions.add, Content: commentContent, Id: commentId};
                        qmAnalyticsService.socialEvent(eventValue, id, resource, sourceElement, condition).then(function(response) {
                            defer.resolve(response);
                        });
                        queueCommentCallToCache(commentsAddCache, resource, id, commentId, commentActions.add, commentContent, callTimestamp);
                    } else {
                        defer.reject();
                    }

                    return defer.promise;
                },
                /**
                 * @ngdoc method
                 * @name editComment
                 * @methodOf social-V2_3.service:qmSocialService
                 * @description
                 * Send edit comment data to analytics
                 * NEW function for V2_3
                 *
                 * @param {string} resource Resource name
                 * @param {string} id Resource id
                 * @param {string} sourceElement Source element
                 * @param {string} condition Event condition
                 * @param {string} commentId comment id
                 * @param {string} comment comment text
                 * @returns {promise} Promise that resolves after getting social data response
                 */
                editComment: function(resource, id, sourceElement, condition, commentId, commentContent) {
                    var qmAnalyticsService = qmSocialDependencyService.getAnalyticsService('qmAnalyticsService'),
                        eventValue,
                        callTimestamp = new Date(),
                        defer = $q.defer();

                    if (qmAnalyticsService) {
                        eventValue = {Action: commentActions.edit, Content: commentContent, Id: commentId};
                        qmAnalyticsService.socialEvent(eventValue, id, resource, sourceElement, condition).then(function(response) {
                            defer.resolve(response);
                        });
                        queueCommentCallToCache(commentsEditCache, resource, id, commentId, commentActions.edit, commentContent, callTimestamp);
                    } else {
                        defer.reject();
                    }

                    return defer.promise;
                },
                /**
                 * @ngdoc method
                 * @name deleteComment
                 * @methodOf social-V2_3.service:qmSocialService
                 * @description
                 * Remove comment corresponding to specified commentId
                 * NEW function for V2_3
                 *
                 * @param {string} resource Resource name
                 * @param {string} id Resource id
                 * @param {string} sourceElement Source element
                 * @param {string} condition Event condition
                 * @param {string} commentId comment id
                 * @returns {promise} Promise that resolves after getting social data response
                 */
                deleteComment: function(resource, id, sourceElement, condition, commentId) {
                    var qmAnalyticsService = qmSocialDependencyService.getAnalyticsService('qmAnalyticsService'),
                        callTimestamp = new Date(),
                        eventValue;

                    if (qmAnalyticsService) {
                        queueCommentCallToCache(commentsDeleteCache, resource, id, commentId, commentActions.delete, '', callTimestamp);
                        eventValue = {Action: commentActions.delete, Content: '', Id: commentId};
                        return qmAnalyticsService.socialEvent(eventValue, id, resource, sourceElement, condition);
                    }
                },
                /**
                 * @ngdoc method
                 * @name getCachedCommentCounts
                 * @methodOf social-V2_3.service:qmSocialService
                 * @description
                 * Get counts of cached comments for each resource id
                 * NEW function for V2_3
                 *
                 * @returns {Object} Object with indices of resource ids and corresponding cached adjustments
                 */
                getCachedCommentCounts: function(resource) {
                    var cachedAddComments = commentsAddCache.getUserItem(resource),
                        cachedDeleteComments = commentsDeleteCache.getUserItem(resource),
                        cachedCommentCounts = {},
                        now = new Date(),
                        defer = $q.defer(),
                        addCallTimestamp,
                        deleteCallTimestamp,
                        addCacheLifetime = cacheLifetime - (1700 / 1000),
                        deleteCacheLifetime = cacheLifetime - (2000 / 1000);

                    $timeout(function() {
                        // create a list of the number of cached comments for each resource id
                        angular.forEach(cachedAddComments, function(addedComment) {
                            // remove old adds
                            addCallTimestamp = new Date(addedComment.callTimestamp);
                            if ((now - addCallTimestamp) / 1000 > addCacheLifetime) {
                                delete cachedAddComments[addedComment.commentId];
                            } else {
                                // adjust cached counts based on cached adds
                                if (typeof cachedCommentCounts[addedComment.resourceId] !== 'undefined') {
                                    cachedCommentCounts[addedComment.resourceId] += 1;
                                } else {
                                    cachedCommentCounts[addedComment.resourceId] = 1;
                                }
                            }
                        });
                        angular.forEach(cachedDeleteComments, function(deletedComment) {
                            // remove old deletes
                            deleteCallTimestamp = new Date(deletedComment.callTimestamp);
                            if ((now - deleteCallTimestamp) / 1000 > deleteCacheLifetime) {
                                delete cachedDeleteComments[deletedComment.commentId];
                            } else {
                                // adjust cached counts based on cached deletes
                                if (typeof cachedCommentCounts[deletedComment.resourceId] !== 'undefined') {
                                    cachedCommentCounts[deletedComment.resourceId] -= 1;
                                } else {
                                    cachedCommentCounts[deletedComment.resourceId] = 0 - 1;
                                }
                            }
                        });
                        commentsAddCache.setUserItem(resource, cachedAddComments);
                        commentsDeleteCache.setUserItem(resource, cachedDeleteComments);
                        defer.resolve(cachedCommentCounts);
                    }, 0);

                    return defer.promise;
                },
                /**
                 * @ngdoc method
                 * @name addCachedSocialData
                 * @methodOf social-V2_3.service:qmSocialService
                 * @description
                 * Add cached social (likes & comments) counts to given social data
                 * NEW function for V2_3
                 *
                 * @param {string} id Resource id
                 * @param {Array} socialDataIndexedArr Array of social data indexed by resource id
                 * @param {Array} cachedResourceLikes Array of cached likes
                 * @param {Array} cachedCommentCounts Array of cached comments
                 */
                addCachedSocialData: function(id, socialDataIndexedArr, cachedResourceLikes, cachedCommentCounts) {
                    var gotData = typeof socialDataIndexedArr[id] !== 'undefined';
                    var socialDataItem = gotData ? socialDataIndexedArr[id] : {};
                    var likeCountDelta = 0;
                    var commentCountDelta;
                    var hasCachedLikeEvent = cachedResourceLikes && typeof cachedResourceLikes[id] !== 'undefined';

                    commentCountDelta = typeof cachedCommentCounts[id] !== 'undefined' ? cachedCommentCounts[id] : 0;
                    socialDataItem.isLiked = gotData && (socialDataItem.status === '1');
                    if (gotData) {
                        // decrement if unliked in local cache but liked on server
                        if (hasCachedLikeEvent && socialDataItem.isLiked && !cachedResourceLikes[id]) {
                            likeCountDelta -= 1;
                            socialDataItem.isLiked = false;
                        }
                        // increment if liked in local cache but unliked on server
                        if (hasCachedLikeEvent && !socialDataItem.isLiked && cachedResourceLikes[id]) {
                            likeCountDelta += 1;
                            socialDataItem.isLiked = true;
                        }
                    } else {
                        // if there is no existing likes data
                        if (hasCachedLikeEvent) {
                            likeCountDelta = cachedResourceLikes[id] || -1;
                            socialDataItem.isLiked = cachedResourceLikes[id];
                        }
                    }
                    socialDataItem.likeCount = gotData ? parseInt(socialDataItem.likesCount) : 0;
                    socialDataItem.likeCount += likeCountDelta;
                    socialDataItem.commentCount = gotData ? parseInt(socialDataItem.commentsCount) : 0;
                    socialDataItem.commentCount += commentCountDelta;
                    socialDataItem.hasComments = socialDataItem.commentCount > 0;

                    return socialDataItem;
                },
                /**
                 * @ngdoc method
                 * @name getSocialData
                 * @methodOf social-V2_3.service:qmSocialService
                 * @description
                 * Get social data for specified resource and ids
                 * NEW function for V2_3
                 *
                 * @param {string} resource Resource the ids are associated with
                 * @param {Array} ids Array of ids to get social data for
                 * @returns {promise} Promise that resolves after getting social data response
                 */
                getSocialData: function(resource, ids) {

                    var defer = $q.defer();
                    var url = qmWebAppService.getBaseRestUrl('component', 'social') + '/socialData/' + resource;

                    if (angular.isUndefined(ids) || ids.length === 0) {

                        defer.resolve([]);
                        return defer.promise;
                    }

                    // get social data
                    qmRest.get(url).then(function(responseData) {

                        var socialData = responseData.data;
                        var cachedResourceLikes = likesCache.getUserItem(resource);

                        // create a list of the number of cached comments for each resource id
                        init.getCachedCommentCounts(resource).then(function(cachedCommentCounts) {
                            var socialDataIndexedArr = {};
                            var resultSocialData = [];
                            socialData.map(function(item) {
                                socialDataIndexedArr[item.id] = item;
                            });
                            resultSocialData = ids.map(function(id) {
                                var socialDataItem = init.addCachedSocialData(id, socialDataIndexedArr, cachedResourceLikes, cachedCommentCounts);

                                return socialDataItem;
                            });
                            defer.resolve(resultSocialData);
                        },
                        function(err) {
                            defer.reject(err);
                        });
                    }, function(err) {
                        defer.reject(err);
                    });

                    return defer.promise;
                },
                /**
                 * @ngdoc method
                 * @name getAllSocialData
                 * @methodOf social-V2_3.service:qmSocialService
                 * @description
                 * Get all social (like & comment) counts for the given resource
                 * NEW function for V2_3
                 *
                 * @param {string} resource Resource
                 * @returns {promise} Promise that resolves after getting social data response
                 */
                getAllSocialData: function(resource, ids) {

                    var defer = $q.defer();

                    init.getSocialData(resource, ids).then(function(resultSocialData) {
                        defer.resolve(resultSocialData);
                    });

                    return defer.promise;
                },
                /**
                 * @ngdoc method
                 * @name getWhoLikes
                 * @methodOf social-V2_3.service:qmSocialService
                 * @description
                 * Get list of users who liked specified resource
                 * NEW function for V2_3
                 *
                 * @param {string} resource Resource the id is associated with
                 * @param {string} id to get likes for
                 * @returns {promise} Promise that resolves to an array of attendee ids after getting likes response
                 */
                getWhoLikes: function(resource, id) {
                    var url = qmWebAppService.getBaseRestUrl('component', 'social') + '/whoLiked/' + resource + '/' + id + '?offset=0&limit=0',
                        defer;
                    // get who likes data
                    defer = $q.defer();
                    qmRest.get(url).then(function(responseData) {
                        // format attendee list
                        var attendeesIdList = responseData.data.filter(function(attendeesId) {
                            return attendeesId !== qmWebAppData.getUserId();
                        });
                        attendeesIdList = attendeesIdList.map(function(attendeesId) {
                            return {attendeeId: attendeesId};
                        });
                        defer.resolve(attendeesIdList);
                    }, function(err) {
                        defer.reject(err);
                    });

                    return defer.promise;
                },
                /**
                 * @ngdoc method
                 * @name getObjectSocialData
                 * @methodOf social-V2_3.service:qmSocialService
                 * @description
                 * Get likes & comments for specified resources and decorate object with like properties
                 * NEW function for V2_3
                 *
                 * @param {Array} objects Array of objects to get social data 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 social data response
                 */
                getObjectSocialData: function(objects, keyField, componentKey) {

                    var resource = init.getResourceKey(componentKey),
                        likesEnabled = init.allowLikes(componentKey),
                        commentsEnabled = init.allowComments(componentKey),
                        ids,
                        defer = $q.defer();
                    if ((commentsEnabled || likesEnabled) && resource !== null && qmLogin.isLoggedIn()) {
                        ids = [];

                        for (var index = 0; index < objects.length; index++) {
                            objects[index].enableLikes = likesEnabled;
                            objects[index].enableComments = commentsEnabled;
                            if (objects[index][keyField]) {
                                ids.push(objects[index][keyField]);
                            }
                        }
                        if (ids.length) {

                            init.getAllSocialData(resource, ids).then(function(socialData) {
                                var id;
                                var socialDataIndexes = [];

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

                                    socialDataIndexes[ids[i]] = socialData[i];
                                }

                                if (Object.keys(socialData).length > 0) {
                                    for (var k = 0; k < objects.length; k++) {
                                        id = objects[k][keyField];

                                        if (!angular.isDefined(socialDataIndexes[id])) {
                                            init.buildSocialObj(0, 0, false, id, undefined, objects[k]);
                                        } else {
                                            init.buildSocialObj(
                                                socialDataIndexes[id].likeCount,
                                                socialDataIndexes[id].commentCount,
                                                socialDataIndexes[id].isLiked,
                                                id,
                                                undefined,
                                                objects[k]);
                                        }
                                    }
                                }
                                defer.resolve(objects);
                            }, function() {
                                defer.resolve();
                            });
                        } else {
                            defer.resolve();
                        }
                    } else {
                        defer.resolve();
                    }
                    return defer.promise;
                },
                /**
                 * @ngdoc method
                 * @name getLikes
                 * @methodOf social-V2_3.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_3.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 allowComments
                 * @methodOf social-V2_3.service:qmSocialService
                 * @description
                 * Check if comments are allowed for component
                 *
                 * @param {string} componentKey component key
                 * @returns {boolean} true if comments are allowed for this component
                 */
                allowComments: function(componentKey) {

                    var resource = init.getResourceKey(componentKey);

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

                    return commentsEnabled;
                },
                /**
                 * @ngdoc method
                 * @name allowLikes
                 * @methodOf social-V2_3.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 like
                 * @methodOf social-V2_3.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_3.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 sessionManagement = angular.module('session-management-V2_0');

sessionManagement.run(['$timeout', '$window', 'qmSessionManager', 'qmConfigManager', '$rootScope', 'qmEventHelper', '$state',
function($timeout, $window, qmSessionManager, qmConfigManager, $rootScope, qmEventHelper, $state) {
    if (qmSessionManager) {
        // We are listening to global events so we need to make sure the current appId has session management configured
        var isSessionManagementConfigured = function() {
            var projectConfig = qmConfigManager.getConfig();
            if (projectConfig && projectConfig.hasConfig() && projectConfig.isComponentConfigured('session-management')) {
                return true;
            }
            return false;
        };

        // Determine if project is event level login to allow checkSessionExpired
        var isEventLevelLoginCheck = function() {
            var projectConfig = qmConfigManager.getConfig();
            if (projectConfig && typeof projectConfig.isEventLoginRequired === 'function') {
                if (projectConfig.isEventLoginRequired()) {
                    return true;
                }
            }
            return false;
        };

        // Determine if project is feature level login and user is in a locked component to allow checkSessionExpired
        var isFeatureLevelLoginCheck = function() {
            if ($state.current && $state.current.name) {
                var currentComponent = qmEventHelper.getComponentKey($state.current.name);
                if (currentComponent) {
                    var projectConfig = qmConfigManager.getConfig();
                    if (projectConfig && typeof projectConfig.isComponentLoginRequired === 'function') {
                        if (projectConfig.isComponentLoginRequired(currentComponent)) {
                            return true;
                        }
                    }
                }
            }
            return false;
        };

        var idleTimeTimeout = null;

        $window.onfocus = function() {
            $timeout.cancel(idleTimeTimeout);
            if (isSessionManagementConfigured()) {
                if (isEventLevelLoginCheck() || isFeatureLevelLoginCheck()) {
                    qmSessionManager.checkSessionExpired();
                }
                qmSessionManager.startExpiryInterval();
            }
        };
        $window.onblur = function() {
            qmSessionManager.stopExpiryInterval();
            if (isSessionManagementConfigured()) {
                qmSessionManager.updateExpiryTime();
                var idleTime = qmSessionManager.getIdleTime();
                if (idleTime) {
                    // When window loses focus, set a timeout that will check expiry
                    idleTimeTimeout = $timeout(function() {
                        if (isSessionManagementConfigured() && (isEventLevelLoginCheck() || isFeatureLevelLoginCheck())) {
                            qmSessionManager.checkSessionExpired();
                        }
                    }, idleTime);
                }
            }
        };
        $window.onunload = function() {
            if (isSessionManagementConfigured()) {
                qmSessionManager.updateExpiryTime();
            }
        };

        $rootScope.$on('$stateChangeSuccess', function() {
            if (isSessionManagementConfigured() && isFeatureLevelLoginCheck()) {
                qmSessionManager.checkSessionExpired();
            }
        });
    }
}]);

var sessionManagement = angular.module('session-management-V2_0');

/**
 * @ngdoc controller
 * @name session-management-V2_0.controller:PasswordController
 * @description
 * Controller for password prompt modal view
 */
sessionManagement.controller('PasswordController',
['$scope', 'qmEventConfig', '$modalInstance', '$q', 'qmRpc', 'qmEventHelper', '$state', 'qmLocalization', 'qmLogin',
    '$injector', 'qmContainerConfig', 'qmStateChange', '$window', 'qmWebAppData',
function($scope, qmEventConfig, $modalInstance, $q, qmRpc, qmEventHelper, $state, qmLocalization, qmLogin, $injector,
         qmContainerConfig, qmStateChange, $window, qmWebAppData) {

    $scope.isSubmitting = false;
    $scope.alertPasswordTitle = qmLocalization.getString('ALERT_SESSION_MANAGEMENT_DIALOG_TITLE');
    $scope.alertPasswordDescription = qmLocalization.getString('ALERT_SESSION_MANAGEMENT_DIALOG_MESSAGE');

    var prompLogin = function() {

        // No need to redirect just show the login popup
        qmLogin.checkLogin().then(function() {

            if (qmStateChange.didCompleteEventTransition) {

                $state.go($state.current, $state.params, {
                    reload: true,
                    priority: 10
                });
            } else {
                $window.location.reload();
            }
        });
    };

    $scope.logout = function() {

        qmLogin.reset();

        if (qmContainerConfig.getIsContainerLogin() || !qmContainerConfig.isSingleEvent()) {

            if ($state.current.name === 'container') {
                // We're at the container already. reload state so that the user gets the login popup
                $state.go($state.current, $state.params, {
                    reload: true
                });
            } else {
                // Send the user back to the container
                $state.go('container');
            }

        } else if (qmEventConfig.isEventLoginRequired()) {
            // Ask user to log in again
            prompLogin();
        } else {

            var currentComponent = qmEventHelper.getComponentKey($state.current.name);
            // If current component is locked, take them to home screen otherwise keep them in current component
            if (qmEventConfig.isComponentLoginRequired(currentComponent)) {
                // Ask user to log in again
                prompLogin();
            } else {
                $state.go($state.current, $state.params, {
                    reload: true,
                    priority: 10
                });
            }
        }

        $modalInstance.dismiss('cancel');
    };

    $scope.refreshSession = function(password) {

        var defer = $q.defer();
        $scope.isSubmitting = true;
        var loginUrl = qmEventConfig.getRpcUrl('login');
        var username = qmLogin.getUserName();

        qmRpc.post(loginUrl, {method: 'login', params: [username, password]}).then(function(response) {
            if (response.token) {
                $scope.incorrectLogin = false;
                $scope.unknownError = false;
                qmLogin.setUserToken(response.token);
                $modalInstance.close(response.token);
                defer.resolve();
            } else {
                $scope.incorrectLogin = false;
                $scope.unknownError = true;
                defer.reject();
            }
        }, function(err) {
            if (err.error) {
                $scope.alertLoginIncorrectMessage = qmLogin.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.login = function(password) {

        var defer = $q.defer();

        try {

            var factory = $injector.get('qmLogonFactoryService');

            if (factory.isContainerLogin()) {

                var username = qmLogin.getUserName();
                var service = factory.getLogonService();
                service.doLogin(username, password).then(function(response) {
                    // Silently log the user into the QE
                    var ssoToken = service.getSSOToken();
                    var currentAppId = service.getCurrentAppId();
                    if (currentAppId) {
                        service.doSilentLogin(ssoToken, username, currentAppId).then(function(token) {

                            // Has the event transition finished ?
                            if (!qmStateChange.didCompleteEventTransition && qmStateChange.transitionLevel === 'container') {
                                // No then we are at the container. Set the event app id no null so that we get the
                                // container config when we try to apply the style
                                qmWebAppData.setEventAppId(null);
                            }
                            $modalInstance.close(token);
                            defer.resolve();

                        }, function() {
                            defer.reject();
                        });
                    } else {
                        $modalInstance.close(response.token);
                        defer.resolve();
                    }

                }, function() {

                    defer.reject();
                });

            } else {

                $scope.refreshSession(password).then(function() {
                    defer.resolve();
                }, function() {
                    defer.reject();
                });
            }

        } catch (e) {
            // This is an app with a lower version of logon ( <= 2.7)
            $scope.refreshSession(password).then(function() {
                defer.resolve();
            }, function() {
                defer.reject();
            });
        }

        return defer.promise;
    };
}]);

var sessionManagement = angular.module('session-management-V2_0');

/**
 * @ngdoc service
 * @name session-management-V2_0.service:qmSessionManager
 * @description
 * Session management service
 */
sessionManagement.factory('qmSessionManager',
['$window', '$interval', 'qmSessionManagerStorage', 'qmLogin', 'qmConfigManager', 'qmWebAppData', '$modal', 'qmRpc',
function($window, $interval, qmSessionManagerStorage, qmLogin, qmConfigManager, qmWebAppData, $modal, qmRpc) {
    var updateExpiryInterval = null;
    return {
        /**
         * @ngdoc method
         * @name promptPassword
         * @methodOf session-management-V2_0.service:qmSessionManager
         * @description
         * Prompts user for password
         *
         * @returns {promise} Promise that resolves if user enters correct password
         */
        promptPassword: function() {
            var openParams = {
                templateUrl: '/asset/component/session-management/2.0/webapp/html/password-modal.html',
                controller: 'PasswordController',
                backdrop: 'static',
                keyboard: false
            };
            var passwordModal = $modal.open(openParams);
            return passwordModal.result;
        },
        /**
         * @ngdoc method
         * @name setSessionId
         * @methodOf session-management-V2_0.service:qmSessionManager
         * @description
         * Set session id
         *
         * @param {string} sessionId Session Id
         */
        setSessionId: function(sessionId) {
            qmSessionManagerStorage.setAppItem('sessionId', sessionId);
        },
        /**
         * @ngdoc method
         * @name getSessionId
         * @methodOf session-management-V2_0.service:qmSessionManager
         * @description
         * Get session id
         *
         * @returns {string} Session id
         */
        getSessionId: function() {
            return qmSessionManagerStorage.getAppItem('sessionId');
        },
        /**
         * @ngdoc method
         * @name setIdleTime
         * @methodOf session-management-V2_0.service:qmSessionManager
         * @description
         * Set session idle time
         *
         * @param {Number} idleTime Idle time in milliseconds
         */
        setIdleTime: function(idleTime) {
            qmSessionManagerStorage.setAppItem('idleTime', idleTime);
        },
        /**
         * @ngdoc method
         * @name setIdleTime
         * @methodOf session-management-V2_0.service:qmSessionManager
         * @description
         * Get session idle time
         *
         * @returns {Number} Idle time in milliseconds
         */
        getIdleTime: function() {
            var idleTime = qmSessionManagerStorage.getAppItem('idleTime');
            return parseInt(idleTime, 10);
        },
        /**
         * @ngdoc method
         * @name setIdleExpiryTime
         * @methodOf session-management-V2_0.service:qmSessionManager
         * @description
         * Set session idle expiry time
         *
         * @param {Number} expiryTime Idle expiry time in unix timestamp
         */
        setIdleExpiryTime: function(expiryTime) {
            qmSessionManagerStorage.setAppItem('idleExpiryTime', expiryTime);
        },
        /**
         * @ngdoc method
         * @name setIdleTime
         * @methodOf session-management-V2_0.service:qmSessionManager
         * @description
         * Get session idle expiry time
         *
         * @returns {Number} Idle expiry time in unix timestamp
         */
        getIdleExpiryTime: function() {
            var idleTime = qmSessionManagerStorage.getAppItem('idleExpiryTime');
            return parseInt(idleTime, 10);
        },
        /**
         * @ngdoc method
         * @name getSessionIdForAppId
         * @methodOf session-management-V2_0.service:qmSessionManager
         * @description
         * Get session id given appId
         *
         * @returns {string} Session id
         */
        getSessionIdForAppId: function(appId) {
            return qmSessionManagerStorage.getItemForAppWithId('sessionId', appId);
        },

        /**
         * @ngdoc method
         * @name setSessionIdForAppId
         * @methodOf session-management-V2_0.service:qmSessionManager
         * @description
         * Sets session id for the app given
         *
         * @returns {string} Session id
         */
        setSessionIdForAppId: function(appId, sessionId) {
            qmSessionManagerStorage.setItemForAppWithId('sessionId', appId, sessionId);
        },

        /**
         * @ngdoc method
         * @name setIdleTimeForAppId
         * @methodOf session-management-V2_0.service:qmSessionManager
         * @description
         * Sets idle time for the app given app id
         */
        setIdleTimeForAppId: function(appId, expiryTime) {
            qmSessionManagerStorage.setItemForAppWithId('idleTime', appId, expiryTime);
        },

        /**
         * @ngdoc method
         * @name getIdleTimeForAppId
         * @methodOf session-management-V2_0.service:qmSessionManager
         * @description
         * Get idle time given appId
         *
         * @returns {Number} Idle time in milliseconds
         */
        getIdleTimeForAppId: function(appId) {
            return qmSessionManagerStorage.getItemForAppWithId('idleTime', appId);
        },

        /**
         * @ngdoc method
         * @name setIdleExpiryTimeForAppId
         * @methodOf session-management-V2_0.service:qmSessionManager
         * @description
         * Sets idle expiry time for the app given app id
         */
        setIdleExpiryTimeForAppId: function(appId, expiryTime) {
            qmSessionManagerStorage.setItemForAppWithId('idleExpiryTime', appId, expiryTime);
        },

        /**
         * @ngdoc method
         * @name getIdleExpiryTimeForAppId
         * @methodOf session-management-V2_0.service:qmSessionManager
         * @description
         * Gets idle expiry time for the app given app id
         *
         * @returns {Number} Idle expiry time in unix timestamp
         */
        getIdleExpiryTimeForAppId: function(appId) {

            var idleTime = qmSessionManagerStorage.getItemForAppWithId('idleExpiryTime', appId);

            return parseInt(idleTime, 10);
        },

        /**
         * @ngdoc method
         * @name updateExpiryTimeForAppId
         * @methodOf session-management-V2_0.service:qmSessionManager
         * @description
         * Updates the expiry time for the app given app id
         */
        updateExpiryTimeForAppId: function(appId) {
            var currentTime = new Date().getTime();
            var idleTime = this.getIdleTimeForAppId(appId);
            var idleExpiryTime = currentTime + idleTime;
            this.setIdleExpiryTimeForAppId(appId, idleExpiryTime);
        },

        /**
         * @ngdoc method
         * @name startExpiryInterval
         * @methodOf session-management-V2_0.service:qmSessionManager
         * @description
         * Start expiry update interval timer
         *
         */
        startExpiryInterval: function() {
            if (!updateExpiryInterval) {
                var that = this;
                var containerAppId = qmWebAppData.getContainerAppId();
                var sessionId = this.getSessionIdForAppId(containerAppId);

                if (sessionId) {
                    updateExpiryInterval = $interval(function() {
                        that.updateExpiryTimeForAppId(containerAppId);
                    }, 60000);
                } else {
                    // Update idle time expiry timestamp every minute in case onunload event listener doesn't complete
                    updateExpiryInterval = $interval(function() {
                        that.updateExpiryTime();
                    }, 60000);
                }
            }
        },

        /**
         * @ngdoc method
         * @name stopExpiryInterval
         * @methodOf session-management-V2_0.service:qmSessionManager
         * @description
         * Stop expiry update interval timer
         *
         */
        stopExpiryInterval: function() {
            if (updateExpiryInterval) {
                $interval.cancel(updateExpiryInterval);
                updateExpiryInterval = null;
            }
        },
        /**
         * @ngdoc method
         * @name updateExpiryTime
         * @methodOf session-management-V2_0.service:qmSessionManager
         * @description
         * Update session expiry based on stored idle time
         *
         */
        updateExpiryTime: function() {
            var currentTime = new Date().getTime();
            var idleTime = this.getIdleTime();
            var idleExpiryTime = currentTime + idleTime;
            this.setIdleExpiryTime(idleExpiryTime);
        },
        /**
         * @ngdoc method
         * @name checkSessionExpired
         * @methodOf session-management-V2_0.service:qmSessionManager
         * @description
         * Check if session is expired
         *
         */
        checkSessionExpired: function() {
            if (qmWebAppData.isEvent() && qmLogin.isLoggedIn()) {
                var currentTime = new Date().getTime();
                var containerAppId = qmWebAppData.getContainerAppId();
                var sessionIdleExpiryTime = this.getIdleExpiryTimeForAppId(containerAppId);
                var sessionId = this.getSessionIdForAppId(containerAppId);

                if (!sessionId) {
                    // Session management storage is stored at the QE
                    sessionIdleExpiryTime = this.getIdleExpiryTime();
                    sessionId = this.getSessionId();
                }

                if (currentTime >= sessionIdleExpiryTime) {
                    if (sessionId) {
                        // We have reached session timeout, lets call logout rpc to let server know
                        var logoutUrl = qmConfigManager.getConfig().getRpcUrl('logout');
                        var params = [];
                        params.push(qmLogin.getUserToken());
                        qmRpc.post(logoutUrl, {method: 'logout', params: params});
                        this.reset();
                    }

                    qmLogin.promptPassword();
                    this.stopExpiryInterval();
                } else {
                    this.updateExpiryTime();
                    // make sure expiry interval is started
                    this.startExpiryInterval();
                }
            }
        },
        /**
         * @ngdoc method
         * @name logout
         * @methodOf session-management-V2_0.service:qmSessionManager
         * @description
         * Reset session management
         *
         */
        reset: function() {

            var containerAppId = qmWebAppData.getContainerAppId();
            var sessionId = this.getSessionIdForAppId(containerAppId);

            if (sessionId) {
                qmSessionManagerStorage.removeAppItemForAppId('sessionId', containerAppId);
                qmSessionManagerStorage.removeAppItemForAppId('idleTime', containerAppId);
                qmSessionManagerStorage.removeAppItemForAppId('idleExpiryTime', containerAppId);

            } else {
                qmSessionManagerStorage.removeAppItem('sessionId');
                qmSessionManagerStorage.removeAppItem('idleTime');
                qmSessionManagerStorage.removeAppItem('idleExpiryTime');
            }

            this.stopExpiryInterval();
        }
    };
}]);

var sessionManagement = angular.module('session-management-V2_0');
/**
 * @ngdoc service
 * @name session-management-V2_0.service:qmSessionManagerStorage
 * @description
 * Service for session management storage
 */
sessionManagement.factory('qmSessionManagerStorage', ['qmBaseLocalStorage', function(qmBaseLocalStorage) {
    var localStorageKey = 'session-management';
    var localStorageVersion = '2.0';

    var sessionManagementStorage = new qmBaseLocalStorage(localStorageKey, localStorageVersion);
    return sessionManagementStorage;
}]);

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

messaging.run(['$rootScope', 'qmLogin', 'qmMessagingService', 'qmDependencyManager',
    function($rootScope, qmLogin, qmMessagingService, qmDependencyManager) {
        $rootScope.$on('refreshEventData', function() {
            if (qmLogin.isLoggedIn() && qmDependencyManager.hasComponent('messaging')) {
                if (qmMessagingService.getMessagingType() !== 'Email') {
                    qmMessagingService.getUnreadMessageCount();
                }
            }
        });
    }]);

messaging.config(['$stateProvider', function($stateProvider) {
    var preventEmailMessaging = ['$q', '$state', '$stateParams', '$timeout', 'qmLogin', 'qmMessagingService',
        function($q, $state, $stateParams, $timeout, qmLogin, qmMessagingService) {
            if (qmLogin.isLoggedIn()) {
                if (qmMessagingService.getMessagingType() === 'Email') {
                    return $timeout(function() {
                        $state.go('event.messaging.email_attendee_list', {appId: $stateParams.appId, componentId: $stateParams.componentId});
                    });
                }
            }
        }];
    $stateProvider
        .state('event.messaging-V2_1', {
            url: '/messaging',
            views: {
                'component@event': {
                    controller: 'MessagingMainController'
                }
            }
        })
        .state('event.messaging-V2_1.home', {
            url: '',
            views: {
                'component@event': {
                    templateUrl: '/asset/component/messaging/2.0/webapp/html/messaging-home.html',
                    controller: 'MessagingHomeController'
                },
                'header@event': {
                    controller: 'MessagingHomeHeaderController'
                }
            },
            resolve: {
                check: preventEmailMessaging
            }
        })
        .state('event.messaging-V2_1.inbox', {
            url: '/inbox',
            views: {
                'component@event': {
                    templateUrl: '/asset/component/messaging/2.0/webapp/html/messaging-inbox-list.html',
                    controller: 'MessagingInboxListController'
                },
                'header@event': {
                    controller: 'MessagingInboxListHeaderController'
                }
            },
            resolve: {
                check: preventEmailMessaging
            }
        })
        .state('event.messaging-V2_1.sent', {
            url: '/sent',
            views: {
                'component@event': {
                    templateUrl: '/asset/component/messaging/2.0/webapp/html/messaging-sent-list.html',
                    controller: 'MessagingSentListController'
                },
                'header@event': {
                    controller: 'MessagingSentListHeaderController'
                }
            },
            resolve: {
                check: preventEmailMessaging
            }
        })
        .state('event.messaging-V2_1.attendee_list', {
            url: '/select',
            views: {
                'component@event': {
                    templateUrl: '/asset/component/messaging/2.0/webapp/html/messaging-attendee-list.html',
                    controller: 'MessagingAttendeeListController'
                },
                'header@event': {
                    controller: 'MessagingAttendeeListHeaderController'
                }
            },
            resolve: {
                check: preventEmailMessaging
            }
        })
        .state('event.messaging-V2_1.forward_attendee_list', {
            url: '/forward/:messageId',
            views: {
                'component@event': {
                    templateUrl: '/asset/component/messaging/2.0/webapp/html/messaging-forward-attendee-list.html',
                    controller: 'MessagingAttendeeListController'
                },
                'header@event': {
                    controller: 'MessagingAttendeeListHeaderController'
                }
            },
            resolve: {
                check: preventEmailMessaging
            }
        })
        .state('event.messaging-V2_1.compose', {
            url: '/compose/:attendeeId',
            views: {
                'component@event': {
                    templateUrl: '/asset/component/messaging/2.0/webapp/html/messaging-compose.html',
                    controller: 'MessagingComposeController'
                },
                'header@event': {
                    controller: 'MessagingComposeHeaderController'
                }
            }
        })
        .state('event.messaging-V2_1.imsg_detail', {
            url: '/inbox/:messageId',
            views: {
                'component@event': {
                    templateUrl: '/asset/component/messaging/2.0/webapp/html/messaging-detail.html',
                    controller: 'MessagingInboxDetailController'
                },
                'header@event': {
                    controller: 'MessagingInboxDetailHeaderController'
                }
            },
            resolve: {
                check: preventEmailMessaging
            }
        })
        .state('event.messaging-V2_1.smsg_detail', {
            url: '/sent/:messageId',
            views: {
                'component@event': {
                    templateUrl: '/asset/component/messaging/2.0/webapp/html/messaging-detail.html',
                    controller: 'MessagingSentDetailController'
                },
                'header@event': {
                    controller: 'MessagingSentDetailHeaderController'
                }
            },
            resolve: {
                check: preventEmailMessaging
            }
        })
        .state('event.messaging-V2_1.reply', {
            url: '/reply/:messageId',
            views: {
                'component@event': {
                    templateUrl: '/asset/component/messaging/2.0/webapp/html/messaging-compose.html',
                    controller: 'MessagingReplyController'
                },
                'header@event': {
                    controller: 'MessagingReplyHeaderController'
                }
            },
            resolve: {
                check: preventEmailMessaging
            }
        })
        .state('event.messaging-V2_1.forward', {
            url: '/forward/:messageId/:attendeeId',
            views: {
                'component@event': {
                    templateUrl: '/asset/component/messaging/2.0/webapp/html/messaging-compose.html',
                    controller: 'MessagingForwardController'
                },
                'header@event': {
                    controller: 'MessagingForwardHeaderController'
                }
            },
            resolve: {
                check: preventEmailMessaging
            }
        })
        .state('event.messaging-V2_1.email_attendee_list', {
            url: '/email',
            views: {
                'component@event': {
                    templateUrl: '/asset/component/messaging/2.0/webapp/html/messaging-email-attendee-list.html',
                    controller: 'MessagingAttendeeListController'
                },
                'header@event': {
                    controller: 'MessagingAttendeeListHeaderController'
                }
            }
        });
}]);

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

/**
 * @ngdoc service
 * @name messaging-V2_0.service:qmMessagingService
 * @description
 * Manages REST API communication for Messaging
 */
messaging.factory('qmMessagingService', ['qmRest', 'qmRpc', '$q', '$filter', 'qmWebAppService',
    'qmEventConfig', 'qmLogin', '$rootScope', 'qmMoment', 'qmAnalytics',
    function(qmRest, qmRpc, $q, $filter, qmWebAppService,
             qmEventConfig, qmLogin, $rootScope, qmMoment, qmAnalytics) {

        var previousStateName = '';

        return {

            setPreviousStateName: function(fromStateName) {
                if (angular.isDefined(fromStateName)) {
                    previousStateName = fromStateName;
                }
            },
            getPreviousStateName: function() {
                return previousStateName;
            },
            /**
             * @ngdoc method
             * @name getInboxMessages
             * @methodOf messaging-V2_0.service:qmMessagingService
             * @description
             * Get list of inbox messages
             *
             * @returns {promise} Promise that resolves when inbox list has been retrieved
             */
            getInboxMessages: function() {

                var url = qmWebAppService.getBaseRestUrl('component', 'messaging') + '/inbox';
                var defer = $q.defer();
                // get list data
                qmRest.get(url).then(function(response) {
                    var messagesList = response;
                    for (var i = 0; i < messagesList.length; i++) {
                        messagesList[i].sentTime = qmMoment(messagesList[i].sentTime).format('LLLL');
                    }
                    defer.resolve(messagesList);
                }, function(err) {
                    defer.reject(err);
                });

                return defer.promise;
            },
            /**
             * @ngdoc method
             * @name getSentMessages
             * @methodOf messaging-V2_0.service:qmMessagingService
             * @description
             * Get list of sent messages
             *
             * @returns {promise} Promise that resolves when list has been retrieved
             */
            getSentMessages: function() {

                var url = qmWebAppService.getBaseRestUrl('component', 'messaging') + '/sent';
                var defer = $q.defer();
                // get list data
                qmRest.get(url).then(function(response) {
                    var messagesList = response;
                    for (var i = 0; i < messagesList.length; i++) {
                        messagesList[i].sentTime = qmMoment(messagesList[i].sentTime).format('LLLL');
                    }
                    defer.resolve(messagesList);
                }, function(err) {
                    defer.reject(err);
                });

                return defer.promise;
            },
            /**
             * @ngdoc method
             * @name getUnreadMessageCount
             * @methodOf messaging-V2_0.service:qmMessagingService
             * @description
             * Get unread message count
             *
             * @returns {promise} Promise that resolves when message count has been returned
             */
            getUnreadMessageCount: function() {

                var defer = $q.defer();
                var url = qmWebAppService.getBaseRestUrl('component', 'messaging') + '/unread-message-count';
                qmRest.get(url).then(function(response) {
                    $rootScope.$broadcast('menuBadgeUpdate', 'messaging', response.count);
                    defer.resolve(response.count);

                });

                return defer.promise;
            },

            /**
             * @ngdoc method
             * @name createMessage
             * @methodOf messaging-V2_0.service:qmMessagingService
             * @description
             * Create new message
             *
             * @returns {promise} Promise that returns when email message has been sent
             */
            createEmailMessage: function(attendeeIds, subject, body) {

                var defer = $q.defer();
                var createUrl = qmEventConfig.getRpcUrl('sendEmailMessage');
                var params = [];
                params.push(qmLogin.getUserToken());
                params.push(attendeeIds);
                params.push(subject);
                params.push(body);

                qmRpc.post(createUrl, {method: 'sendEmailMessage', params: params}).then(function(response) {
                    qmAnalytics.markEvent('MessageSentView (Email)');
                    defer.resolve(response);
                    $rootScope.$broadcast('gameActivity', {activity: 'message'});
                }, function(err) {
                    defer.reject(err);
                });

                return defer.promise;
            },
            /**
             * @ngdoc method
             * @name createMessage
             * @methodOf messaging-V2_0.service:qmMessagingService
             * @description
             * Create new message
             *
             * @returns {promise} Promise that returns when in app message is sent
             */
            createInAppMessage: function(attendeeIds, subject, body) {

                var defer = $q.defer();
                var createUrl = qmEventConfig.getRpcUrl('sendInAppMessage');
                var params = [];
                params.push(qmLogin.getUserToken());
                params.push(attendeeIds);
                params.push(subject);
                params.push(body);

                qmRpc.post(createUrl, {method: 'sendInAppMessage', params: params}).then(function(response) {
                    qmAnalytics.markEvent('MessageSentView');
                    defer.resolve(response);
                    $rootScope.$broadcast('gameActivity', {activity: 'message'});
                }, function(err) {
                    defer.reject(err);
                });

                return defer.promise;
            },
            /**
             * @ngdoc method
             * @name getInboxMessageDetail
             * @methodOf messaging-V2_0.service:qmMessagingService
             * @description
             * Get detail of an inbox message
             *
             * @param {string} messageId Message Id
             * @returns {promise} Promise that resolves when detail has been retrieved
             */
            getInboxMessageDetail: function(messageId) {

                var url = qmWebAppService.getBaseRestUrl('component', 'messaging') + '/inbox/' + messageId;
                var defer = $q.defer();
                qmRest.get(url).then(function(response) {
                    var messageDetail = response;
                    messageDetail.sentTime = qmMoment(messageDetail.sentTime).format('LLLL');
                    defer.resolve(messageDetail);
                }, function(err) {
                    defer.reject(err);
                });

                return defer.promise;
            },
            /**
             * @ngdoc method
             * @name getSentMessageDetail
             * @methodOf messaging-V2_0.service:qmMessagingService
             * @description
             * Get detail of an sent message
             *
             * @param {string} messageId Message Id
             * @returns {promise} Promise that resolves when detail has been retrieved
             */
            getSentMessageDetail: function(messageId) {

                var url = qmWebAppService.getBaseRestUrl('component', 'messaging') + '/sent/' + messageId;
                var defer = $q.defer();
                // get message detail data
                qmRest.get(url).then(function(response) {
                    var messageDetail = response;
                    messageDetail.sentTime = qmMoment(messageDetail.sentTime).format('LLLL');
                    defer.resolve(messageDetail);
                }, function(err) {
                    defer.reject(err);
                });

                return defer.promise;
            },
            /**
             * @ngdoc method
             * @name deleteInboxMessage
             * @methodOf messaging-V2_0.service:qmMessagingService
             * @description
             * Delete a message from the inbox
             *
             * @param {string} id Message Id
             * @returns {promise} Promise that resolves when inbox message is deleted
             */
            deleteInboxMessage: function(id) {

                var url = qmWebAppService.getBaseRestUrl('component', 'messaging') + '/inbox/' + id;
                var defer = $q.defer();
                qmRest.delete(url).then(function(response) {
                    defer.resolve(response);
                }, function(err) {
                    defer.reject(err);
                });

                return defer.promise;
            },
            /**
             * @ngdoc method
             * @name deleteSentMessage
             * @methodOf messaging-V2_0.service:qmMessagingService
             * @description
             * Delete a message from the sent
             *
             * @param {string} id Message Id
             * @returns {promise} Promise that resolves when sent message is deleted
             */
            deleteSentMessage: function(id) {

                var url = qmWebAppService.getBaseRestUrl('component', 'messaging') + '/sent/' + id;
                var defer = $q.defer();
                qmRest.delete(url).then(function(response) {
                    defer.resolve(response);
                }, function(err) {
                    defer.reject(err);
                });

                return defer.promise;
            },
            /**
             * @ngdoc method
             * @name markInboxMessageAsRead
             * @methodOf messaging-V2_0.service:qmMessagingService
             * @description
             * Mark inbox message as read
             *
             * @param {string} messageId messageId Id
             * @returns {promise} Promise that resolves when message has been marked as read
             */
            markInboxMessageAsRead: function(messageId) {

                var url = qmWebAppService.getBaseRestUrl('component', 'messaging') + '/inbox/' + messageId + '/read';
                var defer = $q.defer();
                var $this = this;
                var params = {};
                qmRest.put(url, params).then(function(response) {
                    defer.resolve(response);
                    $this.getUnreadMessageCount();
                }, function(err) {
                    defer.reject(err);
                });

                return defer.promise;
            },
            /**
             * @ngdoc method
             * @name getMessagingType
             * @methodOf messaging-V2_0.service:qmMessagingService
             * @description
             * Get messaging type from messaging component config
             *
             * @returns {string} Messaging type
             */
            getMessagingType: function() {

                var config = qmEventConfig.getComponentConfig('messaging');
                var messagingType = config['@attributes']['messagingType'];
                return messagingType;
            }
        };
    }]);

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

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

/**
 * @ngdoc controller
 * @name messaging-V2_0.controller:MessagingMainController
 * @description
 * Controller for messaging
 */
messaging.controller('MessagingMainController', ['$scope', '$state', 'qmMessagingService',
    function($scope, $state, qmMessagingService) {
        if (qmMessagingService.getMessagingType() !== 'Email') {
            $state.go('event.messaging.home');
        } else {
            $state.go('event.messaging.email_attendee_list');
        }
    }]);

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

/**
 * @ngdoc controller
 * @name messaging-V2_0.controller:MessagingHomeController
 * @description
 * Controller for messaging home view
 */
messaging.controller('MessagingHomeController', ['$scope', 'qmMessagingService',
    function($scope, qmMessagingService) {

        $scope.loadUnreadMessageCount = qmMessagingService.getUnreadMessageCount().then(function(newMessageCount) {
            $scope.newMessageCount = newMessageCount;
        });
    }]);

/**
 * @ngdoc controller
 * @name messaging-V2_0.controller:MessagingHomeHeaderController
 * @description
 * Controller for messaging home header view
 */
messaging.controller('MessagingHomeHeaderController', ['$scope', 'qmLocalization', '$state',
    function($scope, qmLocalization, $state) {

        $scope.headerTitle = qmLocalization.getString('componentMessagingTitle');
        $scope.headerTitleTestId = 'componentTitle';
        $scope.headerRightElements = [];
        var createElement = {};
        createElement.type = 'img';
        createElement.src = '/asset/service/web-app/2.1/images/icon_write.png';
        createElement.dataTestId = 'messagingCompose';
        createElement.clickHandler = function() {
            $state.go('event.messaging.attendee_list');
        };
        $scope.headerRightElements.push(createElement);
    }]);

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

/**
 * @ngdoc controller
 * @name messaging-V2_0.controller:MessagesListController
 * @description
 * Controller for inbox list view
 */
messaging.controller('MessagingInboxListController', ['$scope', 'qmMessagingService', 'qmList', 'qmLocalization',
    function($scope, qmMessagingService, qmList, qmLocalization) {
        $scope.loadMessages = qmMessagingService.getInboxMessages();
        $scope.loadMessages.then(function(messages) {
            $scope.messages = qmList.transformList(messages);
            var inboxTitle = qmLocalization.getString('LABEL_INBOX');
            $scope.emptyText = qmLocalization.getString('LABEL_EMPTY_MESSAGING_TITLE', inboxTitle);
        });
    }
]);

/**
 * @ngdoc controller
 * @name messaging-V2_0.controller:MessagingInboxListHeaderController
 * @description
 * Controller for inbox list header view
 */
messaging.controller('MessagingInboxListHeaderController', ['$scope', 'qmLocalization', '$state', function($scope, qmLocalization, $state) {
    $scope.headerBackState = true;
    $scope.headerTitle = qmLocalization.getString('LABEL_INBOX');
    $scope.headerTitleTestId = 'componentTitle';

    $scope.headerRightElements = [];
    var createElement = {};
    createElement.type = 'icon';
    createElement.class = 'fa fa-pencil-square-o';
    createElement.dataTestId = 'messagingCompose';
    createElement.clickHandler = function() {
        $state.go('event.messaging.attendee_list');
    };
    $scope.headerRightElements.push(createElement);
}]);

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

/**
 * @ngdoc controller
 * @name messaging-V2_0.controller:MessagesSentListController
 * @description
 * Controller for sent message list view
 */
messaging.controller('MessagingSentListController', ['$scope', 'qmMessagingService', 'qmList', 'qmLocalization',
    function($scope, qmMessagingService, qmList, qmLocalization) {
        $scope.loadMessages = qmMessagingService.getSentMessages();
        $scope.loadMessages.then(function(messages) {
            $scope.messages = qmList.transformList(messages);
            var sentTitle = qmLocalization.getString('LABEL_SENT');
            $scope.emptyText = qmLocalization.getString('LABEL_EMPTY_MESSAGING_TITLE', sentTitle);
        });
    }
]);

/**
 * @ngdoc controller
 * @name messaging-V2_0.controller:MessagingSentListHeaderController
 * @description
 * Controller for sent list header view
 */
messaging.controller('MessagingSentListHeaderController', ['$scope', 'qmLocalization', '$state', function($scope, qmLocalization, $state) {
    $scope.headerBackState = true;
    $scope.headerTitle = qmLocalization.getString('LABEL_SENT');
    $scope.headerTitleTestId = 'componentTitle';

    $scope.headerRightElements = [];
    var createElement = {};
    createElement.type = 'icon';
    createElement.class = 'fa fa-pencil-square-o';
    createElement.dataTestId = 'messagingCompose';
    createElement.clickHandler = function() {
        $state.go('event.messaging.attendee_list');
    };
    $scope.headerRightElements.push(createElement);
}]);

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

/**
 * @ngdoc controller
 * @name messaging-V2_0.controller:MessagingComposeController
 * @description
 * Controller for Messaging Compose
 */
messaging.controller('MessagingComposeController', ['$scope', 'qmMessagingService', '$state', 'qmMessagingDependencyService',
    'toastr', 'qmLocalization', 'qmNavigation',
    function($scope, qmMessagingService, $state, qmMessagingDependencyService, toastr, qmLocalization, qmNavigation) {

        var attendeeId = $state.params.attendeeId;

        $scope.attendeeId = attendeeId;

        var qmAttendeeService = qmMessagingDependencyService.getAttendeeService('qmAttendeeService');
        $scope.setting = qmAttendeeService.getSetting();
        $scope.showImage = $scope.setting.photo.isVisible;

        var attendeeDetail = qmAttendeeService.getDetail(attendeeId);
        attendeeDetail.then(function(detail) {
            $scope.attendeeName = detail.firstName + ' ' + detail.lastName;
        });

        $scope.message = {};
        $scope.message.status = 'empty';

        $scope.confirm = {
            no: qmLocalization.getString('ALERT_NO'),
            yes: qmLocalization.getString('ALERT_YES'),
            title: qmLocalization.getString('ALERT_WARNING_TITLE'),
            content: qmLocalization.getString('ALERT_LOST_CHANGE_MESSAGE'),
            isEmpty: function() {
                return ($scope.message.status === 'empty');
            }
        };

        $scope.cancel = function() {
            qmNavigation.goBack();
        };

        $scope.save = function() {
            var subject = $scope.message.subject;
            var body = $scope.message.body;
            if (typeof subject === 'undefined' || subject === '') {
                toastr.warning(qmLocalization.getString('ALERT_EMPTY_MESSAGE'), '', {
                    closeButton: true,
                    timeOut: 5000
                });

                return false;
            }

            var messagingType = qmMessagingService.getMessagingType();
            if (messagingType === 'Email') {
                // Create new message
                return qmMessagingService.createEmailMessage([attendeeId], subject, body).then(function() {
                    $scope.message.status = 'empty';
                    qmNavigation.goBack();
                    toastr.success(qmLocalization.getString('ALERT_EMAIL_SEND_SUCCESS_MESSAGE'), '', {
                        closeButton: true,
                        timeOut: 5000
                    });
                });
            } else {
                // Create new message
                return qmMessagingService.createInAppMessage([attendeeId], subject, body).then(function() {
                    $scope.message.status = 'empty';
                    if (qmMessagingService.getPreviousStateName().match(/event\.messaging(-V[0-9]+_[0-9]+)\.attendee_list/)) {
                        $state.go('event.messaging');
                    } else {
                        qmNavigation.goBack();
                    }
                    toastr.success(qmLocalization.getString('ALERT_SUCCESS_MESSAGE'), '', {
                        closeButton: true,
                        timeOut: 5000
                    });
                });
            }
        };
    }]);

/**
 * @ngdoc controller
 * @name messaging-V2_0.controller:MessagingComposeHeaderController
 * @description
 * Controller for composing header
 */
messaging.controller('MessagingComposeHeaderController', ['$scope', 'qmLocalization', 'qmMessagingService',
    function($scope, qmLocalization, qmMessagingService) {

        var messagingType = qmMessagingService.getMessagingType();
        if (messagingType === 'Email') {
            $scope.headerTitle = qmLocalization.getString('LABEL_EMAIL');
        } else {
            $scope.headerTitle = qmLocalization.getString('componentMessagingTitle');
        }
        $scope.headerBackState = true;
    }
]);

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

/**
 * @ngdoc controller
 * @name messaging-V2_0.controller:MessagingReplyController
 * @description
 * Controller for Messaging Compose
 */
messaging.controller('MessagingReplyController', ['$scope', 'qmMessagingService', '$state', 'toastr', 'qmLocalization', 'qmNavigation',
    function($scope, qmMessagingService, $state, toastr, qmLocalization, qmNavigation) {

        $scope.message = {};
        $scope.message.status = 'empty';
        $scope.confirm = {
            no: qmLocalization.getString('ALERT_NO'),
            yes: qmLocalization.getString('ALERT_YES'),
            title: qmLocalization.getString('ALERT_WARNING_TITLE'),
            content: qmLocalization.getString('ALERT_LOST_CHANGE_MESSAGE'),
            isEmpty: function() {
                return ($scope.message.status === 'empty');
            }
        };

        $scope.cancel = function() {

            qmNavigation.goBack();
        };

        var messageId = $state.params.messageId;
        var messageDetail = qmMessagingService.getInboxMessageDetail(messageId);
        messageDetail.then(function(detail) {

            $scope.attendeeName = detail.senderName;
            $scope.attendeeId = detail.senderId;
            $scope.message.subject = 'RE: ' + detail.subject;
            $scope.message.body = '\n' + '----------------------------------------------------------' +
                    '\n' + qmLocalization.getString('LABEL_SEARCH_MESSAGE_FROM') + ' ' + detail.senderName +
                    '\n' + qmLocalization.getString('LABEL_DATE') + ': ' + detail.sentTime +
                    '\n' + qmLocalization.getString('LABEL_SEARCH_MESSAGE_TO') + ' ' + detail.recipientName +
                    '\n' + qmLocalization.getString('LABEL_SEARCH_MESSAGE_SUBJECT') + ': ' + detail.subject +
                    '\n' + detail.body;
            $scope.save = function() {
                var subject = $scope.message.subject;
                var body = $scope.message.body;
                if (typeof subject === 'undefined' || subject === '' || typeof body === 'undefined' || body === '') {
                    toastr.warning(qmLocalization.getString('ALERT_EMPTY_MESSAGE'), '', {
                        closeButton: true,
                        timeOut: 5000
                    });

                    return false;
                }
                // Create new message
                return qmMessagingService.createInAppMessage([$scope.attendeeId], subject, body).then(function() {
                    $scope.message.status = 'empty';
                    $state.go('event.messaging');
                    toastr.success(qmLocalization.getString('ALERT_SUCCESS_MESSAGE'), '', {
                        closeButton: true,
                        timeOut: 5000
                    });
                });
            };
        });

    }]);

/**
 * @ngdoc controller
 * @name messaging-V2_0.controller:MessagingReplyHeaderController
 * @description
 * Controller for reply message
 */
messaging.controller('MessagingReplyHeaderController', ['$scope', 'qmLocalization',
    function($scope, qmLocalization) {
        $scope.headerTitle = qmLocalization.getString('BUTTON_REPLY');
        $scope.headerBackState = true;
    }
]);

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

/**
 * @ngdoc controller
 * @name messaging-V2_0.controller:MessagingForwardController
 * @description
 * Controller for Messaging Compose
 */
messaging.controller('MessagingForwardController', ['$scope', 'qmMessagingService', 'qmMessagingDependencyService', '$state', 'toastr', 'qmLocalization',
    'qmNavigation',
    function($scope, qmMessagingService, qmMessagingDependencyService, $state, toastr, qmLocalization, qmNavigation) {

        var messageId = $state.params.messageId;
        var attendeeId = $state.params.attendeeId;
        $scope.attendeeId = attendeeId;

        var qmAttendeeService = qmMessagingDependencyService.getAttendeeService('qmAttendeeService');
        var attendeeDetail = qmAttendeeService.getDetail(attendeeId);
        attendeeDetail.then(function(detail) {
            $scope.attendeeName = detail.firstName + ' ' + detail.lastName;
        });

        $scope.message = {};
        $scope.message.status = 'empty';
        $scope.confirm = {
            no: qmLocalization.getString('ALERT_NO'),
            yes: qmLocalization.getString('ALERT_YES'),
            title: qmLocalization.getString('ALERT_WARNING_TITLE'),
            content: qmLocalization.getString('ALERT_LOST_CHANGE_MESSAGE'),
            isEmpty: function() {
                return ($scope.message.status === 'empty');
            }
        };

        $scope.cancel = function() {

            qmNavigation.goBack();
        };

        var messageDetail = qmMessagingService.getInboxMessageDetail(messageId);
        messageDetail.then(function(detail) {

            $scope.attendeeId = attendeeId;
            $scope.message.subject = 'FW: ' + detail.subject;
            $scope.message.body = '\n' + '----------------------------------------------------------' +
                    '\n' + qmLocalization.getString('LABEL_SEARCH_MESSAGE_FROM') + ' ' + detail.senderName +
                    '\n' + qmLocalization.getString('LABEL_DATE') + ': ' + detail.sentTime +
                    '\n' + qmLocalization.getString('LABEL_SEARCH_MESSAGE_TO') + ' ' + detail.recipientName +
                    '\n' + qmLocalization.getString('LABEL_SEARCH_MESSAGE_SUBJECT') + ': ' + detail.subject +
                    '\n' + detail.body;

            $scope.save = function() {

                var subject = $scope.message.subject;
                var body = $scope.message.body;
                if (typeof subject === 'undefined' || subject === '' || typeof body === 'undefined' || body === '') {
                    toastr.warning(qmLocalization.getString('ALERT_EMPTY_MESSAGE'), '', {
                        closeButton: true,
                        timeOut: 5000
                    });

                    return false;
                }

                // Create new message
                return qmMessagingService.createInAppMessage([$scope.attendeeId], subject, body).then(function() {
                    $scope.message.status = 'empty';
                    $state.go('event.messaging.home');
                    toastr.success(qmLocalization.getString('ALERT_SUCCESS_MESSAGE'), '', {
                        closeButton: true,
                        timeOut: 5000
                    });
                });
            };
        });
    }]);

/**
 * @ngdoc controller
 * @name messaging-V2_0.controller:MessagingForwardHeaderController
 * @description
 * Controller for reply message
 */
messaging.controller('MessagingForwardHeaderController', ['$scope', 'qmLocalization',
    function($scope, qmLocalization) {
        $scope.headerTitle = qmLocalization.getString('BUTTON_FORWARD');
        $scope.headerBackState = true;
    }
]);

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

/**
 * @ngdoc controller
 * @name messaging-V2_0.controller:MessagingAttendeeListController
 * @description
 * Controller for messaging attendee list view
 */
messaging.controller('MessagingAttendeeListController', ['$scope', 'qmMessagingDependencyService', 'qmList', '$state', '$q',
    function($scope, qmMessagingDependencyService, qmList, $state, $q) {
        $scope.messageId = $state.params.messageId;
        var qmAttendeeService = qmMessagingDependencyService.getAttendeeService('qmAttendeeService');
        var qmAttendeeDependencyService = qmMessagingDependencyService.getAttendeeService('qmAttendeeDependencyService');

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

        var loadDefer = $q.defer();
        $scope.loadAttendeesPromise = loadDefer.promise;
        $scope.loadAttendees = function(filter, itemOffset, moreItemsNum) {
            var defer = $q.defer();
            qmAttendeeService.getList([], filter, itemOffset, moreItemsNum, true).then(function(data) {
                var attendees = qmList.groupByLetter(data, 'lastName');
                defer.resolve(attendees);
                loadDefer.resolve();
            }, function() {
                defer.reject();
                loadDefer.reject();
            });
            return defer.promise;
        };
    }]);

/**
 * @ngdoc controller
 * @name messaging-V2_0.controller:AttendeeListHeaderController
 * @description
 * Controller for attendee list header view
 */
messaging.controller('MessagingAttendeeListHeaderController', ['$scope', 'qmLocalization', '$state',
    function($scope, qmLocalization, $state) {
        $scope.headerTitle = qmLocalization.getString('componentMessagingTitle');
        if ($state.current.url === '/email') {
            $scope.headerBackState = false;
        } else {
            $scope.headerBackState = true;
        }
    }]);

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

/**
 * @ngdoc controller
 * @name messaging-V2_0.controller:MessagingInboxDetailController
 * @description
 * Controller for message detail view
 */
messaging.controller('MessagingInboxDetailController', ['$scope', '$state', 'qmMessagingService', 'qmLocalization', 'toastr', 'qmMessagingDependencyService',
    function($scope, $state, qmMessagingService, qmLocalization, toastr, qmMessagingDependencyService) {
        var componentTitle = qmLocalization.getString('componentMessagingTitle');

        $scope.confirmTitle = componentTitle;
        $scope.confirmNo = qmLocalization.getString('BUTTON_CANCEL');
        $scope.confirmYes = qmLocalization.getString('BUTTON_OK');
        $scope.confirmContent = qmLocalization.getString('ALERT_REMOVE_MESSAGE').replace('{0}', componentTitle);

        // Get the message detail
        $scope.loadMessages = qmMessagingService.getInboxMessageDetail($state.params.messageId);
        $scope.loadMessages.then(function(messageIn) {
            if (messageIn.isRead === '0') {
                qmMessagingService.markInboxMessageAsRead($state.params.messageId);
            }
            if (messageIn.senderId === 'Admin') {
                $scope.showDelete = 0;
                $scope.showReply = 0;
            } else {
                $scope.showDelete = 1;
                var qmAttendeeService = qmMessagingDependencyService.getAttendeeService('qmAttendeeService');
                qmAttendeeService.getDetail(messageIn.senderId).then(function(attendee) {
                    $scope.showReply = attendee.allowMessage;
                });
            }
            $scope.messagingObj = messageIn;
            $scope.senderId = messageIn.senderId;

        });

        $scope.confirmDelete = function(messageId) {
            return qmMessagingService.deleteInboxMessage(messageId).then(function() {
                $state.go('event.messaging.home');
                toastr.success(qmLocalization.getString('ALERT_EMAIL_DELETED_MESSAGE'), '', {
                    closeButton: true,
                    timeOut: 5000
                });
            });
        };
    }]);

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

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

/**
 * @ngdoc controller
 * @name messaging-V2_0.controller:MessagingSentDetailController
 * @description
 * Controller for message detail view
 */
messaging.controller('MessagingSentDetailController', ['$scope', '$state', 'qmMessagingService', 'toastr',
    'qmLocalization',
    function($scope, $state, qmMessagingService, toastr, qmLocalization) {
        var componentTitle = qmLocalization.getString('componentMessagingTitle');

        $scope.confirmTitle = componentTitle;
        $scope.confirmNo = qmLocalization.getString('BUTTON_CANCEL');
        $scope.confirmYes = qmLocalization.getString('BUTTON_OK');
        $scope.confirmContent = qmLocalization.getString('ALERT_REMOVE_MESSAGE').replace('{0}', componentTitle);

        $scope.loadMessages = qmMessagingService.getSentMessageDetail($state.params.messageId);
        $scope.showReply = 0;
        $scope.showDelete = 0;
        $scope.loadMessages.then(function(messageIn) {
            $scope.messagingObj = messageIn;
        });

        $scope.confirmDelete = function(messageId) {
            return qmMessagingService.deleteSentMessage(messageId).then(function() {
                $state.go('event.messaging.home');
                toastr.success(qmLocalization.getString('ALERT_EMAIL_DELETED_MESSAGE'), '', {
                    closeButton: true,
                    timeOut: 5000
                });
            });
        };
    }]);

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

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

mydocuments.config(['$stateProvider', function($stateProvider) {
    $stateProvider
        .state('event.my-documents-V2_1', {
            url: '/MyDocuments',
            views: {
                'component@event': {
                    templateUrl: '/asset/component/my-documents/2.0/webapp/html/mydocument-list.html',
                    controller: 'MyDocumentsListController'
                },
                'header@event': {
                    controller: 'MyDocumentsListHeaderController'
                }
            }
        });
}]);

var mydocuments = angular.module('my-documents-V2_0');

/**
 * @ngdoc service
 * @name my-documents-V2_0.service:qmMyDocumentsService
 * @description
 * Manages REST API communication for Documents
 */
mydocuments.factory('qmMyDocumentsService',
['$q', 'qmLogin', 'qmDependencyManager', 'qmMyDocumentsDependencyService',
'qmAnalytics', '$rootScope',
    function($q, qmLogin, qmDependencyManager, qmMyDocumentsDependencyService,
        qmAnalytics, $rootScope) {
        var getDocumentsService = function() {
            return qmMyDocumentsDependencyService.getDocumentsService('qmDocumentsService');
        };

        return {
            /**
             * @ngdoc method
             * @name getList
             * @methodOf my-documents-V2_0.service:qmMyDocumentsService
             * @description
             * Get list of documents
             *
             * @returns {promise} Promise that resolves when document list has been retrieved
             */
            getList: function() {
                return getDocumentsService().getMyDocuments();
            },
            /**
             * @ngdoc method
             * @name delete
             * @methodOf my-documents-V2_0.service:qmMyDocumentsService
             * @description
             * Delete attendee document link
             *
             * @param {string} $documentId Document Id
             * @returns {promise} Promise that resolves when my document has been removed
             */
            deleteMyDocument: function($documentId) {
                var defer = $q.defer();
                qmLogin.checkLogin().then(function() {
                    var qmDocumentsService = getDocumentsService();
                    qmDocumentsService.deleteMyDocument($documentId).then(function(response) {
                        qmAnalytics.markEvent('MyDocumentsDelete', $documentId);
                        defer.resolve(response);
                    }, function(err) {
                        defer.reject(err);
                    });
                });

                return defer.promise;
            },
            /**
             * @ngdoc method
             * @name addMyDocument
             * @methodOf my-documents-V2_0.service:qmMyDocumentsService
             * @description
             * Add attendee document link
             *
             * @param {string} $documentId Document Id
             * @returns {promise} Promise that resolves when document is added
             */
            addMyDocument: function($documentId) {
                var defer = $q.defer();
                qmLogin.checkLogin().then(function() {
                    var qmDocumentsService = getDocumentsService();
                    qmDocumentsService.addMyDocument($documentId).then(function(response) {
                        qmAnalytics.markEvent('MyDocumentsAdd', $documentId);
                        defer.resolve(response);
                        $rootScope.$broadcast('gameActivity', {activity: 'myBriefcase'});
                    }, function(err) {
                        defer.reject(err);
                    });
                });

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

var mydocuments = angular.module('my-documents-V2_0');

/**
 * @ngdoc service
 * @name my-documents-V2_0.service:qmMyDocumentsDependencyService
 * @description
 * Manages communication with dependency manager
 */
mydocuments.factory('qmMyDocumentsDependencyService', ['qmDependencyManager', function(qmDependencyManager) {
    return {
        /**
         * @ngdoc method
         * @name getDocumentsService
         * @methodOf my-documents-V2_0.service:qmMyDocumentsDependencyService
         * @description
         * Get service from documents component
         *
         * @param {string} service Service name
         * @returns {service} Angular service or null
         */
        getDocumentsService: function(service) {
            return qmDependencyManager.getRequiredService('documents', '2.1', 'component', service);
        }
    };
}]);

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

/**
 * @ngdoc controller
 * @name my-documents-V2_1.controller:MyDocumentsListController
 * @description
 * Controller for document list view
 */
mydocuments.controller('MyDocumentsListController',
['$scope', 'qmMyDocumentsService', 'qmList', 'qmEventConfig', '$window', '$rootScope', 'qmLocalization', 'qmMyDocumentsDependencyService',
    function($scope, qmMyDocumentsService, qmList, qmEventConfig, $window, $rootScope, qmLocalization, qmMyDocumentsDependencyService) {
        $scope.loadDocuments = qmMyDocumentsService.getList();
        $scope.loadDocuments.then(function(documents) {
            $scope.documents = qmList.groupByGroup(documents, 'groupValue', 'groupLabel');
            var qmDocumentsDependencyService = qmMyDocumentsDependencyService.getDocumentsService('qmDocumentsDependencyService');
            $scope.documentListItemUrl = qmDocumentsDependencyService.getTemplateUrl('document-list-item');

            var componentTitle = qmLocalization.getString('componentMydocumentsTitle');
            $scope.emptyText = qmLocalization.getString('LABEL_EMPTY_MY_DOCUMENT_TITLE', componentTitle);
            var documentTitle = qmLocalization.getString('componentDocumentsTitle');
            $scope.emptySubText = qmLocalization.getString('LABEL_EMPTY_MY_DOCUMENT_MESSAGE', documentTitle);
            if ($scope.documents.length > 0) {
                var hasShareableDocuments = 0;
                for (var i = 0; i < $scope.documents.length; i++) {
                    for (var j = 0; j < $scope.documents[i].items.length; j++) {
                        if (angular.isUndefined($scope.documents[i].items[j].shareable) || $scope.documents[i].items[j].shareable === '1') {
                            hasShareableDocuments++;
                        }
                    }
                }
                if (hasShareableDocuments) {
                    $rootScope.$broadcast('hasMyDocuments');
                }
            }
        });
        $scope.goToDocument = function(documentObj) {
            var qmDocumentsHelperService = qmMyDocumentsDependencyService.getDocumentsService('qmDocumentsHelperService');
            qmDocumentsHelperService.goToDocumentDetail(documentObj);
        };

        $scope.$on('emailMyDocuments', function() {
            var subject = qmLocalization.getString('ALERT_MAIL_EXPORT_SUBJECT');
            subject = subject.replace('{0}', qmLocalization.getString('componentMydocumentsTitle'));
            subject = subject.replace('{1}', qmEventConfig.getEventName());

            var body = qmLocalization.getString('ALERT_MAIL_EXPORT_BODY') + ':\r\n\r\n';
            body = body.replace('{0}', qmLocalization.getString('componentMydocumentsTitle'));
            body = body.replace('{1}', qmEventConfig.getEventName());

            for (var i = 0; i < $scope.documents.length; i++) {
                for (var j = 0; j < $scope.documents[i].items.length; j++) {
                    if (angular.isUndefined($scope.documents[i].items[j].shareable) || $scope.documents[i].items[j].shareable === '1') {
                        body += $scope.documents[i].items[j].title;
                        body += '\r\n';
                        body += $scope.documents[i].items[j].documentUrl;
                        body += '\r\n\r\n';
                    }
                }
            }
            $window.location = 'mailto:?subject=' + encodeURIComponent(subject) + '&body=' + encodeURIComponent(body);
        });
    }
]);

/**
 * @ngdoc controller
 * @name my-documents-V2_1.controller:MyDocumentListHeaderController
 * @description
 * Controller for document list header view
 */
mydocuments.controller('MyDocumentsListHeaderController', ['$scope', 'qmLocalization', '$rootScope', function($scope, qmLocalization, $rootScope) {
    $scope.headerTitle = qmLocalization.getString('componentMydocumentsTitle');

    $scope.headerRightElements = [];

    $scope.$on('hasMyDocuments', function() {
        var emailElement = {};
        emailElement.type = 'icon';
        emailElement.class = 'fa fa-envelope';
        emailElement.clickHandler = function() {
            $rootScope.$broadcast('emailMyDocuments');
        };
        $scope.headerRightElements.push(emailElement);
    });
}]);

var myexhibitors = angular.module('my-exhibitors-V2_0');

myexhibitors.config(['$stateProvider', function($stateProvider) {
    $stateProvider
        .state('event.my-exhibitors-V2_0', {
            url: '/MyExhibitors',
            views: {
                'component@event': {
                    templateUrl: '/asset/component/my-exhibitors/2.0/webapp/html/myexhibitor-list.html',
                    controller: 'MyExhibitorsListController'
                },
                'header@event': {
                    controller: 'MyExhibitorsListHeaderController'
                }
            }
        });
}]);

var myexhibitors = angular.module('my-exhibitors-V2_0');

/**
 * @ngdoc service
 * @name my-exhibitors-V2_0.service:qmMyExhibitorsService
 * @description
 * Manages REST API communication for Exhibitors
 */
myexhibitors.factory('qmMyExhibitorsService', ['qmRest', '$q', 'qmEventConfig', 'qmLogin',
    'qmAnalytics', '$rootScope', 'qmMyExhibitorsDependencyService',
function(qmRest, $q, qmEventConfig, qmLogin, qmAnalytics, $rootScope, qmMyExhibitorsDependencyService) {

    var getExhibitorsService = function() {
        return qmMyExhibitorsDependencyService.getExhibitorsService('qmExhibitorsService');
    };

    return {
        /**
         * @ngdoc method
         * @name getList
         * @methodOf my-exhibitors-V2_0.service:qmMyExhibitorsService
         * @description
         * Get list of exhibitors
         *
         * @returns {promise} Promise that resolves when exhibitor list has been retrieved
         */
        getList: function() {
            var defer = $q.defer();
            var exhibitors = [];
            getExhibitorsService().getList().then(function(response) {
                angular.forEach(response.exhibitors, function(exhibitor) {
                    if (exhibitor.isMyFavourite) {
                        exhibitors.push(exhibitor);
                    }
                });
                if (qmMyExhibitorsDependencyService.hasLikesOnExhibitors()) {
                    getExhibitorsService().getExhibitorLikes(exhibitors).then(function() {
                        defer.resolve(exhibitors);
                    });
                }
                defer.resolve(exhibitors);
            }, function(err) {
                defer.reject(err);
            });

            return defer.promise;
        },

        /**
         * @ngdoc method
         * @name deleteMyExhibitor
         * @methodOf my-exhibitors-V2_0.service:qmMyExhibitorsService
         * @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 qmExhibitorsService = getExhibitorsService();
            var defer = $q.defer();
            qmExhibitorsService.deleteMyExhibitor(exhibitorId).then(function(response) {
                qmAnalytics.markEvent('MyExhibitorsDelete', exhibitorId);
                defer.resolve(response);
            }, function(err) {
                defer.reject(err);
            });

            return defer.promise;
        },
        /**
         * @ngdoc method
         * @name addMyExhibitor
         * @methodOf my-exhibitors-V2_0.service:qmMyExhibitorsService
         * @description
         * Add attendee exhibitor link
         *
         * @param {string} exhibitorId Exhibitor Id
         * @returns {promise} Promise that resolves when exhibitor is added
         */
        addMyExhibitor: function(exhibitorId) {
            var qmExhibitorsService = getExhibitorsService();
            var defer = $q.defer();
            qmExhibitorsService.addMyExhibitor(exhibitorId).then(function(response) {
                qmAnalytics.markEvent('MyExhibitorsAdd', exhibitorId);
                defer.resolve(response);
                $rootScope.$broadcast('gameActivity', {activity: 'myExhibitor'});
            }, function(err) {
                defer.reject(err);
            });

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

var myexhibitors = angular.module('my-exhibitors-V2_0');

/**
 * @ngdoc service
 * @name my-exhibitors-V2_0.service:qmMyExhibitorsDependencyService
 * @description
 * Manages communication with dependency manager
 */
myexhibitors.factory('qmMyExhibitorsDependencyService', ['qmDependencyManager', function(qmDependencyManager) {
    return {
        /**
         * @ngdoc method
         * @name getExhibitorsService
         * @methodOf my-exhibitors-V2_0.service:qmMyExhibitorsDependencyService
         * @description
         * Get service from documents component
         *
         * @param {string} service Service name
         * @returns {service} Angular service or null
         */
        getExhibitorsService: function(service) {
            return qmDependencyManager.getRequiredService('exhibitors', '2.2', 'component', service);
        },
        /**
         * @ngdoc method
         * @name hasLikesOnExhibitors
         * @methodOf my-exhibitors-V2_0.service:qmMyExhibitorsDependencyService
         * @description
         * Check if exhibitors 2.3 configurable for this project
         *
         * @returns {Boolean} Will return true if exhibitors 2.3 configurable for this project
         */
        hasLikesOnExhibitors: function() {
            return qmDependencyManager.hasComponent('exhibitors', '2.3');
        }
    };
}]);

var myexhibitors = angular.module('my-exhibitors-V2_0');

/**
 * @ngdoc controller
 * @name my-exhibitors-V2_0.controller:MyExhibitorsListController
 * @description
 * Controller for exhibitor list view
 */
myexhibitors.controller('MyExhibitorsListController',
['$rootScope', '$scope', '$window', 'qmMyExhibitorsService', 'qmList', 'qmLocalization', 'qmMyExhibitorsDependencyService', 'qmEventConfig',
    function($rootScope, $scope, $window, qmMyExhibitorsService, qmList, qmLocalization, qmMyExhibitorsDependencyService, qmEventConfig) {
        var getEmailRow = function(text) {
            if (text.length > 0) {
                text = '\r\n' + text;
            }

            return text;
        };

        var addNonEmptyToArray = function(array, text) {
            if (text.length > 0) {
                array.push(text);
            }

            return array;
        };

        $scope.loadMyExhibitors = qmMyExhibitorsService.getList();
        $scope.loadMyExhibitors.then(function(exhibitors) {
            $scope.myexhibitors = qmList.groupByLetter(exhibitors, 'company');
            var qmExhibitorsDependencyService = qmMyExhibitorsDependencyService.getExhibitorsService('qmExhibitorsDependencyService');
            $scope.exhibitorListItemUrl = qmExhibitorsDependencyService.getTemplateUrl('exhibitor-list-item');

            var componentTitle = qmLocalization.getString('componentMyexhibitorsTitle');
            $scope.emptyText = qmLocalization.getString('LABEL_EMPTY_MY_EXHIBITORS_TITLE', componentTitle);
            var exhibitorsTitle = qmLocalization.getString('componentExhibitorsTitle');
            $scope.emptySubText = qmLocalization.getString('LABEL_EMPTY_MY_EXHIBITORS_MESSAGE', exhibitorsTitle);
            if ($scope.myexhibitors.length > 0) {
                $rootScope.$broadcast('hasMyExhibitors');
            }
        });
        $scope.$on('emailMyExhibitors', function() {
            var subject = qmLocalization.getString('ALERT_MAIL_MY_FAVOURITE_TITLE');
            subject = subject.replace('{0}', qmLocalization.getString('componentMyexhibitorsTitle'));
            subject = subject.replace('{1}', qmEventConfig.getEventName());

            var body = qmLocalization.getString('ALERT_MAIL_MY_FAVOURITE_MESSAGE') + ':\r\n\r\n';
            body = body.replace('{0}', qmLocalization.getString('componentMyexhibitorsTitle'));
            body = body.replace('{1}', qmEventConfig.getEventName());
            for (var i = 0; i < $scope.myexhibitors.length; i++) {
                for (var j = 0; j < $scope.myexhibitors[i].items.length; j++) {
                    var exhibitor = $scope.myexhibitors[i].items[j];
                    body += exhibitor.company + '\r\n';
                    var mainContactFullName = exhibitor.mainContactFirstName + ' ' + exhibitor.mainContactLastName;
                    body += getEmailRow(mainContactFullName);
                    body += getEmailRow(exhibitor.mainContactTitle);
                    body += getEmailRow(exhibitor.mainContactPhone);
                    body += getEmailRow(exhibitor.mainContactEmail);

                    body += '\r\n';
                    body += getEmailRow(exhibitor.address);
                    var cityStateZip = [];
                    cityStateZip = addNonEmptyToArray(cityStateZip, exhibitor.city);
                    cityStateZip = addNonEmptyToArray(cityStateZip, exhibitor.state);
                    cityStateZip = addNonEmptyToArray(cityStateZip, exhibitor.zipCode);
                    body += getEmailRow(cityStateZip.join(', '));
                    body += getEmailRow(exhibitor.country);
                    body += getEmailRow(exhibitor.url);
                    var boothNumber = qmLocalization.getString('LABEL_BOOTH_NUMBER') + ' ' + exhibitor.boothNumber;
                    body += getEmailRow(boothNumber);
                    body += getEmailRow(exhibitor.description);
                    body += '\r\n\r\n';
                }
            }
            $window.location = 'mailto:?subject=' + encodeURIComponent(subject) + '&body=' + encodeURIComponent(body);
        });
    }
]);

/**
 * @ngdoc controller
 * @name my-exhibitors-V2_0.controller:MyExhibitorListHeaderController
 * @description
 * Controller for exhibitor list header view
 */
myexhibitors.controller('MyExhibitorsListHeaderController', ['$rootScope', '$scope', 'qmLocalization', function($rootScope, $scope, qmLocalization) {
    $scope.headerTitle = qmLocalization.getString('componentMyexhibitorsTitle');

    $scope.headerRightElements = [];

    $scope.$on('hasMyExhibitors', function() {
        var emailElement = {};
        emailElement.type = 'icon';
        emailElement.class = 'fa fa-envelope';
        emailElement.clickHandler = function() {
            $rootScope.$broadcast('emailMyExhibitors');
        };
        $scope.headerRightElements.push(emailElement);
    });
}]);

var mynotes = angular.module('my-notes-V2_0');

mynotes.config(['$stateProvider', function($stateProvider) {
    $stateProvider
        .state('event.my-notes-V2_0', {
            url: '/MyNotes',
            views: {
                'component@event': {
                    templateUrl: '/asset/component/my-notes/2.0/webapp/html/mynotes-list.html',
                    controller: 'MyNotesListController'
                },
                'header@event': {
                    controller: 'MyNotesListHeaderController'
                }
            }
        })
        .state('event.my-notes-V2_0.create', {
            url: '/create',
            views: {
                'component@event': {
                    templateUrl: '/asset/component/my-notes/2.0/webapp/html/mynotes-create.html',
                    controller: 'MyNotesCreateController'
                },
                'header@event': {
                    controller: 'MyNotesCreateHeaderController'
                }
            }
        })
        .state('event.my-notes-V2_0.detail', {
            url: '/:noteId',
            views: {
                'component@event': {
                    templateUrl: '/asset/component/my-notes/2.0/webapp/html/mynotes-detail.html',
                    controller: 'MyNotesDetailController'
                },
                'header@event': {
                    controller: 'MyNotesDetailHeaderController'
                }
            }
        })
        .state('event.my-notes-V2_0.detail.edit', {
            url: '/edit',
            views: {
                'component@event': {
                    templateUrl: '/asset/component/my-notes/2.0/webapp/html/mynotes-create.html',
                    controller: 'MyNotesEditController'
                },
                'header@event': {
                    controller: 'MyNotesEditHeaderController'
                }
            }
        });
}]);

var mynotes = angular.module('my-notes-V2_0');

/**
 * @ngdoc service
 * @name my-notes-V2_0.service:qmMyNotesService
 * @description
 * Manages REST API communication for My Notes
 */
mynotes.factory('qmMyNotesService',
['qmRest', '$q', '$filter', 'qmWebAppService', 'qmLogin', 'qmLocalization', 'qmAnalytics', '$rootScope', 'qmMoment', 'qmMyNotesDependencyService',
function(qmRest, $q, $filter, qmWebAppService, qmLogin, qmLocalization, qmAnalytics, $rootScope, qmMoment, qmMyNotesDependencyService) {

    return {
        /**
         * @ngdoc method
         * @name create
         * @methodOf my-notes-V2_0.service:qmMyNotesService
         * @description
         * Create a new general note
         *
         * @param {string} note Note content
         * @param {string} entityReference Note entity
         * @param {string} entityReferenceId Note entity id
         * @returns {promise} Promise that resolves when note has successfully been created
         */
        create: function(note, entityReference, entityReferenceId) {
            var createUrl = qmWebAppService.getBaseRestUrl('component', 'my-notes') + '/notes';
            var defer = $q.defer();
            var params = {
                note: note,
                entityReference: entityReference,
                entityReferenceId: entityReferenceId
            };
            qmRest.post(createUrl, params).then(function(response) {
                qmAnalytics.markEvent('MyNotesNewSaved');
                defer.resolve(response);
                $rootScope.$broadcast('gameActivity', {activity: 'myNote'});
            }, function(err) {
                defer.reject(err);
            });

            return defer.promise;
        },
        /**
         * @ngdoc method
         * @name edit
         * @methodOf my-notes-V2_0.service:qmMyNotesService
         * @description
         * Edit a note
         *
         * @param {string} id Note Id
         * @param {string} note Note content
         * @returns {promise} Promise that resolves when note has successfully been edited
         */
        edit: function(id, note) {
            var createUrl = qmWebAppService.getBaseRestUrl('component', 'my-notes') + '/notes/' + id;
            var defer = $q.defer();
            var params = {
                note: note
            };
            qmRest.put(createUrl, params).then(function(response) {
                qmAnalytics.markEvent('MyNotesEditViewSaved');
                defer.resolve(response);
            }, function(err) {
                defer.reject(err);
            });

            return defer.promise;
        },
        /**
         * @ngdoc method
         * @name delete
         * @methodOf my-notes-V2_0.service:qmMyNotesService
         * @description
         * Delete a note
         *
         * @param {string} id Note Id
         * @returns {promise} Promise that resolves when note has successfully been deleted
         */
        delete: function(id) {
            var deleteUrl = qmWebAppService.getBaseRestUrl('component', 'my-notes') + '/notes/' + id;
            var defer = $q.defer();
            qmRest.delete(deleteUrl).then(function(response) {
                defer.resolve(response);
            }, function(err) {
                defer.reject(err);
            });

            return defer.promise;
        },
        /**
         * @ngdoc method
         * @name getList
         * @methodOf my-notes-V2_0.service:qmMyNotesService
         * @description
         * Get list of my notes
         *
         * @returns {promise} Promise that resolves when list of notes have been retrieved
         */
        getList: function() {
            var getListUrl = qmWebAppService.getBaseRestUrl('component', 'my-notes') + '/notes';
            var defer = $q.defer();
            // get my notes list data
            qmRest.get(getListUrl).then(function(response) {
                var myNotesList = response;
                var generalNotes = [];
                var sessionNotes = [];
                var eventIds = [];
                for (var i = 0; i < myNotesList.length; i++) {
                    myNotesList[i].originalSortOrder = i;
                    myNotesList[i].lastMod =
                        qmMoment(myNotesList[i].lastMod).format('LLLL');
                    if (!myNotesList[i].entityReference && !myNotesList[i].entityReferenceId) {
                        myNotesList[i].groupValue = 'GROUP_GENERAL';
                        myNotesList[i].groupLabel = qmLocalization.getString('LABEL_GENERAL');
                        generalNotes.push(myNotesList[i]);
                    } else {
                        eventIds.push({'eventId': myNotesList[i].entityReferenceId});
                        sessionNotes.push(myNotesList[i]);
                    }
                }
                var myNotes = generalNotes;
                var eventsService = qmMyNotesDependencyService.getEventsService('qmEventsService');
                if (eventIds.length > 0 && eventsService) {
                    var events = {};
                    var existingSessionNotes = [];
                    var deletedSessionNotes = [];
                    eventsService.getList(eventIds).then(function(eventsList) {
                        for (var i = 0; i < eventsList.length; i++) {
                            events[eventsList[i].eventId] = eventsList[i];
                        }

                        for (var j = 0; j < sessionNotes.length; j++) {
                            var eventId = sessionNotes[j].entityReferenceId;
                            if (events[eventId]) {
                                sessionNotes[j].groupValue = eventId;
                                sessionNotes[j].groupLabel = events[eventId].title;
                                existingSessionNotes.push(sessionNotes[j]);
                            } else {
                                sessionNotes[j].groupValue = 'GROUP_GENERAL';
                                sessionNotes[j].groupLabel = qmLocalization.getString('LABEL_GENERAL');
                                deletedSessionNotes.push(sessionNotes[j]);
                            }
                        }
                        myNotes = myNotes.concat(deletedSessionNotes);
                        myNotes.sort(function(a, b) {
                            if (a.originalSortOrder < b.originalSortOrder) {
                                return -1;
                            }
                            return 1;
                        });
                        myNotes = myNotes.concat(existingSessionNotes);

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

            return defer.promise;
        },

        /**
         * @ngdoc method
         * @name getEventList
         * @methodOf my-notes-V2_0.service:qmMyNotesService
         * @description
         * Get list of my notes for an event
         *
         * @param {string} eventId Event Id
         * @returns {promise} Promise that resolves when list of notes have been retrieved
         */
        getEventList: function(eventId) {
            var getListUrl = qmWebAppService.getBaseRestUrl('component', 'my-notes') + '/notes';
            var defer = $q.defer();
            // get my notes list data
            qmRest.get(getListUrl).then(function(response) {
                var myNotesList = [];
                for (var i = 0; i < response.length; i++) {
                    if (response[i].entityReference === 'Events' && response[i].entityReferenceId === eventId) {
                        response[i].lastMod = qmMoment(response[i].lastMod).format('llll');
                        myNotesList.push(response[i]);
                    }
                }
                defer.resolve(myNotesList);
            }, function(err) {
                defer.reject(err);
            });

            return defer.promise;
        },
        /**
         * @ngdoc method
         * @name getNote
         * @methodOf my-notes-V2_0.service:qmMyNotesService
         * @description
         * Get details of a note
         *
         * @param {string} id Note Id
         * @returns {promise} Promise that resolves when details of a note have been retrieved
         */
        getNote: function(id) {
            var getNoteUrl = qmWebAppService.getBaseRestUrl('component', 'my-notes') + '/notes/' + id;
            var defer = $q.defer();
            qmRest.get(getNoteUrl).then(function(response) {
                var myNote = response;
                defer.resolve(myNote);
            }, function(err) {
                defer.reject(err);
            });

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

var mynotes = angular.module('my-notes-V2_0');

/**
 * @ngdoc service
 * @name my-notes-V2_0.service:qmMyNotesDependencyService
 * @description
 * Manages requests from dependency manager
 */
mynotes.factory('qmMyNotesDependencyService', ['qmDependencyManager', function(qmDependencyManager) {
    return {
        /**
         * @ngdoc method
         * @name getTemplateUrl
         * @methodOf my-notes-V2_0.service:qmMyNotesDependencyService
         * @description
         * Get template url for my notes component
         *
         * @param {string} name Template name
         * @returns {string} Template url
         */
        getTemplateUrl: function(name) {
            if (name === 'mynotes-list-item') {
                return '/asset/component/my-notes/2.0/webapp/html/partials/mynotes-list-item.html';
            }
            return null;
        },

        /**
         * @ngdoc method
         * @name getEventsService
         * @methodOf my-notes-V2_0.service:qmMyNotesDependencyService
         * @description
         * Get service from events component
         *
         * @param {string} service Service name
         * @returns {service} Angular service or null
         */
        getEventsService: function(service) {
            return qmDependencyManager.getService('events', '2.4', 'component', service);
        }
    };
}]);

var mynotes = angular.module('my-notes-V2_0');

/**
 * @ngdoc controller
 * @name my-notes-V2_0.controller:MyNotesListController
 * @description
 * Controller for My Notes list view
 */
mynotes.controller('MyNotesListController',
    ['$scope', 'qmMyNotesService', 'qmList', '$rootScope', '$state', 'qmEventConfig', '$window', 'qmLocalization',
        'qmMyNotesDependencyService', 'qmFlashDataStorage',
    function($scope, qmMyNotesService, qmList, $rootScope, $state, qmEventConfig, $window, qmLocalization,
             qmMyNotesDependencyService, qmFlashDataStorage) {

        var componentTitle = qmLocalization.getString('componentMynotesTitle');
        $scope.myNotesListItemUrl = qmMyNotesDependencyService.getTemplateUrl('mynotes-list-item');
        $scope.loadMyNotes = qmMyNotesService.getList();
        $scope.loadMyNotes.then(function(notes) {
            $scope.myNotes = qmList.groupByGroup(notes, 'groupValue', 'groupLabel');
            if (notes.length) {
                $rootScope.$broadcast('showEmailMyNotes');
            }
            $scope.emptyText = qmLocalization.getString('LABEL_EMPTY_MY_NOTES_TITLE', componentTitle);
        });
        $scope.$on('createMyNote', function() {
            $state.go('event.my-notes.create');
        });
        $scope.$on('emailMyNotes', function() {
            var subject = qmLocalization.getString('ALERT_MAIL_MY_FAVOURITE_TITLE', componentTitle, qmEventConfig.getEventName());
            var body = '';
            for (var i = 0; i < $scope.myNotes.length; i++) {
                if ($scope.myNotes[i].value !== 'GROUP_GENERAL') {
                    body += qmLocalization.getString('LABEL_EVENT') + ': ';
                }
                body += $scope.myNotes[i].label;
                body += '\r\n\r\n';   //newline
                for (var j = 0; j < $scope.myNotes[i].items.length; j++) {
                    body += qmLocalization.getString('LABEL_DATE') + ': ' + $scope.myNotes[i].items[j].lastMod;
                    body += '\r\n';
                    body += qmLocalization.getString('LABEL_NOTE') + ': ' + $scope.myNotes[i].items[j].note;
                    body += '\r\n';
                    body += '\r\n';
                }
            }
            $window.location = 'mailto:?subject=' + encodeURIComponent(subject) + '&body=' + encodeURIComponent(body);
        });

        qmFlashDataStorage.removeData('editNoteEntryPoint');
    }
]);

/**
 * @ngdoc controller
 * @name my-notes-V2_0.controller:MyNotesListHeaderController
 * @description
 * Controller for My Notes list header view
 */
mynotes.controller('MyNotesListHeaderController', ['$scope', 'qmLocalization', '$rootScope', function($scope, qmLocalization, $rootScope) {
    $scope.headerTitle = qmLocalization.getString('componentMynotesTitle');
    $scope.headerTitleTestId = 'componentTitle';

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

    $scope.$on('showEmailMyNotes', function() {
        var emailElement = {};
        emailElement.type = 'icon';
        emailElement.class = 'fa fa-envelope';
        emailElement.dataTestId = 'notesEmail';
        emailElement.clickHandler = function() {
            $rootScope.$broadcast('emailMyNotes');
        };
        $scope.headerRightElements.push(emailElement);
    });
}]);

var mynotes = angular.module('my-notes-V2_0');

/**
 * @ngdoc controller
 * @name my-notes-V2_0.controller:MyNotesCreateController
 * @description
 * Controller for My Notes create view
 */
mynotes.controller('MyNotesCreateController', ['$scope', 'qmMyNotesService', '$state', 'qmLocalization', '$rootScope',
    function($scope, qmMyNotesService, $state, qmLocalization, $rootScope) {
        $scope.note = {};
        $scope.confirm = {
            no: qmLocalization.getString('ALERT_NO'),
            yes: qmLocalization.getString('ALERT_YES'),
            title: qmLocalization.getString('ALERT_WARNING_TITLE'),
            content: qmLocalization.getString('ALERT_MY_NOTES_CLOSE_WITHOUT_SAVING_MESSAGE'),
            isEmpty: function() {
                return (!$scope.note.content || $scope.note.content.length === 0);
            },
            isSubmitting: function() {
                return $scope.savingNote === true;
            }
        };
        $scope.analyticsCreate = true;
        $scope.savingNote = false;
        $scope.$on('saveMyNote', function() {
            $scope.savingNote = true;
            qmMyNotesService.create($scope.note.content).then(function() {
                $state.go('^');
            }, function() {
                $scope.savingNote = false;
            });
        });

        $scope.$watch('note.content', function(newValue) {
            if (newValue === '' || typeof newValue === 'undefined') {
                $rootScope.$broadcast('myNotesDisableSaveButton');
            } else {
                $rootScope.$broadcast('myNotesEnableSaveButton');
            }
        });
    }]);

/**
 * @ngdoc controller
 * @name my-notes-V2_0.controller:MyNotesCreateHeaderController
 * @description
 * Controller for My Notes create header view
 */
mynotes.controller('MyNotesCreateHeaderController', ['$scope', 'qmLocalization', '$rootScope', function($scope, qmLocalization, $rootScope) {
    $scope.headerTitle = qmLocalization.getString('LABEL_NEW_NOTE');
    $scope.headerBackState = true;

    $scope.headerRightElements = [];
    var saveElement = {};
    saveElement.type = 'text';
    saveElement.disabled = false;
    saveElement.text = qmLocalization.getString('BUTTON_SAVE');
    saveElement.dataTestId = 'notesSave';
    saveElement.clickHandler = function() {
        $rootScope.$broadcast('saveMyNote');
    };
    $scope.headerRightElements.push(saveElement);
    $scope.$on('myNotesEnableSaveButton', function() {
        $scope.headerRightElements[0].disabled = false;
    });
    $scope.$on('myNotesDisableSaveButton', function() {
        $scope.headerRightElements[0].disabled = true;
    });
}]);

var mynotes = angular.module('my-notes-V2_0');

/**
 * @ngdoc controller
 * @name my-notes-V2_0.controller:MyNotesEditController
 * @description
 * Controller for My Notes edit view
 */
mynotes.controller('MyNotesEditController', ['$scope', 'qmMyNotesService', '$state', 'toastr', 'qmLocalization',
    'qmUtilities', '$rootScope', 'qmFlashDataStorage', 'qmNavigation',
    function($scope, qmMyNotesService, $state, toastr, qmLocalization, qmUtilities, $rootScope, qmFlashDataStorage,
             qmNavigation) {

        qmUtilities.addPageLoad();
        $scope.note = {};
        $scope.note.status = 'opened';
        $scope.confirm = {
            no: qmLocalization.getString('ALERT_NO'),
            yes: qmLocalization.getString('ALERT_YES'),
            title: qmLocalization.getString('ALERT_WARNING_TITLE'),
            content: qmLocalization.getString('ALERT_MY_NOTES_CLOSE_WITHOUT_SAVING_MESSAGE'),
            isEmpty: function() {
                return ($scope.note.originalContent === $scope.note.content);
            },
            isSubmitting: function() {
                return $scope.savingNote === true;
            }
        };
        $scope.analyticsEdit = true;
        $scope.savingNote = false;
        qmMyNotesService.getNote($state.params.noteId).then(function(response) {
            $scope.note.content = response.note;
            $scope.note.originalContent = response.note;
            qmUtilities.removePageLoad();
        });
        $scope.$on('saveMyNote', function() {
            $scope.savingNote = true;
            qmMyNotesService.edit($state.params.noteId, $scope.note.content).then(function() {

                qmNavigation.goBack();

            }, function() {
                $scope.savingNote = false;
            });
        });
        $scope.$watch('note.content', function(newValue) {
            if (newValue === '' || typeof newValue === 'undefined') {
                $rootScope.$broadcast('myNotesDisableSaveButton');
            } else {
                $rootScope.$broadcast('myNotesEnableSaveButton');
            }
        });
    }]
);

/**
 * @ngdoc controller
 * @name my-notes-V2_0.controller:MyNotesEditHeaderController
 * @description
 * Controller for My Notes edit header view
 */
mynotes.controller('MyNotesEditHeaderController', ['$scope', 'qmLocalization', '$rootScope', function($scope, qmLocalization, $rootScope) {
    $scope.headerTitle = qmLocalization.getString('LABEL_EDIT_MY_NOTE_TITLE');
    $scope.headerBackState = true;

    $scope.headerRightElements = [];
    var saveElement = {};
    saveElement.type = 'text';
    saveElement.text = qmLocalization.getString('BUTTON_SAVE');
    saveElement.clickHandler = function() {
        $rootScope.$broadcast('saveMyNote');
    };
    $scope.headerRightElements.push(saveElement);
    $scope.$on('myNotesEnableSaveButton', function() {
        $scope.headerRightElements[0].disabled = false;
    });
    $scope.$on('myNotesDisableSaveButton', function() {
        $scope.headerRightElements[0].disabled = true;
    });
}]);

var mynotes = angular.module('my-notes-V2_0');

/**
 * @ngdoc controller
 * @name my-notes-V2_0.controller:MyNotesDetailController
 * @description
 * Controller for My Notes detail view
 */
mynotes.controller('MyNotesDetailController', ['$scope', 'qmMyNotesService', '$state',
function($scope, qmMyNotesService, $state) {
    $scope.loadNote = qmMyNotesService.getNote($state.params.noteId);
    $scope.loadNote.then(function(response) {
        $scope.note = {};
        $scope.note.noteId = response.noteId;
        $scope.note.noteContent = response.note;
        if (response.entityReferenceId) {
            $scope.note.analyticsId = response.entityReferenceId;
        } else {
            $scope.note.analyticsId = response.noteId;
        }
    });
}]);

/**
 * @ngdoc controller
 * @name my-notes-V2_0.controller:MyNotesDetailHeaderController
 * @description
 * Controller for My Notes detail view
 */
mynotes.controller('MyNotesDetailHeaderController',
['$scope', 'qmLocalization', '$state', 'qmMyNotesService', 'qmUtilities', 'qmNavigation', 'qmHistory', 'qmEventHelper',
function($scope, qmLocalization, $state, qmMyNotesService, qmUtilities, qmNavigation, qmHistory, qmEventHelper) {
    $scope.headerTitle = qmLocalization.getString('LABEL_DETAILS');
    $scope.headerTitleTestId = 'componentTitle';
    $scope.headerBackState = true;

    $scope.headerRightElements = [];
    var editElement = {};
    editElement.type = 'icon';
    editElement.class = 'fa fa-pencil-square-o';
    editElement.dataTestId = 'noteDetailEdit';
    editElement.clickHandler = function() {
        $state.go('event.my-notes.detail.edit', {noteId: $state.params.noteId});
    };
    $scope.headerRightElements.push(editElement);

    var deleteElement = {};
    var deleteNote = function() {
        qmUtilities.addPageLoad();
        qmMyNotesService.delete($state.params.noteId).then(function() {
            var returnState = 'event.my-notes';
            var returnParams = {};

            var historySize = qmHistory.getSize();
            // User can come to my-notes detail from events component and go to different states like edit before deleting
            // Loop through history until we find the base my-notes component state or another component state
            for (var i = historySize - 1; i >= 0; i--) {
                var previousState = qmHistory.getAtIndex(i);
                var componentKey = qmEventHelper.getComponentKey(previousState.state);
                if (componentKey === 'my-notes') {
                    if (previousState.state.match(/^event\.my-notes(-V[0-9]+_[0-9]+)$/)) {
                        break;
                    }
                } else {
                    returnState = previousState.state;
                    returnParams = previousState.params;
                    break;
                }
            }
            $state.go(returnState, returnParams);
        }).finally(function() {
            qmUtilities.removePageLoad();
        });
    };
    deleteElement.type = 'icon';
    deleteElement.class = 'fa fa-remove';
    deleteElement.dataTestId = 'noteDetailDelete';
    deleteElement.clickHandler = function() {
        var confirmModalObj = {};
        confirmModalObj.title = '';
        confirmModalObj.content = qmLocalization.getString('ALERT_DELETE_CONFIRM_MESSAGE');
        confirmModalObj.noLabel = qmLocalization.getString('BUTTON_CANCEL');
        confirmModalObj.yesLabel = qmLocalization.getString('BUTTON_DELETE');
        qmUtilities.openConfirmModal(confirmModalObj).then(deleteNote);
    };
    $scope.headerRightElements.push(deleteElement);
}]);

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

myschedule.config(['$stateProvider', function($stateProvider) {
    $stateProvider
        .state('event.myschedule-V2_1', {
            url: '/MySchedule',
            views: {
                'component@event': {
                    templateUrl: '/asset/component/myschedule/2.0/webapp/html/myschedule-list.html',
                    controller: 'MyScheduleListController'
                },
                'header@event': {
                    controller: 'MyScheduleHeaderController'
                }
            }
        });
}]);

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

/**
 * @ngdoc service
 * @name myschedule-V2_1.service:qmMyScheduleService
 * @description
 * Manages REST API communication for MySchedule
 */
myschedule.factory('qmMyScheduleService', ['qmRpc', '$q', 'qmMoment', 'qmEventConfig', 'qmLogin', 'qmAnalytics', '$rootScope',
    'qmMyScheduleDependencyService',
    function(qmRpc, $q, qmMoment, qmEventConfig, qmLogin, qmAnalytics, $rootScope, qmMyScheduleDependencyService) {
        var config = qmEventConfig.getComponentConfig('myschedule');

        var getEventsService = function() {
            return qmMyScheduleDependencyService.getEventsService('qmEventsService');
        };

        return {
            /**
             * @ngdoc method
             * @name getList
             * @methodOf myschedule-V2_1.service:qmMyScheduleService
             * @description
             * Get list of Events for My Schedule listing view
             *
             * @returns {promise} Promise that resolves when list of myschedule have been retrieved
             */
            getList: function() {
                var defer = $q.defer();

                var events = [];
                getEventsService().getList().then(function(response) {
                    angular.forEach(response, function(event) {
                        if (event.isMyFavourite) {
                            if (event.eventDateFormatted) {
                                event.eventDateFormatted = qmMoment(event.eventDate).format('dddd, LL');
                            }

                            events.push(event);
                        }
                    });
                    events.sort(function(a, b) {
                        if (qmMoment(a.startDateTime).isSame(b.startDateTime)) {
                            if (qmMoment(a.endDateTime).isSame(b.endDateTime)) {
                                return a.title.localeCompare(b.title, undefined, {sensitivity: 'accent'});
                            }
                            return qmMoment(a.endDateTime).isBefore(b.endDateTime) ? -1 : 1;

                        }
                        return qmMoment(a.startDateTime).isBefore(b.startDateTime) ? -1 : 1;
                    });

                    if (qmMyScheduleDependencyService.hasLikesOnEvent()) {
                        getEventsService().getEventsLike(events).then(function() {
                            defer.resolve(events);
                        });
                    }
                    defer.resolve(events);
                }, function(err) {
                    defer.reject(err);
                });

                return defer.promise;
            },
            /**
             * @ngdoc method
             * @name deleteMySchedule
             * @methodOf myschedule-V2_1.service:qmMyScheduleService
             * @description
             * Delete attendee event link
             *
             * @param {string} eventId Event Id
             * @returns {promise} Promise that resolves when my schedule has been removed
             */
            deleteMySchedule: function(eventId) {
                var url = qmEventConfig.getRpcUrl('AddRemoveMySchedule');
                var defer = $q.defer();
                var params = {
                    method: 'AddRemoveMySchedule',
                    params: [
                        qmLogin.getUserToken(),
                        eventId,
                        'remove'
                    ]
                };
                qmRpc.post(url, params).then(function(response) {
                    qmAnalytics.markEvent('MyScheduleDelete', eventId);
                    defer.resolve(response);
                }, function(err) {
                    defer.reject(err);
                });

                return defer.promise;
            },
            /**
             * @ngdoc method
             * @name addMySchedule
             * @methodOf myschedule-V2_1.service:qmMyScheduleService
             * @description
             * Add attendee event link
             *
             * @param {string} eventId Event Id
             * @returns {promise} Promise that resolves when schedule has been added
             */
            addMySchedule: function(eventId) {
                var url = qmEventConfig.getRpcUrl('AddRemoveMySchedule');
                var defer = $q.defer();
                var params = {
                    method: 'AddRemoveMySchedule',
                    params: [
                        qmLogin.getUserToken(),
                        eventId,
                        'add'
                    ]
                };
                qmRpc.post(url, params).then(function(response) {
                    qmAnalytics.markEvent('MyScheduleAdd', eventId);
                    defer.resolve(response);
                    $rootScope.$broadcast('gameActivity', {activity: 'mySchedule'});
                }, function(err) {
                    defer.reject(err);
                });

                return defer.promise;
            },
            /**
             * @ngdoc method
             * @name allowDeleteMySchedule
             * @methodOf myschedule-V2_1.service:qmMyScheduleService
             * @description
             * Check if attendee is allowed to delete event from mySchedule, for the given eventId
             *
             * @param {string} createdBy Created by
             * @returns {promise} Promise that resolves when allowing deletion for my schedule is determined
             */
            allowDeleteMySchedule: function(createdBy, limitedVisibility) {
                var defer = $q.defer();
                var allowRemoveEvents = this.getSetting('allowRemoveEvents');
                var allowDelete = true;

                if (limitedVisibility === '1') {
                    allowDelete = false;
                } else if (createdBy === 'admin' && allowRemoveEvents === 'false') {
                    allowDelete = false;
                }

                defer.resolve(allowDelete);

                return defer.promise;
            },
            /**
             * @ngdoc method
             * @name getSetting
             * @methodOf myschedule-V2_1.service:qmMyScheduleService
             * @description
             * Get settings for myschedule component
             *
             * @returns {object} My schedule settings object
             */
            getSetting: function(keyName) {
                var setting = null;

                if (keyName in config['@attributes']) {
                    setting = config['@attributes'][keyName];
                }

                return setting;
            }
        };
    }]);

