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

/**
 * @ngdoc controller
 * @name settings.controller:SettingsQrCodeController
 * @description
 * Controller for settings QR code
 */
settings.controller('SettingsQrCodeController',
['$scope', 'qmLogin', 'qmLocalization', 'qmSettingsDependencyService',
function($scope, qmLogin, qmLocalization, qmSettingsDependencyService) {

    var profile = qmLogin.getUserInfo();
    var qmAttendeeService = qmSettingsDependencyService.getAttendeeService('qmAttendeeService');
    qmAttendeeService.getDetail(profile.attendeeId).then(function(attendeeData) {
        profile.pinId = attendeeData.pinId;
        if (profile.pinId === '' || angular.isUndefined(profile.pinId)) {
            $scope.myPin = qmLocalization.getString('LABEL_YOUR_PINCODE').replace('{0}', qmLocalization.getString('LABEL_PIN_UNASSIGNED'));
        } else {
            $scope.myPin = qmLocalization.getString('LABEL_YOUR_PINCODE').replace('{0}', profile.pinId);
            $scope.pinId = profile.pinId;
        }
    });
}]);

/**
 * @ngdoc controller
 * @name settings.controller:SettingsQrCodeHeaderController
 * @description
 * Controller for settings QR code header
 */
settings.controller('SettingsQrCodeHeaderController', ['$scope', 'qmLocalization', function($scope, qmLocalization) {
    $scope.headerTitle = qmLocalization.getString('LABEL_QR_CODE');
    $scope.headerBackState = true;
}]);

var cache = angular.module('cache');

var STORAGE = {
    key: 'webapp',
    version: '2.0',
    // use @ so we won't conflict with keys set by other components
    versionKey: '@version',
    appKey: '@projects',
    userKey: '@users',
    // Since user ids are user defined in QP a string null can cause a conflict with a real null
    // that gets stringified so we will use @null instead since we cannot have @ as part of the user id
    nullUser: '@null'
};

/**
 * @ngdoc service
 * @name config.service:qmLocalStorage
 * @description
 * Base object that qmLocalStorage and qmSessionStorage will inherit from
 */
cache.factory('qmBaseStorage', ['qmWebAppData', function(qmWebAppData) {
    function qmBaseStorage(storage) {
        this.storage = storage;
        this.storageKey = STORAGE.key;
        this.storageVersion = STORAGE.version;
        this.appId = null;
        this.userId = null;
    }
    qmBaseStorage.prototype.init = function(appId, userId) {
        this.appId = appId;
        this.userId = userId;
    };
    qmBaseStorage.prototype.getStorage = function() {
        return this.storage;
    };
    qmBaseStorage.prototype.setAppId = function(appId) {
        this.appId = appId;
    };
    qmBaseStorage.prototype.getAppId = function() {
        return this.appId;
    };
    qmBaseStorage.prototype.setUserId = function(userId) {
        this.userId = userId;
    };
    qmBaseStorage.prototype.getUserId = function() {
        return this.userId;
    };
    qmBaseStorage.prototype.resetUserId = function() {
        this.userId = null;
    };
    qmBaseStorage.prototype.setBaseItem = function(key, value) {
        if (angular.isDefined(value)) {
            var item = JSON.parse(this.storage.getItem(this.storageKey));
            item[key] = value;

            this.storage.setItem(this.storageKey, JSON.stringify(item));
        }
    };
    qmBaseStorage.prototype.setAppItem = function(key, value) {
        if (angular.isDefined(value)) {
            var item = JSON.parse(this.storage.getItem(this.storageKey));

            var appId = qmWebAppData.getAppId();
            if (!appId) {
                throw new Error('setAppItemError: Missing appId');
            }

            if (!item[STORAGE.appKey]) {
                item[STORAGE.appKey] = {};
            }
            if (!item[STORAGE.appKey][appId]) {
                item[STORAGE.appKey][appId] = {};
            }
            item[STORAGE.appKey][appId][key] = value;

            this.storage.setItem(this.storageKey, JSON.stringify(item));
        }
    };
    qmBaseStorage.prototype.setUserItem = function(key, value) {
        if (angular.isDefined(value)) {
            var item = JSON.parse(this.storage.getItem(this.storageKey));

            var appId = qmWebAppData.getAppId();
            if (!appId) {
                throw new Error('setUserItemError: Missing appId');
            }
            var userId = qmWebAppData.getUserId();
            if (!userId) {
                userId = STORAGE.nullUser;
            }

            if (!item[STORAGE.appKey]) {
                item[STORAGE.appKey] = {};
            }
            if (!item[STORAGE.appKey][appId]) {
                item[STORAGE.appKey][appId] = {};
            }
            if (!item[STORAGE.appKey][appId][STORAGE.userKey]) {
                item[STORAGE.appKey][appId][STORAGE.userKey] = {};
            }
            if (!item[STORAGE.appKey][appId][STORAGE.userKey][userId]) {
                item[STORAGE.appKey][appId][STORAGE.userKey][userId] = {};
            }
            item[STORAGE.appKey][appId][STORAGE.userKey][userId][key] = value;

            this.storage.setItem(this.storageKey, JSON.stringify(item));
        }
    };
    qmBaseStorage.prototype.getBaseItem = function(key) {
        try {
            var item = JSON.parse(this.storage.getItem(this.storageKey));
            return item[key];
        }
        catch (ex) {
        }
        return null;
    };
    qmBaseStorage.prototype.getAppItem = function(key) {
        try {
            var item = JSON.parse(this.storage.getItem(this.storageKey));
            var appId = qmWebAppData.getAppId();
            return item[STORAGE.appKey][appId][key];
        }
        catch (ex) {
        }
        return null;
    };
    qmBaseStorage.prototype.getItemForAppWithId = function(key, appId) {
        try {
            var item = JSON.parse(this.storage.getItem(this.storageKey));
            return item[STORAGE.appKey][appId][key];
        }
        catch (ex) {
        }
        return null;
    };

    qmBaseStorage.prototype.setItemForAppWithId = function(key, appId, value) {

        try {
            var item = JSON.parse(this.storage.getItem(this.storageKey));
            if (!appId) {
                throw new Error('setAppItemError: Missing appId');
            }

            if (!item[STORAGE.appKey]) {
                item[STORAGE.appKey] = {};
            }
            if (!item[STORAGE.appKey][appId]) {
                item[STORAGE.appKey][appId] = {};
            }
            item[STORAGE.appKey][appId][key] = value;

            this.storage.setItem(this.storageKey, JSON.stringify(item));
        }
        catch (ex) {
        }
    };
    qmBaseStorage.prototype.getUserItem = function(key) {
        try {
            var item = JSON.parse(this.storage.getItem(this.storageKey));
            var appId = qmWebAppData.getAppId();
            var userId = qmWebAppData.getUserId();
            if (!userId) {
                userId = STORAGE.nullUser;
            }
            return item[STORAGE.appKey][appId][STORAGE.userKey][userId][key];
        }
        catch (ex) {
        }
        return null;
    };
    qmBaseStorage.prototype.removeBaseItem = function(key) {
        try {
            var item = JSON.parse(this.storage.getItem(this.storageKey));
            delete item[key];
            this.storage.setItem(this.storageKey, JSON.stringify(item));
        }
        catch (ex) {
        }
    };
    qmBaseStorage.prototype.removeAppItemForAppId = function(key, appId) {
        try {
            var item = JSON.parse(this.storage.getItem(this.storageKey));
            delete item[STORAGE.appKey][appId][key];
            this.storage.setItem(this.storageKey, JSON.stringify(item));
        }
        catch (ex) {
        }
    };
    qmBaseStorage.prototype.removeAppItem = function(key) {
        try {
            var item = JSON.parse(this.storage.getItem(this.storageKey));
            var appId = qmWebAppData.getAppId();
            delete item[STORAGE.appKey][appId][key];
            this.storage.setItem(this.storageKey, JSON.stringify(item));
        }
        catch (ex) {
        }
    };
    qmBaseStorage.prototype.removeUserItem = function(key) {
        try {
            var item = JSON.parse(this.storage.getItem(this.storageKey));
            var appId = qmWebAppData.getAppId();
            var userId = qmWebAppData.getUserId();
            if (!userId) {
                userId = STORAGE.nullUser;
            }
            delete item[STORAGE.appKey][appId][STORAGE.userKey][userId][key];
            this.storage.setItem(this.storageKey, JSON.stringify(item));
        }
        catch (ex) {
        }
    };
    // todo: start deprecating this
    qmBaseStorage.prototype.setItem = function(key, value) {
        if (angular.isDefined(value)) {
            var item;
            try {
                item = JSON.parse(this.storage.getItem(this.storageKey));
                if (!item) {
                    item = {};
                }
            }
            catch (ex) {
                item = {};
            }
            var appId = this.appId;
            if (appId) {
                if (!item[appId]) {
                    item[appId] = {};
                }
                var userId = this.userId;
                if (!userId) {
                    userId = STORAGE.nullUser;
                }
                if (!item[appId][userId]) {
                    item[appId][userId] = {};
                }
                item[appId][userId][key] = value;
            } else {
                item[key] = value;
            }
            this.storage.setItem(this.storageKey, JSON.stringify(item));
        }
    };
    // todo: start deprecating this
    qmBaseStorage.prototype.getItem = function(key) {
        try {
            var item = JSON.parse(this.storage.getItem(this.storageKey));
            var appId = this.appId;
            if (!appId) {
                return item[key];
            }
            var userId = this.userId;
            if (!userId) {
                userId = STORAGE.nullUser;
            }
            return item[appId][userId][key];
        }
        catch (ex) {
        }
        return null;
    };
    // todo: start deprecating this
    qmBaseStorage.prototype.removeItem = function(key) {
        try {
            var item = JSON.parse(this.storage.getItem(this.storageKey));
            var appId = this.appId;
            if (!appId && item[key]) {
                delete item[key];
                this.storage.setItem(this.storageKey, JSON.stringify(item));
                return;
            }
            var userId = this.userId;
            if (!userId) {
                userId = STORAGE.nullUser;
            }
            if (item[appId][userId][key]) {
                delete item[appId][userId][key];
            }
            this.storage.setItem(this.storageKey, JSON.stringify(item));
        }
        catch (ex) {
        }
    };
    return qmBaseStorage;
}]);

/**
 * @ngdoc service
 * @name config.service:qmBaseLocalStorage
 * @description
 * Base object for managing localStorage
 */
cache.factory('qmBaseLocalStorage', ['$window', 'qmBaseStorage', 'qmTempStorage', 'qmStorageHelper',
function($window, qmBaseStorage, qmTempStorage, qmStorageHelper) {
    function baseLocalStorage(storageKey, storageVersion) {
        this.storageKey = storageKey;
        this.storageVersion = storageVersion;

        var item;
        try {
            item = JSON.parse(this.storage.getItem(this.storageKey));
            if (!item) {
                item = {};
                item[STORAGE.versionKey] = this.storageVersion;
            }
        }
        catch (ex) {
            item = {};
            item[STORAGE.versionKey] = this.storageVersion;
        }
        this.storage.setItem(this.storageKey, JSON.stringify(item));
    }
    // If cookies are disabled, referencing localStorage will throw an exception
    try {
        if (qmStorageHelper.isStorageAvailable($window.localStorage)) {
            // inherit config helpers from qmBaseStorage
            baseLocalStorage.prototype = new qmBaseStorage($window.localStorage);
        } else {
            baseLocalStorage.prototype = new qmBaseStorage(qmTempStorage);
        }
    } catch (ex) {
        baseLocalStorage.prototype = new qmBaseStorage(qmTempStorage);
    }
    return baseLocalStorage;
}]);

/**
 * @ngdoc service
 * @name config.service:qmBaseTempStorage
 * @description
 * Base object for managing temp storage
 */
cache.factory('qmBaseTempStorage', ['$window', 'qmBaseStorage', 'qmTempStorage',
function($window, qmBaseStorage, qmTempStorage) {
    function baseTempStorage(storageKey, storageVersion) {
        this.storageKey = storageKey;
        this.storageVersion = storageVersion;

        var item;
        try {
            item = JSON.parse(this.storage.getItem(this.storageKey));
            if (!item) {
                item = {};
                item[STORAGE.versionKey] = this.storageVersion;
            }
        }
        catch (ex) {
            item = {};
            item[STORAGE.versionKey] = this.storageVersion;
        }
        this.storage.setItem(this.storageKey, JSON.stringify(item));
    }
    baseTempStorage.prototype = new qmBaseStorage(qmTempStorage);

    return baseTempStorage;
}]);

/**
 * @ngdoc service
 * @name config.service:qmLocalStorage
 * @description
 * Manages localStorage on WebApp
 */
cache.factory('qmLocalStorage', ['$window', 'qmBaseStorage', 'qmTempStorage', 'qmStorageHelper',
function($window, qmBaseStorage, qmTempStorage, qmStorageHelper) {
    var newLocalStorage = {};
    // If cookies are disabled, referencing localStorage will throw an exception
    try {
        if (qmStorageHelper.isStorageAvailable($window.localStorage)) {
            // inherit config helpers from qmBaseStorage
            newLocalStorage = new qmBaseStorage($window.localStorage);
        } else {
            newLocalStorage = new qmBaseStorage(qmTempStorage);
        }
    } catch (ex) {
        newLocalStorage = new qmBaseStorage(qmTempStorage);
    }
    return newLocalStorage;
}]);

/**
 * @ngdoc service
 * @name config.service:qmFlashStorage
 * @description
 * Manages localStorage on WebApp
 */
cache.factory('qmFlashDataStorage', ['qmLocalStorage', function(qmLocalStorage) {

    return {
        /**
         * Sets the flash data
         *
         * @param key
         * @param value
         */
        setData: function(key, value) {

            qmLocalStorage.setBaseItem(key, value);
        },
        /**
         * Gets the flash data and removes it from the storage
         *
         * @param key
         * @return {*}
         */
        getData: function(key) {

            var data = qmLocalStorage.getBaseItem(key);
            qmLocalStorage.removeBaseItem(key);

            return data;
        },
        /**
         * Removes the key from the flash storage
         * @param key
         */
        removeData: function(key) {
            qmLocalStorage.removeBaseItem(key);
        }
    };
}]);

/**
 * @ngdoc service
 * @name config.service:qmSessionStorage
 * @description
 * Manages sessionstorage on WebApp
 */
cache.factory('qmSessionStorage', ['$window', 'qmBaseStorage', 'qmTempStorage', 'qmStorageHelper',
function($window, qmBaseStorage, qmTempStorage, qmStorageHelper) {
    var newSessionStorage = {};
    // If cookies are disabled, referencing sessionStorage will throw an exception
    try {
        if (qmStorageHelper.isStorageAvailable($window.sessionStorage)) {
            // inherit config helpers from qmBaseStorage
            newSessionStorage = new qmBaseStorage($window.sessionStorage);
        } else {
            newSessionStorage = new qmBaseStorage(qmTempStorage);
        }
    } catch (ex) {
        newSessionStorage = new qmBaseStorage(qmTempStorage);
    }
    return newSessionStorage;
}]);

/**
 * @ngdoc service
 * @name component.service:qmStorageHelper
 * @description
 * Helper functions for storage services
 */
cache.factory('qmStorageHelper', [function() {
    return {
        /**
         * @ngdoc method
         * @name isStorageAvailable
         * @methodOf component.service:qmStorageHelper
         * @description
         * Determines if storage (localStorage, sessionStorage) is available
         *
         * @param {object} storage localStorage or sessionStorage
         * @return {boolean} Is storage available?
         */
        isStorageAvailable: function(storage) {
            var isStorageAvailable = true;
            if (storage) {
                try {
                    // Safari private window has storage available but throws exception when calling setItem
                    storage.setItem('test', 'test');
                    storage.removeItem('test');
                } catch (ex) {
                    isStorageAvailable = false;
                }
            } else {
                isStorageAvailable = false;
            }
            return isStorageAvailable;
        }
    };
}]);

/**
 * @ngdoc service
 * @name component.service:qmTempStorage
 * @description
 * Storage service used as fallback in case localStorage or sessionStorage are unavailable
 */
cache.factory('qmTempStorage', [function() {
    var tempStorage = {};
    return {
        /**
         * @ngdoc method
         * @name setItem
         * @methodOf component.service:qmTempStorage
         * @description
         * Store item to temp storage
         *
         * @param {string} key Storage key
         * @param {string} value Storage value
         */
        setItem: function(key, value) {
            tempStorage[key] = value;
        },
        /**
         * @ngdoc method
         * @name getItem
         * @methodOf component.service:qmTempStorage
         * @description
         * Store item to temp storage
         *
         * @param {string} key Storage key
         * @return {string} Storage value
         */
        getItem: function(key) {
            return tempStorage[key];
        },
        /**
         * @ngdoc method
         * @name removeItem
         * @methodOf component.service:qmTempStorage
         * @description
         * Remove item from temp storage
         *
         * @param {string} key Storage key
         */
        removeItem: function(key) {
            delete tempStorage[key];
        }
    };
}]);

var infopage = angular.module('info-page');

infopage.config(['$stateProvider', function($stateProvider) {
    $stateProvider
        .state('event.info-page', {
            url: '/Infopage',
            views: {
                'component@event': {
                    templateUrl: '/asset/service/web-app/2.0/html/info-page/info-page.html',
                    controller: 'InfoPageController'
                },
                'header@event': {
                    controller: 'InfoPageHeaderController'
                }
            }
        });
}]);

var infopage = angular.module('info-page');

/**
 * @ngdoc controller
 * @name infopage.controller:InfoPageController
 * @description
 * Controller for Info page view
 */
infopage.controller('InfoPageController', ['$scope', 'qmInfoPageService', function($scope, qmInfoPageService) {
    $scope.loadInfoPage = qmInfoPageService.getInfoPage();
    $scope.loadInfoPage.then(function(infoPage) {
        $scope.infoPage = infoPage.infopage;
    });
}]);

/**
 * @ngdoc controller
 * @name infopage.controller:InfoPageHeaderController
 * @description
 * Controller for Info page header view
 */
infopage.controller('InfoPageHeaderController', ['$scope', 'qmLocalization', function($scope, qmLocalization) {
    $scope.headerTitle = qmLocalization.getString('LABEL_ABOUT_TITLE');
    $scope.headerTitleTestId = 'componentTitle';
}]);

var infopage = angular.module('info-page');

/**
 * @ngdoc service
 * @name infopage.service:qmInfoPageService
 * @description
 * Manages REST API communication for Info Page
 */
infopage.factory('qmInfoPageService', ['qmRest', '$q', 'qmWebAppService', function(qmRest, $q, qmWebAppService) {
    return {
        /**
         * @ngdoc method
         * @name getInfoPage
         * @methodOf infopage.service:qmInfoPageService
         * @description
         * Get info page content
         *
         * @returns {promise} Promise that resolves when info page content is retrieved
         */
        getInfoPage: function() {
            var getInfoPageUrl = qmWebAppService.getBaseRestUrl('service', 'web-app') + '/infopage';
            var defer = $q.defer();
            qmRest.get(getInfoPageUrl).then(function(data) {
                defer.resolve(data);
            }, function() {
                defer.reject();
            });
            return defer.promise;
        }
    };
}]);

var validator = angular.module('validator');

/**
 * @ngdoc service
 * @name validator.service:qmValidator
 * @description
 * Validator service to validate a string against rules
 */
validator.factory('qmValidator', [function() {
    return {

        errors: [], // The errors encountered when running validation
        checks: [], // The checks the validator will run
        matchStr: '',
        notInStr: '',
        notEqualStr: '',
        minLength: 8, // Default length if none is provided
        numberRE: /[0-9]/,
        lowerRE: /[a-z]/,
        upperRE: /[A-Z]/,
        specialRE: /(_|[^\w])/,

        /**
         * @ngdoc method
         * @name setMinLength
         * @methodOf validator.service:qmValidator
         * @description
         * Sets the minimum number of characters for the string being validated
         *
         * @param {number} length Minimum length
         */
        setMinLength: function(length) {

            this.minLength = length;
        },

        /**
         * @ngdoc method
         * @name addCheck
         * @methodOf validator.service:qmValidator
         * @description
         * Push the string passed to the checks array.
         *
         * @param {string} str String to be validated
         */
        addCheck: function(str) {

            this.checks.push(str);
        },

        /**
         * @ngdoc method
         * @name addMatch
         * @methodOf validator.service:qmValidator
         * @description
         * Stored the string passed in the matchStr variable and pushes an item(match) to the checks array
         *
         * @param {string} str String to be validated
         */
        addMatch: function(str) {

            this.checks.push('match');
            this.matchStr = str;
        },

        /**
         * @ngdoc method
         * @name addNotIn
         * @methodOf validator.service:qmValidator
         * @description
         * Stored the string passed in the notInStr variable and pushes an item(notin) to the checks array
         *
         * @param {string} str String to be validated
        */
        addNotIn: function(str) {

            this.checks.push('notin');
            this.notInStr = str;
        },

        /**
         * @ngdoc method
         * @name addNotEqual
         * @methodOf validator.service:qmValidator
         * @description
         * Stored the string passed in the notEqualStr variable and pushes an item(notequal) to the checks array
         *
         * @param {string} str String to be validated
         */
        addNotEqual: function(str) {

            this.checks.push('notequal');
            this.notEqualStr = str;
        },

        /**
         * @ngdoc method
         * @name validate
         * @methodOf validator.service:qmValidator
         * @description
         * Sets the minimum number of characters for the string being validated
         *
         * @param {string} str String that should be validated against the rules
         */
        validate: function(str) {

            // Clean errors.
            this.errors = [];

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

                var rule = this.checks[i];
                this.handleRule(rule, str);
            }

            // Are there any errors
            if (this.errors.length > 0) {

                return false;
            }else {

                return true;
            }
        },

        /**
         * @ngdoc method
         * @name handleRule
         * @methodOf validator.service:qmValidator
         * @description
         * Validates all of the rules specified in the checks array.
         *
         * @param {string} rule The rule that should be validated
         * @param {string} str String used against the rule
         */
        handleRule: function(rule, str) {

            switch (rule) {

                case 'match':
                    if (str !== this.matchStr) {
                        this.errors.push('match');
                    }
                    break;

                case 'length':
                    if (str.length < this.minLength) {
                        this.errors.push('length');
                    }
                    break;

                case 'number':
                    if (!this.numberRE.test(str)) {
                        this.errors.push('number');
                    }
                    break;

                case 'lower':
                    if (!this.lowerRE.test(str)) {
                        this.errors.push('lower');
                    }
                    break;

                case 'upper':
                    if (!this.upperRE.test(str)) {
                        this.errors.push('upper');
                    }
                    break;

                case 'special':
                    if (!this.specialRE.test(str)) {
                        this.errors.push('special');
                    }
                    break;

                case 'notin':
                    // Handle capitalization
                    str = str.toUpperCase();
                    this.notInStr = this.notInStr.toUpperCase();
                    if (str.indexOf(this.notInStr) >= 0) {
                        this.errors.push('notin');
                    }
                    break;

                case 'notequal':
                    if (str === this.notEqualStr) {
                        this.errors.push('notequal');
                    }
                    break;
            }
        },

        /**
         * @ngdoc method
         * @name getErrors
         * @methodOf validator.service:qmValidator
         * @description
         * Returns the errors pushed in the errors array
         *
         * @return {array} Array of errors
         */
        getErrors: function() {

            return this.errors;
        }
    };
}]);

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

/**
 * @ngdoc service
 * @name analytics-V2_0.service:qmAnalyticsService
 * @description
 * Manages REST API communication for Analytics
 */
analytics.factory('qmAnalyticsService',
['qmRpc', '$q', 'qmConfigManager', '$window', 'qmLogin', 'qmAnalyticsDependencyService', 'qmWebAppData', 'qmLocalization', 'qmMoment', 'qmUAParser',
function(qmRpc, $q, qmConfigManager, $window, qmLogin, qmAnalyticsDependencyService, qmWebAppData, qmLocalization, qmMoment, qmUAParser) {
    var analyticsId = 1;
    return {
        /**
         * @ngdoc method
         * @name buildAnalyticEvent
         * @methodOf analytics-V2_0.service:qmAnalyticsService
         * @description
         * Build base analytic event object
         *
         * @returns {object} Base analytic event object
         */
        buildAnalyticEvent: function() {
            var baseEvent = {};
            var eventSource = {};
            var eventCondition = {};
            var extendedData = {};

            baseEvent.EventVersion = '2.0';
            baseEvent.EventDateTime = qmMoment().local().format();
            baseEvent.AppVersion = qmConfigManager.getConfig().getVersion();
            baseEvent.Resolution = $window.innerWidth + 'x' + $window.innerHeight;
            baseEvent.Category = null;      //needs to be overwritten
            if (qmLogin) {
                var userInfo = qmLogin.getUserInfo();
                if (userInfo && userInfo.attendeeId) {
                    baseEvent.UserID = userInfo.attendeeId;
                }
            }
            var qmSessionManager = qmAnalyticsDependencyService.getSessionManagementService('qmSessionManager');
            if (qmSessionManager) {
                var sessionId = qmSessionManager.getSessionId();
                if (sessionId) {
                    baseEvent.SessionID = sessionId;
                }
            }

            eventSource.DeviceID = qmWebAppData.getUdid();
            eventSource.EventLocale = qmLocalization.getLocale();
            eventSource.DeviceType = 'WebApp';
            eventSource.Platform = 'MOBILEAPP';

            var uaParser = new qmUAParser();
            var uaParserInfo = uaParser.getResult();
            if (uaParserInfo.ua) {
                eventSource.UserAgent = uaParserInfo.ua;
            }
            if (uaParserInfo.device && uaParserInfo.device.model) {
                eventSource.DeviceModel = uaParserInfo.device.model;
            }
            if (uaParserInfo.os && uaParserInfo.os.name) {
                eventSource.DeviceOS = uaParserInfo.os.name;
                if (uaParserInfo.os.version) {
                    eventSource.DeviceOS += ' ' + uaParserInfo.os.version;
                }
            }
            // todo: What does application mean?
            eventSource.Application = '';
            eventSource.QuickEvent = qmWebAppData.getAppId();
            eventSource.ComponentID = null;     //needs to be overwritten
            eventSource.ComponentType = null;   //needs to be overwritten
            eventSource.SourceElement = null;   //needs to be overwritten

            eventCondition.Condition = null;    //needs to be overwritten

            extendedData.Name = null;           //needs to be overwritten
            extendedData.DataType = null;       //needs to be overwritten
            extendedData.Value = null;          //needs to be overwritten

            return {
                BaseEvent: baseEvent,
                EventSource: eventSource,
                EventCondition: eventCondition,
                ExtendedData: extendedData
            };
        },
        /**
         * @ngdoc method
         * @name socialEvent
         * @methodOf analytics-V2_0.service:qmAnalyticsService
         * @description
         * Send social event
         *
         * @returns {promise} Promise that resolves when request has successfully sent
         */
        socialEvent: function(eventValue, componentId, componentType, sourceElement, condition) {
            var defer = $q.defer();
            var analyticEventParams = [];
            analyticEventParams.push(qmWebAppData.getAppId());

            var analyticEvent = this.buildAnalyticEvent();
            analyticEvent.BaseEvent.Category = 'Social';
            analyticEvent.EventSource.ComponentID = componentId;
            analyticEvent.EventSource.ComponentType = componentType;
            analyticEvent.EventSource.SourceElement = sourceElement;
            analyticEvent.EventCondition.Condition = condition;
            analyticEvent.ExtendedData.Name = 'Flag';
            analyticEvent.ExtendedData.DataType = 'String';
            analyticEvent.ExtendedData.Value = eventValue;

            analyticEventParams.push([analyticEvent]);
            var analyticsUrl = qmConfigManager.getConfig().getAnalyticsUrl();
            qmRpc.post(analyticsUrl, {
                method: 'submitEvent',
                params: analyticEventParams,
                id: analyticsId++
            }).then(function() {
                defer.resolve();
            }, function() {
                defer.reject();
            });

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

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

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

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

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

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

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

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

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

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

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

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

                var useCategories = init.isCategoriesAvailable();

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

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

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

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

                        return false;
                    }
                });

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

                return returnAttendee;
            },
            /**
             * @ngdoc method
             * @name getListByCategory
             * @methodOf attendees-V2_3.service:qmAttendeeService
             * @description
             * Get list of attendees for category
             *
             * @returns {promise} Promise that resolves when attendee list has been retrieved
             */
            getListByCategory: function(categoryId, searchTerm, offset, limit) {
                searchTerm = (typeof searchTerm === 'undefined') ? '' : searchTerm;
                offset = (typeof offset === 'undefined') ? 0 : offset;
                limit = (typeof limit === 'undefined') ? 0 : limit;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

                return returnObj;
            }
        };

        return init;
    }]);

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

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

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

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

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

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

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

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

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

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

        $scope.panes = panes;
    }]);

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

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

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

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

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

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

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

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

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

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

            return defer.promise;
        };

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

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

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

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

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

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

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

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

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

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

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

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

        $scope.mailClient = function() {

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

    }]);

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

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

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

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

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

/**
 * @ngdoc controller
 * @name attendees-V2_7.controller:AttendeePrivacySettingController
 * @description
 * Controller for attendee privacy setting view
 */
attendees.controller('AttendeePrivacySettingController', ['$scope', '$state', 'qmAttendeeService', 'qmDependencyManager', 'qmLocalization',
    'qmUtilities', '$q', '$modalInstance', 'toastr', 'qmLogonService',
    function($scope, $state, qmAttendeeService, qmDependencyManager, qmLocalization, qmUtilities, $q, $modalInstance,
             toastr, qmLogonService) {

        var userInfo = qmLogonService.getUserInfo();
        var userId = userInfo.attendeeId;
        var originalProfile;
        $scope.setting = qmAttendeeService.getSetting();
        $scope.loadPrivacySetting = qmAttendeeService.getDetail(userId);
        $scope.loadPrivacySetting.then(function(attendee) {

            attendee.allowEmail = (attendee.allowEmail === '1') ? true : false;
            attendee.allowPhone = (attendee.allowPhone === '1') ? true : false;
            attendee.publish = (attendee.publish === '1') ? true : false;

            $scope.profile = attendee;
            originalProfile = angular.copy($scope.profile);
        });
        $scope.showMessaging = qmDependencyManager.hasComponent('messaging');
        $scope.continueText = qmLocalization.getString('BUTTON_CONTINUE');
        $scope.privacyDescription = qmLocalization.getString('LABEL_PRIVACY_SETTINGS_DESCRIPTION');
        $scope.accept = function() {
            // no changes were made so just close window
            if (angular.equals(originalProfile, $scope.profile)) {
                $modalInstance.close();
                return;
            }

            qmUtilities.addPageLoad(qmLocalization.getString('ALERT_SAVING'));

            var profileUpdatePromise = null;
            if ($scope.profile) {
                // Get array of properties to loop through and construct object so that avoid manually copying and pasting a bunch of if statements
                var getUpdateObj = function(properties) {
                    var updateObj = {};
                    for (var i = 0; i < properties.length; i++) {
                        if ($scope.setting[properties[i]] && ($scope.setting[properties[i]].isVisible || $scope.setting[properties[i]].isEditable)) {
                            if (properties[i] === 'allowMessage' || properties[i] === 'allowPhone' ||
                                properties[i] === 'allowEmail' || properties[i] === 'publish') {
                                // convert back from boolean so RPC call uses it properly
                                updateObj[properties[i]] = $scope.profile[properties[i]] === true ? '1' : '0';
                            } else {
                                updateObj[properties[i]] = $scope.profile[properties[i]];
                            }
                        }
                    }
                    return updateObj;
                };
                var attendeeObj = getUpdateObj(['allowMessage', 'allowPhone', 'allowEmail', 'publish']);
                profileUpdatePromise = qmAttendeeService.updateDetail($scope.profile.attendeeId, attendeeObj);
            }

            $q.when(profileUpdatePromise).then(function() {
                $modalInstance.close();
                qmUtilities.removePageLoad();
            }, function(err) {
                if (err.error) {
                    var errorMsg = qmLocalization.getString('ALERT_PROFILE_SAVE_FAILED_MESSAGE');
                    toastr.error(errorMsg, qmLocalization.getString('ALERT_ERROR_TITLE'), {
                        closeButton: true,
                        timeOut: 5000
                    });
                }
                qmUtilities.removePageLoad();
            });
        };
    }]);


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

/**
 * @ngdoc service
 * @name attendees-V2_7.service:qmAttendeePrivacySettingService
 * @description
 * Open up attendee privacy setting window
 */
attendees.factory('qmAttendeePrivacySettingService', ['$q', 'qmLocalStorage', '$modal', 'qmEventConfig',
    function($q, qmLocalStorage, $modal, qmEventConfig) {
        var init = {
            /**
             * @ngdoc method
             * @name checkPrivacySetting
             * @methodOf attendees-V2_7.service:qmAttendeePrivacySettingService
             * @description
             * Show privacy setting if not previously accepted
             *
             * @returns {promise} Promise that resolves when privacy setting has been accepted
             */
            checkPrivacySetting: function() {
                var defer = $q.defer();
                var isPromptPrivacySetting = qmLocalStorage.getItem('isPromptPrivacySetting');
                var config = qmEventConfig.getComponentConfig('attendees');
                var promptSetting = config['@attributes'].promptPrivacy === 'true' ? true : false;
                if (!isPromptPrivacySetting && promptSetting) {
                    var openParams = {
                        templateUrl: '/asset/component/attendees/2.7/webapp/html/attendee-privacy-setting.html',
                        controller: 'AttendeePrivacySettingController',
                        backdrop: 'static',
                        keyboard: false
                    };
                    var privacySettingModal = $modal.open(openParams);
                    privacySettingModal.result.then(function() {
                        qmLocalStorage.setItem('isPromptPrivacySetting', true);
                        defer.resolve();
                    }, function() {
                        qmLocalStorage.setItem('isPromptPrivacySetting', true);
                        defer.reject();
                    });
                } else {
                    defer.resolve();
                }

                return defer.promise;
            }
        };

        return init;
    }]);

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

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

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

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

                return defer.promise;
            }

        };
    }]);

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

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

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

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

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

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

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

// var logon = angular.module('logon-V2_5');

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

/**
 * @ngdoc controller
 * @name logon-V2_1.controller:LogonController
 * @description
 * Controller for component login modal view
 */
logon.controller('LogonController',
    ['$scope', 'qmEventConfig', '$modalInstance', '$q', 'qmRpc', 'qmLogonService', 'qmAnalytics',
        'qmEventHelper', 'userName', '$controller', 'qmLocalization', 'qmContainerConfig', '$sce',
        '$location', 'qmWebAppData', 'qmSessionStorage', 'qmLocalStorage',
        function($scope, qmEventConfig, $modalInstance, $q, qmRpc, qmLogonService, qmAnalytics,
                 qmEventHelper, userName, $controller, qmLocalization, qmContainerConfig, $sce,
                 $location, qmWebAppData, qmSessionStorage, qmLocalStorage) {

            $scope.isSubmitting = false;
            // Save rules in the scope so base controller gets it
            $scope.rules = {};

            // Extend LogonBaseController
            $controller('LogonBaseController', {
                $scope: $scope
            });
            // Find out if this is a first time user
            $scope.firstTimeUser = qmLogonService.isFirstTimeUser();
            $scope.eventLogo = $scope.getEventLogo();
            $scope.showModal = true;
            $scope.hasPasswordReset = false;
            $scope.username = userName;
            $scope.hasPasswordReset = $scope.shouldAllowPasswordReset();
            var isContainerLevel = !qmEventConfig.config ? true : false;
            $scope.hasSso = (isContainerLevel || angular.equals(qmEventConfig.getComponentConfig('sso'), {})) ? false : true;
            $scope.ssoEnabled = false;
            $scope.isSingleEvent = qmContainerConfig.isSingleEvent();
            $scope.cancel = function() {
                $modalInstance.dismiss('cancel');
            };

            qmLogonService.getCcpaUrl().then(function(ccpaUrl) {
                $scope.ccpaUrl = ccpaUrl.ccpaurl;
            });
            $scope.goBack = function() {
                $modalInstance.dismiss('cancel');
                $scope.showModal = false;
            };

            $scope.submit = function(username, password) {

                var defer = $q.defer();
                // log user in with new credentials
                $scope.doLogin(username, password).then(function(token) {
                    qmLogonService.resetSignUpFlow();
                    // Finally close the modal on success
                    $modalInstance.close(token);
                    defer.resolve();
                }, function() {
                    defer.reject();
                });

                return defer.promise;
            };

            $scope.createPassword = function() {

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

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

                }, function() {

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

            $scope.forgotPassword = function() {

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

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

                }, function() {

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

            if ($scope.hasSso) {
                var logonConfig = qmEventConfig.getComponentConfig('logon');
                var ssoConfig = qmEventConfig.getComponentConfig('sso');
                if (ssoConfig['@attributes']['loginType'] !== 'SSO Disabled') {
                    $scope.ssoInstructions = qmLocalization.getString(logonConfig['@attributes']['ssoInstructions']);
                    $scope.isSsoHybridModeEnabled = ssoConfig['@attributes']['loginType'] === 'SSO and In App' ? true : false;
                    $scope.ssoLoginButtonText = qmLocalization.getString(logonConfig['@attributes']['ssoLoginButton']);
                    $scope.continueText = qmLocalization.getString('BUTTON_SIGN_IN');
                    $scope.goToLogin = function() {
                        qmLocalStorage.setItem('goToLoginClicked', true);
                        $scope.showLogin = true;
                        $scope.ssoLoginShow = false;
                    };
                    $scope.cancel = function() {
                        $modalInstance.dismiss('cancel');
                    };

                    $scope.goBack = function() {
                        $scope.showLogin = false;
                        $scope.ssoLoginShow = true;
                    };

                    $scope.trustSrc = function() {
                        var submitUrl = qmLogonService.getSamlUrl();
                        return $sce.trustAsResourceUrl(submitUrl);
                    };

                    if (qmSessionStorage.getItem('componentId')) {
                        $scope.returnUrl = '//' + $location.host() + '/#!/event/' + qmWebAppData.getEventAppId() + '/' +
                            qmSessionStorage.getItem('componentId') + qmSessionStorage.getItem('componentUrl');
                    } else if (qmEventConfig.isComponentConfigured('activityfeed')) {
                        var activityfeedConfig = qmEventConfig.getComponentConfig('activityfeed');
                        $scope.returnUrl = '//' + $location.host() + '/#!/event/' + qmWebAppData.getEventAppId() + '/' +
                            activityfeedConfig['@attributes']['id'];
                    } else {
                        $scope.returnUrl = '//' + $location.host() + '/#!/event/' + qmWebAppData.getEventAppId() + '/';
                    }

                    $scope.ssoEnabled = true;
                }
            }

            $scope.showLogin = ($scope.hasSso && $scope.ssoEnabled) ? false : true;
            $scope.ssoLoginShow = ($scope.hasSso && $scope.ssoEnabled) ? true : false;

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

            // Save rules in the scope so base controller gets it
            $scope.rules = {};
            // Extend LogonBaseController
            $controller('LogonBaseController', {
                $scope: $scope
            });
            $scope.isSubmitting = false;

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

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

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

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

            $scope.submit = function(email) {

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

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

                    var title = qmLocalization.getString('ALERT_PASSWORD_EMAIL_SENT_TITLE');
                    toastr.success(toastMsg, title, {
                        closeButton: true,
                        timeOut: 1000000000
                    });

                }, function(err) {

                    qmAnalytics.markEvent('LoginFailure');

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

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

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

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

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

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

                return defer.promise;
            };
        }
    ]
);

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

        // Save rules in the scope so base controller gets it
        $scope.rules = rules;

        // Extend LogonBaseController
        $controller('LogonBaseController', {
            $scope: $scope
        });

        // Default Labels for template
        $scope.headerLabel = qmLocalization.getString('LABEL_NEW_PASSWORD_TITLE');
        $scope.instructionsText = qmLocalization.getString('LABEL_NEW_PASSWORD_MESSAGE');
        $scope.labelNewPass = qmLocalization.getString('LABEL_NEW_PASSWORD_WATERMARK');
        $scope.labelConfirmNewPass = qmLocalization.getString('LABEL_REENTER_PASSWORD_WATERMARK');
        $scope.labelOldPass = qmLocalization.getString('LABEL_OLD_PASSWORD_WATERMARK');
        $scope.buttonCancel = qmLocalization.getString('BUTTON_CANCEL');
        $scope.buttonSave = qmLocalization.getString('BUTTON_SAVE');
        $scope.passwordRulesTemplate = '/asset/component/logon/2.7/webapp/html/password-rules.html';

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

        $scope.submit = function() {

            if (!$scope.validate()) {
                // Password is not good. Do not submit
                return false;
            }

            // If we reach this point then validation passed
            var defer = $q.defer();
            var url = $scope.getRpcUrl();

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

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

                if (qmLogonService.getIsContainerLevel()) {

                    var token = response.token;
                    var ssoToken = response.ssoToken;
                    var userInfo = attendee;
                    userName = qmLogonService.getUserName();
                    qmAnalytics.startSession(userInfo.attendeeId, true);
                    qmAnalytics.markEvent('ContainerLoginSuccess', userInfo.attendeeId);
                    qmMultiEvent.saveContainerLoginInfo(token, userInfo, ssoToken, userName);

                } else {

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

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

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

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

            return defer.promise;
        };
    }]);

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

/**
 * @ngdoc controller
 * @name logon.controller:LogonSelfSignUpController
 * @description
 * Controller which handles the first logon screen. To determine if user should sign in or create an account
 */
logon.controller('LogonSelfSignUpController',
    ['$scope', '$modalInstance', '$q', 'qmLogonService', 'toastr', 'qmLocalization', 'qmEventHelper',
        function($scope, $modalInstance, $q, qmLogonService, toastr, qmLocalization, qmEventHelper) {
            $scope.isSubmitting = false;
            // Default Labels for template
            $scope.labelEmail = qmLocalization.getString('LABEL_EMAIL_ADDRESS_WATERMARK');
            $scope.buttonCancel = qmLocalization.getString('BUTTON_CANCEL');
            $scope.buttonSubmit = qmLocalization.getString('BUTTON_NEXT');
            $scope.eventLogo = qmEventHelper.getEventLogo();

            // Message shown when user name is invalid
            var toastMsg = qmLocalization.getString('ALERT_INVALID_USER_NAME_MESSAGE');
            var toastTitle = qmLocalization.getString('ALERT_INVALID_USER_NAME_TITLE');

            $scope.cancel = function() {

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

            $scope.submit = function(email) {

                $scope.isSubmitting = true;
                qmLogonService.checkEmail(email).then(function() {

                    $scope.isSubmitting = false;
                    qmLogonService.checkLogin(email).then(function() {
                        $modalInstance.close();
                    }, function() {

                    });
                }, function(err) {
                    if (err.status === 404) {
                        // User does not exist, show self sign up flow
                        err.config.errorHandled = true;
                        $scope.isSubmitting = false;
                        qmLogonService.createAccount(email).then(function() {
                            $modalInstance.close();
                        });
                    } else if (err.status === 400) {
                        err.config.errorHandled = true;
                        $scope.isSubmitting = false;
                        // Invalid user name
                        toastr.error(toastMsg, toastTitle, {
                            closeButton: true,
                            timeOut: 10000
                        });
                    } else {
                        $scope.isSubmitting = false;
                    }
                });
            };
        }
    ]
);
/**
 * @ngdoc controller
 * @name logon.controller:LogonNewAccountController
 * @description
 * Controller which handles the creation of a new account and sign in to the app
 */
logon.controller('LogonNewAccountController',
    ['$scope', '$modalInstance', '$q', 'qmLogonService', 'toastr', 'qmLocalization', 'userName', 'rules', '$controller',
        'qmAnalytics',
        function($scope, $modalInstance, $q, qmLogonService, toastr, qmLocalization, userName, rules, $controller,
                 qmAnalytics) {

            // Save rules in the scope so base controller gets it
            $scope.rules = rules;

            // Extend LogonBaseController
            $controller('LogonBaseController', {
                $scope: $scope
            });

            // Define variables
            $scope.passwordRulesTemplate = $scope.getTemplate('password-rules-block.html');
            $scope.email = userName;
            $scope.isSubmitting = false;
            // Default Labels for template
            $scope.headerLabel = qmLocalization.getString('LABEL_CREATE_ACCOUNT_TITLE');
            $scope.labelFirstName = qmLocalization.getString('LABEL_FIRST_NAME');
            $scope.labelLastName = qmLocalization.getString('LABEL_LAST_NAME');
            $scope.labelEmail = qmLocalization.getString('LABEL_EMAIL_ADDRESS_WATERMARK');
            $scope.labelPassword = qmLocalization.getString('LABEL_PASSWORD');
            $scope.buttonCancel = qmLocalization.getString('BUTTON_CANCEL');
            $scope.buttonSubmit = qmLocalization.getString('BUTTON_SUBMIT');
            $scope.showMustMatchRule = false;
            $scope.errorFeedback = '';

            $scope.cancel = function() {

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

            $scope.shouldEnableSubmit = function() {

                if (angular.isUndefined($scope.firstName) ||
                    angular.isUndefined($scope.lastName) ||
                    angular.isUndefined($scope.email) ||
                    angular.isUndefined($scope.password)) {

                    $scope.submitEnabled = false;

                } else {
                    $scope.submitEnabled = true;
                }
            };

            $scope.submit = function() {

                qmLogonService.setUserName($scope.email);
                $scope.newPass = $scope.password;
                $scope.confirmPass = $scope.password;
                $scope.errorFeedback = '';
                var defer = $q.defer();

                if (!$scope.validate()) {
                    // Password is not good. Do not submit
                    defer.resolve('BAD PASSWORD');
                    return defer.promise;
                }

                var details = {
                    email: $scope.email,
                    password: $scope.password,
                    firstName: $scope.firstName,
                    lastName: $scope.lastName
                };
                qmLogonService.newAccount(details).then(function(response) {
                    // Account created successfully
                    var attendeeId = response.attendeeID;
                    qmAnalytics.markEvent('SelfSignUpSuccess', attendeeId);
                    // log user in with new credentials
                    $scope.doLogin($scope.email, $scope.password).then(function(token) {
                        // Finally close the modal on success
                        $modalInstance.close(token);
                        defer.resolve();
                    }, function() {
                        defer.reject();
                    });

                }, function() {
                    $scope.isSubmitting = false;
                    $scope.submitEnabled = true;
                    $scope.incorrectLogin = true;
                    $scope.errorFeedback = '';
                    defer.reject();
                });

                return defer.promise;
            };
        }
    ]
);

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

/**
 * @ngdoc controller
 * @name logon.controller:LogonBaseController
 * @description
 * Controller
 */
logon.controller('LogonBaseController', ['$scope', 'qmLocalization', 'qmValidator', 'qmLogonService', 'qmAnalytics',
    'qmRpc', '$q', 'qmEventConfig','qmEventHelper', 'qmMultiEventHelper', 'qmContainerConfig', 'qmMultiEvent',
    function($scope, qmLocalization, qmValidator, qmLogonService, qmAnalytics, qmRpc, $q, qmEventConfig,
             qmEventHelper, qmMultiEventHelper, qmContainerConfig, qmMultiEvent) {

        $scope.isSubmitting = false;
        // Define constants
        var STATUS_NORMAL = 0;
        var STATUS_VALID = 1;
        var STATUS_INVALID = 2;

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

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

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

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

        $scope.getEventLogo = function() {

            if (qmContainerConfig.getIsContainerLogin()) {
                return qmMultiEventHelper.getContainerLogo();
            } else {
                return qmEventHelper.getEventLogo();
            }
        };

        $scope.shouldAllowPasswordReset = function() {

            if (qmContainerConfig.getIsContainerLogin()) {
                return qmContainerConfig.isContainerPasswordResetAllowed();
            } else {
                return qmEventConfig.isEventPasswordResetAllowed();
            }
        };

        $scope.getRpcUrl = function() {

            return qmLogonService.getRpcUrl('login');
        };

        $scope.validate = function() {

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

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

            qmValidator.addMatch($scope.confirmPass);

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

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

                return false;
            }

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

            return true;
        };

        $scope.handleErrors = function(errors) {
            if (typeof errors === 'string' || errors instanceof String) {
                return;
            }

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

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

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

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

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

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

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

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

        $scope.getTemplate = function(template) {

            return '/asset/component/logon/2.4/webapp/html/' + template;
        };

        $scope.doLogin = function(username, password) {

            $scope.isSubmitting = true;
            var defer = $q.defer();
            var loginUrl = $scope.getRpcUrl();
            var bGetInfo = true;
            qmRpc.post(loginUrl, {method: 'login', params: [username, password, bGetInfo]}).then(function(response) {
                // Did we get a token ?
                if (response.token) {

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

                    if (response.needChangePassword) {

                        $scope.showModal = false;
                        qmLogonService.setUserName(username);
                        qmLogonService.newPassword(response.token, response.attendee, password).then(function(token) {
                            defer.resolve(token);
                        }, function() {
                            $scope.showModal = true;
                        });
                    } else {
                        // Log the user in and close modal.
                        if (qmLogonService.getIsContainerLevel()) {

                            var token = response.token;
                            var ssoToken = response.ssoToken;
                            var userInfo = response.attendee;
                            var userName = username;
                            qmAnalytics.startSession(userInfo.attendeeId, true);
                            qmAnalytics.markEvent('ContainerLoginSuccess', userInfo.attendeeId);
                            qmMultiEvent.saveContainerLoginInfo(token, userInfo, ssoToken, userName);
                        } else {
                            qmAnalytics.markEvent('LoginSuccess', response.attendee.attendeeId);
                            qmAnalytics.startSession(response.attendee.attendeeId, true);
                            qmLogonService.setLoginInfo(response.token, response.attendee);
                            qmLogonService.setUserName(username);
                        }

                        defer.resolve(response.token);
                    }
                } else {
                    // No token mark login failure
                    qmAnalytics.markEvent('LoginFailure');
                    $scope.incorrectLogin = false;
                    $scope.unknownError = true;
                    defer.reject();
                }
            }, function(err) {
                // Mark login failure
                qmAnalytics.markEvent('LoginFailure');
                if (err.error) {
                    $scope.alertLoginIncorrectMessage = qmLogonService.getLocalizedStringFromErrorMsg(err.error);
                    $scope.incorrectLogin = true;
                    $scope.unknownError = false;
                    defer.reject();
                } else {
                    $scope.incorrectLogin = false;
                    $scope.unknownError = true;
                    defer.reject();
                }
            }).finally(function() {
                $scope.isSubmitting = false;
            });

            return defer.promise;
        };
    }
]);

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

/**
 * @ngdoc service
 * @name logon.service:qmLogonService
 * @description
 * Manages user login
 */
logon.factory('qmLogonService', ['$modal', '$q', 'qmLocalStorage', 'qmSessionStorage', 'qmContainerConfig',
    'qmEventConfig', '$rootScope', 'qmLocalization', 'qmRpc', 'qmWebAppService', 'qmRest', 'qmWebAppData',
    'qmMultiEvent', 'qmAnalytics', 'toastr', '$window', '$cookies', '$location',
    function($modal, $q, qmLocalStorage, qmSessionStorage, qmContainerConfig, qmEventConfig, $rootScope, qmLocalization,
             qmRpc, qmWebAppService, qmRest, qmWebAppData, qmMultiEvent, qmAnalytics, toastr, $window, $cookies, $location) {
        return {

            username: '',
            didSelfSignUpFlow: false,
            isContainerLevel: false,

            /**
             * @ngdoc method
             * @name setUserName
             * @methodOf logon.service:qmLogonService
             * @description
             * Sets username variable
             *
             * @param {string} str Username
             */
            setUserName: function(userName) {
                qmLocalStorage.setItem('userName', userName);
            },

            /**
             * @ngdoc method
             * @name getUserName
             * @methodOf logon.service:qmLogonService
             * @description
             * Gets username variable
             *
             * @returns {string} Username
             */
            getUserName: function() {
                return qmLocalStorage.getItem('userName');
            },
            /**
             * @ngdoc method
             * @name reset
             * @methodOf logon.service:qmLogonService
             * @description
             * Reset login info back to null
             *
             */
            /**
             * @ngdoc method
             * @name reset
             * @methodOf logon.service:qmLogonService
             * @description
             * Reset login info back to null
             *
             */
            reset: function() {

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

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

                // remove user id info from outside user specific settings
                qmLocalStorage.removeItem('userId');
                qmSessionStorage.removeItem('userId');

                // Remove container login info
                qmMultiEvent.removeContainerLogin();

                this.resetSignUpFlow();
            },
            /**
             * @ngdoc method
             * @name isLoggedIn
             * @methodOf logon.service:qmLogonService
             * @description
             * Is there a user logged in?
             *
             * @returns {boolean} Is user logged in?
             */
            isLoggedIn: function() {

                if (this.isContainerLevel) {
                    return this.isLoggedInAtContainerLevel();
                } else {
                    return this.isLoggedInAtEventLevel();
                }
            },
            /**
             * @ngdoc method
             * @name setUserToken
             * @methodOf logon.service:qmLogonService
             * @description
             * Set user token
             *
             * @param {string} token User token to be set
             */
            setUserToken: function(token) {
                qmLocalStorage.setItem('userToken', token);
            },
            /**
             * @ngdoc method
             * @name getUserToken
             * @methodOf logon.service:qmLogonService
             * @description
             * Get user token
             *
             * @returns {string} User token
             */
            getUserToken: function() {
                return qmLocalStorage.getItem('userToken');
            },
            /**
             * @ngdoc method
             * @name setUserInfo
             * @methodOf logon.service:qmLogonService
             * @description
             * Set user info
             *
             * @param {object} user User info to be set
             */
            setUserInfo: function(user) {
                qmLocalStorage.setItem('userInfo', user);
            },
            /**
             * @ngdoc method
             * @name getUserInfo
             * @methodOf logon.service:qmLogonService
             * @description
             * Get user info
             *
             * @returns {object} User info
             */
            getUserInfo: function() {
                return qmLocalStorage.getItem('userInfo');
            },
            /**
             * @ngdoc method
             * @name setLoginInfo
             * @methodOf logon.service:qmLogonService
             * @description
             * Set the login info
             *
             * @param {string} token User token to be set
             * @param {object} info User info object to be set
             */
            setLoginInfo: function(userToken, userInfo) {
                // store current userId outside user specific settings so we can set the storage user id correctly on app init
                qmLocalStorage.setItem('userId', userInfo.attendeeId);
                qmSessionStorage.setItem('userId', userInfo.attendeeId);

                qmWebAppData.setUserId(userInfo.attendeeId);

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

                this.setUserToken(userToken);
                this.setUserInfo(userInfo);
            },
            /**
             * @ngdoc method
             * @name setUserId
             * @methodOf logon.service:qmLogonService
             * @description
             * Set user id to cache storage
             *
             * @param {string} userId User id
             */
            setUserId: function(userId) {
                return qmLocalStorage.setItem('userId', userId);
            },
            /**
             * @ngdoc method
             * @name getUserId
             * @methodOf logon.service:qmLogonService
             * @description
             * Get user id from cache. This should only be called before userId is set in cache storage
             *
             * @returns {string} User Id
             */
            getUserId: function() {
                return qmLocalStorage.getItem('userId');
            },
            /**
             * @ngdoc method
             * @name checkSSO
             * @methodOf logon.service:qmLogonService
             * @description
             * Show sso login if not previously accepted
             *
             * @returns {promise} Promise that resolves when sso has been accepted
             */
            checkSSO: function() {
                var defer = $q.defer();
                var openParams = {
                    templateUrl: '/asset/component/logon/2.7/webapp/html/sso-modal.html',
                    controller: 'logonSSOController',
                    backdrop: 'static',
                    keyboard: false
                };

                if (qmEventConfig.isComponentConfigured('sso')) {
                    var ssoModal = $modal.open(openParams);
                    ssoModal.result.then(function() {
                        defer.resolve();
                    }, function() {
                        defer.reject();
                    });
                } else {
                    defer.resolve();
                }
                return defer.promise;
            },
            /**
             * @ngdoc method
             * @name checkLogin
             * @methodOf logon.service:qmLogonService
             * @description
             * Checks if user is logged in and launches login dialog if not
             *
             * @returns {promise} Promise that resolves if already logged in or after user interaction with login modal
             */
            checkLogin: function(email) {
                var defer = $q.defer();
                var data = qmMultiEvent.getContainerLoginInfo();

                var cookies = {};

                //"goToLoginClicked" will be true when gotoLogin button has been clicked.  Also we need set it back to false during this process.
                if ($cookies.get('authData') && !qmLocalStorage.getItem('samlLoginFailed') && !qmLocalStorage.getItem('goToLoginClicked')) {
                    cookies = JSON.parse($cookies.get('authData'));
                } else if (qmLocalStorage.getItem('samlLoginFailed') === true) {
                    qmLocalStorage.setItem('samlLoginFailed', false);
                }

                qmLocalStorage.setItem('goToLoginClicked', false);

                if (this.shouldLoginAtContainer(data)) {
                    defer.reject('redirect');
                    $window.location.assign('/');

                    return defer.promise;
                }

                if (data.ssoToken !== null && !this.isContainerLevel && !this.isLoggedInAtEventLevel()) {
                    // We have logged in at container level. Use ssoToken to silently login at event level
                    this.doSilentLogin(data.ssoToken, data.userName, data.activeAppId).then(function(token) {

                        defer.resolve(token);
                    }, function() {

                        defer.reject();
                    });

                } else if (!qmLocalStorage.getItem('samlLoginFailed') && !this.isLoggedInAtEventLevel() &&
                    cookies.nameId && cookies.token && cookies.appID === qmWebAppData.getAppId()) {

                    // We have logged in by Saml. Use cookies to silently login at event
                    this.doSamlSilentLogin(cookies.token, cookies.nameId).then(function(token) {
                        qmLocalStorage.setItem('samlLoginFailed', false);
                        defer.resolve(token);
                    }, function() {
                        qmLocalStorage.setItem('samlLoginFailed', true);
                        defer.reject();
                    });

                } else if (!this.isLoggedIn()) {
                    var scope = $rootScope.$new();
                    scope.hasCancel = true;
                    var openParams = {
                        templateUrl: '/asset/component/logon/2.7/webapp/html/login-modal.html',
                        controller: 'LogonController',
                        resolve: {
                            userName: function() {
                                return email;
                            }
                        }
                    };

                    if (this.isSelfSignUpEnabled() && !this.didSelfSignUpFlow) {
                        // We need to load a different modal and controller when self sign up is enabled
                        this.didSelfSignUpFlow = true;
                        openParams = {
                            templateUrl: '/asset/component/logon/2.4/webapp/html/login-check-email.html',
                            controller: 'LogonSelfSignUpController'
                        };
                    }

                    if (qmContainerConfig.getIsContainerLogin()) {
                        openParams.backdrop = 'static';
                        openParams.keyboard = false;
                        scope.hasCancel = false;
                    } else if (qmContainerConfig.isSingleEvent() && qmEventConfig.isEventLoginRequired()) {
                        openParams.backdrop = 'static'; // prevent modal from being closed when clicking on background
                        openParams.keyboard = false;    // prevent modal from being closed by pressing ESC
                        scope.hasCancel = false;
                    }
                    openParams.scope = scope;
                    var loginModal = $modal.open(openParams);

                    return loginModal.result;

                } else {
                    this.resetSignUpFlow();
                    defer.resolve();
                }
                return defer.promise;
            },
            /**
             * @ngdoc method
             * @name resetPassword
             * @methodOf logon.service:qmLogonService
             * @description
             * Checks if user is logged in and launches reset password dialog if not
             *
             * @returns {promise} Promise that resolves if already logged in or after user interaction with reset password modal
             */
            resetPassword: function(type) {

                var defer = $q.defer();

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

                    return loginModal.result;

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

                var defer = $q.defer();
                var that = this;

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

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

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

                }).finally(function() {
                    if (that.isLoggedIn()) {
                        defer.resolve();
                    } else {
                        defer.reject();
                    }
                });

                return defer.promise;
            },
            /**
             * @ngdoc method
             * @name configModalParams
             * @methodOf logon.service:qmLogonService
             * @description
             * Returns the login modal params
             *
             * @returns {object} The params the modal instance will use
             */
            configModalParams: function(authToken, attendee, rules, tempPassword) {

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

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

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

                if (!hasLoggedIn) {
                    return true;
                } else {
                    return false;
                }
            },
            /**
             * @ngdoc method
             * @name markFirstTimeLogin
             * @methodOf logon.service:qmLogonService
             * @description
             * Stores first time login in the local storage
             */
            markFirstTimeLogin: function() {

                // User has performed first time login flow
                qmLocalStorage.setItem('QM_HAS_LOGGED_IN', true);
            },
            /**
             * @ngdoc method
             * @name getLocalizedStringFromErrorMsg
             * @methodOf logon.service:qmLogonService
             * @description
             * Returns the localized string from the error message
             */
            getLocalizedStringFromErrorMsg: function(errorMsg) {

                var localizedString = '';

                switch (errorMsg) {

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

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

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

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

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

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

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

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

                    case 'Authentication Failure - Wrong attendeeId' :
                        localizedString = qmLocalization.getString('ALERT_SSO_LOGON_FAILED');
                        break;
                }

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

                    // remove sso cookie
                    if ($cookies.get('authData')) {
                        // delete $cookies.get('authData');
                        var domainName = $location.host().split('.').splice(1).join('.');

                        document.cookie = 'authData=;expires=Thu, 01 Jan 1970 00:00:00 UTC;path=/;domain=.' + domainName + ';';
                    }

                    // Reset internal login info
                    this.reset();
                }
            },
            /**
             * @ngdoc method
             * @name resetSignUpFlow
             * @methodOf logon.service:qmLogonService
             * @description
             * Reset the value of the didSelfSignUpFlow variable to false
             */
            resetSignUpFlow: function() {

                this.didSelfSignUpFlow = false;
            },
            /**
             * @ngdoc method
             * @name newAccount
             * @methodOf logon.service:qmLogonService
             * @description
             * Makes a post request to create a new account.
             *
             * @returns {promise} Promise that gets resolved when new account gets created
             */
            newAccount: function(details) {

                var defer = $q.defer();
                var url = qmWebAppService.getBaseRestUrl('component', 'logon') + '/attendees';
                // Make the post request
                qmRest.post(url, details).then(function(response) {
                    defer.resolve(response);
                }, function(err) {
                    defer.reject(err);
                });

                return defer.promise;
            },
            /**
             * @ngdoc method
             * @name createAccount
             * @methodOf logon.service:qmLogonService
             * @description
             * Prepares the new account modal
             *
             * @returns {promise} Promise that gets resolved when we get the password rules
             */
            createAccount: function(email) {

                // Let's get the password rules
                var url = qmWebAppService.getBaseRestUrl('component', 'logon') + '/passwordRules';
                var defer = $q.defer();

                qmRest.get(url).then(function(response) {
                    var rules = response;
                    var scope = $rootScope.$new();
                    scope.hasCancel = true;
                    var openParams = {
                        templateUrl: '/asset/component/logon/2.4/webapp/html/new-account-modal.html',
                        controller: 'LogonNewAccountController',
                        backdrop: 'static', // prevent modal from being closed when clicking on background
                        keyboard: false, // prevent modal from being closed by pressing ESC,
                        scope: scope,
                        resolve: {
                            userName: function() {
                                return email;
                            },
                            rules: function() {
                                return rules;
                            }
                        }
                    };

                    var loginModal = $modal.open(openParams);
                    defer.resolve(loginModal.result);

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

                return defer.promise;
            },
            /**
             * @ngdoc method
             * @name checkEmail
             * @methodOf logon.service:qmLogonService
             * @description
             * Checks if user exists in the DB
             *
             * @returns {promise} Promise that gets resolved after checking email
             */
            checkEmail: function(email) {

                var url = qmWebAppService.getBaseRestUrl('component', 'logon') + '/registeredUsernames/' + encodeURIComponent(email);
                var defer = $q.defer();
                qmRest.get(url).then(function() {
                    defer.resolve();
                }, function(err) {
                    defer.reject(err);
                });

                return defer.promise;
            },
            /**
             * @ngdoc method
             * @name isSelfSignUpEnabled
             * @methodOf logon.service:qmLogonService
             * @description
             * Is self sign up enabled for the project
             *
             * @returns {boolean} Is self sign up enabled ?
             */
            isSelfSignUpEnabled: function() {

                var config = null;

                if (qmContainerConfig.getIsContainerLogin()) {
                    config = qmContainerConfig.getComponentConfig('logon');
                    return (config['@attributes']['allowSelfSignUp'] === 'true');
                } else {
                    config = qmEventConfig.getComponentConfig('logon');
                    return (config['@attributes']['allowSelfSignUp'] === 'true');
                }
            },
            /**
             * @ngdoc method
             * @name isLoggedInAtEventLevel
             * @methodOf logon.service:qmLogonService
             * @description
             * Is there a user logged in at container level?
             *
             * @returns {boolean} Is user logged in?
             */
            isLoggedInAtEventLevel: function() {
                var isLoggedIn = false;
                if (this.getUserToken() && this.getUserInfo()) {
                    isLoggedIn = true;
                }

                return isLoggedIn;
            },
            /**
             * @ngdoc method
             * @name isLoggedInAtContainerLevel
             * @methodOf logon.service:qmLogonService
             * @description
             * Is there a user logged in at container level?
             *
             * @returns {boolean} Is user logged in?
             */
            isLoggedInAtContainerLevel: function()
            {
                var isLoggedIn = false;
                var data = this.getContainerInfo();
                if (data.containerToken && data.userInfo) {
                    isLoggedIn = true;
                }

                return isLoggedIn;
            },
            /**
             * @ngdoc method
             * @name getContainerInfo
             * @methodOf logon.service:qmLogonService
             * @description
             * Gets the container login data
             *
             * @returns {object} Object with the login info
             */
            getContainerInfo: function() {
                return qmMultiEvent.getContainerLoginInfo();
            },
            /**
             * @ngdoc method
             * @name getRpcUrl
             * @methodOf logon.service:qmLogonService
             * @description
             * Gets the rpc url for container/event
             *
             * @returns {boolean} Is self sign up enabled ?
             */
            getRpcUrl: function(action) {

                if (qmContainerConfig.getIsContainerLogin() && this.getIsContainerLevel()) {
                    return qmContainerConfig.getRpcUrl(action);
                } else {
                    return qmEventConfig.getRpcUrl(action);
                }
            },
            /**
             * @ngdoc method
             * @name doSilentLogin
             * @methodOf logon.service:qmLogonService
             * @description
             * Does a silent login(event level) using the token given after login at the container
             *
             * @returns {promise} Promise that gets resolved after performing the silent login
             */
            doSilentLogin: function(ssoToken, userName, appId) {

                var url = this.getQuickEventRpcUrl(appId);
                var bGetInfo = true;
                var toastMsg = this.getLocalizedStringFromErrorMsg('Authentication Failure');
                var title = '';
                var that = this;
                var defer = $q.defer();
                // Set the event app id so that we use the right Authorization header
                qmWebAppService.initEvent(appId);

                qmRpc.post(url, {method: 'login', params: [userName, ssoToken, bGetInfo]}).then(function(response) {
                    // Did we get a token ?
                    if (response.token) {
                        // We need to grab the event config first so that we can start the session, etc.
                        qmEventConfig.getStatus().then(function() {
                            that.resetSignUpFlow();
                            // User has logged in. Let's change the first time user flag
                            that.markFirstTimeLogin();
                            qmAnalytics.markEvent('LoginSuccess', response.attendee.attendeeId);
                            qmAnalytics.startSession(response.attendee.attendeeId, true);
                            that.setLoginInfo(response.token, response.attendee);
                            defer.resolve(response.token);
                        }, function() {
                            defer.reject();
                        });

                    } else {
                        // No token mark login failure
                        qmAnalytics.markEvent('LoginFailure');
                        toastr.error(toastMsg, title, {
                            closeButton: true,
                            timeOut: 5000
                        });
                        defer.reject();
                    }
                }, function(err) {
                    // Mark login failure
                    qmAnalytics.markEvent('LoginFailure');
                    // We need to unset the event appId since the silent login failed. Not doing this results in getting the
                    // config of the quickEvent which is not even present at this point.
                    qmWebAppData.setEventAppId(null);
                    if (err.error) {
                        toastMsg = that.getLocalizedStringFromErrorMsg(err.error);
                        toastr.error(toastMsg, title, {
                            closeButton: true,
                            timeOut: 5000
                        });
                    }
                    defer.reject();
                });

                return defer.promise;
            },
            /**
             * @ngdoc method
             * @name getQuickEventRpcUrl
             * @methodOf logon.service:qmLogonService
             * @description
             * Gets the rpc url for the quick event using the appId
             *
             * @returns {string} The url to use
             */
            getQuickEventRpcUrl: function(appId) {

                // We don't have the quickEvent config at this point. So we have to generate the url from the container
                // RPC call replacing the quickEvent appId

                var url = qmContainerConfig.getRpcUrl('login');
                var parts = url.split('//');
                url = parts[0] + '//';
                parts = parts[1].split('/');
                parts.pop();
                // Use the quickEvent appId instead
                parts.push(appId);
                url = url + parts.join('/');

                return url;
            },
            /**
             * @ngdoc method
             * @name setIsContainerLevel
             * @methodOf logon.service:qmLogonService
             * @description
             * Sets a flag to find out the app's level (container or quickEvent)
             *
             */
            setIsContainerLevel: function(isContainer) {
                this.isContainerLevel = isContainer;
            },
            /**
             * @ngdoc method
             * @name getIsContainerLevel
             * @methodOf logon.service:qmLogonService
             * @description
             * Gets app's level (container or quickEvent)
             *
             * @returns {boolean} Is the app at container level
             */
            getIsContainerLevel: function() {
                return this.isContainerLevel;
            },
            /**
             * @ngdoc method
             * @name shouldLoginAtContainer
             * @methodOf logon.service:qmLogonService
             * @description
             * Checks if the user needs to do the login at container. This is use to determine if the app should
             * redirect to the container and prompt login
             *
             * @returns {boolean} Does the user need to login at the container ?
             */
            shouldLoginAtContainer: function(data) {

                if (data.ssoToken === null && qmContainerConfig.getIsContainerLogin()) {
                    // User has not logged in. Verify if he's requesting something at event level
                    if (!this.getIsContainerLevel()) {
                        // At event level with no authorization.
                        return true;
                    }
                }

                return false;
            },
            /**
             * @ngdoc method
             * @name getSamlUrl
             * @methodOf logon.service:qmLogonService
             * @description
             * Get saml login url
             *
             * @returns {string} url to saml login ?
             */
            getSamlUrl: function() {

                var url = qmWebAppService.getBaseRestUrl('component', 'sso') + '/saml/authenticate';

                return url;
            },
            /**
             * @ngdoc method
             * @name doSamlSilentLogin
             * @methodOf logon.service:qmLogonService
             * @description
             * Does a Saml silent login(event level) using the token given after login at the external SSO login process
             *
             * @returns {promise} Promise that gets resolved after performing the silent login
             */
            doSamlSilentLogin: function(ssoToken, userName) {

                var url = this.getRpcUrl('login');

                var bGetInfo = true;
                var ssoState = 'SAML';
                var toastMsg = this.getLocalizedStringFromErrorMsg('Authentication Failure - Wrong attendeeId');
                var title = '';
                var that = this;
                var defer = $q.defer();

                qmRpc.post(url, {method: 'login', params: [userName, ssoToken, bGetInfo, ssoState]}).then(function(response) {
                    // Did we get a token ?
                    if (response.token) {
                        // We need to grab the event config first so that we can start the session, etc.
                        qmEventConfig.getStatus().then(function() {
                            that.resetSignUpFlow();
                            // User has logged in. Let's change the first time user flag
                            that.markFirstTimeLogin();
                            qmAnalytics.markEvent('LoginSuccess', response.attendee.attendeeId);
                            qmAnalytics.startSession(response.attendee.attendeeId, true);
                            that.setLoginInfo(response.token, response.attendee);
                            defer.resolve(response.token);
                        }, function() {
                            defer.reject();
                        });

                    } else {
                        // No token mark login failure
                        qmAnalytics.markEvent('LoginFailure');
                        toastr.error(toastMsg, title, {
                            closeButton: true,
                            timeOut: 5000
                        });
                        defer.reject();
                    }
                }, function(err) {
                    // Mark login failure
                    qmAnalytics.markEvent('LoginFailure');

                    if (err.error) {
                        toastMsg = that.getLocalizedStringFromErrorMsg('Authentication Failure - Wrong attendeeId');
                        toastr.error(toastMsg, title, {
                            closeButton: true,
                            timeOut: 5000
                        });
                    }
                    defer.reject();
                });

                return defer.promise;
            },

            getCcpaUrl: function() {
                var getCcpaUrl = qmWebAppService.getBaseRestUrl('service', 'web-app') + '/ccpaurl';
                var defer = $q.defer();
                qmRest.get(getCcpaUrl).then(function(data) {
                    defer.resolve(data);
                }, function() {
                    defer.reject();
                });
                return defer.promise;
            }
        };
    }
]);

var aboutTheApp = angular.module('about-the-app-V2_0');

aboutTheApp.config(['$stateProvider', function($stateProvider) {
    $stateProvider
        .state('event.about-the-app-V2_0', {
            views: {
            },
            resolve: {
                openAboutTheApp: ['$stateParams', 'qmEventConfig', 'qmUtilities', '$q',
                    function($stateParams, qmEventConfig, qmUtilities, $q) {
                    var componentConfig = qmEventConfig.getComponentConfig('about-the-app', $stateParams.componentId);
                    var url = componentConfig['@attributes'].url;
                    var defer = $q.defer();
                    if (!qmUtilities.openUrl(url)) {
                        defer.resolve();
                    }
                    return defer.promise;
                }]
            }
        });
}]);

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

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


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

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

                var checkValue = config['@attributes'][componentKey] === 'true' ? true : false;

                return checkValue;
            }
        };
    }
]);

var activityfeed = angular.module('activityfeed-V2_2');
/**
 * @ngdoc service
 * @name activityfeed-V2_2.service:qmActivityFeedDependencyService
 * @description
 * Manages communication with dependency manager
 */
activityfeed.factory('qmActivityFeedDependencyService', ['qmDependencyManager', function(qmDependencyManager) {
    return {
        /**
         * @ngdoc method
         * @name getSpeakoutsService
         * @methodOf activityfeed-V2_2.service:qmActivityFeedDependencyService
         * @description
         * Get service from speakout's component
         *
         * @param {string} service Service name
         * @returns {service} Angular service or null
         */
        getSpeakoutsService: function(service) {
            return qmDependencyManager.getService('speakouts', '2.0', 'component', service);
        },

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

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

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

        getTemplateUrl: function(template) {

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

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

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

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

            /* Filters the data for activity feed */
            $scope.filterItems = function(data) {
                var sortedItems = [];
                var filter = [];

                if (!data || data.length === 0) {
                    $scope.hasItems = false;
                    return;
                }

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

                for (var component in data) {
                    if (data.hasOwnProperty(component)) {
                        var isSelected = false;
                        for (var j = 0; j < filter.length; j++) {
                            if (filter[j].isSelected && component === filter[j].value) {
                                isSelected = true;
                                break;
                            }
                        }
                        if (component === 'announcements' || (isSelected && config['@attributes'][component] === 'true')) {
                            sortedItems = sortedItems.concat(data[component]);
                        }
                    }
                }
                // Let's sort the items by dateTime DESC
                var sortByDate = function(a, b) {
                    if (qmMoment(a.activityFeedDateTime).isAfter(b.activityFeedDateTime)) {
                        return -1;
                    } else {
                        if (a.activityFeedDateTime === b.activityFeedDateTime && a.title < b.title) {
                            return -1;
                        } else {
                            return 1;
                        }
                    }
                };

                sortedItems.sort(function(a, b) {
                    var now = qmMoment();
                    //If one of the items is pinned follow these rules
                    if (a.pinned === '1' && qmMoment(a.pinnedUntil).isAfter(now)) {
                        if (b.pinned === '1' && qmMoment(b.pinnedUntil).isAfter(now)) {
                            return sortByDate(a, b);
                        } else {
                            return -1;
                        }
                    } else if (b.pinned === '1' && qmMoment(b.pinnedUntil).isAfter(now)) {
                        return 1;
                    }
                    // If not, then use normal sorting process
                    // Find out if the activityFeedDateTime of current item is after the one in the sorted array
                    return sortByDate(a, b);
                });

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

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

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

            $scope.eventsTemplate = qmActivityFeedDependencyService.getTemplateUrl('activityfeed-events.html');
            $scope.announcementsTemplate = qmActivityFeedDependencyService.getTemplateUrl('activityfeed-announcements.html');
            $scope.photosTemplate = qmActivityFeedDependencyService.getTemplateUrl('activityfeed-photos.html');
            $scope.speakoutTemplate = qmActivityFeedDependencyService.getTemplateUrl('activityfeed-speakouts.html');

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

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

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

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

/**
 * @ngdoc controller
 * @name activityfeed-V2_2.controller:ActivityFeedListHeaderController
 * @description
 * Controller for activity feed list header view
 */
activityfeed.controller('ActivityFeedListHeaderController', ['$scope', 'qmLocalization', '$rootScope', '$state', 'qmEventConfig',
    function($scope, qmLocalization, $rootScope, $state, qmEventConfig) {
        var config = qmEventConfig.getComponentConfig('activityfeed');
        var filtersDisabled = (
            config['@attributes']['events'] !== 'true' &&
            config['@attributes']['gallery'] !== 'true' &&
            config['@attributes']['speakouts'] !== 'true'
        );

        $scope.headerRightElements = [];
        $scope.headerBackState = false;
        $scope.headerTitle = qmLocalization.getString('componentActivityfeedTitle');

        $scope.headerTitleClickHandler = filtersDisabled ? false : function() {
            $rootScope.$broadcast('dropDownToggle');
        };
        $scope.headerTitleTestId = 'componentTitle';
    }]);

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

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

            var originalItems = qmSessionStorage.getItem('feedDropdownItems');
            if (originalItems && originalItems.length) {
                $scope.dropdownItems = angular.copy(originalItems);
            } else {
                // Default configuration for the dropdown items
                $scope.dropdownItems = [
                    {
                        title: qmLocalization.getString('componentGalleryTitle'),
                        value: 'gallery',
                        icon: 'fa fa-camera',
                        isSelected: true
                    },
                    {
                        title: qmLocalization.getString('componentEventsTitle'),
                        value: 'events',
                        icon: 'fa fa-clock-o',
                        isSelected: true
                    },
                    {
                        title: qmLocalization.getString('componentSpeakOutTitle'),
                        value: 'speakouts',
                        icon: 'fa fa-commenting-o',
                        isSelected: true
                    }
                ];
            }
            // sort dropdown filter items alphabetically
            $scope.dropdownItems.sort(function(a,b) {
                return (a.title > b.title) ? 1 : -1;
            });
            $scope.checkServiceAvailable = function(componentKey) {
                var hasService = false;

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

                    switch (componentKey) {
                        case 'announcements':
                            hasService = true;  // announcements should always be available to activity feed
                            break;
                        case 'gallery':
                            var qmGalleryService = qmActivityFeedDependencyService.getGalleryService('qmGalleryService');
                            if (qmGalleryService) {
                                hasService = true;
                            }
                            break;
                        case 'events':
                            var qmEventsService = qmActivityFeedDependencyService.getEventsService('qmEventsService');
                            if (qmEventsService) {
                                hasService = true;
                            }
                            break;
                        case 'speakouts':
                            var qmSpeakoutsService = qmActivityFeedDependencyService.getSpeakoutsService('qmSpeakoutsService');
                            if (qmSpeakoutsService) {
                                hasService = true;
                            }
                            break;
                        default:
                            hasService = false;
                            break;
                    }
                } else {
                    hasService = false;
                }

                return hasService;
            };

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

                return itemDisplay;
            };

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

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

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

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

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

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

            $scope.filterUpdate();
        }]);

var activityfeed = angular.module('activityfeed-V2_2');
/**
 * @ngdoc service
 * @name activityfeed-V2_2.service:qmActivityFeedService
 * @description
 * Manages REST API communication for ActivityFeed
 */
activityfeed.factory('qmActivityFeedService',
    ['qmRest', '$rootScope', '$q', 'qmLocalization', 'qmWebAppService', 'qmActivityFeedDependencyService', 'qmEventConfig', 'qmLogin', 'qmMoment',
    function(qmRest, $rootScope, $q, qmLocalization, qmWebAppService, qmActivityFeedDependencyService, qmEventConfig, qmLogin, qmMoment) {
        return {
            unknownAttendeeImgSrc: '/asset/service/web-app/2.0/images/image_attendee_default.png',
            /**
             * @ngdoc method
             * @name getList
             * @methodOf activityfeed-V2_2.service:qmActivityFeedService
             * @description
             * Gets the list of activity feed items
             *
             * @returns {promise} Promise that resolves when the activity feed items have been retrieved
             */
            getList: function() {
                var url = qmWebAppService.getBaseRestUrl('component', 'activityfeed') + '/activityfeed';
                var defer = $q.defer();
                var scope = this;
                var items = [];

                // get activity feed data
                qmRest.get(url).then(function(data) {
                    var announcementsPromise = scope.getAnnouncements();
                    var eventsPromise = scope.getEvents(data['events']);
                    var speakoutsPromise = scope.getSpeakouts();
                    var galleryIds = (data['gallery']) ? data['gallery'] : [];
                    var galleryPromise = scope.getPhotos(galleryIds);

                    if (data.length === 0) {
                        defer.resolve(items);
                        return defer.promise;
                    }

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

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

                return defer.promise;
            },

            /**
             * @ngdoc method
             * @name getSpeakouts
             * @methodOf activityfeed-V2_2.service:qmActivityFeedService
             * @description
             * Gets the list of speakouts
             *
             * @returns {promise} Promise that resolves when the speakouts items have been retrieved
             */
            getSpeakouts: function() {
                var defer = $q.defer();
                var qmSpeakoutsService = qmActivityFeedDependencyService.getSpeakoutsService('qmSpeakoutsService');
                var promise;
                var scope = this;

                if (!qmSpeakoutsService) {
                    defer.resolve([]);
                    return defer.promise;
                }
                promise = qmSpeakoutsService.getList();
                promise.then(function(data) {
                    var result = data.map(function(item) {
                        item.activityFeedDateTime = item.sentTime;
                        item.type = 'speakouts';
                        item.attendeeImageUrl = qmWebAppService.appendAuthHeader(item.attendeeImageUrl);
                        if (!item.attendeeId) {
                            item.attendeeImageUrl = scope.unknownAttendeeImgSrc;
                            item.firstName = qmLocalization.getString('LABEL_ANONYMOUS');
                            item.lastName = '';
                        }
                        return item;
                    });
                    // Resolve promise
                    defer.resolve(result);
                }, function(err) {
                    defer.reject(err);
                });

                return defer.promise;
            },

            /**
             * @ngdoc method
             * @name getAnnouncements
             * @methodOf activityfeed-V2_2.service:qmActivityFeedService
             * @description
             * Gets the list of announcements given an array of ids
             *
             * @returns {promise} Promise that resolves when the announcements items have been retrieved
             */
            getAnnouncements: function() {
                var defer = $q.defer();
                var announcementsFinal = [];
                var queryParams = [];
                var url;

                queryParams.push('locale=' + qmLocalization.getLocale());
                url = qmWebAppService.getBaseRestUrl('component', 'activityfeed') + '/announcements?' + queryParams.join('&');
                qmRest.get(url).then(function(announcementsRough) {
                    // reformat dates and times for view
                    var now = qmMoment();
                    announcementsFinal = announcementsRough.map(function(announcement) {
                        announcement.announcementImageUrl = qmWebAppService.appendAuthHeader(announcement.announcementImageUrl);
                        announcement.activityFeedDateTime = announcement.scheduledDate;
                        announcement.type = 'announcements';
                        //Takes into account those announcement that were pinned but should show date on activity feed
                        if (announcement.pinned === '1' && now.isAfter(qmMoment(announcement.pinnedUntil))) {
                            announcement.showDate = true;
                        }
                        return announcement;
                    });
                    defer.resolve(announcementsFinal);
                }, function(err) {
                    defer.reject(err);
                });

                return defer.promise;
            },

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

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

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

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

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

                    var promise = qmEventsService.getList(eventIdList, '', 0, 0, 1, 1);
                    var qmSpeakerService = qmActivityFeedDependencyService.getSpeakersService('qmSpeakerService');
                    var speakersRequiresLogin = qmEventConfig.isComponentLoginRequired('speakers');
                    var handleSpeakersPromise = function(speakerPromise, defer, eventObj) {
                        speakerPromise.then(function(speakers) {
                            if (speakers.length > 0) {
                                eventObj.speakers = speakers;
                            }
                            defer.resolve();
                        }, function() {
                            defer.reject();
                        });
                    };
                    var speakerPromises = [];
                    promise.then(function(sessions) {
                        var events = [];

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

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

                            sessions[i].activityFeedDateTime = sessions[i].startDateTime;
                            sessions[i].type = 'events';
                            if (qmSpeakerService && (!speakersRequiresLogin || qmLogin.isLoggedIn())) {
                                var defer = $q.defer();
                                var speakerPromise = qmSpeakerService.getEventSpeakers(sessions[i].eventId);

                                speakerPromises.push(speakerPromise);
                                handleSpeakersPromise(speakerPromise, defer, sessions[i]);
                            }
                            events.push(sessions[i]);
                        }
                        $q.all(speakerPromises).then(function() {
                            mainDefer.resolve(events);
                        }, function(err) {
                            mainDefer.reject(err);
                        });

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

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

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

                return timeDiffMins;
            },

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

                var mainDefer = $q.defer();
                var feedPhotos = [];
                var qmGalleryService = qmActivityFeedDependencyService.getGalleryService('qmGalleryService');
                var galleryRequiresLogin = qmEventConfig.isComponentLoginRequired('gallery');
                var photoCommentChars = 280;
                var photoLimit = 70;
                var scope = this;

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

                if (qmGalleryService && (!galleryRequiresLogin || qmLogin.isLoggedIn())) {

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

                        var detailPromises = [];

                        // handle individual gallery detail promises
                        var handleDetailPromise = function(detailPromise, defer, gallery) {
                            detailPromise.then(function(photos) {
                                photos.sort(function(a,b) {return a.qmLastMod < b.qmLastMod;});
                                if (photos.length > 0) {
                                    gallery.activityFeedDateTime = photos[0].qmLastMod;
                                    gallery.photos = photos;
                                    for (var i = 0; i < gallery.photos.length; i++) {
                                        gallery.photos[i].type = 'photo';
                                        gallery.photos[i].activityFeedDateTime = gallery.photos[i].qmLastMod;
                                        gallery.photos[i].galleryId = gallery.galleryId;
                                        gallery.photos[i].attendeeImageUrl = qmWebAppService.appendAuthHeader(gallery.photos[i].attendeeImageUrl);
                                        if (gallery.photos[i].comment.length > photoCommentChars) {
                                            gallery.photos[i].comment = gallery.photos[i].comment.substr(0, photoCommentChars) + '...';
                                        } else {
                                            gallery.photos[i].comment = gallery.photos[i].comment.substr(0, photoCommentChars);
                                        }
                                        if (!gallery.photos[i].attendeeId) {
                                            gallery.photos[i].attendeeImageUrl = scope.unknownAttendeeImgSrc;
                                            gallery.photos[i].attendeeFirstName = qmLocalization.getString('LABEL_ANONYMOUS');
                                            gallery.photos[i].attendeeLastName = '';
                                        }
                                        feedPhotos.push(gallery.photos[i]);
                                    }
                                }
                                defer.resolve();
                            }, function() {
                                defer.reject();
                            });
                        };

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

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

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

var widgets = angular.module('activityfeed-V2_2');

/**
 * @ngdoc directive
 * @name widgets.directive:qmButtonWidget
 * @scope
 * @restrict E
 *
 * @description
 * This is the button widget
 *
 * @param {string} content Button text
 * @param {function} action Function to execute when button is clicked
 */
widgets.directive('activityFeedSpeakersList', [function() {
    return {
        restrict: 'E',
        transclude: true,
        scope: {
            speakers: '='
        },
        templateUrl: '/asset/component/activityfeed/2.2/webapp/html/activityfeed-speakers-list-widget.html',
        link: function() {
        }
    };
}]);

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

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

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

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

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

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

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

                if (!entity || type === 'components') {
                    $state.go('event.articles');
                } else {
                    qmArticlesService.getList(entity).then(function(articleResponse) {
                        var gotArticle = articleResponse.find(function(article) {
                            return article.articleId === entity;
                        });

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

        return init;
    }]);

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

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

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

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

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

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

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

/**
 * Created by Oscar Salvador on 2017-01-24
 */

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

authors.config(['$stateProvider', function($stateProvider) {
    $stateProvider
        .state('event.authors-V2_0', {
            url: '/Authors',
            views: {
                'component@event': {
                    templateUrl: '/asset/component/authors/2.0/webapp/html/author-list.html',
                    controller: 'AuthorListController'
                },
                'header@event': {
                    controller: 'AuthorListHeaderController'
                }
            }
        })
        .state('event.authors-V2_0.detail', {
            url: '/:authorId',
            views: {
                'component@event': {
                    templateUrl: '/asset/component/authors/2.0/webapp/html/author-detail.html',
                    controller: 'AuthorDetailController'
                },
                'header@event': {
                    controller: 'AuthorDetailHeaderController'
                }
            }
        })
        .state('event.authors-V2_0.comments', {
            url: '/Comments/:authorId',
            views: {
                'component@event': {
                    templateUrl: '/asset/component/authors/2.0/webapp/html/author-comments.html',
                    controller: 'AuthorCommentsController'
                },
                'header@event': {
                    controller: 'AuthorCommentsHeaderController'
                }
            }
        })
        .state('event.authors-V2_0.comment-editor', {
            url: '/Comment/:authorId/:commentId',
            views: {
                'component@event': {
                    templateUrl: '/asset/component/authors/2.0/webapp/html/author-comment-editor.html',
                    controller: 'AuthorCommentEditorController'
                },
                'header@event': {
                    controller: 'AuthorCommentEditorHeaderController'
                }
            }
        });
}]);

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

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

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

            /**
             * Gets a list of documents with the documentIds given using the documentsService
             * @param documentIds
             *
             * @returns {promise} Promise that resolves when the documents have been retrieved
             */
            var getAuthorDocumentsByIds = function(documentIds) {

                var defer = $q.defer();
                var documentsService = qmAuthorsDependencyService.getDocumentsService('qmDocumentsService');
                documentsService.getAuthorDocumentsList(documentIds).then(function(data) {
                    var publishedDocs = [];
                    for (var i = 0; i < data.length; i++) {
                        if (data[i].publish === '1') {
                            publishedDocs.push(data[i]);
                        }
                    }
                    // Resolve published documents
                    defer.resolve(publishedDocs);

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

            /**
             * Gets a list of events with the eventIds given using the eventsService
             * @param eventIds
             *
             * @returns {promise} Promise that resolves when the events have been retrieved
             */
            var getEventsByIds = function(eventIds) {

                var defer = $q.defer();
                var eventService = qmAuthorsDependencyService.getEventsService('qmEventsService');
                var events = eventService.getList(eventIds);
                events.then(function(data) {
                    defer.resolve(data);
                }, function(err) {
                    defer.reject(err);
                });

                return defer.promise;
            };

            return {
                /**
                 * @ngdoc method
                 * @name getCommentListState
                 * @methodOf authors-V2_0.service:qmAuthorService
                 * @description
                 * Gets comments list page state for UI router
                 *
                 * @returns {string} UI router state
                 */
                getCommentListState: function() {
                    return 'event.authors.comments';
                },
                /**
                 * @ngdoc method
                 * @name getDetailState
                 * @methodOf authors-V2_0.service:qmAuthorService
                 * @description
                 * Gets entity detail page state for UI router
                 *
                 * @returns {string} UI router state
                 */
                getDetailState: function() {
                    return 'event.authors.detail';
                },
                /**
                 * @ngdoc method
                 * @name getList
                 * @methodOf authors-V2_0.service:qmAuthorService
                 * @description
                 * Gets list of authors
                 *
                 * @param {string} Search term
                 * @param {int}    Zero based offset
                 * @param {int}    Limit the response items
                 *
                 * @returns {promise} Promise that resolves when author list has been retrieved
                 */
                getList: function(searchTerm, offset, limit) {

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

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

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

                    return defer.promise;
                },
                /**
                 * @ngdoc method
                 * @name getDetail
                 * @methodOf authors-V2_0.service:qmAuthorService
                 * @description
                 * Gets details of a author
                 *
                 * @param {string} authorId       Speaker Id
                 * @param {bool}   checkEvents    If true we should get events with the eventIds in the response
                 * @param {bool}   checkDocuments If true we should get documents with the documentIds in the response
                 *
                 * @returns {promise} Promise that resolves when author details has been retrieved
                 */
                getDetail: function(authorId, checkEvents, checkDocuments) {

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

                        data['events'] = [];
                        data['documents'] = [];

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

                        // Now get the events with these ids
                        if (checkEvents && data['eventIds'] && data['eventIds'].length > 0) {
                            // Get the events
                            getEventsByIds(data['eventIds']).then(function(eventData) {
                                // Add the events to the object
                                data['events'] = eventData;
                                if (checkDocuments && data['documentIds'] && data['documentIds'].length > 0) {
                                    // Get the documents
                                    getAuthorDocumentsByIds(data['documentIds']).then(function(documentsData) {
                                        // Add the documents to the object
                                        data['documents'] = documentsData;
                                        defer.resolve(data);
                                    });
                                } else {
                                    defer.resolve(data);
                                }
                            });
                        } else if (checkDocuments && data['documentIds'] && data['documentIds'].length > 0) {
                            // Get the documents
                            getAuthorDocumentsByIds(data['documentIds']).then(function(documentsData) {
                                data['documents'] = documentsData;
                                defer.resolve(data);
                            }, function() {
                                defer.resolve(data);
                            });
                        } else {
                            defer.resolve(data);
                        }

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

                    return defer.promise;
                },
                /**
                 * @ngdoc method
                 * @name getEventAuthors
                 * @methodOf authors-V2_0.service:qmAuthorService
                 * @description
                 * Gets list of authors for an event
                 *
                 * @returns {promise} Promise that resolves when author list has been retrieved
                 */
                getEventAuthors: function(eventId) {

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

                    return defer.promise;
                },
                /**
                 * @ngdoc method
                 * @name getDocumentAuthors
                 * @methodOf authors-V2_0.service:qmAuthorService
                 * @description
                 * Gets a list of authors for a document
                 *
                 * @returns {promise} Promise that resolves when author list has been retrieved
                 */
                getDocumentAuthors: function(documentId) {

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

                    return defer.promise;
                },
                /**
                 * @ngdoc method
                 * @name getEventAuthorsLike
                 * @methodOf authors-V2_0.service:qmAuthorService
                 * @description
                 * Get likes data and add them to author list view
                 *
                 * @param {Array} authors list of Authors
                 * @returns {promise} Promise that resolves when likes data have successfully been added
                 */
                getEventAuthorsSocialData: function(authors) {

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

                        return qmSocialService.getObjectSocialData(authors, 'authorId', 'authors');
                    } else {
                        defer.reject();
                    }
                    return defer.promise;
                },
                /**
                 * @ngdoc method
                 * @name getEventAuthorsLike
                 * @methodOf authors-V2_0.service:qmAuthorService
                 * @description
                 * Get likes data and add them to author list view
                 *
                 * @param {Array} authors list of Authors
                 * @returns {promise} Promise that resolves when likes data have successfully been added
                 */
                getEventAuthorsLike: function(authors) {

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

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

/**
 * Created by Oscar Salvador on 2017-01-23
 */

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

/**
 * @ngdoc service
 * @name authors-V2_0.service:qmAuthorsDependencyService
 * @description
 * Manages communication with dependency manager
 */
authors.factory('qmAuthorsDependencyService', ['qmDependencyManager', function(qmDependencyManager) {
    return {
        /**
         * @ngdoc method
         * @name getTemplateUrl
         * @methodOf authors-V2_0.service:qmAuthorsDependencyService
         * @description
         * Get template url for authors component
         *
         * @param {string} name Template name
         * @returns {string} Template url
         */
        getTemplateUrl: function(name) {

            if (name === 'author-list-item') {
                return '/asset/component/authors/2.0/webapp/html/partials/author-list-item.html';
            }
            return null;
        },
        /**
         * @ngdoc method
         * @name getEventsService
         * @methodOf authors-V2_0.service:qmAuthorsDependencyService
         * @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.9', 'component', service);
        },
        /**
         * @ngdoc method
         * @name getDocumentsService
         * @methodOf authors-V2_0.service:qmAuthorsDependencyService
         * @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 getSocialService
         * @methodOf authors-V2_0.service:qmAuthorsDependencyService
         * @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);
        }
    };
}]);

/**
 * Created by Oscar Salvador on 2017-01-23
 */

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

/**
 * @ngdoc controller
 * @name authors-V2_0.controller:AuthorListController
 * @description
 * Controller for authors list view
 */
authors.controller('AuthorListController', ['$scope', 'qmAuthorService', 'qmEventConfig', 'qmLogin', 'qmList',
    'qmLocalization', 'qmAuthorsDependencyService',
    function($scope, qmAuthorService, qmEventConfig, qmLogin, qmList, qmLocalization, qmAuthorsDependencyService) {

        var qmSocialCheck;
        var socialConfig;
        var likesEnabled;
        var commentsEnabled;

        $scope.authorListItemUrl = qmAuthorsDependencyService.getTemplateUrl('author-list-item');
        $scope.loadAuthors = qmAuthorService.getList();
        qmSocialCheck = qmAuthorsDependencyService.getSocialService('qmSocialService');

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

        $scope.loadAuthors.then(function(authors) {
            qmAuthorService.getEventAuthorsSocialData(authors);
            $scope.authors = qmList.groupByLetter(authors, 'lastName');
            var componentTitle = qmLocalization.getString('componentAuthorsTitle');
            $scope.emptyText = qmLocalization.getString('LABEL_EMPTY_AUTHORS', componentTitle);
        });
    }
]);

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


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

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

/**
 * @ngdoc controller
 * @name authors-V2_0.controller:AuthorDetailController
 * @description
 * Controller for author detail view
 */
authors.controller('AuthorDetailController',
    ['$scope', '$state', 'qmList', 'qmEventConfig', 'qmAuthorService', 'qmDependencyManager', 'qmLocalization', 'qmAuthorsDependencyService', 'qmLogin',
        function($scope, $state, qmList, qmEventConfig, qmAuthorService, qmDependencyManager, qmLocalization, qmAuthorsDependencyService, qmLogin) {

            var isLoggedIn = qmLogin.isLoggedIn();
            // Documents variables
            var checkDocuments;
            var hasDocuments = qmDependencyManager.hasComponent('documents');
            var documentsIsConfigured = qmEventConfig.isComponentConfigured('documents');
            var documentsRequiresLogin = qmEventConfig.isComponentLoginRequired('documents');
            var documentsLoginRequired = documentsRequiresLogin && !isLoggedIn;
            // Events variables
            var hasEvents = qmDependencyManager.hasComponent('events');
            var eventsIsConfigured = qmEventConfig.isComponentConfigured('events');
            var eventsRequiresLogin = qmEventConfig.isComponentLoginRequired('events');
            var eventsLoginRequired = eventsRequiresLogin && !isLoggedIn;
            var checkEvents;
            var qmEventsDependencyService;
            var qmSocialConstant;

            checkDocuments = hasDocuments && documentsIsConfigured && !documentsLoginRequired;
            checkEvents = hasEvents && eventsIsConfigured && !eventsLoginRequired;
            $scope.showEvents = false;
            $scope.showDocuments = false;
            $scope.documentsLoginRequired = documentsLoginRequired;
            $scope.documentsTitle = qmLocalization.getString('componentDocumentsTitle');
            $scope.documentsDataTestIdObj = {title: 'authorDetailsDocuments', expander: 'authorSessionExpander'};

            $scope.eventsLoginRequired = eventsLoginRequired;
            $scope.eventsTitle = qmLocalization.getString('componentEventsTitle');
            $scope.dataTestIdObj = {title: 'authorDetailsSchedule', expander: 'authorSessionExpander'};
            $scope.commentsState = 'event.authors.comments';
            $scope.newCommentState = 'event.authors.comment-editor';
            $scope.editCommentState = 'event.authors.comment-editor';
            $scope.whoLikesState = 'event.authors.likes';
            $scope.stateAttrs = {authorId: $state.params.authorId};
            $scope.loadAuthor = qmAuthorService.getDetail($state.params.authorId, checkEvents, checkDocuments);
            $scope.loadAuthor.then(function(author) {

                $scope.author = author;

                // Does the response have events ?
                if (author['events'].length > 0) {
                    // Show Events if there are some to show
                    qmEventsDependencyService = qmAuthorsDependencyService.getEventsService('qmEventsDependencyService');
                    $scope.eventListItemUrl = qmEventsDependencyService.getTemplateUrl('event-list-item');

                    $scope.showEvents = true;
                    $scope.author.events = qmList.groupByGroup(author['events'], 'eventDate', 'eventDateFormatted');
                }
                if (eventsLoginRequired) {
                    $scope.showEvents = true;
                }

                // Does the response have documents ?
                if (author['documents'].length > 0) {
                    // Show documents if there are some to show
                    var qmDocumentsDependencyService = qmAuthorsDependencyService.getDocumentsService('qmDocumentsDependencyService');
                    $scope.documentListItemUrl = qmDocumentsDependencyService.getTemplateUrl('document-list-item');
                    $scope.showDocuments = true;
                    $scope.author.documents = qmList.transformList(author['documents']);
                }
                if (documentsLoginRequired) {
                    $scope.showDocuments = true;
                }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            var defer = $q.defer();

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

                return communityItems;
            }
        };
    }]);

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

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

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

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

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

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

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

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

    var loadDisclaimer = function() {

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

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

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

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

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

    loadDisclaimer();
}]);

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

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

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

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

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

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

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

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

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

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

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

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

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

}]);

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

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

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

/**
 * @ngdoc service
 * @name documents-V2_6.service:qmDocumentDeepLinkingService
 * @description
 * Manages Speaker Deep Linking
 */
documents.factory('qmDocumentDeepLinkingService', ['$state', 'qmDocumentsService', 'qmDocumentsDependencyService', '$rootScope',
    function($state, qmDocumentsService, qmDocumentsDependencyService, $rootScope) {
        var init = {
            /**
             * @ngdoc method
             * @name handleDeeplinkState
             * @methodOf documents-V2_6.service:qmDocumentDeepLinkingService
             * @description
             * Get state for sponsor component deep link
             *
             * @param {string} type Type of component
             * @param {string} entity Entity key
             */
            handleDeepLinkState: function(type, entity) {
                var qmDeeplinkingService = qmDocumentsDependencyService.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.documents');
                } else {
                    qmDocumentsService.getDetail(entity).then(function(documentResponse) {
                        if (!documentResponse || documentResponse.securityType === '1') {
                            qmDeeplinkingService.handleUnavailableDeeplink(type, entity);
                            return;
                        }
                        $state.go('event.documents.detail', {documentId: entity});
                    }, function(err) {
                        err.config.errorHandled = true;
                        if (err.status !== 401) {
                            qmDeeplinkingService.handleUnavailableDeeplink(type, entity);
                        }
                    });
                }
            }
        };

        return init;
    }]);

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

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

        function decorator(document) {

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

            return document;
        }

        function formatDocumentList(documentList) {
            for (var i = 0; i < documentList.length; i++) {
                if (!documentList[i].categoryType) {
                    documentList[i].groupValue = 'GROUP_GENERAL';
                    documentList[i].groupLabel = qmLocalization.getString('LABEL_GENERAL');
                } else {
                    documentList[i].groupValue = documentList[i].categoryType;
                    documentList[i].groupLabel = documentList[i].categoryType;
                }
            }

            return documentList;
        }

        function makeEndPointUrl(endPoint, documentIds) {
            var url = qmWebAppService.getBaseRestUrl('component', 'documents') + '/' + endPoint;
            url += '?locale=' + qmLocalization.getLocale();
            if (documentIds !== undefined && documentIds.length) {
                for (var i = 0; i < documentIds.length; i++) {
                    url += '&documentIds[]=' + documentIds[i].documentId;
                }
            }

            return url;
        }

        return {
            /**
             * @ngdoc method
             * @name getList
             * @methodOf documents-V2_6.service:qmDocumentsService
             * @description
             * Get list of documents
             *
             * @param {array} documentIds Document Ids
             *
             * @returns {promise} Promise that resolves when document list has been retrieved
             */
            getList: function(documentIds) {
                var url = makeEndPointUrl('documents', documentIds);
                var defer = $q.defer();
                // get document list data
                qmRest.get(url).then(function(response) {
                    var documentList = formatDocumentList(response);
                    defer.resolve(documentList);
                }, function(err) {
                    defer.reject(err);
                });

                return defer.promise;
            },
            /**
             * @ngdoc method
             * @name getList
             * @methodOf documents-V2_6.service:qmDocumentsService
             * @description
             * Get list of documents
             *
             * @param {array} documentIds Document Ids
             *
             * @returns {promise} Promise that resolves when document list has been retrieved
             */
            getExhibitorDocumentList: function(documentIds) {
                var url = makeEndPointUrl('exhibitor-documents', documentIds);
                var defer = $q.defer();
                // get document list data
                qmRest.get(url).then(function(response) {
                    var documentList = formatDocumentList(response);
                    defer.resolve(documentList);
                }, function(err) {
                    defer.reject(err);
                });

                return defer.promise;
            },
            /**
             * @ngdoc method
             * @name getList
             * @methodOf documents-V2_6.service:qmDocumentsService
             * @description
             * Get list of author documents
             *
             * @param {array} documentIds Document Ids
             *
             * @returns {promise} Promise that resolves when document list has been retrieved
             */
            getAuthorDocumentsList: function(documentIds) {
                var url = makeEndPointUrl('author-documents', documentIds);
                var defer = $q.defer();
                // get document list data
                qmRest.get(url).then(function(response) {
                    var documentList = formatDocumentList(response);
                    defer.resolve(documentList);
                }, function(err) {
                    defer.reject(err);
                });

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

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

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

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

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

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

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

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

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

                var defer = $q.defer();
                // get document list data
                qmRest.get(url).then(function(response) {
                    var documentList = formatDocumentList(response);
                    defer.resolve(documentList);
                }, function(err) {
                    defer.reject(err);
                });

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

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

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

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

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

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

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

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

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

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

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

/**
 * @ngdoc controller
 * @name documents-V2_6.controller:DocumentDetailController
 * @description
 * Controller for document detail view
 */
documents.controller('DocumentDetailController', ['$scope', '$state', 'qmDocumentsService', 'qmDocumentsDependencyService',
    'qmDependencyManager', 'qmEventConfig', 'qmLogin', 'qmUtilities', 'qmLocalization', 'qmList',
    function($scope, $state, qmDocumentsService, qmDocumentsDependencyService, qmDependencyManager, qmEventConfig,
             qmLogin, qmUtilities, qmLocalization, qmList) {

        $scope.showAuthors = false;
        $scope.loadDocument = qmDocumentsService.getDetail($state.params.documentId);
        $scope.loadDocument.then(function(documentIn) {

            $scope.documentObj = documentIn;
            $scope.isMyDocument = documentIn.isMyFavourite;
            $scope.authorsTitle = qmLocalization.getString('componentAuthorsTitle');
            $scope.authorsDataTestIdObj = {expander: 'sessionAuthorsExpander'};

            var isLoggedIn = qmLogin.isLoggedIn();
            var hasMyDocs = qmDependencyManager.hasComponent('my-documents');
            var showAddRemoveButton = hasMyDocs && qmLogin.isLoggedIn();
            var authorsIsConfigured = qmEventConfig.isComponentConfigured('authors');
            var authorsRequiresLogin = qmEventConfig.isComponentLoginRequired('authors');

            $scope.showAddRemoveButton = showAddRemoveButton;

            /**
             * Asks user to login
             */
            $scope.promptLogin = function() {
                qmLogin.checkLogin().then(function() {
                    $state.reload();
                });
            };

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

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

            // Let's get the list of authors
            if (authorsIsConfigured && (!authorsRequiresLogin || isLoggedIn)) {

                var qmAuthorsService = qmDocumentsDependencyService.getAuthorsService('qmAuthorService');
                $scope.loadAuthors = qmAuthorsService.getDocumentAuthors($state.params.documentId);
                $scope.loadAuthors.then(function(authors) {

                    $scope.authors = qmList.transformList(authors);
                    var qmAuthorsDependencyService = qmDocumentsDependencyService.getAuthorsService('qmAuthorsDependencyService');
                    $scope.authorListItemUrl = qmAuthorsDependencyService.getTemplateUrl('author-list-item');
                    // Show Authors if there are some to show
                    if ($scope.authors.length > 0) {
                        $scope.showAuthors = true;
                    }
                });
            } else if (authorsIsConfigured) {
                $scope.showAuthors = true;
                $scope.authorsLoginRequired = true;
            }
        });
    }
]);

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

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

events.config(['$stateProvider', function($stateProvider) {
    $stateProvider
        .state('event.events-V2_9', {
            url: '/Schedule',
            data: {
                componentKey: 'events',
                resourceKey: 'session',
                entityType: 'eventId'
            },
            views: {
                'component@event': {
                    templateUrl: '/asset/component/events/2.9/webapp/html/event-list.html',
                    controller: 'EventListController'
                },
                'header@event': {
                    controller: 'EventListHeaderController'
                }
            }
        })
        .state('event.events-V2_9.detail', {
            url: '/:eventId',
            views: {
                'component@event': {
                    templateUrl: '/asset/component/events/2.9/webapp/html/event-detail.html',
                    controller: 'EventDetailController'
                },
                'header@event': {
                    controller: 'EventDetailHeaderController'
                }
            }
        })
        .state('event.events-V2_9.likes', {
            url: '/Likes/:eventId',
            views: {
                'component@event': {
                    templateUrl: '/asset/component/events/2.8/webapp/html/event-likes.html',
                    controller: 'EventLikesController'
                },
                'header@event': {
                    controller: 'EventLikesHeaderController'
                }
            }
        })
        .state('event.events-V2_9.comments', {
            url: '/Comments/:eventId',
            views: {
                'component@event': {
                    templateUrl: '/asset/component/events/2.8/webapp/html/event-comments.html',
                    controller: 'EventCommentsController'
                },
                'header@event': {
                    controller: 'EventCommentsHeaderController'
                }
            }
        })
        .state('event.events-V2_9.comment-editor', {
            url: '/Comment/:eventId/:commentId',
            views: {
                'component@event': {
                    templateUrl: '/asset/component/events/2.8/webapp/html/event-comment-editor.html',
                    controller: 'EventCommentEditorController'
                },
                'header@event': {
                    controller: 'EventCommentEditorHeaderController'
                }
            }
        })
        .state('event.events-V2_9.social-comments', {
            url: '/SocialComments/:eventId',
            views: {
                'component@event': {
                    templateUrl: '/asset/component/social/2.5/webapp/html/social-comments.html',
                    controller: 'SocialCommentsController'
                },
                'header@event': {
                    controller: 'SocialCommentsHeaderController'
                }
            }
        })
        .state('event.events-V2_9.social-edit-comment', {
            url: '/SocialComment/:eventId/:commentId',
            views: {
                'component@event': {
                    templateUrl: '/asset/component/social/2.5/webapp/html/social-edit-comment.html',
                    controller: 'SocialEditCommentController'
                },
                'header@event': {
                    controller: 'SocialEditCommentHeaderController'
                }
            }
        });
}]);

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

/**
 * @ngdoc service
 * @name events-V2_6.service:qmEventDeepLinkingService
 * @description
 * Manages Event Deep Linking
 */
events.factory('qmEventDeepLinkingService', ['$state', 'qmEventsService', 'qmEventsDependencyService', '$rootScope',
    function($state, qmEventsService, qmEventsDependencyService, $rootScope) {
        var init = {
            /**
             * @ngdoc method
             * @name handleDeeplinkState
             * @methodOf events-V2_6.service:qmEventDeepLinkingService
             * @description
             * Get state for event component deep link
             *
             * @param {string} type Type of component
             * @param {string} entity Entity key
             */
            handleDeepLinkState: function(type, entity) {
                var qmDeeplinkingService = qmEventsDependencyService.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.events');
                } else {
                    qmEventsService.getDetail(entity).then(function(eventResponse) {
                        if (!eventResponse) {
                            qmDeeplinkingService.handleUnavailableDeeplink(type, entity);
                            return;
                        }
                        $state.go('event.events.detail', {eventId: entity});
                    }, function(err) {
                        err.config.errorHandled = true;
                        if (err.status !== 401) {
                            qmDeeplinkingService.handleUnavailableDeeplink(type, entity);
                        }
                    });
                }
            }
        };

        return init;
    }]);

/**
 * Created by Oscar Salvador on 2016-10-27
 */

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

/**
 * @ngdoc service
 * @name events-V2_9.service:qmEventsService
 * @description
 * Manages REST API communication for events
 */
events.factory('qmEventsService', ['qmRest', '$q', '$timeout', 'qmWebAppService',
    'qmLocalization', 'qmEventsDependencyService', 'qmMoment', 'qmList', '$modal',
    function(qmRest, $q, $timeout, qmWebAppService,
             qmLocalization, qmEventsDependencyService, qmMoment, qmList, $modal) {
        return {
            /**
             * @ngdoc method
             * @name formatEventsDates
             * @methodOf events-V2_9.service:qmEventsService
             * @description
             * NEW function for V2_9
             * Takes a list of events and formats the dates with moment
             *
             * @param {Array} eventList List of events
             *
             * @returns {Array} list of events with formatted dates
             */
            formatEventsDates: function(eventList) {

                // Reformat dates and times for the view and ignores sub-sessions
                for (var i = 0; i < eventList.length; i++) {
                    var eventDate = eventList[i].eventDate;
                    eventList[i].eventDateFormatted = qmMoment(eventDate).format('LLLL').replace(qmMoment(eventDate).format('LT'), '');
                    eventList[i].startTimeFormatted = qmMoment(eventList[i].startDateTime).format('LT');
                    eventList[i].endTimeFormatted = qmMoment(eventList[i].endDateTime).format('LT');
                }

                return eventList;
            },
            /**
             * @ngdoc method
             * @name getCommentListState
             * @methodOf events-V2_9.service:qmEventsService
             * @description
             * Get comments list page state for UI router
             * * Get entity detail page state for UI router
             * NEW function for V2_8
             *
             * @returns {string} UI router state
             */
            getCommentListState: function() {
                return 'event.events.comments';
            },
            /**
             * @ngdoc method
             * @name getDetailState
             * @methodOf events-V2_9.service:qmEventsService
             * @description
             * Get entity detail page state for UI router
             * NEW function for V2_8
             *
             * @returns {string} UI router state
             */
            getDetailState: function() {
                return 'event.events.detail';
            },
            /**
             * @ngdoc method
             * @name getList
             * @methodOf events-V2_9.service:qmEventsService
             * @description
             * Get the list of sessions
             *
             * @param {Array} eventIds Event Ids
             * @param {string} searchTerm Search term
             * @param {number}    offset     Zero based offset
             * @param {number}    limit      Limit the response items
             *
             * @returns {promise} Promise that resolves when the sessions have been retrieved
             */
            getList: function(eventIds, searchTerm, offset, limit, isCheckInList, useSortOrder) {
                var url = qmWebAppService.getBaseRestUrl('component', 'events') + '/events';
                var defer = $q.defer();

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

                /*
                 * convert [{eventId:"E1"}] to ['E1'] which past from multiple
                 * pleaces from webapp. keep original place's format, build a filter here.
                 */

                var postEventIds = [];
                angular.forEach(eventIds, function(val) {
                    if (val.eventId) {
                        postEventIds.push(val.eventId);
                    } else {
                        postEventIds.push(val);
                    }
                });

                var params = {
                    locale:qmLocalization.getLocale(),
                    eventIds: postEventIds,
                    searchTerm:searchTerm,
                    offset:offset,
                    limit:limit,
                    isCheckInList: isCheckInList,
                    useSortOrder:useSortOrder
                };

                var $this = this;

                // get session check in list data
                qmRest.post(url, params).then(function(events) {
                    var eventList = $this.formatEventsDates(events);
                    defer.resolve(eventList);
                }, function(err) {
                    defer.reject(err);
                });

                return defer.promise;
            },
            /**
             * @ngdoc method
             * @name getEventsLike
             * @methodOf events-V2_9.service:qmEventsService
             * @description
             * Get likes data and add them to event list view
             *
             * @param {Array} events list of Events
             * @returns {promise} Promise that resolves when likes data have successfully been added
             */
            getEventsLike: function(events) {
                var defer = $q.defer();
                var qmSocialService = qmEventsDependencyService.getSocialService('qmSocialService');
                if (qmSocialService) {
                    qmSocialService.getObjectSocialData(events, 'eventId', 'events').then(function(data) {
                        defer.resolve(data);
                    });
                } else {
                    defer.reject();
                }
                return defer.promise;
            },
            /**
             * @ngdoc method
             * @name getDetail
             * @methodOf events-V2_9.service:qmEventsService
             * @description
             * Get details of an event
             *
             * @param {string} eventId Event Id
             * @returns {promise} Promise that resolves when event details has been retrieved
             */
            getDetail: function(eventId) {
                var url = qmWebAppService.getBaseRestUrl('component', 'events') + '/events/' + eventId;
                url += '?locale=' + qmLocalization.getLocale();
                var defer = $q.defer();
                // get event data
                qmRest.get(url).then(function(data) {
                    // Decorate date and times
                    data.eventDateFormatted = qmMoment(data.eventDate).format('LLLL');
                    data.startTimeFormatted = qmMoment(data.startDateTime).format('h:mm A');
                    data.endTimeFormatted = qmMoment(data.endDateTime).format('h:mm A');
                    // Resolve now
                    defer.resolve(data);
                }, function(err) {
                    defer.reject(err);
                });

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

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

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

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

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

                return defer.promise;
            },
            /**
             * @ngdoc method
             * @name handleEventList
             * @methodOf events-V2_9.service:qmEventsService
             * @description
             * It filters the table to exclude sub-sessions as long as we're not performing a search
             *
             * @param {array} events Events List
             * @param {string} searchTerms Search terms
             * @returns {promise} Decorated array of events
             */
            handleEventList: function(events, searchTerms) {
                var eventList = [];

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

                    if (searchTerms !== '') {
                        eventList.push(events[i]);
                    } else if (events[i].parentId === null && searchTerms === '') {
                        eventList.push(events[i]);
                    }
                }

                return eventList;
            },
            /**
             * @ngdoc method
             * @name doSearch
             * @methodOf events-V2_9.service:qmEventsService
             * @description
             * It filters the table to exclude sub-sessions as long as we're not performing a search
             *
             * @param {string} searchTerm Search term
             * @returns {promise} Promise that resolves when search results are retrieved
             */
            doSearch: function(searchTerm) {
                var defer = $q.defer();
                var searchResultsPromise = this.getList([], searchTerm);

                searchResultsPromise.then(function(events) {
                    var searchResults = qmList.groupByGroup(events, 'eventDate', 'eventDateFormatted');
                    defer.resolve(searchResults);
                }, function(err) {
                    defer.reject(err);
                });

                return defer.promise;
            },
            /**
             * @ngdoc method
             * @name openConfirmModal
             * @methodOf events-V2_9.service:qmEventsService
             * @description
             * Open a confirmation modal
             *
             * @param {object} scope Scope object that is passed to modal
             * @returns {promise} Promise object that resolves when user interacts with modal
             */
            openConfirmModal: function(scope) {
                var confirmModal = $modal.open({
                    templateUrl: '/asset/component/events/2.9/webapp/html/location-options.html',
                    controller: ['$modalInstance', '$scope', function($modalInstance, $scope) {
                        $scope.title = scope.title;
                        $scope.venuesContent = scope.venuesContent;
                        $scope.interactionMapContent = scope.mapsContent;
                        $scope.mapId = scope.mapId;
                        $scope.landmarkId = scope.landmarkId;
                        $scope.venueId = scope.venueId;
                        $scope.afterAction = function() {
                            $modalInstance.dismiss('cancel');
                        };
                    }],
                    size: 'sm'
                });

                return confirmModal.result;
            }
        };
    }]);

/**
 * Created by Oscar Salvador on 2017-01-19
 */

var events = angular.module('events-V2_9');
/**
 * @ngdoc service
 * @name events-V2_9.service:qmEventsDependencyService
 * @description
 * Manages communication with dependency manager
 */
events.factory('qmEventsDependencyService', ['qmDependencyManager', function(qmDependencyManager) {
    return {
        /**
         * @ngdoc method
         * @name getAuthorsService
         * @methodOf events-V2_9.service:qmEventsDependencyService
         * @description
         * Get service from authors component.
         * events-V2_9 NEW FUNCTION
         *
         * @param {string} service Service name
         * @returns {service} Angular service or null
         */
        getAuthorsService: function(service) {
            return qmDependencyManager.getService('authors', '2.0', 'component', service);
        },
        /**
         * @ngdoc method
         * @name getTemplateUrl
         * @methodOf events-V2_9.service:qmEventsDependencyService
         * @description
         * NEW FUNCTION FOR events-V2_9
         * Get template url for events component. Now using the partial in version 2.9
         *
         * @param {string} name Template name
         *
         * @returns {string} Template url
         */
        getTemplateUrl: function(name) {
            if (name === 'event-list-item') {
                return '/asset/component/events/2.9/webapp/html/partials/event-list-item.html';
            } else if (name === 'event-list') {
                return '/asset/component/events/2.9/webapp/html/event-list.html';
            }

            return null;
        },
        /**
         * @ngdoc method
         * @name getDocumentsService
         * @methodOf events-V2_9.service:qmEventsDependencyService
         * @description
         * Get service from documents component
         *
         * @param {string} service Service name
         * @returns {service} Angular service or null
         */
        getDocumentsService: function(service) {
            return qmDependencyManager.getService('documents', '2.1', 'component', service);
        },
        /**
         * @ngdoc method
         * @name getSpeakersService
         * @methodOf events-V2_9.service:qmEventsDependencyService
         * @description
         * Get service from speakers component
         *
         * @param {string} service Service name
         * @returns {service} Angular service or null
         */
        getSpeakersService: function(service) {
            return qmDependencyManager.getService('speakers', '2.0', 'component', service);
        },
        /**
         * @ngdoc method
         * @name getSessionQAService
         * @methodOf events-V2_9.service:qmEventsDependencyService
         * @description
         * Get service from session qa component
         *
         * @param {string} service Service name
         * @returns {service} Angular service or null
         */
        getSessionQAService: function(service) {
            return qmDependencyManager.getService('sessionqa', '2.0', 'component', service);
        },
        /**
         * @ngdoc method
         * @name getVenuesService
         * @methodOf events-V2_9.service:qmEventsDependencyService
         * @description
         * Get service from venues component
         *
         * @param {string} service Service name
         * @returns {service} Angular service or null
         */
        getVenuesService: function(service) {
            return qmDependencyManager.getService('venues', '2.0', 'component', service);
        },
        /**
         * @ngdoc method
         * @name getSurveysService
         * @methodOf events-V2_9.service:qmEventsDependencyService
         * @description
         * Get service from surveys component
         *
         * @param {string} service Service name
         * @returns {service} Angular service or null
         */
        getSurveysService: function(service) {
            return qmDependencyManager.getService('surveys', '2.2', 'component', service);
        },
        /**
         * @ngdoc method
         * @name getMyNotesService
         * @methodOf events-V2_9.service:qmEventsDependencyService
         * @description
         * Get service from my notes component
         *
         * @param {string} service Service name
         * @returns {service} Angular service or null
         */
        getMyNotesService: function(service) {
            return qmDependencyManager.getService('my-notes', '2.0', 'component', service);
        },
        /**
         * @ngdoc method
         * @name getMyScheduleService
         * @methodOf events-V2_9.service:qmEventsDependencyService
         * @description
         * Get service from my schedule component
         *
         * @param {string} service Service name
         * @returns {service} Angular service or null
         */
        getMyScheduleService: function(service) {
            return qmDependencyManager.getService('myschedule', '2.0', 'component', service);
        },
        /**
         * @ngdoc method
         * @name getInteractiveMapsService
         * @methodOf events-V2_9.service:qmEventsDependencyService
         * @description
         * Get service from my interactive maps component
         *
         * @param {string} service Service name
         * @returns {service} Angular service or null
         */
        getInteractiveMapsService: function(service) {
            return qmDependencyManager.getService('interactive-maps', '2.1', 'component', service);
        },
        /**
         * @ngdoc method
         * @name getLivePollingService
         * @methodOf events-V2_9.service:qmEventsDependencyService
         * @description
         * Get service from my live polling component
         *
         * @param {string} service Service name
         * @returns {service} Angular service or null
         */
        getLivePollingService: function(service) {
            return qmDependencyManager.getService('live-polling', '2.2', 'component', service);
        },
        /**
         * @ngdoc method
         * @name getSocialService
         * @methodOf events-V2_9.service:qmEventsDependencyService
         * @description
         * Get service from social component
         *
         * @param {string} service Service name
         * @returns {service} Angular service or null
         */
        getSocialService: function(service) {
            return qmDependencyManager.getService('social', '2.1', 'component', service);
        },
        /**
         * @ngdoc method
         * @name getSocialCommentService
         * @methodOf events-V2_9.service:qmEventsDependencyService
         * @description
         * Get the social service for comments
         *
         * @param {string} service Service name
         * @returns {service} Angular service or null
         */
        getSocialCommentService: function(service) {
            return qmDependencyManager.getService('social', '2.5', 'component', service);
        },
        /**
         * @ngdoc method
         * @name getCommentListState
         * @methodOf events-V2_9.service:qmEventsDependencyService
         * @description
         * Get comments list page state for UI router
         *
         * @returns {string} UI router state
         */
        getCommentListState: function() {
            var qmSocialService = this.getSocialCommentService('qmSocialService');
            if (qmSocialService) {
                return qmSocialService.getCommentListState('events');
            }

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

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

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


/**
 * Created by Oscar Salvador on 2017-01-19
 */

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

/**
 * @ngdoc controller
 * @name events-V2_9.controller:EventListController
 * @description
 * Controller for event list view
 */
events.controller('EventListController', ['$scope', 'qmEventsService', 'qmList', 'qmLocalization',
    'qmEventsDependencyService', 'qmEventConfig', 'qmLogin', '$timeout', 'qmUtilities', 'qmLocalStorage', 'qmMoment',
    function($scope, qmEventsService, qmList, qmLocalization, qmEventsDependencyService, qmEventConfig, qmLogin, $timeout, qmUtilities, qmLocalStorage, qmMoment) {
        var qmSocialCheck = qmEventsDependencyService.getSocialService('qmSocialService');
        var commentsEnabled;
        var timerPromise = null;
        var socialConfig;
        var likesEnabled;
        var unFormatedEventsList;
        var clearTimePromise = function() {
            return timerPromise ? $timeout.cancel(timerPromise) : false;
        };

        var groupEvents = function() {
            $scope.events = qmEventsService.handleEventList(unFormatedEventsList, $scope.searchTerms);
            $scope.events = qmList.groupByGroup($scope.events, 'eventDate', 'eventDateFormatted');
        };

        $scope.hasResults = false;
        $scope.isSearchActive = false;
        $scope.searching = false;
        $scope.searchTerms = '';
        $scope.eventListItemUrl = qmEventsDependencyService.getTemplateUrl('event-list-item');

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

        $scope.cancelSearch = function() {
            clearTimePromise();
            $scope.hasResults = false;
            $scope.searchTerms = '';
            $scope.isSearchActive = false;
            $scope.searching = false;
            qmLocalStorage.removeItem('searchEvent');
            groupEvents();
        };

        $scope.$on('$stateChangeSuccess', function(event, toState) {
            if (toState.name.match(/^event\.(events)(-V[0-9]+_[0-9]+)\.detail$/) ||
                toState.name.match(/^event\.events(-V[0-9]+_[0-9]+)/)) {
                return;
            } else {
                qmLocalStorage.removeItem('searchEvent');
            }
        });

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

        $scope.loadEvents = qmEventsService.getList();
        $scope.loadEvents.then(function(events) {
            unFormatedEventsList = events;
            var cachedSearchTerm = qmLocalStorage.getItem('searchEvent');
            if (qmSocialCheck) {
                qmEventsService.getEventsLike(unFormatedEventsList).then(function() {
                    groupEvents();
                }).finally(function() {
                    var currentDate = new Date();
                    currentDate.setHours(0, 0, 0, 0);
                    var momentCurrentDate = qmMoment(currentDate).startOf('day');
                    for (var i = 0; i < $scope.events.length; i++) {
                        var timeStringBeforeParse = $scope.events[i].value;
                        var timeStringSplit = timeStringBeforeParse.split("T");
                        // we only really care about the date
                        var tabDate = timeStringSplit[0];
                        var momentEventGroupDate = qmMoment(tabDate).startOf('day');

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

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


/**
 * Created by Oscar Salvador on 2017-01-18
 */

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

/**
 * @ngdoc controller
 * @name events-V2_9.controller:EventDetailController
 * @description
 *
 * Controller for event detail view
 */
events.controller('EventDetailController',
    ['$scope', '$state', '$location', 'qmList', 'qmEventConfig', 'qmEventsService', 'qmLocalization', 'qmAnalytics',
        'toastr', 'ical', 'qmLogin', 'qmEventsDependencyService', 'qmUtilities', '$controller', '$q', 'qmLocalStorage',
        '$window', 'qmFlashDataStorage',
        function($scope, $state, $location, qmList, qmEventConfig, qmEventsService, qmLocalization, qmAnalytics,
                 toastr, ical, qmLogin, qmEventsDependencyService, qmUtilities, $controller, $q, qmLocalStorage, $window,
                 qmFlashDataStorage) {

            // Sub-Sessions variables
            $scope.showSubSessions = false;
            $scope.subSessionsTitle = qmLocalization.getString('LABEL_SUB_EVENTS');
            $scope.subSessionsDataTestIdObj = {expander: 'sessionSubSessionExpander'};
            var componentMyScheduleTitle = qmLocalization.getString('componentMyscheduleTitle');
            $scope.myScheduleButtonName = componentMyScheduleTitle ? componentMyScheduleTitle : qmLocalization.getString('LABEL_MY_SCHEDULE');
            $scope.addSessionToMyScheduleRemind = qmLocalization.getString('LABEL_MY_SCHEDULE_ADDED').replace('{0}', componentMyScheduleTitle);

            var entryPoint = {url: 'event.events.detail', params: {eventId: $state.params.eventId}};
            qmFlashDataStorage.setData('editNoteEntryPoint', entryPoint);

            /**
             * Handles the logic for sub-sessions. Function created for events-2_9
             *
             * @param {object} The event object
             */
            var handleSubSessions = function(event) {

                // Does this event have a parent ?
                if (event.parentId !== null) {
                    qmEventsService.getDetail(event.parentId).then(function(parent) {
                        $scope.event.parent = parent;
                    });
                }
                // Check if we need to do work with sub-sessions
                if (event.subSessions.length > 0) {
                    $scope.showSubSessions = true;
                    // Format sub-sessions dates
                    var eventList = qmEventsService.formatEventsDates(event.subSessions);
                    // Group the sub-sessions by date
                    $scope.event.subSessions = qmList.groupByGroup(eventList, 'eventDate', 'eventDateFormatted');
                    // Grab the template url from the dependency service
                    $scope.eventListItemUrl = qmEventsDependencyService.getTemplateUrl('event-list-item');
                }
            };
            $scope.clickLocation = function(mapId, landmarkId) {
                if (angular.isDefined(mapId) && angular.isDefined(landmarkId)) {
                    $state.go('event.interactive-maps.detail', {
                        mapId: mapId,
                        landmarkId: landmarkId,
                        eventId: null,
                        exhibitorId: null
                    });
                }
            };

            // Scope variables
            $scope.loadEvent = qmEventsService.getDetail($state.params.eventId);
            $scope.showEvent = false;
            // Additional Fields Section 1
            $scope.additionalLabel1 = qmLocalization.getString('LABEL_EVENT_ADDITIONAL_SECTION1');
            $scope.additionalDataTestIdObj1 = {expander: 'sessionAdditionalHeaderExpander1'};
            // Additional Fields Section 2
            $scope.additionalLabel2 = qmLocalization.getString('LABEL_EVENT_ADDITIONAL_SECTION2');
            $scope.additionalDataTestIdObj2 = {expander: 'sessionAdditionalHeaderExpander2'};
            // Description variables
            $scope.descriptionLabel = qmLocalization.getString('LABEL_DESCRIPTION');
            $scope.descriptionDataTestIdObj = {expander: 'sessionDetailsHeaderExpander'};
            $scope.showSpeakers = false;
            $scope.speakersTitle = qmLocalization.getString('componentSpeakersTitle');
            $scope.speakersDataTestIdObj = {expander: 'sessionSpeakersExpander'};
            $scope.showAuthors = false;
            $scope.authorsTitle = qmLocalization.getString('componentAuthorsTitle');
            $scope.authorsDataTestIdObj = {expander: 'sessionAuthorsExpander'};
            $scope.showSessionQA = false;
            $scope.sessionQATitle = qmLocalization.getString('componentSessionQATitle');
            $scope.sessionQAInput = qmLocalization.getString('LABEL_TYPE_A_QUESTION');
            $scope.withoutSavingSessionQA = qmLocalization.getString('ALERT_QA_CLOSE_WITHOUT_SAVING_MESSAGE');
            $scope.sessionQADataTestIdObj = {expander: 'sessionQAExpander'};
            $scope.showSurveys = false;
            $scope.surveysTitle = qmLocalization.getString('componentSurveysTitle');
            $scope.surveysDataTestIdObj = {expander: 'sessionSurveysExpander'};
            $scope.showDocuments = false;
            $scope.documentsTitle = qmLocalization.getString('componentDocumentsTitle');
            $scope.documentsDataTestIdObj = {expander: 'sessionDocumentsExpander'};
            $scope.myNotesTitle = qmLocalization.getString('componentMynotesTitle');
            $scope.newNoteTitle = qmLocalization.getString('LABEL_NEW_NOTE');
            $scope.myNotesInput = qmLocalization.getString('LABEL_TYPE_A_NOTE');
            $scope.withoutSavingNote = qmLocalization.getString('ALERT_MY_NOTES_CLOSE_WITHOUT_SAVING_MESSAGE');
            $scope.showMyNotes = false;
            $scope.myNotesAllowEmptySubmit = false;
            $scope.myNotesDataTestIdObj = {expander: 'sessionNotesExpander'};
            $scope.showMySchedule = false;
            $scope.showInteractiveMaps = false;
            $scope.showAttendeeSeating = false;
            $scope.showLivePolling = false;
            $scope.commentsState = qmEventsDependencyService.getCommentListState();
            $scope.newCommentState = qmEventsDependencyService.getNewCommentState();
            $scope.editCommentState = qmEventsDependencyService.getEditCommentState();
            $scope.whoLikesState = 'event.events.likes';
            $scope.stateAttrs = {eventId: $state.params.eventId};
            $scope.venuesTitle = qmLocalization.getString('componentVenuesTitle');
            $scope.interactiveMapsTitle = qmLocalization.getString('componentInteractiveMapsTitle');

            var isLoggedIn = qmLogin.isLoggedIn();
            var venuesIsConfigured = qmEventConfig.isComponentConfigured('venues');
            var venuesRequiresLogin = qmEventConfig.isComponentLoginRequired('venues');
            var speakersIsConfigured = qmEventConfig.isComponentConfigured('speakers');
            var speakersRequiresLogin = qmEventConfig.isComponentLoginRequired('speakers');
            var authorsIsConfigured = qmEventConfig.isComponentConfigured('authors');
            var authorsRequiresLogin = qmEventConfig.isComponentLoginRequired('authors');
            var sessionQAIsConfigured = qmEventConfig.isComponentConfigured('sessionqa');
            var sessionQARequiresLogin = qmEventConfig.isComponentLoginRequired('sessionqa');
            var surveysIsConfigured = qmEventConfig.isComponentConfigured('surveys');
            var surveysRequiresLogin = qmEventConfig.isComponentLoginRequired('surveys');
            var documentsIsConfigured = qmEventConfig.isComponentConfigured('documents');
            var documentsRequiresLogin = qmEventConfig.isComponentLoginRequired('documents');
            var myNotesIsConfigured = qmEventConfig.isComponentConfigured('my-notes');
            var myNotesRequiresLogin = qmEventConfig.isComponentLoginRequired('my-notes');
            var myScheduleIsConfigured = qmEventConfig.isComponentConfigured('myschedule');
            var interactiveMapsIsConfigured = qmEventConfig.isComponentConfigured('interactive-maps');
            var interactiveMapsRequiresLogin = qmEventConfig.isComponentLoginRequired('interactive-maps');
            var livePollingIsConfigured = qmEventConfig.isComponentConfigured('live-polling');

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

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

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

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

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

                return qmMyScheduleService.addMySchedule(eventId).then(function() {
                    $scope.isInMySchedule = true;
                    $scope.myScheduleAllowDelete = true;
                }, function(rejection) {
                    rejection.config.errorHandled = true;
                    switch (rejection.headers('X-QM-RPC-Error-Identifier')) {
                        case 'MY_SCHEDULE_SYNC_SESSION_CONFLICT':
                            qmUtilities.showErrorToast(qmLocalization.getString('ALERT_ERROR_TITLE'),
                                qmLocalization.getString('ALERT_MY_SCHEDULE_UPDATE_SESSION_CONFLICT_MESSAGE'));
                            break;
                        case 'MY_SCHEDULE_SYNC_API_PROVIDER_FAILURE':
                            qmUtilities.showErrorToast(qmLocalization.getString('ALERT_ERROR_TITLE'),
                                qmLocalization.getString('ALERT_MY_SCHEDULE_UPDATE_API_PROVIDER_FAILURE_MESSAGE'));
                            break;
                        case 'MY_SCHEDULE_SYNC_CAPACITY_REACHED':
                            qmUtilities.showErrorToast(qmLocalization.getString('ALERT_ERROR_TITLE'),
                                qmLocalization.getString('ALERT_MY_SCHEDULE_UPDATE_CAPACITY_REACHED_MESSAGE'));
                            break;
                        case 'MY_SCHEDULE_SYNC_ATTENDEE_PERMISSION_INSUFFICIENT':
                            qmUtilities.showErrorToast(qmLocalization.getString('ALERT_ERROR_TITLE'),
                                qmLocalization.getString('ALERT_MY_SCHEDULE_UPDATE_ATTENDEE_PERMISSION_INSUFFICIENT_MESSAGE'));
                            break;

                        default:
                            qmUtilities.showErrorToast(qmLocalization.getString('ALERT_ERROR_TITLE'),
                                qmLocalization.getString('ALERT_MY_SCHEDULE_UPDATE_ERROR_MESSAGE'));
                            break;

                    }
                });
            };

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

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

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

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

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

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

                comp.addSubcomponent(vevent);

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

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

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

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

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

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

                $scope.event = event;
                // Let's run the logic for sub-sessions
                handleSubSessions(event);

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

                if (venuesIsConfigured && (!venuesRequiresLogin || isLoggedIn)) {
                    $scope.loadVenues.then(function(venues) {
                        $scope.event.venues = qmList.transformList(venues);
                        // Save only the first Venue for display, currently a work around due to Product Managements design
                        if ($scope.event.venues.length > 0) {
                            $scope.event.venue = venues[0];
                        }
                    });
                }

                if (authorsIsConfigured) {
                    if (!authorsRequiresLogin || isLoggedIn) {
                        $scope.authorsLoginRequired = false;
                        $scope.loadAuthors.then(function(authors) {

                            $scope.event.authors = qmList.transformList(authors);
                            var qmAuthorsDependencyService = qmEventsDependencyService.getAuthorsService('qmAuthorsDependencyService');
                            $scope.authorListItemUrl = qmAuthorsDependencyService.getTemplateUrl('author-list-item');

                            // Show Authors if there are some to show
                            if ($scope.event.authors.length > 0) {
                                $scope.showAuthors = true;
                            }
                        });
                    } else {
                        $scope.showAuthors = true;
                        $scope.authorsLoginRequired = true;
                    }
                }

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

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

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

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

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

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

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

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

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

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

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

                if (myScheduleIsConfigured && isLoggedIn) {

                    $scope.showMySchedule = true;

                    if (event.isMyFavourite) {

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

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

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

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

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

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

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

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

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

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

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

                var qmSocialConstant = qmEventsDependencyService.getSocialService('SOCIAL');
                if (qmSocialConstant) {
                    $scope.socialResourceKey = qmSocialConstant.resource.events;
                }
            });
        }]);

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

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

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

/**
 * @ngdoc controller
 * @name events-V2_8.controller:EventLikesController
 * @description
 * Controller for event's who likes view
 */
events.controller('EventLikesController', ['$scope', '$state', 'qmEventsDependencyService', 'qmEventConfig',
    function($scope, $state, qmEventsDependencyService, qmEventConfig) {
        var qmSocialConstant = qmEventsDependencyService.getSocialService('SOCIAL'),
            eventsConfig,
            socialConfig,
            likesEnabled;
        $scope.eventId = $state.params.eventId;
        $scope.newCommentState = 'event.events.comment-editor';
        $scope.editCommentState = 'event.events.comment-editor';
        $scope.stateAttrs = {eventId: $state.params.eventId};

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

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

/**
 * @ngdoc controller
 * @name events-V2_8.controller:EventLikesHeaderController
 * @description
 * Controller for event's who likes header view
 */
events.controller('EventLikesHeaderController', ['$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 events = angular.module('events-V2_8');

/**
 * @ngdoc controller
 * @name events-V2_8.controller:EventCommentsController
 * @description
 * Controller for event's comment list view
 */
events.controller('EventCommentsController', ['$scope', '$state', 'qmEventsDependencyService', 'qmEventConfig', 'qmLogin', 'qmEventsService', 'qmNavigation',
    function($scope, $state, qmEventsDependencyService, qmEventConfig, qmLogin, qmEventsService, qmNavigation) {
        var qmSocialConstant = qmEventsDependencyService.getSocialService('SOCIAL');
        var qmSocialService = qmEventsDependencyService.getSocialService('qmSocialService');
        var socialConfig;
        var commentsEnabled;
        $scope.eventId = $state.params.eventId;
        $scope.socialResourceKey = qmSocialConstant ? qmSocialConstant.resource.events : null;
        $scope.newCommentState = 'event.events.comment-editor';
        $scope.editCommentState = 'event.events.comment-editor';
        $scope.stateAttrs = {eventId: $state.params.eventId};

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

/**
 * @ngdoc controller
 * @name events-V2_8.controller:EventCommentsHeaderController
 * @description
 * Controller for event's comment list header view
 */
events.controller('EventCommentsHeaderController', ['$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 events = angular.module('events-V2_8');

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

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

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

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

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

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

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

/**
 * Created by Simon Sun on 2017-10-19
 */

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

/**
 * @ngdoc directive
 * @name events-V2_9.directive:eventLocationOptions
 * @description
 * open an options selection popup for venues and maps
 */
events.directive('eventLocationOptions', ['qmEventsService', 'qmUtilities', function(qmEventsService, qmUtilities) {
    return {
        restrict: 'E',
        scope: {
            venuesContent: '=?',
            mapsContent: '=?',
            mapId: '=?',
            landmarkId: '=?',
            venueId: '=?'
        },
        link: function(scope, element) {
            element.on('click', function(event) {
                var confirmModal;
                if (qmUtilities.handledOfflineBrowserEvent(event)) {
                    return;
                }
                confirmModal = qmEventsService.openConfirmModal(scope);
            });
        }
    };
}]);

/**
 * Created by Oscar Salvador on 2016-08-11.
 */

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

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


