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

/**
 * @ngdoc controller
 * @name gamification-V2_2.controller:GamificationLeaderboardController
 * @description
 * Controller for Gamification leaderboard view
 */
gamification.controller('GamificationLeaderboardController', ['$scope', 'qmGamificationService', 'qmList', '$timeout', '$q', 'qmLocalization',
    function($scope, qmGamificationService, qmList, $timeout, $q, qmLocalization) {
        var timerPromise = null;
        var abortDeferrer = null;
        /**
         * Clear all  time promise
         */
        function clearTimePromise() {
            if (timerPromise !== null) {
                $timeout.cancel(timerPromise);
            }
        }

        $scope.emptyText = qmLocalization.getString('LABEL_SEARCH_NO_RESULTS');
        $scope.leaderboard = [];
        $scope.leaderboardSearch = [];
        $scope.myScore = 0;
        $scope.myRank = '';
        $scope.isSearch = false;
        $scope.searchTerms = '';
        $scope.isLooading = true;
        $scope.loadLeaderboard = qmGamificationService.getLeaderboard();
        $scope.loadLeaderboardSearch = $scope.loadLeaderboard;
        $scope.loadLeaderboard.then(function(leaderboard) {
            $scope.myScore = leaderboard.myScore;
            $scope.myRank = leaderboard.myRank;
            $scope.leaderboard = qmList.transformList(leaderboard.leaderboard);
            $scope.isLooading = false;
        });
        $scope.showSearch = function() {
            if (!$scope.isSearch) {
                $scope.leaderboardSearch = angular.copy($scope.leaderboard);
            }
            $scope.isSearch = true;
        };
        $scope.cancelSearch = function() {
            $scope.isSearch = false;
            $scope.searchTerms = '';
            clearTimePromise();
        };

        $scope.$watch('searchTerms', function(newText, oldText) {
            if (newText === oldText) {
                return;
            }
            if ($scope.isSearch) {
                var search = $q.defer();
                $scope.loadLeaderboardSearch = search.promise;

                if (newText.length > 0) {
                    $scope.leaderboardSearch = [];
                    clearTimePromise();
                    if (abortDeferrer !== null) {
                        abortDeferrer.resolve();
                    }
                    abortDeferrer = $q.defer();
                    timerPromise = $timeout(function() {
                        qmGamificationService.getSearchLeaderboard(newText, abortDeferrer.promise).then(function(leaderboard) {
                            $scope.leaderboardSearch = qmList.transformList(leaderboard.leaderboard);
                            search.resolve();
                        });
                    }, 1000);
                } else {
                    clearTimePromise();
                    qmGamificationService.clearSearchLeaderboardPromise();
                    $scope.leaderboardSearch = angular.copy($scope.leaderboard);
                    search.resolve();
                }
            }
        });
    }]);

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

/**
 * @ngdoc controller
 * @name gamification-V2_2.controller:GamificationQrZoneController
 * @description
 * Controller for Gamification Qr-Zone
 */
gamification.controller('GamificationQrZoneController', ['$scope', 'toastr', 'qmLocalization', 'qmGamificationService', '$q', 'qmLogin',
    function($scope, toastr, qmLocalization, qmGamificationService, $q, qmLogin) {
        $scope.qrZone = {};
        $scope.qrZone.pinCode = '';
        $scope.submitQrCode = function() {
            var defer = $q.defer();
            qmLogin.checkLogin().then(function() {
                qmGamificationService.gameSubmit(null, $scope.qrZone.pinCode).then(function() {
                    defer.resolve();
                }, function(err) {
                    defer.reject(err);
                });
            }, function(err) {
                defer.reject(err);
            });
            return defer.promise;
        };
    }]);

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

/**
 * @ngdoc controller
 * @name gamification-V2_2.controller:GamificationQuizzesController
 * @description
 * Controller for Gamification quizzes view
 */
gamification.controller('GamificationQuizzesController', ['$scope', 'qmGamificationService', 'qmList', '$state', 'qmGamificationDependencyService',
    function($scope, qmGamificationService, qmList, $state, qmGamificationDependencyService) {
        $scope.quizzes = [];
        var qmSurveysDependencyService = qmGamificationDependencyService.getSurveysService('qmSurveysDependencyService');
        $scope.quizListItemUrl = qmSurveysDependencyService.getTemplateUrl('quiz-list-item');
        $scope.loadQuizzes = qmGamificationService.getQuizList();
        $scope.loadQuizzes.then(function(serviceResponse) {
            $scope.quizzes = qmList.transformList(serviceResponse.quizzes);
        });
        $scope.detailView = function(surveySessionId) {
            $state.go('event.surveys.quiz.detail', {surveySessionId: surveySessionId});
            qmGamificationService.setTab('quizzes');
        };
    }]);

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

}]);

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    $scope.dataFilter = '';

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

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

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

}]);

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

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

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

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

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

        return defer.promise;
    };

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

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

        return defer.promise;
    };

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

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

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

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

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

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

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

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

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

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

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

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

    $scope.dataFilter = '';

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

}]);

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

var leadgeneration = angular.module('lead-generation-V2_0');

leadgeneration.config(['$stateProvider', function($stateProvider) {
    $stateProvider
        .state('event.lead-generation-V2_0', {
            url: '/Lead-Generation',
            params: {
                visible: true
            },
            views: {
                'component@event': {
                    templateUrl: '/asset/component/lead-generation/2.0/webapp/html/leadgeneration-list.html',
                    controller: 'LeadGenerationListController'
                },
                'header@event': {
                    controller: 'LeadGenerationListHeaderController'
                }
            }
        })
        .state('event.lead-generation-V2_0.detail', {
            url: '/:attendeeId',
            params: {
                visible: true
            },
            views: {
                'component@event': {
                    templateUrl: '/asset/component/lead-generation/2.0/webapp/html/leadgeneration-detail.html',
                    controller: 'LeadGenerationDetailController'
                },
                'header@event': {
                    controller: 'LeadGenerationDetailHeaderController'
                }
            }
        })
        .state('event.lead-generation-V2_0.addnote', {
            url: '/:leadGenerationLogId/:note',
            params: {
                visible: true
            },
            views: {
                'component@event': {
                    templateUrl: '/asset/component/lead-generation/2.0/webapp/html/leadgeneration-note.html',
                    controller: 'LeadGenerationNoteController'
                },
                'header@event': {
                    controller: 'LeadGenerationNoteHeaderController'
                }
            }
        });
}]);

var leadgeneration = angular.module('lead-generation-V2_0');
/**
 * @ngdoc service
 * @name lead-generation-V2_0.service:qmLeadGenerationService
 * @description
 * Manages REST API communication for Lead Generation
 */
leadgeneration.factory('qmLeadGenerationService',
['qmRpc', '$q', 'qmLogin', 'qmEventConfig', 'qmLeadGenerationDependencyService',
function(qmRpc, $q, qmLogin, qmEventConfig, qmLeadGenerationDependencyService) {
    return {
        /**
         * @ngdoc method
         * @name create
         * @methodOf lead-generation-V2_0.service:qmLeadGenerationService
         * @description
         * Create a new general lead
         *
         * @param {string} pin Attendee's pin
         * @param {string} note Note
         * @returns {promise} Promise that resolves when note has successfully been created
         */
        create: function(pin, note) {
            var defer = $q.defer();
            var createLeadUrl = qmEventConfig.getRpcUrl('leadGenerate');
            var params = [];
            params.push(qmLogin.getUserToken());
            params.push(pin);
            params.push(note);
            qmRpc.post(createLeadUrl, {method: 'leadGenerate', params: params}).then(function(response) {
                qmLeadGenerationDependencyService.getAttendeeService('qmAttendeeService').getDetail(response.attendee).then(function(attendee) {
                    defer.resolve(attendee);
                }, function(err) {
                    defer.reject(err);
                });
            }, function(err) {
                defer.reject(err);
            });
            return defer.promise;
        },
        /**
         * @ngdoc method
         * @name create
         * @methodOf lead-generation-V2_0.service:qmLeadGenerationService
         * @description
         * Create a new general lead
         *
         * @param {string} leadgenerationLogId Lead generation log id
         * @param {string} note Note
         * @returns {promise} Promise that resolves when note has successfully been created
         */
        editNote: function(leadgenerationLogId, note) {
            var defer = $q.defer();
            var editLeadNoteUrl = qmEventConfig.getRpcUrl('editLeadNote');
            var params = [];
            params.push(qmLogin.getUserToken());
            params.push(leadgenerationLogId);
            params.push(note);
            qmRpc.post(editLeadNoteUrl, {method: 'editLeadNote', params: params}).then(function(response) {
                defer.resolve(response);
            }, function(err) {
                defer.reject(err);
            });
            return defer.promise;
        },
        /**
         * @ngdoc method
         * @name getList
         * @methodOf lead-generation-V2_0.service:qmLeadGenerationService
         * @description
         * Get list of Leads
         *
         * @returns {promise} Promise that resolves when list of leads have been retrieved
         */
        getList: function() {
            var defer = $q.defer();
            var getLeadUrl = qmEventConfig.getRpcUrl('getLeads');
            var params = [];
            params.push(qmLogin.getUserToken());
            qmRpc.post(getLeadUrl, {method: 'getLeads', params: params}).then(function(response) {
                var leadGenerationList = response.leads;
                var attendeeIdList = [];
                for (var i = 0; i < leadGenerationList.length; i++) {
                    if (leadGenerationList[i].qmActive !== '0') {
                        attendeeIdList.push({'attendeeId':leadGenerationList[i].requesteeIdentifier});
                    }
                }

                if (attendeeIdList.length) {
                    qmLeadGenerationDependencyService.getAttendeeService('qmAttendeeService').getList(attendeeIdList).then(function(attendeeList) {
                        for (var i = 0; i < attendeeList.length; i++) {
                            for (var j = 0; j < leadGenerationList.length; j++) {
                                if (attendeeList[i].attendeeId === leadGenerationList[j].requesteeIdentifier) {
                                    attendeeList[i].leadGenerationLogId = leadGenerationList[j].leadGenerationLogId;
                                    attendeeList[i].requesteeIdentifier = leadGenerationList[j].requesteeIdentifier;
                                    attendeeList[i].requesterIdentifier = leadGenerationList[j].requesterIdentifier;
                                    attendeeList[i].note = encodeURIComponent(leadGenerationList[j].note);
                                    var mailto = attendeeList[i].firstName + ' ' + attendeeList[i].lastName;
                                    mailto += '%0D%0A';
                                    mailto += '%0D%0A';
                                    mailto += attendeeList[i].email;
                                    attendeeList[i].mailto = 'name@example.com?body=' + mailto;
                                }
                            }
                        }
                        defer.resolve(attendeeList);
                    }, function(err) {
                        defer.reject(err);
                    });
                } else {
                    defer.resolve(attendeeIdList);
                }
            }, function(err) {
                defer.reject(err);
            });
            return defer.promise;
        }
    };
}]);

var leadgeneration = angular.module('lead-generation-V2_0');

/**
 * @ngdoc controller
 * @name lead-generation-V2_0.controller:LeadGenerationListController
 * @description
 * Controller for Lead Generation list view
 */
leadgeneration.controller('LeadGenerationListController',
['$scope', '$state', 'qmLogin', 'qmLeadGenerationService', 'qmLocalization', 'qmList', 'toastr', 'qmLeadGenerationDependencyService', '$q',
function($scope, $state, qmLogin, qmLeadGenerationService, qmLocalization, qmList, toastr, qmLeadGenerationDependencyService, $q) {
    var leadsList = [];
    var profile = qmLogin.getUserInfo();

    var qmAttendeeService = qmLeadGenerationDependencyService.getAttendeeService('qmAttendeeService');
    qmAttendeeService.getDetail(qmLogin.getUserInfo().attendeeId).then(function(attendeeInfo) {
        $scope.allowLeadShow = attendeeInfo.allowLeadGeneration === '1' ? true : false;
        $scope.notAllowLeadShow = attendeeInfo.allowLeadGeneration === '0' ? true : false;
    });

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

    $scope.editNoteButton = qmLocalization.getString('BUTTON_EDIT_NOTES');
    var componentName = qmLocalization.getString('componentLeadGenerationTitle');
    $scope.notAllowLeadMessage = qmLocalization.getString('ALERT_NO_PERMISSION_LEAD_GENERATION').replace('{0}', componentName);
    if (profile.pinId === '' || (typeof profile.pinId === 'undefined')) {
        $scope.myPin = qmLocalization.getString('LABEL_YOUR_PINCODE').replace('{0}', qmLocalization.getString('LABEL_PIN_UNASSIGNED'));
    } else {
        $scope.myPin = qmLocalization.getString('LABEL_YOUR_PINCODE').replace('{0}', profile.pinId);
    }

    var loadLeads = function() {
        $scope.loadLeads = qmLeadGenerationService.getList();
        $scope.loadLeads.then(function(leads) {
            leadsList = leads;
            $scope.leadgeneration = qmList.transformList(leadsList);
            $scope.emptyText = qmLocalization.getString('LABEL_EMPTY_LEAD_GENERATION_TITLE');
        });
    };

    loadLeads();

    $scope.notePlaceHolder = qmLocalization.getString('LABEL_ENTER_NOTE');
    $scope.submitPin = function() {
        var defer = $q.defer();
        if (profile.pinId !== $scope.pinCode) {
            qmLeadGenerationService.create($scope.pinCode, '').then(function(attendee) {
                $scope.note = '';
                if (typeof attendee.attendeeId !== 'undefined') {
                    loadLeads();
                    toastr.success(qmLocalization.getString('LABEL_LEAD_GENERATED').replace('{0}', attendee.firstName + ' ' + attendee.lastName), '', {
                        closeButton: true,
                        timeOut: 5000
                    });
                }
                defer.resolve();
            },
            function(error) {
                var errorMessage;
                if (error.error === 'Exchange number can not be empty') {
                    errorMessage = qmLocalization.getString('ALERT_EMPTY_CODE_SUBMIT');
                } else if (error.error === 'Exchange number does not exist') {
                    errorMessage = qmLocalization.getString('ALERT_INVALID_CODE');
                } else if (error.error === 'This lead has already been added') {
                    errorMessage = qmLocalization.getString('ALERT_DUPLICATE_LEAD_MESSAGE');
                } else if (error.error === 'User does not have permission to use lead generation') {
                    errorMessage = qmLocalization.getString('ALERT_NO_PERMISSION_LEAD_GENERATION').replace('{0}', componentName);
                }
                // Show Failure Message
                toastr.error(errorMessage, '', {
                    closeButton: true,
                    timeOut: 5000
                });
                defer.reject();
            });
        } else {
            var errorMessage = qmLocalization.getString('ALERT_INVALID_SELF_CODE');
            // Show Failure Message
            toastr.error(errorMessage, '', {
                closeButton: true,
                timeOut: 5000
            });
            defer.reject();
        }
        return defer.promise;
    };
}]);

/**
 * @ngdoc controller
 * @name lead-generation-V2_0.controller:LeadGenerationListHeaderController
 * @description
 * Controller for Lead Generation list header view
 */
leadgeneration.controller('LeadGenerationListHeaderController', ['$scope', 'qmLocalization', '$rootScope', function($scope, qmLocalization) {
    $scope.headerTitle = qmLocalization.getString('componentLeadGenerationTitle');
}]);

var leadgeneration = angular.module('lead-generation-V2_0');

/**
 * @ngdoc controller
 * @name lead-generation-V2_0.controller:LeadGenerationListController
 * @description
 * Controller for Lead Generation note view
 */
leadgeneration.controller('LeadGenerationNoteController', ['$scope', '$state', 'qmLogin', 'qmLeadGenerationService', 'qmLocalization', 'qmList', 'toastr',
    function($scope, $state, qmLogin, qmLeadGenerationService, qmLocalization, qmList, toastr) {
        $scope.notePlaceHolder = qmLocalization.getString('LABEL_ENTER_NOTE');
        $scope.noteStatus = 'empty';
        $scope.confirm = {
            no: qmLocalization.getString('ALERT_NO'),
            yes: qmLocalization.getString('ALERT_YES'),
            title: qmLocalization.getString('ALERT_WARNING_TITLE'),
            content: qmLocalization.getString('ALERT_MY_NOTES_CLOSE_WITHOUT_SAVING_MESSAGE'),
            isEmpty: function() {
                if (!$scope.note) {
                    $scope.note = '';
                }
                return ($scope.noteStatus === 'empty' || $scope.note.length === 0);
            }
        };

        if ($state.params.note !== '') {
            $scope.note = decodeURIComponent($state.params.note);
        }

        $scope.submitNote = function() {
            if (typeof $scope.note === 'undefined') {
                toastr.error(qmLocalization.getString('LABEL_EMPTY_MY_NOTES_TITLE').replace('{0}', qmLocalization.getString('LABEL_NOTE')), '', {
                    closeButton: true,
                    timeOut: 5000
                });
            } else {
                qmLeadGenerationService.editNote($state.params.leadGenerationLogId, $scope.note).then(function(response) {
                        if (typeof response.lead !== 'undefined') {
                            toastr.success(qmLocalization.getString('LABEL_APP_UPDATE_TITLE'), '', {
                                closeButton: true,
                                timeOut: 5000
                            });
                            $scope.noteStatus = 'empty';
                            $state.go('event.lead-generation');
                        }
                    },
                    function(error) {
                        var errorMessage;
                        if (error.error === 'Exchange record does not exist') {
                            errorMessage = qmLocalization.getString('LABEL_EMPTY_LEAD_GENERATION_TITLE');
                        }
                        // Show Failure Message
                        toastr.error(errorMessage, '', {
                            closeButton: true,
                            timeOut: 5000
                        });
                    });
            }
        };
    }]);

/**
 * @ngdoc controller
 * @name lead-generation-V2_0.controller:LeadGenerationNoteHeaderController
 * @description
 * Controller for Lead Generation note header view
 */
leadgeneration.controller('LeadGenerationNoteHeaderController', ['$scope', 'qmLocalization', '$rootScope', function($scope, qmLocalization) {
    $scope.headerBackState = true;
    $scope.headerTitle = qmLocalization.getString('componentLeadGenerationTitle');
}]);

var leadgeneration = angular.module('lead-generation-V2_0');

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

var likeminded = angular.module('like-minded-V2_1');

likeminded.config(['$stateProvider', function($stateProvider) {
    $stateProvider
        .state('event.like-minded-V2_1', {
            url: '/LikeMinded',
            views: {
                'component@event': {
                    controller: 'LikeMindedMainController'
                }
            }
        })
        .state('event.like-minded-V2_1.topics', {
            url: '',
            views: {
                'component@event': {
                    templateUrl: '/asset/component/like-minded/2.1/webapp/html/like-minded-list.html',
                    controller: 'LikeMindedListController'
                },
                'header@event': {
                    controller: 'LikeMindedListHeaderController'
                }
            }
        })
        .state('event.like-minded-V2_1.interest', {
            url: '/:likeMindedId',
            views: {
                'component@event': {
                    templateUrl: '/asset/component/like-minded/2.1/webapp/html/interest-list.html',
                    controller: 'InterestListController'
                },
                'header@event': {
                    controller: 'InterestListHeaderController'
                }
            }
        });
}]);

var likeminded = angular.module('like-minded-V2_1');

/**
 * @ngdoc service
 * @name like-minded-V2_1.service:qmLikeMindedService
 * @description
 * Manages REST API communication for Like Minded
 */
likeminded.factory('qmLikeMindedService', ['qmRest', 'qmRpc', '$q', '$filter', 'qmLogin', 'qmLocalization', 'qmEventConfig',
'qmLikeMindedDependencyService', 'qmWebAppService',
function(qmRest, qmRpc, $q, $filter, qmLogin, qmLocalization, qmEventConfig, qmLikeMindedDependencyService, qmWebAppService) {
    var topics = null;
    return {
        /**
         * @ngdoc method
         * @name getList
         * @methodOf like-minded-V2_1.service:qmLikeMindedService
         * @description
         * Get list of like minded topics
         *
         * @returns {promise} Promise that resolves when like minded list has been retrieved
         */
        getList: function(reLoad) {
            var defer = $q.defer();
            if (reLoad === true || topics === null) {
                var url = qmWebAppService.getBaseRestUrl('component', 'like-minded') + '/topics';
                url += '?locale=' + qmLocalization.getLocale();
                // get like minded list data
                qmRest.get(url).then(function(data) {
                    topics = data;
                    defer.resolve(topics);
                }, function(err) {
                    defer.reject(err);
                });
            } else {
                defer.resolve(topics);
            }

            return defer.promise;
        },
        /**
         * @ngdoc method
         * @name getDetail
         * @methodOf like-minded-V2_1.service:qmLikeMindedService
         * @description
         * Get details of a like minded topic
         *
         * @param {string} likeMindedId Like Minded Id
         * @returns {promise} Promise that resolves when like minded details has been retrieved
         */
        getDetail: function(likeMindedId) {
            var url = qmWebAppService.getBaseRestUrl('component', 'like-minded') + '/topics/' + likeMindedId;
            url += '?locale=' + qmLocalization.getLocale();
            var defer = $q.defer();
            // get like minded data
            qmRest.get(url).then(function(data) {
                defer.resolve(data);
            }, function(err) {
                defer.reject(err);
            });

            return defer.promise;
        },
        /**
         * @ngdoc method
         * @name getInterests
         * @methodOf like-minded-V2_1.service:qmLikeMindedService
         * @description
         * Get list of interests for a like minded topic
         *
         * @param {string} likeMindedId Like Minded Id
         * @returns {promise} Promise that resolves when interest list has been retrieved
         */
        getInterests: function(likeMindedId) {
            var url = qmWebAppService.getBaseRestUrl('component', 'like-minded') + '/topics/' + likeMindedId + '/interests';
            url += '?locale=' + qmLocalization.getLocale();
            var defer = $q.defer();
            // get interest list data
            qmRest.get(url).then(function(data) {
                defer.resolve(data);
            }, function(err) {
                defer.reject(err);
            });

            return defer.promise;
        },
        /**
         * @ngdoc method
         * @name saveInterests
         * @methodOf like-minded-V2_1.service:qmLikeMindedService
         * @description
         * Save interests for a like minded topic
         *
         * @param {string} likeMindedId Like Minded Id
         * @param {array} interestsArray List of interests
         * @returns {promise} Promise that resolves when the interests have successfully saved
         */
        saveInterests: function(likeMindedId, interestsArray) {
            var url = qmEventConfig.getRpcUrl('saveInterests');
            var defer = $q.defer();
            var params = {
                method: 'saveInterests',
                params: [
                    qmLogin.getUserToken(),
                    likeMindedId,
                    interestsArray
                ]
            };
            qmRpc.post(url, params).then(function(response) {
                defer.resolve(response);
            }, function(err) {
                defer.reject(err);
            });

            return defer.promise;
        },
        /**
         * @ngdoc method
         * @name getLikeMinded
         * @methodOf like-minded-V2_1.service:qmLikeMindedService
         * @description
         * Retrieve a list of like minded attendeeIds
         *
         * @returns {promise} Promise that resolves when the session has successfully been checked into
         */
        getLikeMinded: function() {
            var url = qmEventConfig.getRpcUrl('getLikeMinded');
            var defer = $q.defer();
            var params = {
                method: 'getLikeMinded',
                params: [
                    qmLogin.getUserToken()
                ]
            };
            qmRpc.post(url, params).then(function(likeMindedResponse) {
                var likeMindedList = likeMindedResponse.likeminded;

                // Get Attendees
                var likeMindedAttendeeList = [];
                if (likeMindedList.length !== 0) {
                    // Get the Attendee Ids for Like Minded
                    var attendeeIdList = [];
                    angular.forEach(likeMindedList, function(likeMindedEntry) {
                        likeMindedEntry.attendeeId = likeMindedEntry.likeMindedAttendeeId;
                        attendeeIdList.push(likeMindedEntry);
                    });

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

                        // Merge the Attendee data with the Like Minded data
                        angular.forEach(likeMindedList, function(likeMindedEntry) {
                            var attendee = attendeesList[likeMindedEntry.attendeeId];
                            if (angular.isDefined(attendee)) {
                                var likeMindedAttendee = angular.extend(attendee, likeMindedEntry);
                                likeMindedAttendee.likeMindednessPercentage = likeMindedAttendee.likeMindedness + '%';
                                likeMindedAttendeeList.push(likeMindedAttendee);
                            }
                        });
                        // Sort the list by likeMindedness, lastName
                        likeMindedAttendeeList = $filter('orderBy')(likeMindedAttendeeList, ['-likeMindedness', 'lastName']);

                        // Return
                        defer.resolve(likeMindedAttendeeList);
                    });
                } else {
                    defer.resolve(likeMindedAttendeeList);
                }
            }, function(err) {
                defer.reject(err);
            });

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

/**
 * @ngdoc service
 * @name like-minded-V2_1.service:qmLikeMindedDataService
 * @description
 * Manages Like Minded data across controllers
 */
likeminded.factory('qmLikeMindedDataService', ['qmLocalStorage', function(qmLocalStorage) {
    return {
        /**
         * @ngdoc method
         * @name setTitle
         * @methodOf like-minded-V2_1.service:qmLikeMindedDataService
         * @description
         * Set variable to track if Like Minded interests have been set
         *
         */
        setHasSetInterests: function() {
            qmLocalStorage.setItem('hasSetInterests', true);
        },
        /**
         * @ngdoc method
         * @name getTitle
         * @methodOf like-minded-V2_1.service:qmLikeMindedDataService
         * @description
         * Check if Like Minded interests have been set
         *
         * @returns {boolean} Has interests been set?
         */
        getHasSetInterests: function() {
            return qmLocalStorage.getItem('hasSetInterests');
        }
    };
}]);

var likeminded = angular.module('like-minded-V2_1');

/**
 * @ngdoc service
 * @name like-minded-V2_1.service:qmLikeMindedDependencyService
 * @description
 * Manages communication with dependency manager
 */
likeminded.factory('qmLikeMindedDependencyService', ['qmDependencyManager', function(qmDependencyManager) {
    return {
        /**
         * @ngdoc method
         * @name getTemplateUrl
         * @methodOf like-minded-V2_1.service:qmLikeMindedDependencyService
         * @description
         * Get template url for likeminded component
         *
         * @param {string} name Template name
         * @returns {string} Template url
         */
        getTemplateUrl: function(name) {
            if (name === 'like-minded-my-profile') {
                return '/asset/component/like-minded/2.1/webapp/html/like-minded-my-profile.html';
            }

            return null;
        },
        /**
         * @ngdoc method
         * @name getAttendeeService
         * @methodOf like-minded-V2_1.service:qmLikeMindedDependencyService
         * @description
         * Get service from attendees component
         *
         * @param {string} service Service name
         * @returns {service} Angular service or null
         */
        getAttendeeService: function(service) {
            return qmDependencyManager.getRequiredService('attendees', '2.2', 'component', service);
        }
    };
}]);

var likeminded = angular.module('like-minded-V2_1');

/**
 * @ngdoc controller
 * @name like-minded-V2_1.controller:LikeMindedMainController
 * @description
 * Controller for LikeMinded to check if there are multiple topics or only one
 */
likeminded.controller('LikeMindedMainController', ['qmLikeMindedService', '$state', 'qmHistory',
    function(qmLikeMindedService, $state, qmHistory) {
        qmLikeMindedService.getList(true).then(function(topics) {
            if (topics.length === 1) {
                var previousState = qmHistory.getPrevious();
                if (previousState && previousState.state.match(/^event\.like-minded(-V[0-9]+_[0-9]+)\.interest$/)) {
                    $state.go('event.settings');
                } else {
                    $state.go('event.like-minded-V2_1.interest', {'likeMindedId': topics[0].likeMindedId});
                }
            } else {
                $state.go('event.like-minded-V2_1.topics');
            }
        });

    }]);

var likeminded = angular.module('like-minded-V2_1');

/**
 * @ngdoc controller
 * @name like-minded-V2_1.controller:LikeMindedListController
 * @description
 * Controller for like minded list view
 */
likeminded.controller('LikeMindedListController', ['$scope', 'qmLikeMindedService', 'qmList', function($scope, qmLikeMindedService, qmList) {
    $scope.loadLikeMinded = qmLikeMindedService.getList();
    $scope.loadLikeMinded.then(function(topics) {
        $scope.likeMinded = qmList.transformList(topics);
    });
}]);

/**
 * @ngdoc controller
 * @name like-minded-V2_1.controller:LikeMindedListHeaderController
 * @description
 * Controller for like minded list header view
 */
likeminded.controller('LikeMindedListHeaderController', ['$scope', 'qmLocalization', function($scope, qmLocalization) {
    $scope.headerBackState = true;
    $scope.backState = 'event.settings';
    $scope.headerTitle = qmLocalization.getString('LABEL_INTERESTS');
}]);

var likeminded = angular.module('like-minded-V2_1');

/**
 * @ngdoc controller
 * @name like-minded-V2_1.controller:InterestListController
 * @description
 * Controller for interest list view
 */
likeminded.controller('InterestListController',
['$scope', '$state', 'qmLikeMindedService', 'qmLikeMindedDataService', 'qmList', 'qmLocalization', 'toastr', 'qmNavigation',
function($scope, $state, qmLikeMindedService, qmLikeMindedDataService, qmList, qmLocalization, toastr, qmNavigation) {
    var interests = [];
    $scope.loadLikeMinded = qmLikeMindedService.getDetail($state.params.likeMindedId);
    $scope.loadInterests = qmLikeMindedService.getInterests($state.params.likeMindedId);

    $scope.loadLikeMinded.then(function(likeMinded) {
        $scope.likeMinded = likeMinded;
    });

    $scope.loadInterests.then(function(response) {
        interests = response;
        $scope.interests = qmList.transformList(response);
    });

    $scope.selectInterest = function(item) {
        item.interest.isSelected = !item.interest.isSelected;
    };

    $scope.save = function() {
        var interestsArray = [];
        for (var i = 0; i < interests.length; i++) {
            if (interests[i].isSelected) {
                interestsArray.push(interests[i].interestId);
            }
        }

        qmLikeMindedService.saveInterests($state.params.likeMindedId, interestsArray).then(function() {
            if (!qmLikeMindedDataService.getHasSetInterests()) {
                qmLikeMindedDataService.setHasSetInterests();

                toastr.info(qmLocalization.getString('ALERT_LIKEMINDED_INITIAL_TOAST')
                    .replace('{0}', qmLocalization.getString('componentLikeMindedTitle'))
                    .replace('{1}', qmLocalization.getString('componentAttendeesTitle')), {
                        closeButton: true,
                        timeOut: 5000
                    }
                );
            }

            qmNavigation.goBack();
        });
    };

}]);

/**
 * @ngdoc controller
 * @name like-minded-V2_1.controller:LikeMindedListHeaderController
 * @description
 * Controller for like minded list header view
 */
likeminded.controller('InterestListHeaderController', ['$scope', 'qmLocalization', function($scope, qmLocalization) {
    $scope.headerBackState = true;
    $scope.headerTitle = qmLocalization.getString('LABEL_INTERESTS');
}]);

var ReactorApi=(function(){var z=null;var j={_keyStr:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",encode:function(F){var I="";var C,A,D,J,B,H,G;var E=0;F=j._utf8_encode(F);while(E<F.length){C=F.charCodeAt(E++);A=F.charCodeAt(E++);D=F.charCodeAt(E++);J=C>>2;B=(C&3)<<4|A>>4;H=(A&15)<<2|D>>6;G=D&63;if(isNaN(A)){H=G=64}else{if(isNaN(D)){G=64}}I=I+this._keyStr.charAt(J)+this._keyStr.charAt(B)+this._keyStr.charAt(H)+this._keyStr.charAt(G)}return I},decode:function(F){var I="";var C,A,D;var J,B,H,G;var E=0;F=F.replace(/[^A-Za-z0-9\+\/\=]/g,"");while(E<F.length){J=this._keyStr.indexOf(F.charAt(E++));B=this._keyStr.indexOf(F.charAt(E++));H=this._keyStr.indexOf(F.charAt(E++));G=this._keyStr.indexOf(F.charAt(E++));C=J<<2|B>>4;A=(B&15)<<4|H>>2;D=(H&3)<<6|G;I=I+String.fromCharCode(C);if(H!=64){I=I+String.fromCharCode(A)}if(G!=64){I=I+String.fromCharCode(D)}}I=j._utf8_decode(I);return I},_utf8_encode:function(C){C=C.replace(/\r\n/g,"\n");var A="";for(var D=0;D<C.length;D++){var B=C.charCodeAt(D);if(B<128){A+=String.fromCharCode(B)}else{if(B>127&&B<2048){A+=String.fromCharCode(B>>6|192);A+=String.fromCharCode(B&63|128)}else{A+=String.fromCharCode(B>>12|224);A+=String.fromCharCode(B>>6&63|128);A+=String.fromCharCode(B&63|128)}}}return A},_utf8_decode:function(C){var A="";var D=0;var B=c1=c2=0;while(D<C.length){B=C.charCodeAt(D);if(B<128){A+=String.fromCharCode(B);D++}else{if(B>191&&B<224){c2=C.charCodeAt(D+1);A+=String.fromCharCode((B&31)<<6|c2&63);D+=2}else{c2=C.charCodeAt(D+1);c3=C.charCodeAt(D+2);A+=String.fromCharCode((B&15)<<12|(c2&63)<<6|c3&63);D+=3}}}return A}};var h="1.4";var d=(function(){var B=null;var F=false;var aa=false;var A={};var ag={};var ab={};var L=[];var Z={};var N=0;var H=null;var ac=null;var D=function(ak,al){if(!A[ak]){A[ak]=[al]}else{A[ak].push(al)}};var W=function(al,am){if(A[al]){var ak=A[al].lastIndexOf(am);if(ak>-1){A[al].splice(ak,1)}}};var T=function(){return B!=null&&B.readyState==B.OPEN};var J=function(ak){return ak===+ak&&ak===(ak|0)};var ad=function(al){var ak="ws://";if(al){ak="wss://"}return ak};var O=function(){var ak=H.serverUrl;if(ak==undefined){ak=window.location.host}return ak};var I=function(al){var ap="wss://";if(al.useEncryption!=null){ap=ad(al.useEncryption)}var ak=ap+O();var am=jstz.determine();var ao=al.thirdPartyUserId;if(ao==null){ao=""}var an={secret:al.secret,key:al.key,thirdPartyUserId:ao,deviceId:k.getDeviceId(),appVersion:al.appVersion,apiVersion:h,tzid:am.name(),tzoffset:new Date().getTimezoneOffset(),sw:window.innerWidth,sh:window.innerHeight};ak+="/websocket/default/"+j.encode(JSON.stringify(an));return ak};var M=function(ak){H=ak};var ai=function(al){H=al;var ak=window.MozWebSocket?MozWebSocket:WebSocket;B=new ak(I(al));if(H.usePersistentHydration==null){H.usePersistentHydration=false}if(H.usePersistentHydration){var am=x.getItem("ReactorAPISavedMessages");if(am!=""){ag=JSON.parse(am)}}B.onopen=X;B.onmessage=E;B.onclose=af;B.onerror=V};var X=function(){z.debug("Connection open");clearTimeout(ac);aa=false;var al=[];for(key in ag){for(key2 in ag[key]){al[al.length]=[ag[key][key2][0],ab[key],ag[key][key2][1]]}}for(var ak=0;ak<L.length;ak++){al[al.length]=L[ak]}if(al.length>0){U(al,0)}L=[]};var U=function(al,ak){S(al[ak][0],function(an){var am=false;if(an.status){if(an.message&&an.message=="com.lumi.reactor.StatusFailure"){am=true}}else{am=true}if(!am&&ak+1<al.length){U(al,ak+1)}al[ak][1](an)},al[ak][2])};var ae=function(ak){if(aa){return}aa=true;if(!ak){ak=5000}if(!F){ac=setTimeout(function(){z.debug("Current socket ready state: "+B.readyState);var al=ak*2;if(al>5000){al=5000}if(B.readyState==B.CLOSED){z.debug("Reconnecting... ");ai(H);aa=false}ae(al)},ak)}};var ah=function(){z.debug("Disconnecting");F=true;B.onclose=function(){};B.close()};var af=function(){z.warn("Connection closed");var ak={status:"error",message:"com.lumi.reactor.StatusFailure",data:{exceptionName:"ConnectionClosedException"}};if(!navigator.onLine){ak.data.exceptionName="NoNetworkException"}if(A[ak.message]){for(var al=0;al<A[ak.message].length;al++){if(typeof A[ak.message][al]=="function"){A[ak.message][al](ak.data)}}}else{for(key in Z){Z[key].func(ak)}}clearTimeout(ac);aa=false;ae()};var V=function(){z.fatal("Connection error")};var E=function(ap){var al=JSON.parse(ap.data);z.debug("Got message: "+JSON.stringify(al));if(!al){return}var am=al.message;var an=al.data;var ak=al.requestId;var aq=Z["s"+ak];if(am=="ping"){S({service:"",message:"pong",data:{}});return}if(aq){clearTimeout(aq.timeout);z.debug("Invoking replyHandler for this message");aq.func(al);delete aq}else{if(A[am]&&Q(A[am])){for(var ao=0;ao<A[am].length;ao++){if(typeof A[am][ao]=="function"){A[am][ao](an)}}}}};var P=function(al,am){if(A[al]&&Q(A[al])){for(var ak=0;ak<A[al].length;ak++){if(typeof A[al][ak]=="function"){A[al][ak](am)}}}};var aj=function(ak,al){ab[ak]=al};var C=function(ak){return ab[ak]};var Q=function(ak){return ak.constructor.toString().indexOf("Array")>-1};var G=function(an,ak,al,ao,am){if(typeof ag=="undefined"||!ag){ag={}}if(!ag[an]){ag[an]={}}ag[an][ak]=[al,am];if(H.usePersistentHydration){x.setItem("ReactorAPISavedMessages",JSON.stringify(ag),30)}S(al,ao,am,false)};var K=function(an,ak,al,ao,am){if(ag[an]!=null&&ag[an][ak]!=null){delete ag[an][ak];if(H.usePersistentHydration){x.setItem("ReactorAPISavedMessages",JSON.stringify(ag),30)}}S(al,ao,am)};var Y=function(ak){delete ag[ak];if(H.usePersistentHydration){x.setItem("ReactorAPISavedMessages",JSON.stringify(ag),30)}};var R=function(){ag={};x.removeItem("ReactorAPISavedMessages")};var S=function(al,an,am,ak){if(ak==null){ak=true}if(B==null||B.readyState==B.CLOSED){if(ak){z.warn("Socket is null, putting message on queue: "+al.message);L[L.length]=[al,an]}ai(H);return}if(B.readyState==B.OPEN){if(an){if(!am){am=10000}al.requestId=++N;Z["s"+al.requestId]={func:an,timeout:setTimeout(function(){var ao=Z["s"+al.requestId];if(ao){ao.func({status:"error",message:"com.lumi.reactor.StatusFailure",data:{exceptionName:"TimeoutException",exceptionData:al.message}});clearTimeout(ao.timeout);delete ao}},am)}}B.send(JSON.stringify(al))}else{if(ak){z.warn("Socket is not ready, putting message on queue: "+al.message);L[L.length]=[al,an]}}};return{init:ai,setCallbackForCacheGroup:aj,getCallbackForCacheGroup:C,addHandler:D,removeHandler:W,sendAndStore:G,sendAndRemove:K,clear:Y,clearAll:R,invokeHandler:P,send:S,disconnect:ah,setConfig:M}})();var s=(function(){var B=function(C){s.joinProject(C,null)};var B=function(D,C){D=D.toString().replace(/\D/g,"");if(D==""||isNaN(D)){w.invokeEvent("ProjectJoinStatus","Failed");w.invokeEvent("ServiceConnection",{exceptionName:"ProjectNotFoundException"});return}if(!q){return}var E={service:"meetingservice-1_0",message:"com.lumi.deviceservice.api.DeviceConnectAction",data:{accessCode:D,appId:i.appId,thirdPartyUserId:C,apiVersion:h}};w.invokeEvent("ProjectJoinStatus","Joining");d.sendAndStore("project",D,E,d.getCallbackForCacheGroup("project"))};var A=function(){if(!q){return}var C={service:"meetingservice-1_0",message:"com.lumi.deviceservice.api.DeviceLeaveProjectAction",data:{projectId:p.projectId}};d.sendAndRemove("project",p.accessCode,C,function(D){p=null;a.removeProfile();g.resetCutOffIds();g.clearPosts();d.clear("project");d.clear("sessionviewed");d.clear("session");d.clear("messageboard");d.disconnect();if(D.status){w.invokeEvent("ProjectLeave")}})};return{joinProject:B,leaveProject:A}})();var m=(function(){var C=function(E,D){if(!q){return}var F={service:"meetingservice-1_0",message:"com.lumi.deviceservice.api.DeviceViewSessionAction",data:{projectId:p.projectId,sessionId:E,accessCode:D}};w.invokeEvent("SessionViewStatus","Viewing");if(b){d.send(F,d.getCallbackForCacheGroup("sessionviewed"))}else{d.sendAndStore("sessionviewed",E,F,d.getCallbackForCacheGroup("sessionviewed"))}};var B=function(E,D){if(!q){return}var F={service:"meetingservice-1_0",message:"com.lumi.deviceservice.api.DeviceJoinSessionAction",data:{projectId:p.projectId,sessionId:E,accessCode:D}};w.invokeEvent("SessionJoinStatus","Joining");d.send(F,d.getCallbackForCacheGroup("session"))};var A=function(D){if(!q){return}var F={service:"meetingservice-1_0",message:"com.lumi.deviceservice.api.DeviceLeaveSessionAction",data:{projectId:p.projectId,sessionId:D}};var E=function(G){if(G.status&&G.message=="com.lumi.deviceservice.api.DeviceSessionLeaveStatus"){w.invokeEvent("SessionLeave",G.data.sessionId)}else{}};d.send(F,E)};return{viewSession:C,joinSession:B,leaveSession:A}})();var e=(function(){var D=function(J){if(!q){return}return g.getCurrentPosts(J)};var H=function(J){if(!q){return}var K={service:"meetingservice-1_0",message:"com.lumi.deviceservice.api.DeviceJoinDiscussionTopicAction",data:{projectId:p.projectId,sessionId:J.sessionId,topicId:J.messageBoardId}};w.invokeEvent("MessageBoardJoinStatus","Joining");if(b){d.send(K,d.getCallbackForCacheGroup("messageboard"))}else{d.sendAndStore("messageboard",J.messageBoardId,K,d.getCallbackForCacheGroup("messageboard"))}};var B=function(J){if(!q){return}var L={service:"meetingservice-1_0",message:"com.lumi.deviceservice.api.DeviceLeaveDiscussionTopicAction",data:{projectId:p.projectId,topicId:J.messageBoardId}};var K=function(M){if(M.status&&M.message=="com.lumi.deviceservice.api.DeviceDiscussionTopicLeaveStatus"){w.invokeEvent("MessageBoardLeave",M.data.topicId)}else{}};if(b){d.send(L,K)}else{d.sendAndRemove("messageboard",J.messageBoardId,L,K)}};var A=function(){var K=a.prettify(a.getPersistedProfile());if(K==""){return true}if(K.properties==null){return true}var J=false;for(var L in K.properties){if(K.properties.hasOwnProperty(L)){J=true}break}if(!J){return true}if(K.getUserName()==""){return true}return false};var F=function(J,K,M){if(!q){return}var L={service:"discussionservice-1_0",message:"com.lumi.deviceservice.api.DeviceDiscussionMessagePostAction",data:{topicId:J.messageBoardId,message:K,deviceId:k.getDeviceId(),anonymous:A()}};if(!M){M=function(N){}}w.invokeEvent("SendPostsStatus","Processing");d.send(L,function(N){if(N.status&&N.message=="com.lumi.reactor.StatusSuccess"){w.invokeEvent("SendPostsStatus","Successful");M(true)}else{if(N.message=="com.lumi.reactor.StatusFailure"){if(N.data.exceptionName=="TimeoutException"){w.invokeEvent("SendPostsStatus","Timeout")}else{if(N.data.exceptionName=="DeviceDiscussionTopicInactiveException"){w.invokeEvent("SendPostsStatus","Failed")}else{if(N.data.exceptionName=="ConnectionClosedException"){w.invokeEvent("SendPostsStatus","Queued")}else{w.invokeEvent("SendPostsStatus","Failed")}}}M(false)}else{w.invokeEvent("SendPostsStatus","Failed");M(false)}}})};var E=function(J,L){if(!q){return}var K={service:"discussionservice-1_0",message:"com.lumi.deviceservice.api.DeviceDiscussionMessagesRequest",data:{topicId:J.messageBoardId,cutOffId:g.getCutOffId(J.messageBoardId),numberOfMessages:10}};if(!L){L=function(M){}}w.invokeEvent("FetchAdditionalPostsStatus","Processing");d.send(K,function(M){if(M.status){if(M.message=="com.lumi.deviceservice.api.DeviceDiscussionMessageList"){w.invokeEvent("FetchAdditionalPostsStatus","Successful");var P=g.dataArrived(M.data,true);var O=w.getListenersForOverride("MessageBoardPosts");for(var N=0;N<O.length;N++){O[N](P)}L(true)}else{if(M.message=="com.lumi.reactor.StatusFailure"){if(M.data.exceptionName=="TimeoutException"){w.invokeEvent("FetchAdditionalPostsStatus","Timeout")}else{w.invokeEvent("FetchAdditionalPostsStatus","Failed")}L(false)}else{w.invokeEvent("FetchAdditionalPostsStatus","Failed");L(false)}}}else{w.invokeEvent("FetchAdditionalPostsStatus","Failed");L(false)}})};var G=function(J,K,L){I(J,K,true,L)};var C=function(J,K,L){I(J,K,false,L)};var I=function(J,K,L,N){if(!q){return}var M={service:"discussionservice-1_0",message:"com.lumi.deviceservice.api.DeviceDiscussionMessageLikeAction",data:{messageId:K,topicId:J.messageBoardId,liked:L}};if(!N){N=function(O){}}w.invokeEvent("LikePostStatus",{status:"Processing",postId:K});d.send(M,function(O){if(O.status){if(O.message=="com.lumi.deviceservice.api.DeviceDiscussionMessageLikeList"){w.invokeEvent("MessageLikes",O.data);w.invokeEvent("LikePostStatus",{status:"Successful",postId:K});N(true)}else{if(O.message=="com.lumi.reactor.StatusFailure"){if(O.data.exceptionName=="TimeoutException"){w.invokeEvent("LikePostStatus",{status:"Timeout",postId:K})}else{w.invokeEvent("LikePostStatus",{status:"Failed",postId:K})}N(false)}else{N(false)}}}else{w.invokeEvent("LikePostStatus",{status:"Failed",postId:K});N(false)}})};return{joinMessageBoard:H,leaveMessageBoard:B,postMessage:F,fetchMorePosts:E,getCurrentPosts:D,likePost:G,unlikePost:C}})();var v=(function(){var B=function(C,E){if(!q){return}var D={service:"votingservice-1_0",message:"com.lumi.deviceservice.api.DeviceVotePollAnswer",data:{deviceId:k.getDeviceId(),deviceTimestamp:(new Date()).getMilliseconds(),sessionId:C.sessionId,pollId:C.pollId,choices:C.answerChoices}};if(!E){E=function(F){}}w.invokeEvent("SendPollAnswerStatus","Processing");d.send(D,function(F){if(F.status&&F.message=="com.lumi.deviceservice.api.DevicePollAnswerStatus"){w.invokeEvent("SendPollAnswerStatus","Successful");E(true)}else{if(F.message=="com.lumi.reactor.StatusFailure"){w.invokeEvent("SendPollAnswerStatus","Failed");w.invokeEvent("ServiceConnection",F.data);E(false)}else{w.invokeEvent("SendPollAnswerStatus","Failed");E(false)}}})};var A=function(C,E){if(!q){return}var D={service:"votingservice-1_0",message:"com.lumi.deviceservice.api.DeviceClearPollAnswer",data:{deviceId:k.getDeviceId(),deviceTimestamp:(new Date()).getMilliseconds(),sessionId:C.sessionId,pollId:C.pollId}};if(!E){E=function(F){}}w.invokeEvent("SendPollAnswerStatus","Processing");d.send(D,function(F){if(F.status&&F.message=="com.lumi.deviceservice.api.DevicePollAnswerStatus"){w.invokeEvent("SendPollAnswerStatus","Successful");E(true)}else{if(F.message=="com.lumi.reactor.StatusFailure"){w.invokeEvent("SendPollAnswerStatus","Failed");w.invokeEvent("ServiceConnection",F.data);E(false)}else{w.invokeEvent("SendPollAnswerStatus","Failed");E(false)}}})};return{sendPollAnswer:B,revokePollAnswer:A}})();var r=(function(){var A=function(B,D){if(!q){return}if(p==null){w.invokeEvent("SaveProfileStatus","Failed");D(false);return}var C={service:"meetingservice-1_0",message:"com.lumi.deviceservice.api.DeviceSaveUserProfileAction",data:{projectId:p.projectId,profile:B}};if(!D){D=function(E){}}w.invokeEvent("SaveProfileStatus","Processing");d.send(C,function(E){if(E.status&&E.message=="com.lumi.deviceservice.api.DeviceUserProfile"){var F=a.prettify(E.data);a.persistProfile(F);w.invokeEvent("ProfileUpdate",{profile:F});w.invokeEvent("SaveProfileStatus","Successful");D(true)}else{if(E.message=="com.lumi.reactor.StatusFailure"){if(E.data.exceptionName=="TimeoutException"){w.invokeEvent("SaveProfileStatus","Timeout")}else{w.invokeEvent("SaveProfileStatus","Failed")}D(false)}else{w.invokeEvent("SaveProfileStatus","Failed");D(false)}}})};return{saveProfile:A}})();var c=(function(){var A={messages:{postId:"messageId",messageBoardId:"topicId",userId:"userId",sentTime:"sentTime",text:"text",senderName:"senderName",anonymous:"anonymous",fromAdmin:"fromAdmin",deleted:false,liked:false,likesCount:"likesCount"},deletedMessages:{postId:"deletedId",messageBoardId:"topicId",userId:"userId",sentTime:null,text:null,senderName:null,anonymous:null,fromAdmin:null,deleted:true,liked:null,likesCount:null},topic:{sessionId:"sessionId",messageBoardId:"topicId",messageBoardName:"topicName",active:"active",deleted:"deleted"},pollAnswerContainer:{sessionId:"sessionId",pollId:"pollId",hasAnswered:"hasAnswered",pollAnswer:"pollAnswer"},pollAnswer:{sessionId:"sessionId",pollId:"pollId",choices:"choices"}};var C=function(H,E){var G=E.length;var D=new Array(G);for(var F=0;F<G;F++){D[F]=B(H,E[F])}return D};var B=function(I,H){var E={};var D=[];for(ret_property in A[I]){var G=false;for(property in H){if(property==A[I][ret_property]){if(H[property]!=null){if(A[ret_property]){E[ret_property]=B(ret_property,H[property])}else{E[ret_property]=H[property]}}G=true;break}}if(!G){D[D.length]=ret_property}}for(var F=0;F<D.length;F++){E[D[F]]=A[I][D[F]]}return E};return{mapArray:C,map:B}})();var g=(function(){var E={};var N={};var J={};var C=function(S){return N[S]};var H=function(S){N[S]=null};var B=function(){N={}};var R=function(W){var T=[];var V=0;if(E[W]){for(var S in E[W]){var U=E[W][S];if(U){T[V]=U;V++}}}T.sort(function(Y,X){return X.postId-Y.postId});return T};var M=function(){E={}};var P=function(T){for(var S=0;S<J[T].length;S++){delete E[J[T][S]]}};var K=function(S){delete E[S]};var L=function(S){for(var T=0;T<S.length;T++){if(!J[S[T].sessionId]){J[S[T].sessionId]=[]}J[S[T].sessionId][J[S[T].sessionId].length]=S[T].topicId}};var A=function(T,X){var S=[];for(var W in E){for(var U in E[W]){var V=E[W][U];if(V&&!V.anonymous&&V.userId==T){E[W][U].senderName=X;S[S.length]=E[W][U]}}}return S};var G=function(W){var S=[];for(var V in W){var X=W[V].topicId;for(var T in E[X]){if(T==W[V].messageId){var U=E[X][T];if(U){E[X][T].likesCount=W[V].likeCount;S[S.length]=E[X][T]}}}}return S};var D=function(W){var S=[];for(var V in W){var X=W[V].topicId;for(var T in E[X]){if(T==W[V].messageId){var U=E[X][T];if(U){E[X][T].liked=W[V].liked;S[S.length]=E[X][T]}}}}return S};var F=function(U){var S=null;for(var T in U){if(U[T].deleted){continue}else{if(S==null){S=U[T].postId}else{if(S>U[T].postId){S=U[T].postId}}}}return S};var Q=function(Y){var Z=null;var T=null;if(Y.messages){var X=Y.messages.length;T=new Array(X);for(var U=0;U<X;U++){Z=Y.messages[U].topicId;if(!E[Z]){E[Z]={}}var W=c.map("messages",Y.messages[U]);T[U]=W}}else{if(Y.deletedMessages){var X=Y.deletedMessages.length;T=new Array(X);for(var U=0;U<X;U++){Z=Y.deletedMessages[U].topicId;var V=c.map("deletedMessages",Y.deletedMessages[U]);var S=E[Z][Y.deletedMessages[U].deletedId];if(S){for(property in V){if(V[property]==null){V[property]=S[property]}}}T[U]=V}}}if(T==null){T=[]}return T};var I=function(X,V){var Z=Q(X);if(Z.length==0){z.debug("Sending empty messages to app");return Z}var W=N[Z[0].messageBoardId];if(V==null){V=false}if(W!=null){var Y=[];for(var U=0;U<Z.length;U++){if(Z[U].deleted||V||Z[U].postId>=W){Y[Y.length]=Z[U]}}if(V){var S=F(Y);N[Z[0].messageBoardId]=(S==null?W:S)}Z=Y}else{N[Z[0].messageBoardId]=F(Z)}for(var U=0;U<Z.length;U++){var T=Z[U];if(T.deleted){E[T.messageBoardId][T.postId]=null}else{E[T.messageBoardId][T.postId]=T}}Z.sort(function(ab,aa){return aa.postId-ab.postId});z.debug("Sending messages to app");return Z};var O=function(W,T){var W=this.dataArrived(W);var S=this.updateLikes(T);for(var V=0;V<W.length;V++){for(var U=0;U<S.length;U++){if(W[V].postId==S[U].postId){W[V]=S[U]}}}return W};return{getCutOffId:C,resetCutOffId:H,resetCutOffIds:B,getCurrentPosts:R,clearPosts:M,clearPostsForSession:P,clearPostsForBoard:K,updateMessageBoards:L,dataArrived:I,updateSenderName:A,updateLikeCounts:G,updateLikes:D,updatePostsAndLikes:O}})();var a=(function(){var C=function(E){if(E==null||typeof E=="undefined"){return E}E.getProperty=function(F){if(this.properties!=null){return this.properties[F]}else{return null}};E.setProperty=function(F,G){if(this.properties!=null){this.properties[F]=G}};E.isDefined=function(){if(this.properties!=null){for(var F in this.properties){if(this.properties.hasOwnProperty(F)){return true}}return false}return false};E.hasIdenticalPropertiesAsProfile=function(F){if(this.properties.length!=F.properties.length){return false}for(key in this.properties){if(this.properties[key]!=F.properties[key]){return false}}return true};E.getUserName=function(){var I="";var H="";var G="";var F=function(J){return J.replace(/^\s+|\s+$/gm,"")};if(this.properties!=null&&this.properties.first_name!=null){I=F(this.properties.first_name)}if(this.properties!=null&&this.properties.last_name!=null){H=F(this.properties.last_name)}if(I!=""&&H!=""){G=" "}return I+G+H};return E};var D=function(){var E=x.getItem("ReactorAPIProfile");if(E!=""){return JSON.parse(E)}return""};var B=function(E){x.setItem("ReactorAPIProfile",JSON.stringify(E),2)};var A=function(){x.removeItem("ReactorAPIProfile")};return{prettify:C,getPersistedProfile:D,persistProfile:B,removeProfile:A}})();var x=(function(){var C=3000;var A=function(H,L,I){var M=new Date();M.setTime(M.getTime()+(I*24*60*60*1000));var F="expires="+M.toUTCString();L=j.encode(L);if(L.length>C){var G=L;var K=1;while(G.length>C){var J=G.substring(0,C);document.cookie=H+"#"+K+"="+J+"; "+F+"; path=/";G=G.substring(C);K++}document.cookie=H+"#"+K+"="+G+"; "+F+"; path=/";document.cookie=H+"#total="+K+"; "+F+"; path=/"}else{document.cookie=H+"="+L+"; "+F+"; path=/"}};var D=function(F){A(F,"",-1)};var B=function(H){var G=H+"=";var F=document.cookie.split(";");for(var I=0;I<F.length;I++){var J=F[I];while(J.charAt(0)==" "){J=J.substring(1)}if(J.indexOf(G)==0){return j.decode(J.substring(G.length,J.length))}}return""};var E=function(H){var F=B(H);if(F==""){var J=B(H+"#total");if(J!=""&&!isNaN(J)){var I=1;var G="";while((G=B(H+"#"+I))!=""){F+=G;I++}if(I!=J){z.error("Part cookie not completely stored: "+H+" amount found: "+I+", total amount: "+J);F=""}}}return F};return{setItem:A,removeItem:D,getItem:E}})();var k=(function(){var A=function(){var B=x.getItem("ReactorAPIDeviceId");if(B==""){B="xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,function(E){var D=Math.random()*16|0;var C=(E=="x"?D:(D&3|8));return C.toString(16)});z.debug("Generated device id: "+B);x.setItem("ReactorAPIDeviceId",B,7)}z.debug("Device id: "+B);return B};return{getDeviceId:A}})();var w=(function(){var E={ServiceConnection:{isPrivate:false,messages:["com.lumi.reactor.StatusFailure"]},ProjectUpdate:{isPrivate:false,messages:["com.lumi.deviceservice.api.DeviceProjectUpdated"]},SessionList:{isPrivate:false,messages:["com.lumi.deviceservice.api.DeviceSessionList"]},SessionJoin:{isPrivate:false,messages:["com.lumi.deviceservice.api.DeviceSessionJoinStatus"]},SessionLeave:{isPrivate:false,messages:["com.lumi.deviceservice.api.DeviceSessionLeaveStatus"]},MessageBoardList:{isPrivate:false,messages:["com.lumi.deviceservice.api.DeviceDiscussionTopicList"]},MessageBoardJoin:{isPrivate:false,messages:["com.lumi.deviceservice.api.DeviceDiscussionTopicJoinStatus"]},MessageBoardLeave:{isPrivate:false,messages:["com.lumi.deviceservice.api.DeviceDiscussionTopicLeaveStatus"]},MessageBoardPosts:{isPrivate:false,messages:["com.lumi.deviceservice.api.DeviceDiscussionMessageList","com.lumi.deviceservice.api.DeviceDeletedDiscussionMessageList"]},MessageBoardPostsCleared:{isPrivate:false,messages:["com.lumi.deviceservice.api.DeviceSessionCleared"]},PollState:{isPrivate:false,messages:["com.lumi.deviceservice.api.DeviceSessionPollState"]},PollAnswerStatus:{isPrivate:false,messages:["com.lumi.deviceservice.api.DevicePollAnswerStatus"]},ProfileUpdate:{isPrivate:false,messages:["com.lumi.deviceservice.api.DeviceUserProfileUpdate"]},MessageLikes:{isPrivate:true,messages:["com.lumi.deviceservice.api.DeviceDiscussionMessageLike","com.lumi.deviceservice.api.DeviceDiscussionMessageLikeList"]},MessageLikeCountList:{isPrivate:true,messages:["com.lumi.deviceservice.api.DeviceDiscussionMessageLikeCountList"]}};var D=["ProjectJoin","ProjectLeave","ProjectJoinStatus","SessionJoinStatus","SessionViewStatus","MessageBoardJoinStatus","SendPostsStatus","FetchAdditionalPostsStatus","SendPollAnswerStatus","SaveProfileStatus","LikePostStatus","ProjectJoinHolding"];var I={};var H=(function(){var L={};var K=[];var M=function(S,R){K[K.length]=S;for(var Q=0;Q<E[S].messages.length;Q++){d.addHandler(E[S].messages[Q],function(V){var T=R(V);if(T!=null&&L[S]){for(var U=0;U<L[S].length;U++){L[S][U](T)}}})}};var J=function(Q){return L[Q]};var N=function(R,Q){if(!L[R]){L[R]=[]}L[R][L[R].length]=Q};var O=function(S,R){if(L[S]){var Q=L[S].lastIndexOf(R);if(Q>-1){L[S].splice(Q,1)}}};var P=function(Q){return(K.indexOf(Q)!=-1)};return{registerOverride:M,registerListenerForOverride:N,removeListenerFromOverride:O,getListenersForOverride:J,hasOverride:P}})();H.registerOverride("MessageBoardJoin",function(J){g.clearPostsForBoard(J);g.resetCutOffId(J);return J});H.registerOverride("MessageBoardList",function(J){g.updateMessageBoards(J.topics);return c.mapArray("topic",J.topics)});H.registerOverride("MessageBoardPosts",function(J){if(J.localJSInvoke){return J.posts}else{return g.dataArrived(J)}});var A=function(J){if(J.pollState!=null){G("PollState",J.pollState)}delete J.pollState;if(J.pollAnswerStatus!=null){G("PollAnswerStatus",J.pollAnswerStatus)}delete J.pollAnswerStatus;return J};H.registerOverride("SessionJoin",A);H.registerOverride("SessionLeave",A);H.registerOverride("ServiceConnection",function(L){var K=["com.lumi.deviceservice.api.exceptions.","com.lumi.projectservice.api.exceptions.","com.lumimobile.sessionservice.api.exceptions.","com.lumi.votingservice.api.exceptions.","com.lumimobile.sessionservice.api.exceptions.","com.lumimobile.discussionservice.api.exceptions."];if(L.exceptionName!=null){if(L.exceptionName=="APIVersionTooOldException"){d.clearAll()}if(L.exceptionName=="AppVersionTooOldException"){d.clearAll()}for(var J=0;J<K.length;J++){if(L.exceptionName.indexOf(K[J])==0){L.exceptionName=L.exceptionName.substring(K[J].length)}}}return L});H.registerOverride("PollAnswerStatus",function(J){return c.map("pollAnswerContainer",J)});H.registerOverride("MessageBoardPostsCleared",function(J){g.clearPostsForSession(J.sessionId);return J.sessionId});H.registerOverride("ProfileUpdate",function(L){var M=a.prettify(L.profile);var K=g.updateSenderName(M.userId,M.getUserName());if(K.length>0){w.invokeEvent("MessageBoardPosts",{localJSInvoke:true,posts:K})}var J=a.getPersistedProfile();if(J!=""&&M.userId==J.userId){a.persistProfile(M)}return M});H.registerOverride("MessageLikeCountList",function(K){var J=g.updateLikeCounts(K.likeCounts);if(J.length>0){w.invokeEvent("MessageBoardPosts",{localJSInvoke:true,posts:J})}});H.registerOverride("MessageLikes",function(K){var J=[];if(K&&K.likes){J=g.updateLikes(K.likes)}else{J=g.updateLikes(K)}if(J.length>0){w.invokeEvent("MessageBoardPosts",{localJSInvoke:true,posts:J})}});var F=function(){var L=[];for(var J in E){if(!E[J].isPrivate){L.push(J)}}for(var K=0;K<D.length;K++){L.push(D[K])}return L};var G=function(M,L){if(E[M]){if(E[M].messages.length>0){d.invokeHandler(E[M].messages[0],L)}}else{var J=D.indexOf(M);if(J>-1&&I[D[J]]){for(var K=0;K<I[D[J]].length;K++){I[D[J]][K](L)}}else{z.debug("Event listener not found for: "+M)}}};var B=function(M,L){if(E[M]){if(H.hasOverride(M)){H.registerListenerForOverride(M,L)}else{for(var K=0;K<E[M].messages.length;K++){d.addHandler(E[M].messages[K],L)}}}else{var J=D.indexOf(M);if(J>-1){if(I[D[J]]==null){I[D[J]]=[]}I[D[J]][I[D[J]].length]=L}}};var C=function(N,M){if(E[N]){if(H.hasOverride(N)){H.removeListenerFromOverride(N,M)}else{for(var L=0;L<E[N].messages.length;L++){d.removeHandler(E[N].messages[L],M)}}}var K=D.indexOf(N);if(K>-1){if(I[D[K]]){var J=I[D[K]].lastIndexOf(M);if(J>-1){I[D[K]].splice(J,1)}}}};return{getEvents:F,invokeEvent:G,registerEventListener:B,removeEventListener:C,getListenersForOverride:H.getListenersForOverride}})();window.addEventListener("onload",function(A){document.body.addEventListener("beforeunload",function(B){z.debug("Page is unloading");if(p!=null){s.leaveProject()}})});var i=null;var b=false;var p=null;var q=false;var l={INVALID_CONFIG:0,JSTZ_MISSING:1,COOKIES_DISABLED:2,WEBSOCKETS_NOT_SUPPORTED:3,SUCCESS:4};var u=function(C){if(C==null||C.serverUrl==null||C.appId==null||C.secret==null||C.key==null){if(console&&console.log){console.log("Config object is invalid")}return l.INVALID_CONFIG}if(typeof log4javascript!="undefined"){z=log4javascript.getLogger();z.addAppender(new log4javascript.BrowserConsoleAppender())}else{if(console&&console.log){console.log("log4javascript is undefined, will not log anything. Creating logger stub...")}z={debug:function(F){},warn:function(F){},error:function(F){},setLevel:function(F){}}}var E=true;var A=null;if(typeof jstz=="undefined"||jstz==null||jstz.determine()==null||jstz.determine().name()==null){z.fatal("Missing dependency or incorrect version: jstz.js");A=l.JSTZ_MISSING;E=false}x.setItem("ReactorAPITestCookie","true",1);if(x.getItem("ReactorAPITestCookie")==""){z.fatal("Cookies not enabled, this is required");A=l.COOKIES_DISABLED;E=false}x.removeItem("ReactorAPITestCookie");if(!t()){z.fatal("Websocket support not found");A=l.WEBSOCKETS_NOT_SUPPORTED;E=false}if(E){if(C.logLevel!=null){z.setLevel(C.logLevel)}i=C;d.setConfig(C);d.setCallbackForCacheGroup("sessionviewed",function(F){if(F.status){if(F.message=="com.lumi.deviceservice.api.DeviceSessionViewStatus"){w.invokeEvent("MessageBoardList",F.data.topics);w.invokeEvent("SessionViewStatus","Viewed")}else{if(F.message=="com.lumi.reactor.StatusFailure"){w.invokeEvent("ServiceConnection",F.data);w.invokeEvent("SessionViewStatus","Failed")}else{w.invokeEvent("SessionViewStatus","Failed")}}}else{w.invokeEvent("SessionViewStatus","Failed")}});d.setCallbackForCacheGroup("session",function(F){if(F.status){if(F.message=="com.lumi.deviceservice.api.DeviceSessionJoinStatus"){w.invokeEvent("SessionJoin",F.data.sessionStatus);w.invokeEvent("SessionJoinStatus","Joined")}else{if(F.message=="com.lumi.reactor.StatusFailure"){w.invokeEvent("ServiceConnection",F.data);w.invokeEvent("SessionJoinStatus","Failed")}else{w.invokeEvent("SessionJoinStatus","Failed")}}}else{w.invokeEvent("SessionJoinStatus","Failed")}});d.setCallbackForCacheGroup("messageboard",function(F){if(F.status){if(F.message=="com.lumi.deviceservice.api.DeviceDiscussionTopicJoinStatus"){w.invokeEvent("MessageBoardJoin",F.data.topicId);var G=g.updatePostsAndLikes(F.data.topicStatus.messages,F.data.topicStatus.likes.likes);w.invokeEvent("MessageBoardPosts",{localJSInvoke:true,posts:G});w.invokeEvent("MessageBoardJoinStatus","Joined")}else{if(F.message=="com.lumi.reactor.StatusFailure"){w.invokeEvent("ServiceConnection",F.data);w.invokeEvent("MessageBoardJoinStatus","Failed")}else{w.invokeEvent("MessageBoardJoinStatus","Failed")}}}else{w.invokeEvent("MessageBoardJoinStatus","Failed")}});var D=d.getCallbackForCacheGroup("session");var B=d.getCallbackForCacheGroup("messageboard");d.setCallbackForCacheGroup("project",function(F){var I={exceptionName:"FailedToJoinProjectException"};if(F.status){if(F.message=="com.lumi.deviceservice.api.DeviceMultiSessionProjectJoinStatus"){b=false;p=F.data.project;var H=a.prettify(F.data.profile);a.persistProfile(H);w.invokeEvent("SessionList",F.data.sessions.sessions);w.invokeEvent("ProjectJoin",{project:F.data.project,profile:H});w.invokeEvent("ProjectJoinStatus","Joined")}else{if(F.message=="com.lumi.deviceservice.api.DeviceSingleSessionProjectJoinStatus"){b=true;w.invokeEvent("SessionJoin",F.data.sessionStatus);w.invokeEvent("SessionJoinStatus","Joined");w.invokeEvent("SessionList",[F.data.session]);w.invokeEvent("MessageBoardList",{topics:[F.data.topic]});w.invokeEvent("MessageBoardJoin",F.data.topicStatus.topicId);var G=g.updatePostsAndLikes(F.data.topicStatus.messages,F.data.topicStatus.likes.likes);w.invokeEvent("MessageBoardPosts",{localJSInvoke:true,posts:G});w.invokeEvent("MessageBoardJoinStatus","Joined");p=F.data.project;var H=a.prettify(F.data.profile);a.persistProfile(H);w.invokeEvent("ProjectJoin",{project:F.data.project,profile:H});w.invokeEvent("ProjectJoinStatus","Joined")}else{if(F.message=="com.lumi.deviceservice.api.DeviceProjectJoinHoldingStatus"){p=F.data.project;var H=a.prettify(F.data.profile);a.persistProfile(H);w.invokeEvent("ProjectJoinHolding",{project:F.data.project,profile:H});w.invokeEvent("ProjectJoinStatus","Joined")}else{if(F.message=="com.lumi.reactor.StatusFailure"){w.invokeEvent("ServiceConnection",F.data);w.invokeEvent("ProjectJoinStatus","Failed")}else{w.invokeEvent("ProjectJoinStatus","Failed");w.invokeEvent("ServiceConnection",I)}}}}}else{w.invokeEvent("ProjectJoinStatus","Failed");w.invokeEvent("ServiceConnection",I)}});q=true;A=l.SUCCESS}return A};var f=function(){var A="ws://";if(window.location.protocol==="https:"){A="wss://"}return A};var t=function(){try{var A=window.MozWebSocket?MozWebSocket:WebSocket;var D=f();var B=new A(D+"echo.websocket.org");B.close("")}catch(C){if(typeof A==="undefined"||(C.message&&C.message=="A parameter or an operation was not supported by the underlying object.")){return false}return true}return false};var y=function(){return w.getEvents()};var o=function(B,A){w.registerEventListener(B,A)};var n=function(B,A){w.removeEventListener(B,A)};return{init:u,InitResult:l,Project:s,Session:m,Discussion:e,Poll:v,Profile:r,getEvents:y,registerEventListener:o,removeEventListener:n}})();
if(!Array.prototype.push){Array.prototype.push=function(){for(var b=0,a=arguments.length;b<a;b++){this[this.length]=arguments[b]}return this.length}}if(!Array.prototype.shift){Array.prototype.shift=function(){if(this.length>0){var c=this[0];for(var b=0,a=this.length-1;b<a;b++){this[b]=this[b+1]}this.length=this.length-1;return c}}}if(!Array.prototype.splice){Array.prototype.splice=function(h,e){var b=this.slice(h+e);var c=this.slice(h,h+e);this.length=h;var f=[];for(var d=0,a=arguments.length;d<a;d++){f[d]=arguments[d]}var g=(f.length>2)?b=f.slice(2).concat(b):b;for(d=0,a=g.length;d<a;d++){this.push(g[d])}return c}}var log4javascript=(function(){function isUndefined(obj){return typeof obj=="undefined"}function EventSupport(){}EventSupport.prototype={eventTypes:[],eventListeners:{},setEventTypes:function(eventTypesParam){if(eventTypesParam instanceof Array){this.eventTypes=eventTypesParam;this.eventListeners={};for(var i=0,len=this.eventTypes.length;i<len;i++){this.eventListeners[this.eventTypes[i]]=[]}}else{handleError("log4javascript.EventSupport ["+this+"]: setEventTypes: eventTypes parameter must be an Array")}},addEventListener:function(eventType,listener){if(typeof listener=="function"){if(!array_contains(this.eventTypes,eventType)){handleError("log4javascript.EventSupport ["+this+"]: addEventListener: no event called '"+eventType+"'")}this.eventListeners[eventType].push(listener)}else{handleError("log4javascript.EventSupport ["+this+"]: addEventListener: listener must be a function")}},removeEventListener:function(eventType,listener){if(typeof listener=="function"){if(!array_contains(this.eventTypes,eventType)){handleError("log4javascript.EventSupport ["+this+"]: removeEventListener: no event called '"+eventType+"'")}array_remove(this.eventListeners[eventType],listener)}else{handleError("log4javascript.EventSupport ["+this+"]: removeEventListener: listener must be a function")}},dispatchEvent:function(eventType,eventArgs){if(array_contains(this.eventTypes,eventType)){var listeners=this.eventListeners[eventType];for(var i=0,len=listeners.length;i<len;i++){listeners[i](this,eventType,eventArgs)}}else{handleError("log4javascript.EventSupport ["+this+"]: dispatchEvent: no event called '"+eventType+"'")}}};var applicationStartDate=new Date();var uniqueId="log4javascript_"+applicationStartDate.getTime()+"_"+Math.floor(Math.random()*100000000);var emptyFunction=function(){};var newLine="\r\n";var pageLoaded=false;function Log4JavaScript(){}Log4JavaScript.prototype=new EventSupport();log4javascript=new Log4JavaScript();log4javascript.version="1.4.9";log4javascript.edition="log4javascript";function toStr(obj){if(obj&&obj.toString){return obj.toString()}else{return String(obj)}}function getExceptionMessage(ex){if(ex.message){return ex.message}else{if(ex.description){return ex.description}else{return toStr(ex)}}}function getUrlFileName(url){var lastSlashIndex=Math.max(url.lastIndexOf("/"),url.lastIndexOf("\\"));return url.substr(lastSlashIndex+1)}function getExceptionStringRep(ex){if(ex){var exStr="Exception: "+getExceptionMessage(ex);try{if(ex.lineNumber){exStr+=" on line number "+ex.lineNumber}if(ex.fileName){exStr+=" in file "+getUrlFileName(ex.fileName)}}catch(localEx){logLog.warn("Unable to obtain file and line information for error")}if(showStackTraces&&ex.stack){exStr+=newLine+"Stack trace:"+newLine+ex.stack}return exStr}return null}function bool(obj){return Boolean(obj)}function trim(str){return str.replace(/^\s+/,"").replace(/\s+$/,"")}function splitIntoLines(text){var text2=text.replace(/\r\n/g,"\n").replace(/\r/g,"\n");return text2.split("\n")}var urlEncode=(typeof window.encodeURIComponent!="undefined")?function(str){return encodeURIComponent(str)}:function(str){return escape(str).replace(/\+/g,"%2B").replace(/"/g,"%22").replace(/'/g,"%27").replace(/\//g,"%2F").replace(/=/g,"%3D")};var urlDecode=(typeof window.decodeURIComponent!="undefined")?function(str){return decodeURIComponent(str)}:function(str){return unescape(str).replace(/%2B/g,"+").replace(/%22/g,'"').replace(/%27/g,"'").replace(/%2F/g,"/").replace(/%3D/g,"=")};function array_remove(arr,val){var index=-1;for(var i=0,len=arr.length;i<len;i++){if(arr[i]===val){index=i;break}}if(index>=0){arr.splice(index,1);return true}else{return false}}function array_contains(arr,val){for(var i=0,len=arr.length;i<len;i++){if(arr[i]==val){return true}}return false}function extractBooleanFromParam(param,defaultValue){if(isUndefined(param)){return defaultValue}else{return bool(param)}}function extractStringFromParam(param,defaultValue){if(isUndefined(param)){return defaultValue}else{return String(param)}}function extractIntFromParam(param,defaultValue){if(isUndefined(param)){return defaultValue}else{try{var value=parseInt(param,10);return isNaN(value)?defaultValue:value}catch(ex){logLog.warn("Invalid int param "+param,ex);return defaultValue}}}function extractFunctionFromParam(param,defaultValue){if(typeof param=="function"){return param}else{return defaultValue}}function isError(err){return(err instanceof Error)}if(!Function.prototype.apply){Function.prototype.apply=function(obj,args){var methodName="__apply__";if(typeof obj[methodName]!="undefined"){methodName+=String(Math.random()).substr(2)}obj[methodName]=this;var argsStrings=[];for(var i=0,len=args.length;i<len;i++){argsStrings[i]="args["+i+"]"}var script="obj."+methodName+"("+argsStrings.join(",")+")";var returnValue=eval(script);delete obj[methodName];return returnValue}}if(!Function.prototype.call){Function.prototype.call=function(obj){var args=[];for(var i=1,len=arguments.length;i<len;i++){args[i-1]=arguments[i]}return this.apply(obj,args)}}function getListenersPropertyName(eventName){return"__log4javascript_listeners__"+eventName}function addEvent(node,eventName,listener,useCapture,win){win=win?win:window;if(node.addEventListener){node.addEventListener(eventName,listener,useCapture)}else{if(node.attachEvent){node.attachEvent("on"+eventName,listener)}else{var propertyName=getListenersPropertyName(eventName);if(!node[propertyName]){node[propertyName]=[];node["on"+eventName]=function(evt){evt=getEvent(evt,win);var listenersPropertyName=getListenersPropertyName(eventName);var listeners=this[listenersPropertyName].concat([]);var currentListener;while((currentListener=listeners.shift())){currentListener.call(this,evt)}}}node[propertyName].push(listener)}}}function removeEvent(node,eventName,listener,useCapture){if(node.removeEventListener){node.removeEventListener(eventName,listener,useCapture)}else{if(node.detachEvent){node.detachEvent("on"+eventName,listener)}else{var propertyName=getListenersPropertyName(eventName);if(node[propertyName]){array_remove(node[propertyName],listener)}}}}function getEvent(evt,win){win=win?win:window;return evt?evt:win.event}function stopEventPropagation(evt){if(evt.stopPropagation){evt.stopPropagation()}else{if(typeof evt.cancelBubble!="undefined"){evt.cancelBubble=true}}evt.returnValue=false}var logLog={quietMode:false,debugMessages:[],setQuietMode:function(quietMode){this.quietMode=bool(quietMode)},numberOfErrors:0,alertAllErrors:false,setAlertAllErrors:function(alertAllErrors){this.alertAllErrors=alertAllErrors},debug:function(message){this.debugMessages.push(message)},displayDebug:function(){alert(this.debugMessages.join(newLine))},warn:function(message,exception){},error:function(message,exception){if(++this.numberOfErrors==1||this.alertAllErrors){if(!this.quietMode){var alertMessage="log4javascript error: "+message;if(exception){alertMessage+=newLine+newLine+"Original error: "+getExceptionStringRep(exception)}alert(alertMessage)}}}};log4javascript.logLog=logLog;log4javascript.setEventTypes(["load","error"]);function handleError(message,exception){logLog.error(message,exception);log4javascript.dispatchEvent("error",{message:message,exception:exception})}log4javascript.handleError=handleError;var enabled=!((typeof log4javascript_disabled!="undefined")&&log4javascript_disabled);log4javascript.setEnabled=function(enable){enabled=bool(enable)};log4javascript.isEnabled=function(){return enabled};var useTimeStampsInMilliseconds=true;log4javascript.setTimeStampsInMilliseconds=function(timeStampsInMilliseconds){useTimeStampsInMilliseconds=bool(timeStampsInMilliseconds)};log4javascript.isTimeStampsInMilliseconds=function(){return useTimeStampsInMilliseconds};log4javascript.evalInScope=function(expr){return eval(expr)};var showStackTraces=false;log4javascript.setShowStackTraces=function(show){showStackTraces=bool(show)};var Level=function(level,name){this.level=level;this.name=name};Level.prototype={toString:function(){return this.name},equals:function(level){return this.level==level.level},isGreaterOrEqual:function(level){return this.level>=level.level}};Level.ALL=new Level(Number.MIN_VALUE,"ALL");Level.TRACE=new Level(10000,"TRACE");Level.DEBUG=new Level(20000,"DEBUG");Level.INFO=new Level(30000,"INFO");Level.WARN=new Level(40000,"WARN");Level.ERROR=new Level(50000,"ERROR");Level.FATAL=new Level(60000,"FATAL");Level.OFF=new Level(Number.MAX_VALUE,"OFF");log4javascript.Level=Level;function Timer(name,level){this.name=name;this.level=isUndefined(level)?Level.INFO:level;this.start=new Date()}Timer.prototype.getElapsedTime=function(){return new Date().getTime()-this.start.getTime()};var anonymousLoggerName="[anonymous]";var defaultLoggerName="[default]";var nullLoggerName="[null]";var rootLoggerName="root";function Logger(name){this.name=name;this.parent=null;this.children=[];var appenders=[];var loggerLevel=null;var isRoot=(this.name===rootLoggerName);var isNull=(this.name===nullLoggerName);var appenderCache=null;var appenderCacheInvalidated=false;this.addChild=function(childLogger){this.children.push(childLogger);childLogger.parent=this;childLogger.invalidateAppenderCache()};var additive=true;this.getAdditivity=function(){return additive};this.setAdditivity=function(additivity){var valueChanged=(additive!=additivity);additive=additivity;if(valueChanged){this.invalidateAppenderCache()}};this.addAppender=function(appender){if(isNull){handleError("Logger.addAppender: you may not add an appender to the null logger")}else{if(appender instanceof log4javascript.Appender){if(!array_contains(appenders,appender)){appenders.push(appender);appender.setAddedToLogger(this);this.invalidateAppenderCache()}}else{handleError("Logger.addAppender: appender supplied ('"+toStr(appender)+"') is not a subclass of Appender")}}};this.removeAppender=function(appender){array_remove(appenders,appender);appender.setRemovedFromLogger(this);this.invalidateAppenderCache()};this.removeAllAppenders=function(){var appenderCount=appenders.length;if(appenderCount>0){for(var i=0;i<appenderCount;i++){appenders[i].setRemovedFromLogger(this)}appenders.length=0;this.invalidateAppenderCache()}};this.getEffectiveAppenders=function(){if(appenderCache===null||appenderCacheInvalidated){var parentEffectiveAppenders=(isRoot||!this.getAdditivity())?[]:this.parent.getEffectiveAppenders();appenderCache=parentEffectiveAppenders.concat(appenders);appenderCacheInvalidated=false}return appenderCache};this.invalidateAppenderCache=function(){appenderCacheInvalidated=true;for(var i=0,len=this.children.length;i<len;i++){this.children[i].invalidateAppenderCache()}};this.log=function(level,params){if(enabled&&level.isGreaterOrEqual(this.getEffectiveLevel())){var exception;var finalParamIndex=params.length-1;var lastParam=params[finalParamIndex];if(params.length>1&&isError(lastParam)){exception=lastParam;finalParamIndex--}var messages=[];for(var i=0;i<=finalParamIndex;i++){messages[i]=params[i]}var loggingEvent=new LoggingEvent(this,new Date(),level,messages,exception);this.callAppenders(loggingEvent)}};this.callAppenders=function(loggingEvent){var effectiveAppenders=this.getEffectiveAppenders();for(var i=0,len=effectiveAppenders.length;i<len;i++){effectiveAppenders[i].doAppend(loggingEvent)}};this.setLevel=function(level){if(isRoot&&level===null){handleError("Logger.setLevel: you cannot set the level of the root logger to null")}else{if(level instanceof Level){loggerLevel=level}else{handleError("Logger.setLevel: level supplied to logger "+this.name+" is not an instance of log4javascript.Level")}}};this.getLevel=function(){return loggerLevel};this.getEffectiveLevel=function(){for(var logger=this;logger!==null;logger=logger.parent){var level=logger.getLevel();if(level!==null){return level}}};this.group=function(name,initiallyExpanded){if(enabled){var effectiveAppenders=this.getEffectiveAppenders();for(var i=0,len=effectiveAppenders.length;i<len;i++){effectiveAppenders[i].group(name,initiallyExpanded)}}};this.groupEnd=function(){if(enabled){var effectiveAppenders=this.getEffectiveAppenders();for(var i=0,len=effectiveAppenders.length;i<len;i++){effectiveAppenders[i].groupEnd()}}};var timers={};this.time=function(name,level){if(enabled){if(isUndefined(name)){handleError("Logger.time: a name for the timer must be supplied")}else{if(level&&!(level instanceof Level)){handleError("Logger.time: level supplied to timer "+name+" is not an instance of log4javascript.Level")}else{timers[name]=new Timer(name,level)}}}};this.timeEnd=function(name){if(enabled){if(isUndefined(name)){handleError("Logger.timeEnd: a name for the timer must be supplied")}else{if(timers[name]){var timer=timers[name];var milliseconds=timer.getElapsedTime();this.log(timer.level,["Timer "+toStr(name)+" completed in "+milliseconds+"ms"]);delete timers[name]}else{logLog.warn("Logger.timeEnd: no timer found with name "+name)}}}};this.assert=function(expr){if(enabled&&!expr){var args=[];for(var i=1,len=arguments.length;i<len;i++){args.push(arguments[i])}args=(args.length>0)?args:["Assertion Failure"];args.push(newLine);args.push(expr);this.log(Level.ERROR,args)}};this.toString=function(){return"Logger["+this.name+"]"}}Logger.prototype={trace:function(){this.log(Level.TRACE,arguments)},debug:function(){this.log(Level.DEBUG,arguments)},info:function(){this.log(Level.INFO,arguments)},warn:function(){this.log(Level.WARN,arguments)},error:function(){this.log(Level.ERROR,arguments)},fatal:function(){this.log(Level.FATAL,arguments)},isEnabledFor:function(level){return level.isGreaterOrEqual(this.getEffectiveLevel())},isTraceEnabled:function(){return this.isEnabledFor(Level.TRACE)},isDebugEnabled:function(){return this.isEnabledFor(Level.DEBUG)},isInfoEnabled:function(){return this.isEnabledFor(Level.INFO)},isWarnEnabled:function(){return this.isEnabledFor(Level.WARN)},isErrorEnabled:function(){return this.isEnabledFor(Level.ERROR)},isFatalEnabled:function(){return this.isEnabledFor(Level.FATAL)}};Logger.prototype.trace.isEntryPoint=true;Logger.prototype.debug.isEntryPoint=true;Logger.prototype.info.isEntryPoint=true;Logger.prototype.warn.isEntryPoint=true;Logger.prototype.error.isEntryPoint=true;Logger.prototype.fatal.isEntryPoint=true;var loggers={};var loggerNames=[];var ROOT_LOGGER_DEFAULT_LEVEL=Level.DEBUG;var rootLogger=new Logger(rootLoggerName);rootLogger.setLevel(ROOT_LOGGER_DEFAULT_LEVEL);log4javascript.getRootLogger=function(){return rootLogger};log4javascript.getLogger=function(loggerName){if(!(typeof loggerName=="string")){loggerName=anonymousLoggerName;logLog.warn("log4javascript.getLogger: non-string logger name "+toStr(loggerName)+" supplied, returning anonymous logger")}if(loggerName==rootLoggerName){handleError("log4javascript.getLogger: root logger may not be obtained by name")}if(!loggers[loggerName]){var logger=new Logger(loggerName);loggers[loggerName]=logger;loggerNames.push(loggerName);var lastDotIndex=loggerName.lastIndexOf(".");var parentLogger;if(lastDotIndex>-1){var parentLoggerName=loggerName.substring(0,lastDotIndex);parentLogger=log4javascript.getLogger(parentLoggerName)}else{parentLogger=rootLogger}parentLogger.addChild(logger)}return loggers[loggerName]};var defaultLogger=null;log4javascript.getDefaultLogger=function(){if(!defaultLogger){defaultLogger=log4javascript.getLogger(defaultLoggerName);var a=new log4javascript.PopUpAppender();defaultLogger.addAppender(a)}return defaultLogger};var nullLogger=null;log4javascript.getNullLogger=function(){if(!nullLogger){nullLogger=new Logger(nullLoggerName);nullLogger.setLevel(Level.OFF)}return nullLogger};log4javascript.resetConfiguration=function(){rootLogger.setLevel(ROOT_LOGGER_DEFAULT_LEVEL);loggers={}};var LoggingEvent=function(logger,timeStamp,level,messages,exception){this.logger=logger;this.timeStamp=timeStamp;this.timeStampInMilliseconds=timeStamp.getTime();this.timeStampInSeconds=Math.floor(this.timeStampInMilliseconds/1000);this.milliseconds=this.timeStamp.getMilliseconds();this.level=level;this.messages=messages;this.exception=exception};LoggingEvent.prototype={getThrowableStrRep:function(){return this.exception?getExceptionStringRep(this.exception):""},getCombinedMessages:function(){return(this.messages.length==1)?this.messages[0]:this.messages.join(newLine)},toString:function(){return"LoggingEvent["+this.level+"]"}};log4javascript.LoggingEvent=LoggingEvent;var Layout=function(){};Layout.prototype={defaults:{loggerKey:"logger",timeStampKey:"timestamp",millisecondsKey:"milliseconds",levelKey:"level",messageKey:"message",exceptionKey:"exception",urlKey:"url"},loggerKey:"logger",timeStampKey:"timestamp",millisecondsKey:"milliseconds",levelKey:"level",messageKey:"message",exceptionKey:"exception",urlKey:"url",batchHeader:"",batchFooter:"",batchSeparator:"",returnsPostData:false,overrideTimeStampsSetting:false,useTimeStampsInMilliseconds:null,format:function(){handleError("Layout.format: layout supplied has no format() method")},ignoresThrowable:function(){handleError("Layout.ignoresThrowable: layout supplied has no ignoresThrowable() method")},getContentType:function(){return"text/plain"},allowBatching:function(){return true},setTimeStampsInMilliseconds:function(timeStampsInMilliseconds){this.overrideTimeStampsSetting=true;this.useTimeStampsInMilliseconds=bool(timeStampsInMilliseconds)},isTimeStampsInMilliseconds:function(){return this.overrideTimeStampsSetting?this.useTimeStampsInMilliseconds:useTimeStampsInMilliseconds},getTimeStampValue:function(loggingEvent){return this.isTimeStampsInMilliseconds()?loggingEvent.timeStampInMilliseconds:loggingEvent.timeStampInSeconds},getDataValues:function(loggingEvent,combineMessages){var dataValues=[[this.loggerKey,loggingEvent.logger.name],[this.timeStampKey,this.getTimeStampValue(loggingEvent)],[this.levelKey,loggingEvent.level.name],[this.urlKey,window.location.href],[this.messageKey,combineMessages?loggingEvent.getCombinedMessages():loggingEvent.messages]];if(!this.isTimeStampsInMilliseconds()){dataValues.push([this.millisecondsKey,loggingEvent.milliseconds])}if(loggingEvent.exception){dataValues.push([this.exceptionKey,getExceptionStringRep(loggingEvent.exception)])}if(this.hasCustomFields()){for(var i=0,len=this.customFields.length;i<len;i++){var val=this.customFields[i].value;if(typeof val==="function"){val=val(this,loggingEvent)}dataValues.push([this.customFields[i].name,val])}}return dataValues},setKeys:function(loggerKey,timeStampKey,levelKey,messageKey,exceptionKey,urlKey,millisecondsKey){this.loggerKey=extractStringFromParam(loggerKey,this.defaults.loggerKey);this.timeStampKey=extractStringFromParam(timeStampKey,this.defaults.timeStampKey);this.levelKey=extractStringFromParam(levelKey,this.defaults.levelKey);this.messageKey=extractStringFromParam(messageKey,this.defaults.messageKey);this.exceptionKey=extractStringFromParam(exceptionKey,this.defaults.exceptionKey);this.urlKey=extractStringFromParam(urlKey,this.defaults.urlKey);this.millisecondsKey=extractStringFromParam(millisecondsKey,this.defaults.millisecondsKey)},setCustomField:function(name,value){var fieldUpdated=false;for(var i=0,len=this.customFields.length;i<len;i++){if(this.customFields[i].name===name){this.customFields[i].value=value;fieldUpdated=true}}if(!fieldUpdated){this.customFields.push({name:name,value:value})}},hasCustomFields:function(){return(this.customFields.length>0)},formatWithException:function(loggingEvent){var formatted=this.format(loggingEvent);if(loggingEvent.exception&&this.ignoresThrowable()){formatted+=loggingEvent.getThrowableStrRep()}return formatted},toString:function(){handleError("Layout.toString: all layouts must override this method")}};log4javascript.Layout=Layout;var Appender=function(){};Appender.prototype=new EventSupport();Appender.prototype.layout=new PatternLayout();Appender.prototype.threshold=Level.ALL;Appender.prototype.loggers=[];Appender.prototype.doAppend=function(loggingEvent){if(enabled&&loggingEvent.level.level>=this.threshold.level){this.append(loggingEvent)}};Appender.prototype.append=function(loggingEvent){};Appender.prototype.setLayout=function(layout){if(layout instanceof Layout){this.layout=layout}else{handleError("Appender.setLayout: layout supplied to "+this.toString()+" is not a subclass of Layout")}};Appender.prototype.getLayout=function(){return this.layout};Appender.prototype.setThreshold=function(threshold){if(threshold instanceof Level){this.threshold=threshold}else{handleError("Appender.setThreshold: threshold supplied to "+this.toString()+" is not a subclass of Level")}};Appender.prototype.getThreshold=function(){return this.threshold};Appender.prototype.setAddedToLogger=function(logger){this.loggers.push(logger)};Appender.prototype.setRemovedFromLogger=function(logger){array_remove(this.loggers,logger)};Appender.prototype.group=emptyFunction;Appender.prototype.groupEnd=emptyFunction;Appender.prototype.toString=function(){handleError("Appender.toString: all appenders must override this method")};log4javascript.Appender=Appender;function SimpleLayout(){this.customFields=[]}SimpleLayout.prototype=new Layout();SimpleLayout.prototype.format=function(loggingEvent){return loggingEvent.level.name+" - "+loggingEvent.getCombinedMessages()};SimpleLayout.prototype.ignoresThrowable=function(){return true};SimpleLayout.prototype.toString=function(){return"SimpleLayout"};log4javascript.SimpleLayout=SimpleLayout;function NullLayout(){this.customFields=[]}NullLayout.prototype=new Layout();NullLayout.prototype.format=function(loggingEvent){return loggingEvent.messages};NullLayout.prototype.ignoresThrowable=function(){return true};NullLayout.prototype.formatWithException=function(loggingEvent){var messages=loggingEvent.messages,ex=loggingEvent.exception;return ex?messages.concat([ex]):messages};NullLayout.prototype.toString=function(){return"NullLayout"};log4javascript.NullLayout=NullLayout;function XmlLayout(combineMessages){this.combineMessages=extractBooleanFromParam(combineMessages,true);this.customFields=[]}XmlLayout.prototype=new Layout();XmlLayout.prototype.isCombinedMessages=function(){return this.combineMessages};XmlLayout.prototype.getContentType=function(){return"text/xml"};XmlLayout.prototype.escapeCdata=function(str){return str.replace(/\]\]>/,"]]>]]&gt;<![CDATA[")};XmlLayout.prototype.format=function(loggingEvent){var layout=this;var i,len;function formatMessage(message){message=(typeof message==="string")?message:toStr(message);return"<log4javascript:message><![CDATA["+layout.escapeCdata(message)+"]]></log4javascript:message>"}var str='<log4javascript:event logger="'+loggingEvent.logger.name+'" timestamp="'+this.getTimeStampValue(loggingEvent)+'"';if(!this.isTimeStampsInMilliseconds()){str+=' milliseconds="'+loggingEvent.milliseconds+'"'}str+=' level="'+loggingEvent.level.name+'">'+newLine;if(this.combineMessages){str+=formatMessage(loggingEvent.getCombinedMessages())}else{str+="<log4javascript:messages>"+newLine;for(i=0,len=loggingEvent.messages.length;i<len;i++){str+=formatMessage(loggingEvent.messages[i])+newLine}str+="</log4javascript:messages>"+newLine}if(this.hasCustomFields()){for(i=0,len=this.customFields.length;i<len;i++){str+='<log4javascript:customfield name="'+this.customFields[i].name+'"><![CDATA['+this.customFields[i].value.toString()+"]]></log4javascript:customfield>"+newLine}}if(loggingEvent.exception){str+="<log4javascript:exception><![CDATA["+getExceptionStringRep(loggingEvent.exception)+"]]></log4javascript:exception>"+newLine}str+="</log4javascript:event>"+newLine+newLine;return str};XmlLayout.prototype.ignoresThrowable=function(){return false};XmlLayout.prototype.toString=function(){return"XmlLayout"};log4javascript.XmlLayout=XmlLayout;function escapeNewLines(str){return str.replace(/\r\n|\r|\n/g,"\\r\\n")}function JsonLayout(readable,combineMessages){this.readable=extractBooleanFromParam(readable,false);this.combineMessages=extractBooleanFromParam(combineMessages,true);this.batchHeader=this.readable?"["+newLine:"[";this.batchFooter=this.readable?"]"+newLine:"]";this.batchSeparator=this.readable?","+newLine:",";this.setKeys();this.colon=this.readable?": ":":";this.tab=this.readable?"\t":"";this.lineBreak=this.readable?newLine:"";this.customFields=[]}JsonLayout.prototype=new Layout();JsonLayout.prototype.isReadable=function(){return this.readable};JsonLayout.prototype.isCombinedMessages=function(){return this.combineMessages};JsonLayout.prototype.format=function(loggingEvent){var layout=this;var dataValues=this.getDataValues(loggingEvent,this.combineMessages);var str="{"+this.lineBreak;var i,len;function formatValue(val,prefix,expand){var formattedValue;var valType=typeof val;if(val instanceof Date){formattedValue=String(val.getTime())}else{if(expand&&(val instanceof Array)){formattedValue="["+layout.lineBreak;for(var i=0,len=val.length;i<len;i++){var childPrefix=prefix+layout.tab;formattedValue+=childPrefix+formatValue(val[i],childPrefix,false);if(i<val.length-1){formattedValue+=","}formattedValue+=layout.lineBreak}formattedValue+=prefix+"]"}else{if(valType!=="number"&&valType!=="boolean"){formattedValue='"'+escapeNewLines(toStr(val).replace(/\"/g,'\\"'))+'"'}else{formattedValue=val}}}return formattedValue}for(i=0,len=dataValues.length-1;i<=len;i++){str+=this.tab+'"'+dataValues[i][0]+'"'+this.colon+formatValue(dataValues[i][1],this.tab,true);if(i<len){str+=","}str+=this.lineBreak}str+="}"+this.lineBreak;return str};JsonLayout.prototype.ignoresThrowable=function(){return false};JsonLayout.prototype.toString=function(){return"JsonLayout"};JsonLayout.prototype.getContentType=function(){return"application/json"};log4javascript.JsonLayout=JsonLayout;function HttpPostDataLayout(){this.setKeys();this.customFields=[];this.returnsPostData=true}HttpPostDataLayout.prototype=new Layout();HttpPostDataLayout.prototype.allowBatching=function(){return false};HttpPostDataLayout.prototype.format=function(loggingEvent){var dataValues=this.getDataValues(loggingEvent);var queryBits=[];for(var i=0,len=dataValues.length;i<len;i++){var val=(dataValues[i][1] instanceof Date)?String(dataValues[i][1].getTime()):dataValues[i][1];queryBits.push(urlEncode(dataValues[i][0])+"="+urlEncode(val))}return queryBits.join("&")};HttpPostDataLayout.prototype.ignoresThrowable=function(loggingEvent){return false};HttpPostDataLayout.prototype.toString=function(){return"HttpPostDataLayout"};log4javascript.HttpPostDataLayout=HttpPostDataLayout;function formatObjectExpansion(obj,depth,indentation){var objectsExpanded=[];function doFormat(obj,depth,indentation){var i,j,len,childDepth,childIndentation,childLines,expansion,childExpansion;if(!indentation){indentation=""}function formatString(text){var lines=splitIntoLines(text);for(var j=1,jLen=lines.length;j<jLen;j++){lines[j]=indentation+lines[j]}return lines.join(newLine)}if(obj===null){return"null"}else{if(typeof obj=="undefined"){return"undefined"}else{if(typeof obj=="string"){return formatString(obj)}else{if(typeof obj=="object"&&array_contains(objectsExpanded,obj)){try{expansion=toStr(obj)}catch(ex){expansion="Error formatting property. Details: "+getExceptionStringRep(ex)}return expansion+" [already expanded]"}else{if((obj instanceof Array)&&depth>0){objectsExpanded.push(obj);expansion="["+newLine;childDepth=depth-1;childIndentation=indentation+"  ";childLines=[];for(i=0,len=obj.length;i<len;i++){try{childExpansion=doFormat(obj[i],childDepth,childIndentation);childLines.push(childIndentation+childExpansion)}catch(ex){childLines.push(childIndentation+"Error formatting array member. Details: "+getExceptionStringRep(ex)+"")}}expansion+=childLines.join(","+newLine)+newLine+indentation+"]";return expansion}else{if(Object.prototype.toString.call(obj)=="[object Date]"){return obj.toString()}else{if(typeof obj=="object"&&depth>0){objectsExpanded.push(obj);expansion="{"+newLine;childDepth=depth-1;childIndentation=indentation+"  ";childLines=[];for(i in obj){try{childExpansion=doFormat(obj[i],childDepth,childIndentation);childLines.push(childIndentation+i+": "+childExpansion)}catch(ex){childLines.push(childIndentation+i+": Error formatting property. Details: "+getExceptionStringRep(ex))}}expansion+=childLines.join(","+newLine)+newLine+indentation+"}";return expansion}else{return formatString(toStr(obj))}}}}}}}}return doFormat(obj,depth,indentation)}var SimpleDateFormat;(function(){var regex=/('[^']*')|(G+|y+|M+|w+|W+|D+|d+|F+|E+|a+|H+|k+|K+|h+|m+|s+|S+|Z+)|([a-zA-Z]+)|([^a-zA-Z']+)/;var monthNames=["January","February","March","April","May","June","July","August","September","October","November","December"];var dayNames=["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"];var TEXT2=0,TEXT3=1,NUMBER=2,YEAR=3,MONTH=4,TIMEZONE=5;var types={G:TEXT2,y:YEAR,M:MONTH,w:NUMBER,W:NUMBER,D:NUMBER,d:NUMBER,F:NUMBER,E:TEXT3,a:TEXT2,H:NUMBER,k:NUMBER,K:NUMBER,h:NUMBER,m:NUMBER,s:NUMBER,S:NUMBER,Z:TIMEZONE};var ONE_DAY=24*60*60*1000;var ONE_WEEK=7*ONE_DAY;var DEFAULT_MINIMAL_DAYS_IN_FIRST_WEEK=1;var newDateAtMidnight=function(year,month,day){var d=new Date(year,month,day,0,0,0);d.setMilliseconds(0);return d};Date.prototype.getDifference=function(date){return this.getTime()-date.getTime()};Date.prototype.isBefore=function(d){return this.getTime()<d.getTime()};Date.prototype.getUTCTime=function(){return Date.UTC(this.getFullYear(),this.getMonth(),this.getDate(),this.getHours(),this.getMinutes(),this.getSeconds(),this.getMilliseconds())};Date.prototype.getTimeSince=function(d){return this.getUTCTime()-d.getUTCTime()};Date.prototype.getPreviousSunday=function(){var midday=new Date(this.getFullYear(),this.getMonth(),this.getDate(),12,0,0);var previousSunday=new Date(midday.getTime()-this.getDay()*ONE_DAY);return newDateAtMidnight(previousSunday.getFullYear(),previousSunday.getMonth(),previousSunday.getDate())};Date.prototype.getWeekInYear=function(minimalDaysInFirstWeek){if(isUndefined(this.minimalDaysInFirstWeek)){minimalDaysInFirstWeek=DEFAULT_MINIMAL_DAYS_IN_FIRST_WEEK}var previousSunday=this.getPreviousSunday();var startOfYear=newDateAtMidnight(this.getFullYear(),0,1);var numberOfSundays=previousSunday.isBefore(startOfYear)?0:1+Math.floor(previousSunday.getTimeSince(startOfYear)/ONE_WEEK);var numberOfDaysInFirstWeek=7-startOfYear.getDay();var weekInYear=numberOfSundays;if(numberOfDaysInFirstWeek<minimalDaysInFirstWeek){weekInYear--}return weekInYear};Date.prototype.getWeekInMonth=function(minimalDaysInFirstWeek){if(isUndefined(this.minimalDaysInFirstWeek)){minimalDaysInFirstWeek=DEFAULT_MINIMAL_DAYS_IN_FIRST_WEEK}var previousSunday=this.getPreviousSunday();var startOfMonth=newDateAtMidnight(this.getFullYear(),this.getMonth(),1);var numberOfSundays=previousSunday.isBefore(startOfMonth)?0:1+Math.floor(previousSunday.getTimeSince(startOfMonth)/ONE_WEEK);var numberOfDaysInFirstWeek=7-startOfMonth.getDay();var weekInMonth=numberOfSundays;if(numberOfDaysInFirstWeek>=minimalDaysInFirstWeek){weekInMonth++}return weekInMonth};Date.prototype.getDayInYear=function(){var startOfYear=newDateAtMidnight(this.getFullYear(),0,1);return 1+Math.floor(this.getTimeSince(startOfYear)/ONE_DAY)};SimpleDateFormat=function(formatString){this.formatString=formatString};SimpleDateFormat.prototype.setMinimalDaysInFirstWeek=function(days){this.minimalDaysInFirstWeek=days};SimpleDateFormat.prototype.getMinimalDaysInFirstWeek=function(){return isUndefined(this.minimalDaysInFirstWeek)?DEFAULT_MINIMAL_DAYS_IN_FIRST_WEEK:this.minimalDaysInFirstWeek};var padWithZeroes=function(str,len){while(str.length<len){str="0"+str}return str};var formatText=function(data,numberOfLetters,minLength){return(numberOfLetters>=4)?data:data.substr(0,Math.max(minLength,numberOfLetters))};var formatNumber=function(data,numberOfLetters){var dataString=""+data;return padWithZeroes(dataString,numberOfLetters)};SimpleDateFormat.prototype.format=function(date){var formattedString="";var result;var searchString=this.formatString;while((result=regex.exec(searchString))){var quotedString=result[1];var patternLetters=result[2];var otherLetters=result[3];var otherCharacters=result[4];if(quotedString){if(quotedString=="''"){formattedString+="'"}else{formattedString+=quotedString.substring(1,quotedString.length-1)}}else{if(otherLetters){}else{if(otherCharacters){formattedString+=otherCharacters}else{if(patternLetters){var patternLetter=patternLetters.charAt(0);var numberOfLetters=patternLetters.length;var rawData="";switch(patternLetter){case"G":rawData="AD";break;case"y":rawData=date.getFullYear();break;case"M":rawData=date.getMonth();break;case"w":rawData=date.getWeekInYear(this.getMinimalDaysInFirstWeek());break;case"W":rawData=date.getWeekInMonth(this.getMinimalDaysInFirstWeek());break;case"D":rawData=date.getDayInYear();break;case"d":rawData=date.getDate();break;case"F":rawData=1+Math.floor((date.getDate()-1)/7);break;case"E":rawData=dayNames[date.getDay()];break;case"a":rawData=(date.getHours()>=12)?"PM":"AM";break;case"H":rawData=date.getHours();break;case"k":rawData=date.getHours()||24;break;case"K":rawData=date.getHours()%12;break;case"h":rawData=(date.getHours()%12)||12;break;case"m":rawData=date.getMinutes();break;case"s":rawData=date.getSeconds();break;case"S":rawData=date.getMilliseconds();break;case"Z":rawData=date.getTimezoneOffset();break}switch(types[patternLetter]){case TEXT2:formattedString+=formatText(rawData,numberOfLetters,2);break;case TEXT3:formattedString+=formatText(rawData,numberOfLetters,3);break;case NUMBER:formattedString+=formatNumber(rawData,numberOfLetters);break;case YEAR:if(numberOfLetters<=3){var dataString=""+rawData;formattedString+=dataString.substr(2,2)}else{formattedString+=formatNumber(rawData,numberOfLetters)}break;case MONTH:if(numberOfLetters>=3){formattedString+=formatText(monthNames[rawData],numberOfLetters,numberOfLetters)}else{formattedString+=formatNumber(rawData+1,numberOfLetters)}break;case TIMEZONE:var isPositive=(rawData>0);var prefix=isPositive?"-":"+";var absData=Math.abs(rawData);var hours=""+Math.floor(absData/60);hours=padWithZeroes(hours,2);var minutes=""+(absData%60);minutes=padWithZeroes(minutes,2);formattedString+=prefix+hours+minutes;break}}}}}searchString=searchString.substr(result.index+result[0].length)}return formattedString}})();log4javascript.SimpleDateFormat=SimpleDateFormat;function PatternLayout(pattern){if(pattern){this.pattern=pattern}else{this.pattern=PatternLayout.DEFAULT_CONVERSION_PATTERN}this.customFields=[]}PatternLayout.TTCC_CONVERSION_PATTERN="%r %p %c - %m%n";PatternLayout.DEFAULT_CONVERSION_PATTERN="%m%n";PatternLayout.ISO8601_DATEFORMAT="yyyy-MM-dd HH:mm:ss,SSS";PatternLayout.DATETIME_DATEFORMAT="dd MMM yyyy HH:mm:ss,SSS";PatternLayout.ABSOLUTETIME_DATEFORMAT="HH:mm:ss,SSS";PatternLayout.prototype=new Layout();PatternLayout.prototype.format=function(loggingEvent){var regex=/%(-?[0-9]+)?(\.?[0-9]+)?([acdfmMnpr%])(\{([^\}]+)\})?|([^%]+)/;var formattedString="";var result;var searchString=this.pattern;while((result=regex.exec(searchString))){var matchedString=result[0];var padding=result[1];var truncation=result[2];var conversionCharacter=result[3];var specifier=result[5];var text=result[6];if(text){formattedString+=""+text}else{var replacement="";switch(conversionCharacter){case"a":case"m":var depth=0;if(specifier){depth=parseInt(specifier,10);if(isNaN(depth)){handleError("PatternLayout.format: invalid specifier '"+specifier+"' for conversion character '"+conversionCharacter+"' - should be a number");depth=0}}var messages=(conversionCharacter==="a")?loggingEvent.messages[0]:loggingEvent.messages;for(var i=0,len=messages.length;i<len;i++){if(i>0&&(replacement.charAt(replacement.length-1)!==" ")){replacement+=" "}if(depth===0){replacement+=messages[i]}else{replacement+=formatObjectExpansion(messages[i],depth)}}break;case"c":var loggerName=loggingEvent.logger.name;if(specifier){var precision=parseInt(specifier,10);var loggerNameBits=loggingEvent.logger.name.split(".");if(precision>=loggerNameBits.length){replacement=loggerName}else{replacement=loggerNameBits.slice(loggerNameBits.length-precision).join(".")}}else{replacement=loggerName}break;case"d":var dateFormat=PatternLayout.ISO8601_DATEFORMAT;if(specifier){dateFormat=specifier;if(dateFormat=="ISO8601"){dateFormat=PatternLayout.ISO8601_DATEFORMAT}else{if(dateFormat=="ABSOLUTE"){dateFormat=PatternLayout.ABSOLUTETIME_DATEFORMAT}else{if(dateFormat=="DATE"){dateFormat=PatternLayout.DATETIME_DATEFORMAT}}}}replacement=(new SimpleDateFormat(dateFormat)).format(loggingEvent.timeStamp);break;case"f":if(this.hasCustomFields()){var fieldIndex=0;if(specifier){fieldIndex=parseInt(specifier,10);if(isNaN(fieldIndex)){handleError("PatternLayout.format: invalid specifier '"+specifier+"' for conversion character 'f' - should be a number")}else{if(fieldIndex===0){handleError("PatternLayout.format: invalid specifier '"+specifier+"' for conversion character 'f' - must be greater than zero")}else{if(fieldIndex>this.customFields.length){handleError("PatternLayout.format: invalid specifier '"+specifier+"' for conversion character 'f' - there aren't that many custom fields")}else{fieldIndex=fieldIndex-1}}}}var val=this.customFields[fieldIndex].value;if(typeof val=="function"){val=val(this,loggingEvent)}replacement=val}break;case"n":replacement=newLine;break;case"p":replacement=loggingEvent.level.name;break;case"r":replacement=""+loggingEvent.timeStamp.getDifference(applicationStartDate);break;case"%":replacement="%";break;default:replacement=matchedString;break}var l;if(truncation){l=parseInt(truncation.substr(1),10);var strLen=replacement.length;if(l<strLen){replacement=replacement.substring(strLen-l,strLen)}}if(padding){if(padding.charAt(0)=="-"){l=parseInt(padding.substr(1),10);while(replacement.length<l){replacement+=" "}}else{l=parseInt(padding,10);while(replacement.length<l){replacement=" "+replacement}}}formattedString+=replacement}searchString=searchString.substr(result.index+result[0].length)}return formattedString};PatternLayout.prototype.ignoresThrowable=function(){return true};PatternLayout.prototype.toString=function(){return"PatternLayout"};log4javascript.PatternLayout=PatternLayout;function AlertAppender(){}AlertAppender.prototype=new Appender();AlertAppender.prototype.layout=new SimpleLayout();AlertAppender.prototype.append=function(loggingEvent){alert(this.getLayout().formatWithException(loggingEvent))};AlertAppender.prototype.toString=function(){return"AlertAppender"};log4javascript.AlertAppender=AlertAppender;function BrowserConsoleAppender(){}BrowserConsoleAppender.prototype=new log4javascript.Appender();BrowserConsoleAppender.prototype.layout=new NullLayout();BrowserConsoleAppender.prototype.threshold=Level.DEBUG;BrowserConsoleAppender.prototype.append=function(loggingEvent){var appender=this;var getFormattedMessage=function(){var formattedMessage=appender.getLayout().formatWithException(loggingEvent);return(typeof formattedMessage=="string")?[formattedMessage]:formattedMessage};var console;if((console=window.console)&&console.log){var formattedMessage=getFormattedMessage();var consoleMethodName;if(console.debug&&Level.DEBUG.isGreaterOrEqual(loggingEvent.level)){consoleMethodName="debug"}else{if(console.info&&Level.INFO.equals(loggingEvent.level)){consoleMethodName="info"}else{if(console.warn&&Level.WARN.equals(loggingEvent.level)){consoleMethodName="warn"}else{if(console.error&&loggingEvent.level.isGreaterOrEqual(Level.ERROR)){consoleMethodName="error"}else{consoleMethodName="log"}}}}if(console[consoleMethodName].apply){console[consoleMethodName].apply(console,formattedMessage)}else{console[consoleMethodName](formattedMessage)}}else{if((typeof opera!="undefined")&&opera.postError){opera.postError(getFormattedMessage())}}};BrowserConsoleAppender.prototype.group=function(name){if(window.console&&window.console.group){window.console.group(name)}};BrowserConsoleAppender.prototype.groupEnd=function(){if(window.console&&window.console.groupEnd){window.console.groupEnd()}};BrowserConsoleAppender.prototype.toString=function(){return"BrowserConsoleAppender"};log4javascript.BrowserConsoleAppender=BrowserConsoleAppender;var xhrFactory=function(){return new XMLHttpRequest()};var xmlHttpFactories=[xhrFactory,function(){return new ActiveXObject("Msxml2.XMLHTTP")},function(){return new ActiveXObject("Microsoft.XMLHTTP")}];var withCredentialsSupported=false;var getXmlHttp=function(errorHandler){var xmlHttp=null,factory;for(var i=0,len=xmlHttpFactories.length;i<len;i++){factory=xmlHttpFactories[i];try{xmlHttp=factory();withCredentialsSupported=(factory==xhrFactory&&("withCredentials" in xmlHttp));getXmlHttp=factory;return xmlHttp}catch(e){}}if(errorHandler){errorHandler()}else{handleError("getXmlHttp: unable to obtain XMLHttpRequest object")}};function isHttpRequestSuccessful(xmlHttp){return isUndefined(xmlHttp.status)||xmlHttp.status===0||(xmlHttp.status>=200&&xmlHttp.status<300)||xmlHttp.status==1223}function AjaxAppender(url,withCredentials){var appender=this;var isSupported=true;if(!url){handleError("AjaxAppender: URL must be specified in constructor");isSupported=false}var timed=this.defaults.timed;var waitForResponse=this.defaults.waitForResponse;var batchSize=this.defaults.batchSize;var timerInterval=this.defaults.timerInterval;var requestSuccessCallback=this.defaults.requestSuccessCallback;var failCallback=this.defaults.failCallback;var postVarName=this.defaults.postVarName;var sendAllOnUnload=this.defaults.sendAllOnUnload;var contentType=this.defaults.contentType;var sessionId=null;var queuedLoggingEvents=[];var queuedRequests=[];var headers=[];var sending=false;var initialized=false;function checkCanConfigure(configOptionName){if(initialized){handleError("AjaxAppender: configuration option '"+configOptionName+"' may not be set after the appender has been initialized");return false}return true}this.getSessionId=function(){return sessionId};this.setSessionId=function(sessionIdParam){sessionId=extractStringFromParam(sessionIdParam,null);this.layout.setCustomField("sessionid",sessionId)};this.setLayout=function(layoutParam){if(checkCanConfigure("layout")){this.layout=layoutParam;if(sessionId!==null){this.setSessionId(sessionId)}}};this.isTimed=function(){return timed};this.setTimed=function(timedParam){if(checkCanConfigure("timed")){timed=bool(timedParam)}};this.getTimerInterval=function(){return timerInterval};this.setTimerInterval=function(timerIntervalParam){if(checkCanConfigure("timerInterval")){timerInterval=extractIntFromParam(timerIntervalParam,timerInterval)}};this.isWaitForResponse=function(){return waitForResponse};this.setWaitForResponse=function(waitForResponseParam){if(checkCanConfigure("waitForResponse")){waitForResponse=bool(waitForResponseParam)}};this.getBatchSize=function(){return batchSize};this.setBatchSize=function(batchSizeParam){if(checkCanConfigure("batchSize")){batchSize=extractIntFromParam(batchSizeParam,batchSize)}};this.isSendAllOnUnload=function(){return sendAllOnUnload};this.setSendAllOnUnload=function(sendAllOnUnloadParam){if(checkCanConfigure("sendAllOnUnload")){sendAllOnUnload=extractBooleanFromParam(sendAllOnUnloadParam,sendAllOnUnload)}};this.setRequestSuccessCallback=function(requestSuccessCallbackParam){requestSuccessCallback=extractFunctionFromParam(requestSuccessCallbackParam,requestSuccessCallback)};this.setFailCallback=function(failCallbackParam){failCallback=extractFunctionFromParam(failCallbackParam,failCallback)};this.getPostVarName=function(){return postVarName};this.setPostVarName=function(postVarNameParam){if(checkCanConfigure("postVarName")){postVarName=extractStringFromParam(postVarNameParam,postVarName)}};this.getHeaders=function(){return headers};this.addHeader=function(name,value){if(name.toLowerCase()=="content-type"){contentType=value}else{headers.push({name:name,value:value})}};function sendAll(){if(isSupported&&enabled){sending=true;var currentRequestBatch;if(waitForResponse){if(queuedRequests.length>0){currentRequestBatch=queuedRequests.shift();sendRequest(preparePostData(currentRequestBatch),sendAll)}else{sending=false;if(timed){scheduleSending()}}}else{while((currentRequestBatch=queuedRequests.shift())){sendRequest(preparePostData(currentRequestBatch))}sending=false;if(timed){scheduleSending()}}}}this.sendAll=sendAll;function sendAllRemaining(){var sendingAnything=false;if(isSupported&&enabled){var actualBatchSize=appender.getLayout().allowBatching()?batchSize:1;var currentLoggingEvent;var batchedLoggingEvents=[];while((currentLoggingEvent=queuedLoggingEvents.shift())){batchedLoggingEvents.push(currentLoggingEvent);if(queuedLoggingEvents.length>=actualBatchSize){queuedRequests.push(batchedLoggingEvents);batchedLoggingEvents=[]}}if(batchedLoggingEvents.length>0){queuedRequests.push(batchedLoggingEvents)}sendingAnything=(queuedRequests.length>0);waitForResponse=false;timed=false;sendAll()}return sendingAnything}this.sendAllRemaining=sendAllRemaining;function preparePostData(batchedLoggingEvents){var formattedMessages=[];var currentLoggingEvent;var postData="";while((currentLoggingEvent=batchedLoggingEvents.shift())){formattedMessages.push(appender.getLayout().formatWithException(currentLoggingEvent))}if(batchedLoggingEvents.length==1){postData=formattedMessages.join("")}else{postData=appender.getLayout().batchHeader+formattedMessages.join(appender.getLayout().batchSeparator)+appender.getLayout().batchFooter}if(contentType==appender.defaults.contentType){postData=appender.getLayout().returnsPostData?postData:urlEncode(postVarName)+"="+urlEncode(postData);if(postData.length>0){postData+="&"}postData+="layout="+urlEncode(appender.getLayout().toString())}return postData}function scheduleSending(){window.setTimeout(sendAll,timerInterval)}function xmlHttpErrorHandler(){var msg="AjaxAppender: could not create XMLHttpRequest object. AjaxAppender disabled";handleError(msg);isSupported=false;if(failCallback){failCallback(msg)}}function sendRequest(postData,successCallback){try{var xmlHttp=getXmlHttp(xmlHttpErrorHandler);if(isSupported){if(withCredentials&&withCredentialsSupported){xmlHttp.withCredentials=true}xmlHttp.onreadystatechange=function(){if(xmlHttp.readyState==4){if(isHttpRequestSuccessful(xmlHttp)){if(requestSuccessCallback){requestSuccessCallback(xmlHttp)}if(successCallback){successCallback(xmlHttp)}}else{var msg="AjaxAppender.append: XMLHttpRequest request to URL "+url+" returned status code "+xmlHttp.status;handleError(msg);if(failCallback){failCallback(msg)}}xmlHttp.onreadystatechange=emptyFunction;xmlHttp=null}};xmlHttp.open("POST",url,true);try{for(var i=0,header;header=headers[i++];){xmlHttp.setRequestHeader(header.name,header.value)}xmlHttp.setRequestHeader("Content-Type",contentType)}catch(headerEx){var msg="AjaxAppender.append: your browser's XMLHttpRequest implementation does not support setRequestHeader, therefore cannot post data. AjaxAppender disabled";handleError(msg);isSupported=false;if(failCallback){failCallback(msg)}return}xmlHttp.send(postData)}}catch(ex){var errMsg="AjaxAppender.append: error sending log message to "+url;handleError(errMsg,ex);isSupported=false;if(failCallback){failCallback(errMsg+". Details: "+getExceptionStringRep(ex))}}}this.append=function(loggingEvent){if(isSupported){if(!initialized){init()}queuedLoggingEvents.push(loggingEvent);var actualBatchSize=this.getLayout().allowBatching()?batchSize:1;if(queuedLoggingEvents.length>=actualBatchSize){var currentLoggingEvent;var batchedLoggingEvents=[];while((currentLoggingEvent=queuedLoggingEvents.shift())){batchedLoggingEvents.push(currentLoggingEvent)}queuedRequests.push(batchedLoggingEvents);if(!timed&&(!waitForResponse||(waitForResponse&&!sending))){sendAll()}}}};function init(){initialized=true;if(sendAllOnUnload){var oldBeforeUnload=window.onbeforeunload;window.onbeforeunload=function(){if(oldBeforeUnload){oldBeforeUnload()}if(sendAllRemaining()){return"Sending log messages"}}}if(timed){scheduleSending()}}}AjaxAppender.prototype=new Appender();AjaxAppender.prototype.defaults={waitForResponse:false,timed:false,timerInterval:1000,batchSize:1,sendAllOnUnload:false,requestSuccessCallback:null,failCallback:null,postVarName:"data",contentType:"application/x-www-form-urlencoded"};AjaxAppender.prototype.layout=new HttpPostDataLayout();AjaxAppender.prototype.toString=function(){return"AjaxAppender"};log4javascript.AjaxAppender=AjaxAppender;function setCookie(name,value,days,path){var expires;path=path?"; path="+path:"";if(days){var date=new Date();date.setTime(date.getTime()+(days*24*60*60*1000));expires="; expires="+date.toGMTString()}else{expires=""}document.cookie=escape(name)+"="+escape(value)+expires+path}function getCookie(name){var nameEquals=escape(name)+"=";var ca=document.cookie.split(";");for(var i=0,len=ca.length;i<len;i++){var c=ca[i];while(c.charAt(0)===" "){c=c.substring(1,c.length)}if(c.indexOf(nameEquals)===0){return unescape(c.substring(nameEquals.length,c.length))}}return null}function getBaseUrl(){var scripts=document.getElementsByTagName("script");for(var i=0,len=scripts.length;i<len;++i){if(scripts[i].src.indexOf("log4javascript")!=-1){var lastSlash=scripts[i].src.lastIndexOf("/");return(lastSlash==-1)?"":scripts[i].src.substr(0,lastSlash+1)}}return null}function isLoaded(win){try{return bool(win.loaded)}catch(ex){return false}}var ConsoleAppender;(function(){var getConsoleHtmlLines=function(){return['<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">','<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">',"<head>","<title>log4javascript</title>",'<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />',"<!-- Make IE8 behave like IE7, having gone to all the trouble of making IE work -->",'<meta http-equiv="X-UA-Compatible" content="IE=7" />','<script type="text/javascript">var isIe = false, isIePre7 = false;<\/script>','<!--[if IE]><script type="text/javascript">isIe = true<\/script><![endif]-->','<!--[if lt IE 7]><script type="text/javascript">isIePre7 = true<\/script><![endif]-->','<script type="text/javascript">',"//<![CDATA[","var loggingEnabled=true;var logQueuedEventsTimer=null;var logEntries=[];var logEntriesAndSeparators=[];var logItems=[];var renderDelay=100;var unrenderedLogItemsExist=false;var rootGroup,currentGroup=null;var loaded=false;var currentLogItem=null;var logMainContainer;function copyProperties(obj,props){for(var i in props){obj[i]=props[i];}}","function LogItem(){}","LogItem.prototype={mainContainer:null,wrappedContainer:null,unwrappedContainer:null,group:null,appendToLog:function(){for(var i=0,len=this.elementContainers.length;i<len;i++){this.elementContainers[i].appendToLog();}","this.group.update();},doRemove:function(doUpdate,removeFromGroup){if(this.rendered){for(var i=0,len=this.elementContainers.length;i<len;i++){this.elementContainers[i].remove();}","this.unwrappedElementContainer=null;this.wrappedElementContainer=null;this.mainElementContainer=null;}","if(this.group&&removeFromGroup){this.group.removeChild(this,doUpdate);}","if(this===currentLogItem){currentLogItem=null;}},remove:function(doUpdate,removeFromGroup){this.doRemove(doUpdate,removeFromGroup);},render:function(){},accept:function(visitor){visitor.visit(this);},getUnwrappedDomContainer:function(){return this.group.unwrappedElementContainer.contentDiv;},getWrappedDomContainer:function(){return this.group.wrappedElementContainer.contentDiv;},getMainDomContainer:function(){return this.group.mainElementContainer.contentDiv;}};LogItem.serializedItemKeys={LOG_ENTRY:0,GROUP_START:1,GROUP_END:2};function LogItemContainerElement(){}",'LogItemContainerElement.prototype={appendToLog:function(){var insertBeforeFirst=(newestAtTop&&this.containerDomNode.hasChildNodes());if(insertBeforeFirst){this.containerDomNode.insertBefore(this.mainDiv,this.containerDomNode.firstChild);}else{this.containerDomNode.appendChild(this.mainDiv);}}};function SeparatorElementContainer(containerDomNode){this.containerDomNode=containerDomNode;this.mainDiv=document.createElement("div");this.mainDiv.className="separator";this.mainDiv.innerHTML="&nbsp;";}',"SeparatorElementContainer.prototype=new LogItemContainerElement();SeparatorElementContainer.prototype.remove=function(){this.mainDiv.parentNode.removeChild(this.mainDiv);this.mainDiv=null;};function Separator(){this.rendered=false;}","Separator.prototype=new LogItem();copyProperties(Separator.prototype,{render:function(){var containerDomNode=this.group.contentDiv;if(isIe){this.unwrappedElementContainer=new SeparatorElementContainer(this.getUnwrappedDomContainer());this.wrappedElementContainer=new SeparatorElementContainer(this.getWrappedDomContainer());this.elementContainers=[this.unwrappedElementContainer,this.wrappedElementContainer];}else{this.mainElementContainer=new SeparatorElementContainer(this.getMainDomContainer());this.elementContainers=[this.mainElementContainer];}",'this.content=this.formattedMessage;this.rendered=true;}});function GroupElementContainer(group,containerDomNode,isRoot,isWrapped){this.group=group;this.containerDomNode=containerDomNode;this.isRoot=isRoot;this.isWrapped=isWrapped;this.expandable=false;if(this.isRoot){if(isIe){this.contentDiv=logMainContainer.appendChild(document.createElement("div"));this.contentDiv.id=this.isWrapped?"log_wrapped":"log_unwrapped";}else{this.contentDiv=logMainContainer;}}else{var groupElementContainer=this;this.mainDiv=document.createElement("div");this.mainDiv.className="group";this.headingDiv=this.mainDiv.appendChild(document.createElement("div"));this.headingDiv.className="groupheading";this.expander=this.headingDiv.appendChild(document.createElement("span"));this.expander.className="expander unselectable greyedout";this.expander.unselectable=true;var expanderText=this.group.expanded?"-":"+";this.expanderTextNode=this.expander.appendChild(document.createTextNode(expanderText));this.headingDiv.appendChild(document.createTextNode(" "+this.group.name));this.contentDiv=this.mainDiv.appendChild(document.createElement("div"));var contentCssClass=this.group.expanded?"expanded":"collapsed";this.contentDiv.className="groupcontent "+contentCssClass;this.expander.onclick=function(){if(groupElementContainer.group.expandable){groupElementContainer.group.toggleExpanded();}};}}','GroupElementContainer.prototype=new LogItemContainerElement();copyProperties(GroupElementContainer.prototype,{toggleExpanded:function(){if(!this.isRoot){var oldCssClass,newCssClass,expanderText;if(this.group.expanded){newCssClass="expanded";oldCssClass="collapsed";expanderText="-";}else{newCssClass="collapsed";oldCssClass="expanded";expanderText="+";}',"replaceClass(this.contentDiv,newCssClass,oldCssClass);this.expanderTextNode.nodeValue=expanderText;}},remove:function(){if(!this.isRoot){this.headingDiv=null;this.expander.onclick=null;this.expander=null;this.expanderTextNode=null;this.contentDiv=null;this.containerDomNode=null;this.mainDiv.parentNode.removeChild(this.mainDiv);this.mainDiv=null;}},reverseChildren:function(){var node=null;var childDomNodes=[];while((node=this.contentDiv.firstChild)){this.contentDiv.removeChild(node);childDomNodes.push(node);}",'while((node=childDomNodes.pop())){this.contentDiv.appendChild(node);}},update:function(){if(!this.isRoot){if(this.group.expandable){removeClass(this.expander,"greyedout");}else{addClass(this.expander,"greyedout");}}},clear:function(){if(this.isRoot){this.contentDiv.innerHTML="";}}});function Group(name,isRoot,initiallyExpanded){this.name=name;this.group=null;this.isRoot=isRoot;this.initiallyExpanded=initiallyExpanded;this.elementContainers=[];this.children=[];this.expanded=initiallyExpanded;this.rendered=false;this.expandable=false;}',"Group.prototype=new LogItem();copyProperties(Group.prototype,{addChild:function(logItem){this.children.push(logItem);logItem.group=this;},render:function(){if(isIe){var unwrappedDomContainer,wrappedDomContainer;if(this.isRoot){unwrappedDomContainer=logMainContainer;wrappedDomContainer=logMainContainer;}else{unwrappedDomContainer=this.getUnwrappedDomContainer();wrappedDomContainer=this.getWrappedDomContainer();}","this.unwrappedElementContainer=new GroupElementContainer(this,unwrappedDomContainer,this.isRoot,false);this.wrappedElementContainer=new GroupElementContainer(this,wrappedDomContainer,this.isRoot,true);this.elementContainers=[this.unwrappedElementContainer,this.wrappedElementContainer];}else{var mainDomContainer=this.isRoot?logMainContainer:this.getMainDomContainer();this.mainElementContainer=new GroupElementContainer(this,mainDomContainer,this.isRoot,false);this.elementContainers=[this.mainElementContainer];}","this.rendered=true;},toggleExpanded:function(){this.expanded=!this.expanded;for(var i=0,len=this.elementContainers.length;i<len;i++){this.elementContainers[i].toggleExpanded();}},expand:function(){if(!this.expanded){this.toggleExpanded();}},accept:function(visitor){visitor.visitGroup(this);},reverseChildren:function(){if(this.rendered){for(var i=0,len=this.elementContainers.length;i<len;i++){this.elementContainers[i].reverseChildren();}}},update:function(){var previouslyExpandable=this.expandable;this.expandable=(this.children.length!==0);if(this.expandable!==previouslyExpandable){for(var i=0,len=this.elementContainers.length;i<len;i++){this.elementContainers[i].update();}}},flatten:function(){var visitor=new GroupFlattener();this.accept(visitor);return visitor.logEntriesAndSeparators;},removeChild:function(child,doUpdate){array_remove(this.children,child);child.group=null;if(doUpdate){this.update();}},remove:function(doUpdate,removeFromGroup){for(var i=0,len=this.children.length;i<len;i++){this.children[i].remove(false,false);}","this.children=[];this.update();if(this===currentGroup){currentGroup=this.group;}","this.doRemove(doUpdate,removeFromGroup);},serialize:function(items){items.push([LogItem.serializedItemKeys.GROUP_START,this.name]);for(var i=0,len=this.children.length;i<len;i++){this.children[i].serialize(items);}","if(this!==currentGroup){items.push([LogItem.serializedItemKeys.GROUP_END]);}},clear:function(){for(var i=0,len=this.elementContainers.length;i<len;i++){this.elementContainers[i].clear();}}});function LogEntryElementContainer(){}",'LogEntryElementContainer.prototype=new LogItemContainerElement();copyProperties(LogEntryElementContainer.prototype,{remove:function(){this.doRemove();},doRemove:function(){this.mainDiv.parentNode.removeChild(this.mainDiv);this.mainDiv=null;this.contentElement=null;this.containerDomNode=null;},setContent:function(content,wrappedContent){if(content===this.formattedMessage){this.contentElement.innerHTML="";this.contentElement.appendChild(document.createTextNode(this.formattedMessage));}else{this.contentElement.innerHTML=content;}},setSearchMatch:function(isMatch){var oldCssClass=isMatch?"searchnonmatch":"searchmatch";var newCssClass=isMatch?"searchmatch":"searchnonmatch";replaceClass(this.mainDiv,newCssClass,oldCssClass);},clearSearch:function(){removeClass(this.mainDiv,"searchmatch");removeClass(this.mainDiv,"searchnonmatch");}});function LogEntryWrappedElementContainer(logEntry,containerDomNode){this.logEntry=logEntry;this.containerDomNode=containerDomNode;this.mainDiv=document.createElement("div");this.mainDiv.appendChild(document.createTextNode(this.logEntry.formattedMessage));this.mainDiv.className="logentry wrapped "+this.logEntry.level;this.contentElement=this.mainDiv;}','LogEntryWrappedElementContainer.prototype=new LogEntryElementContainer();LogEntryWrappedElementContainer.prototype.setContent=function(content,wrappedContent){if(content===this.formattedMessage){this.contentElement.innerHTML="";this.contentElement.appendChild(document.createTextNode(this.formattedMessage));}else{this.contentElement.innerHTML=wrappedContent;}};function LogEntryUnwrappedElementContainer(logEntry,containerDomNode){this.logEntry=logEntry;this.containerDomNode=containerDomNode;this.mainDiv=document.createElement("div");this.mainDiv.className="logentry unwrapped "+this.logEntry.level;this.pre=this.mainDiv.appendChild(document.createElement("pre"));this.pre.appendChild(document.createTextNode(this.logEntry.formattedMessage));this.pre.className="unwrapped";this.contentElement=this.pre;}','LogEntryUnwrappedElementContainer.prototype=new LogEntryElementContainer();LogEntryUnwrappedElementContainer.prototype.remove=function(){this.doRemove();this.pre=null;};function LogEntryMainElementContainer(logEntry,containerDomNode){this.logEntry=logEntry;this.containerDomNode=containerDomNode;this.mainDiv=document.createElement("div");this.mainDiv.className="logentry nonielogentry "+this.logEntry.level;this.contentElement=this.mainDiv.appendChild(document.createElement("span"));this.contentElement.appendChild(document.createTextNode(this.logEntry.formattedMessage));}',"LogEntryMainElementContainer.prototype=new LogEntryElementContainer();function LogEntry(level,formattedMessage){this.level=level;this.formattedMessage=formattedMessage;this.rendered=false;}",'LogEntry.prototype=new LogItem();copyProperties(LogEntry.prototype,{render:function(){var logEntry=this;var containerDomNode=this.group.contentDiv;if(isIe){this.formattedMessage=this.formattedMessage.replace(/\\r\\n/g,"\\r");this.unwrappedElementContainer=new LogEntryUnwrappedElementContainer(this,this.getUnwrappedDomContainer());this.wrappedElementContainer=new LogEntryWrappedElementContainer(this,this.getWrappedDomContainer());this.elementContainers=[this.unwrappedElementContainer,this.wrappedElementContainer];}else{this.mainElementContainer=new LogEntryMainElementContainer(this,this.getMainDomContainer());this.elementContainers=[this.mainElementContainer];}','this.content=this.formattedMessage;this.rendered=true;},setContent:function(content,wrappedContent){if(content!=this.content){if(isIe&&(content!==this.formattedMessage)){content=content.replace(/\\r\\n/g,"\\r");}',"for(var i=0,len=this.elementContainers.length;i<len;i++){this.elementContainers[i].setContent(content,wrappedContent);}",'this.content=content;}},getSearchMatches:function(){var matches=[];var i,len;if(isIe){var unwrappedEls=getElementsByClass(this.unwrappedElementContainer.mainDiv,"searchterm","span");var wrappedEls=getElementsByClass(this.wrappedElementContainer.mainDiv,"searchterm","span");for(i=0,len=unwrappedEls.length;i<len;i++){matches[i]=new Match(this.level,null,unwrappedEls[i],wrappedEls[i]);}}else{var els=getElementsByClass(this.mainElementContainer.mainDiv,"searchterm","span");for(i=0,len=els.length;i<len;i++){matches[i]=new Match(this.level,els[i]);}}',"return matches;},setSearchMatch:function(isMatch){for(var i=0,len=this.elementContainers.length;i<len;i++){this.elementContainers[i].setSearchMatch(isMatch);}},clearSearch:function(){for(var i=0,len=this.elementContainers.length;i<len;i++){this.elementContainers[i].clearSearch();}},accept:function(visitor){visitor.visitLogEntry(this);},serialize:function(items){items.push([LogItem.serializedItemKeys.LOG_ENTRY,this.level,this.formattedMessage]);}});function LogItemVisitor(){}","LogItemVisitor.prototype={visit:function(logItem){},visitParent:function(logItem){if(logItem.group){logItem.group.accept(this);}},visitChildren:function(logItem){for(var i=0,len=logItem.children.length;i<len;i++){logItem.children[i].accept(this);}},visitLogEntry:function(logEntry){this.visit(logEntry);},visitSeparator:function(separator){this.visit(separator);},visitGroup:function(group){this.visit(group);}};function GroupFlattener(){this.logEntriesAndSeparators=[];}",'GroupFlattener.prototype=new LogItemVisitor();GroupFlattener.prototype.visitGroup=function(group){this.visitChildren(group);};GroupFlattener.prototype.visitLogEntry=function(logEntry){this.logEntriesAndSeparators.push(logEntry);};GroupFlattener.prototype.visitSeparator=function(separator){this.logEntriesAndSeparators.push(separator);};window.onload=function(){if(location.search){var queryBits=unescape(location.search).substr(1).split("&"),nameValueBits;for(var i=0,len=queryBits.length;i<len;i++){nameValueBits=queryBits[i].split("=");if(nameValueBits[0]=="log4javascript_domain"){document.domain=nameValueBits[1];break;}}}','logMainContainer=$("log");if(isIePre7){addClass(logMainContainer,"oldIe");}','rootGroup=new Group("root",true);rootGroup.render();currentGroup=rootGroup;setCommandInputWidth();setLogContainerHeight();toggleLoggingEnabled();toggleSearchEnabled();toggleSearchFilter();toggleSearchHighlight();applyFilters();checkAllLevels();toggleWrap();toggleNewestAtTop();toggleScrollToLatest();renderQueuedLogItems();loaded=true;$("command").value="";$("command").autocomplete="off";$("command").onkeydown=function(evt){evt=getEvent(evt);if(evt.keyCode==10||evt.keyCode==13){evalCommandLine();stopPropagation(evt);}else if(evt.keyCode==27){this.value="";this.focus();}else if(evt.keyCode==38&&commandHistory.length>0){currentCommandIndex=Math.max(0,currentCommandIndex-1);this.value=commandHistory[currentCommandIndex];moveCaretToEnd(this);}else if(evt.keyCode==40&&commandHistory.length>0){currentCommandIndex=Math.min(commandHistory.length-1,currentCommandIndex+1);this.value=commandHistory[currentCommandIndex];moveCaretToEnd(this);}};$("command").onkeypress=function(evt){evt=getEvent(evt);if(evt.keyCode==38&&commandHistory.length>0&&evt.preventDefault){evt.preventDefault();}};$("command").onkeyup=function(evt){evt=getEvent(evt);if(evt.keyCode==27&&evt.preventDefault){evt.preventDefault();this.focus();}};document.onkeydown=function keyEventHandler(evt){evt=getEvent(evt);switch(evt.keyCode){case 69:if(evt.shiftKey&&(evt.ctrlKey||evt.metaKey)){evalLastCommand();cancelKeyEvent(evt);return false;}',"break;case 75:if(evt.shiftKey&&(evt.ctrlKey||evt.metaKey)){focusSearch();cancelKeyEvent(evt);return false;}","break;case 40:case 76:if(evt.shiftKey&&(evt.ctrlKey||evt.metaKey)){focusCommandLine();cancelKeyEvent(evt);return false;}","break;}};setTimeout(setLogContainerHeight,20);setShowCommandLine(showCommandLine);doSearch();};window.onunload=function(){if(mainWindowExists()){appender.unload();}",'appender=null;};function toggleLoggingEnabled(){setLoggingEnabled($("enableLogging").checked);}',"function setLoggingEnabled(enable){loggingEnabled=enable;}","var appender=null;function setAppender(appenderParam){appender=appenderParam;}",'function setShowCloseButton(showCloseButton){$("closeButton").style.display=showCloseButton?"inline":"none";}','function setShowHideButton(showHideButton){$("hideButton").style.display=showHideButton?"inline":"none";}',"var newestAtTop=false;function LogItemContentReverser(){}","LogItemContentReverser.prototype=new LogItemVisitor();LogItemContentReverser.prototype.visitGroup=function(group){group.reverseChildren();this.visitChildren(group);};function setNewestAtTop(isNewestAtTop){var oldNewestAtTop=newestAtTop;var i,iLen,j,jLen;newestAtTop=Boolean(isNewestAtTop);if(oldNewestAtTop!=newestAtTop){var visitor=new LogItemContentReverser();rootGroup.accept(visitor);if(currentSearch){var currentMatch=currentSearch.matches[currentMatchIndex];var matchIndex=0;var matches=[];var actOnLogEntry=function(logEntry){var logEntryMatches=logEntry.getSearchMatches();for(j=0,jLen=logEntryMatches.length;j<jLen;j++){matches[matchIndex]=logEntryMatches[j];if(currentMatch&&logEntryMatches[j].equals(currentMatch)){currentMatchIndex=matchIndex;}","matchIndex++;}};if(newestAtTop){for(i=logEntries.length-1;i>=0;i--){actOnLogEntry(logEntries[i]);}}else{for(i=0,iLen=logEntries.length;i<iLen;i++){actOnLogEntry(logEntries[i]);}}","currentSearch.matches=matches;if(currentMatch){currentMatch.setCurrent();}}else if(scrollToLatest){doScrollToLatest();}}",'$("newestAtTop").checked=isNewestAtTop;}','function toggleNewestAtTop(){var isNewestAtTop=$("newestAtTop").checked;setNewestAtTop(isNewestAtTop);}',"var scrollToLatest=true;function setScrollToLatest(isScrollToLatest){scrollToLatest=isScrollToLatest;if(scrollToLatest){doScrollToLatest();}",'$("scrollToLatest").checked=isScrollToLatest;}','function toggleScrollToLatest(){var isScrollToLatest=$("scrollToLatest").checked;setScrollToLatest(isScrollToLatest);}','function doScrollToLatest(){var l=logMainContainer;if(typeof l.scrollTop!="undefined"){if(newestAtTop){l.scrollTop=0;}else{var latestLogEntry=l.lastChild;if(latestLogEntry){l.scrollTop=l.scrollHeight;}}}}',"var closeIfOpenerCloses=true;function setCloseIfOpenerCloses(isCloseIfOpenerCloses){closeIfOpenerCloses=isCloseIfOpenerCloses;}","var maxMessages=null;function setMaxMessages(max){maxMessages=max;pruneLogEntries();}",'var showCommandLine=false;function setShowCommandLine(isShowCommandLine){showCommandLine=isShowCommandLine;if(loaded){$("commandLine").style.display=showCommandLine?"block":"none";setCommandInputWidth();setLogContainerHeight();}}','function focusCommandLine(){if(loaded){$("command").focus();}}','function focusSearch(){if(loaded){$("searchBox").focus();}}',"function getLogItems(){var items=[];for(var i=0,len=logItems.length;i<len;i++){logItems[i].serialize(items);}","return items;}","function setLogItems(items){var loggingReallyEnabled=loggingEnabled;loggingEnabled=true;for(var i=0,len=items.length;i<len;i++){switch(items[i][0]){case LogItem.serializedItemKeys.LOG_ENTRY:log(items[i][1],items[i][2]);break;case LogItem.serializedItemKeys.GROUP_START:group(items[i][1]);break;case LogItem.serializedItemKeys.GROUP_END:groupEnd();break;}}","loggingEnabled=loggingReallyEnabled;}","function log(logLevel,formattedMessage){if(loggingEnabled){var logEntry=new LogEntry(logLevel,formattedMessage);logEntries.push(logEntry);logEntriesAndSeparators.push(logEntry);logItems.push(logEntry);currentGroup.addChild(logEntry);if(loaded){if(logQueuedEventsTimer!==null){clearTimeout(logQueuedEventsTimer);}","logQueuedEventsTimer=setTimeout(renderQueuedLogItems,renderDelay);unrenderedLogItemsExist=true;}}}","function renderQueuedLogItems(){logQueuedEventsTimer=null;var pruned=pruneLogEntries();var initiallyHasMatches=currentSearch?currentSearch.hasMatches():false;for(var i=0,len=logItems.length;i<len;i++){if(!logItems[i].rendered){logItems[i].render();logItems[i].appendToLog();if(currentSearch&&(logItems[i]instanceof LogEntry)){currentSearch.applyTo(logItems[i]);}}}","if(currentSearch){if(pruned){if(currentSearch.hasVisibleMatches()){if(currentMatchIndex===null){setCurrentMatchIndex(0);}","displayMatches();}else{displayNoMatches();}}else if(!initiallyHasMatches&&currentSearch.hasVisibleMatches()){setCurrentMatchIndex(0);displayMatches();}}","if(scrollToLatest){doScrollToLatest();}","unrenderedLogItemsExist=false;}","function pruneLogEntries(){if((maxMessages!==null)&&(logEntriesAndSeparators.length>maxMessages)){var numberToDelete=logEntriesAndSeparators.length-maxMessages;var prunedLogEntries=logEntriesAndSeparators.slice(0,numberToDelete);if(currentSearch){currentSearch.removeMatches(prunedLogEntries);}","var group;for(var i=0;i<numberToDelete;i++){group=logEntriesAndSeparators[i].group;array_remove(logItems,logEntriesAndSeparators[i]);array_remove(logEntries,logEntriesAndSeparators[i]);logEntriesAndSeparators[i].remove(true,true);if(group.children.length===0&&group!==currentGroup&&group!==rootGroup){array_remove(logItems,group);group.remove(true,true);}}","logEntriesAndSeparators=array_removeFromStart(logEntriesAndSeparators,numberToDelete);return true;}","return false;}",'function group(name,startExpanded){if(loggingEnabled){initiallyExpanded=(typeof startExpanded==="undefined")?true:Boolean(startExpanded);var newGroup=new Group(name,false,initiallyExpanded);currentGroup.addChild(newGroup);currentGroup=newGroup;logItems.push(newGroup);if(loaded){if(logQueuedEventsTimer!==null){clearTimeout(logQueuedEventsTimer);}',"logQueuedEventsTimer=setTimeout(renderQueuedLogItems,renderDelay);unrenderedLogItemsExist=true;}}}","function groupEnd(){currentGroup=(currentGroup===rootGroup)?rootGroup:currentGroup.group;}","function mainPageReloaded(){currentGroup=rootGroup;var separator=new Separator();logEntriesAndSeparators.push(separator);logItems.push(separator);currentGroup.addChild(separator);}","function closeWindow(){if(appender&&mainWindowExists()){appender.close(true);}else{window.close();}}","function hide(){if(appender&&mainWindowExists()){appender.hide();}}",'var mainWindow=window;var windowId="log4javascriptConsoleWindow_"+new Date().getTime()+"_"+(""+Math.random()).substr(2);function setMainWindow(win){mainWindow=win;mainWindow[windowId]=window;if(opener&&closeIfOpenerCloses){pollOpener();}}',"function pollOpener(){if(closeIfOpenerCloses){if(mainWindowExists()){setTimeout(pollOpener,500);}else{closeWindow();}}}","function mainWindowExists(){try{return(mainWindow&&!mainWindow.closed&&mainWindow[windowId]==window);}catch(ex){}","return false;}",'var logLevels=["TRACE","DEBUG","INFO","WARN","ERROR","FATAL"];function getCheckBox(logLevel){return $("switch_"+logLevel);}','function getIeWrappedLogContainer(){return $("log_wrapped");}','function getIeUnwrappedLogContainer(){return $("log_unwrapped");}',"function applyFilters(){for(var i=0;i<logLevels.length;i++){if(getCheckBox(logLevels[i]).checked){addClass(logMainContainer,logLevels[i]);}else{removeClass(logMainContainer,logLevels[i]);}}","updateSearchFromFilters();}",'function toggleAllLevels(){var turnOn=$("switch_ALL").checked;for(var i=0;i<logLevels.length;i++){getCheckBox(logLevels[i]).checked=turnOn;if(turnOn){addClass(logMainContainer,logLevels[i]);}else{removeClass(logMainContainer,logLevels[i]);}}}','function checkAllLevels(){for(var i=0;i<logLevels.length;i++){if(!getCheckBox(logLevels[i]).checked){getCheckBox("ALL").checked=false;return;}}','getCheckBox("ALL").checked=true;}',"function clearLog(){rootGroup.clear();currentGroup=rootGroup;logEntries=[];logItems=[];logEntriesAndSeparators=[];doSearch();}",'function toggleWrap(){var enable=$("wrap").checked;if(enable){addClass(logMainContainer,"wrap");}else{removeClass(logMainContainer,"wrap");}',"refreshCurrentMatch();}","var searchTimer=null;function scheduleSearch(){try{clearTimeout(searchTimer);}catch(ex){}","searchTimer=setTimeout(doSearch,500);}","function Search(searchTerm,isRegex,searchRegex,isCaseSensitive){this.searchTerm=searchTerm;this.isRegex=isRegex;this.searchRegex=searchRegex;this.isCaseSensitive=isCaseSensitive;this.matches=[];}","Search.prototype={hasMatches:function(){return this.matches.length>0;},hasVisibleMatches:function(){if(this.hasMatches()){for(var i=0;i<this.matches.length;i++){if(this.matches[i].isVisible()){return true;}}}","return false;},match:function(logEntry){var entryText=String(logEntry.formattedMessage);var matchesSearch=false;if(this.isRegex){matchesSearch=this.searchRegex.test(entryText);}else if(this.isCaseSensitive){matchesSearch=(entryText.indexOf(this.searchTerm)>-1);}else{matchesSearch=(entryText.toLowerCase().indexOf(this.searchTerm.toLowerCase())>-1);}","return matchesSearch;},getNextVisibleMatchIndex:function(){for(var i=currentMatchIndex+1;i<this.matches.length;i++){if(this.matches[i].isVisible()){return i;}}","for(i=0;i<=currentMatchIndex;i++){if(this.matches[i].isVisible()){return i;}}","return-1;},getPreviousVisibleMatchIndex:function(){for(var i=currentMatchIndex-1;i>=0;i--){if(this.matches[i].isVisible()){return i;}}","for(var i=this.matches.length-1;i>=currentMatchIndex;i--){if(this.matches[i].isVisible()){return i;}}",'return-1;},applyTo:function(logEntry){var doesMatch=this.match(logEntry);if(doesMatch){logEntry.group.expand();logEntry.setSearchMatch(true);var logEntryContent;var wrappedLogEntryContent;var searchTermReplacementStartTag="<span class=\\"searchterm\\">";var searchTermReplacementEndTag="<"+"/span>";var preTagName=isIe?"pre":"span";var preStartTag="<"+preTagName+" class=\\"pre\\">";var preEndTag="<"+"/"+preTagName+">";var startIndex=0;var searchIndex,matchedText,textBeforeMatch;if(this.isRegex){var flags=this.isCaseSensitive?"g":"gi";var capturingRegex=new RegExp("("+this.searchRegex.source+")",flags);var rnd=(""+Math.random()).substr(2);var startToken="%%s"+rnd+"%%";var endToken="%%e"+rnd+"%%";logEntryContent=logEntry.formattedMessage.replace(capturingRegex,startToken+"$1"+endToken);logEntryContent=escapeHtml(logEntryContent);var result;var searchString=logEntryContent;logEntryContent="";wrappedLogEntryContent="";while((searchIndex=searchString.indexOf(startToken,startIndex))>-1){var endTokenIndex=searchString.indexOf(endToken,searchIndex);matchedText=searchString.substring(searchIndex+startToken.length,endTokenIndex);textBeforeMatch=searchString.substring(startIndex,searchIndex);logEntryContent+=preStartTag+textBeforeMatch+preEndTag;logEntryContent+=searchTermReplacementStartTag+preStartTag+matchedText+',"preEndTag+searchTermReplacementEndTag;if(isIe){wrappedLogEntryContent+=textBeforeMatch+searchTermReplacementStartTag+","matchedText+searchTermReplacementEndTag;}","startIndex=endTokenIndex+endToken.length;}",'logEntryContent+=preStartTag+searchString.substr(startIndex)+preEndTag;if(isIe){wrappedLogEntryContent+=searchString.substr(startIndex);}}else{logEntryContent="";wrappedLogEntryContent="";var searchTermReplacementLength=searchTermReplacementStartTag.length+',"this.searchTerm.length+searchTermReplacementEndTag.length;var searchTermLength=this.searchTerm.length;var searchTermLowerCase=this.searchTerm.toLowerCase();var logTextLowerCase=logEntry.formattedMessage.toLowerCase();while((searchIndex=logTextLowerCase.indexOf(searchTermLowerCase,startIndex))>-1){matchedText=escapeHtml(logEntry.formattedMessage.substr(searchIndex,this.searchTerm.length));textBeforeMatch=escapeHtml(logEntry.formattedMessage.substring(startIndex,searchIndex));var searchTermReplacement=searchTermReplacementStartTag+","preStartTag+matchedText+preEndTag+searchTermReplacementEndTag;logEntryContent+=preStartTag+textBeforeMatch+preEndTag+searchTermReplacement;if(isIe){wrappedLogEntryContent+=textBeforeMatch+searchTermReplacementStartTag+","matchedText+searchTermReplacementEndTag;}","startIndex=searchIndex+searchTermLength;}","var textAfterLastMatch=escapeHtml(logEntry.formattedMessage.substr(startIndex));logEntryContent+=preStartTag+textAfterLastMatch+preEndTag;if(isIe){wrappedLogEntryContent+=textAfterLastMatch;}}","logEntry.setContent(logEntryContent,wrappedLogEntryContent);var logEntryMatches=logEntry.getSearchMatches();this.matches=this.matches.concat(logEntryMatches);}else{logEntry.setSearchMatch(false);logEntry.setContent(logEntry.formattedMessage,logEntry.formattedMessage);}","return doesMatch;},removeMatches:function(logEntries){var matchesToRemoveCount=0;var currentMatchRemoved=false;var matchesToRemove=[];var i,iLen,j,jLen;for(i=0,iLen=this.matches.length;i<iLen;i++){for(j=0,jLen=logEntries.length;j<jLen;j++){if(this.matches[i].belongsTo(logEntries[j])){matchesToRemove.push(this.matches[i]);if(i===currentMatchIndex){currentMatchRemoved=true;}}}}","var newMatch=currentMatchRemoved?null:this.matches[currentMatchIndex];if(currentMatchRemoved){for(i=currentMatchIndex,iLen=this.matches.length;i<iLen;i++){if(this.matches[i].isVisible()&&!array_contains(matchesToRemove,this.matches[i])){newMatch=this.matches[i];break;}}}","for(i=0,iLen=matchesToRemove.length;i<iLen;i++){array_remove(this.matches,matchesToRemove[i]);matchesToRemove[i].remove();}","if(this.hasVisibleMatches()){if(newMatch===null){setCurrentMatchIndex(0);}else{var newMatchIndex=0;for(i=0,iLen=this.matches.length;i<iLen;i++){if(newMatch===this.matches[i]){newMatchIndex=i;break;}}","setCurrentMatchIndex(newMatchIndex);}}else{currentMatchIndex=null;displayNoMatches();}}};function getPageOffsetTop(el,container){var currentEl=el;var y=0;while(currentEl&&currentEl!=container){y+=currentEl.offsetTop;currentEl=currentEl.offsetParent;}","return y;}",'function scrollIntoView(el){var logContainer=logMainContainer;if(!$("wrap").checked){var logContainerLeft=logContainer.scrollLeft;var logContainerRight=logContainerLeft+logContainer.offsetWidth;var elLeft=el.offsetLeft;var elRight=elLeft+el.offsetWidth;if(elLeft<logContainerLeft||elRight>logContainerRight){logContainer.scrollLeft=elLeft-(logContainer.offsetWidth-el.offsetWidth)/2;}}',"var logContainerTop=logContainer.scrollTop;var logContainerBottom=logContainerTop+logContainer.offsetHeight;var elTop=getPageOffsetTop(el)-getToolBarsHeight();var elBottom=elTop+el.offsetHeight;if(elTop<logContainerTop||elBottom>logContainerBottom){logContainer.scrollTop=elTop-(logContainer.offsetHeight-el.offsetHeight)/2;}}","function Match(logEntryLevel,spanInMainDiv,spanInUnwrappedPre,spanInWrappedDiv){this.logEntryLevel=logEntryLevel;this.spanInMainDiv=spanInMainDiv;if(isIe){this.spanInUnwrappedPre=spanInUnwrappedPre;this.spanInWrappedDiv=spanInWrappedDiv;}","this.mainSpan=isIe?spanInUnwrappedPre:spanInMainDiv;}",'Match.prototype={equals:function(match){return this.mainSpan===match.mainSpan;},setCurrent:function(){if(isIe){addClass(this.spanInUnwrappedPre,"currentmatch");addClass(this.spanInWrappedDiv,"currentmatch");var elementToScroll=$("wrap").checked?this.spanInWrappedDiv:this.spanInUnwrappedPre;scrollIntoView(elementToScroll);}else{addClass(this.spanInMainDiv,"currentmatch");scrollIntoView(this.spanInMainDiv);}},belongsTo:function(logEntry){if(isIe){return isDescendant(this.spanInUnwrappedPre,logEntry.unwrappedPre);}else{return isDescendant(this.spanInMainDiv,logEntry.mainDiv);}},setNotCurrent:function(){if(isIe){removeClass(this.spanInUnwrappedPre,"currentmatch");removeClass(this.spanInWrappedDiv,"currentmatch");}else{removeClass(this.spanInMainDiv,"currentmatch");}},isOrphan:function(){return isOrphan(this.mainSpan);},isVisible:function(){return getCheckBox(this.logEntryLevel).checked;},remove:function(){if(isIe){this.spanInUnwrappedPre=null;this.spanInWrappedDiv=null;}else{this.spanInMainDiv=null;}}};var currentSearch=null;var currentMatchIndex=null;function doSearch(){var searchBox=$("searchBox");var searchTerm=searchBox.value;var isRegex=$("searchRegex").checked;var isCaseSensitive=$("searchCaseSensitive").checked;var i;if(searchTerm===""){$("searchReset").disabled=true;$("searchNav").style.display="none";removeClass(document.body,"searching");removeClass(searchBox,"hasmatches");removeClass(searchBox,"nomatches");for(i=0;i<logEntries.length;i++){logEntries[i].clearSearch();logEntries[i].setContent(logEntries[i].formattedMessage,logEntries[i].formattedMessage);}','currentSearch=null;setLogContainerHeight();}else{$("searchReset").disabled=false;$("searchNav").style.display="block";var searchRegex;var regexValid;if(isRegex){try{searchRegex=isCaseSensitive?new RegExp(searchTerm,"g"):new RegExp(searchTerm,"gi");regexValid=true;replaceClass(searchBox,"validregex","invalidregex");searchBox.title="Valid regex";}catch(ex){regexValid=false;replaceClass(searchBox,"invalidregex","validregex");searchBox.title="Invalid regex: "+(ex.message?ex.message:(ex.description?ex.description:"unknown error"));return;}}else{searchBox.title="";removeClass(searchBox,"validregex");removeClass(searchBox,"invalidregex");}','addClass(document.body,"searching");currentSearch=new Search(searchTerm,isRegex,searchRegex,isCaseSensitive);for(i=0;i<logEntries.length;i++){currentSearch.applyTo(logEntries[i]);}',"setLogContainerHeight();if(currentSearch.hasVisibleMatches()){setCurrentMatchIndex(0);displayMatches();}else{displayNoMatches();}}}","function updateSearchFromFilters(){if(currentSearch){if(currentSearch.hasMatches()){if(currentMatchIndex===null){currentMatchIndex=0;}","var currentMatch=currentSearch.matches[currentMatchIndex];if(currentMatch.isVisible()){displayMatches();setCurrentMatchIndex(currentMatchIndex);}else{currentMatch.setNotCurrent();var nextVisibleMatchIndex=currentSearch.getNextVisibleMatchIndex();if(nextVisibleMatchIndex>-1){setCurrentMatchIndex(nextVisibleMatchIndex);displayMatches();}else{displayNoMatches();}}}else{displayNoMatches();}}}","function refreshCurrentMatch(){if(currentSearch&&currentSearch.hasVisibleMatches()){setCurrentMatchIndex(currentMatchIndex);}}",'function displayMatches(){replaceClass($("searchBox"),"hasmatches","nomatches");$("searchBox").title=""+currentSearch.matches.length+" matches found";$("searchNav").style.display="block";setLogContainerHeight();}','function displayNoMatches(){replaceClass($("searchBox"),"nomatches","hasmatches");$("searchBox").title="No matches found";$("searchNav").style.display="none";setLogContainerHeight();}','function toggleSearchEnabled(enable){enable=(typeof enable=="undefined")?!$("searchDisable").checked:enable;$("searchBox").disabled=!enable;$("searchReset").disabled=!enable;$("searchRegex").disabled=!enable;$("searchNext").disabled=!enable;$("searchPrevious").disabled=!enable;$("searchCaseSensitive").disabled=!enable;$("searchNav").style.display=(enable&&($("searchBox").value!=="")&&currentSearch&&currentSearch.hasVisibleMatches())?"block":"none";if(enable){removeClass($("search"),"greyedout");addClass(document.body,"searching");if($("searchHighlight").checked){addClass(logMainContainer,"searchhighlight");}else{removeClass(logMainContainer,"searchhighlight");}','if($("searchFilter").checked){addClass(logMainContainer,"searchfilter");}else{removeClass(logMainContainer,"searchfilter");}','$("searchDisable").checked=!enable;}else{addClass($("search"),"greyedout");removeClass(document.body,"searching");removeClass(logMainContainer,"searchhighlight");removeClass(logMainContainer,"searchfilter");}',"setLogContainerHeight();}",'function toggleSearchFilter(){var enable=$("searchFilter").checked;if(enable){addClass(logMainContainer,"searchfilter");}else{removeClass(logMainContainer,"searchfilter");}',"refreshCurrentMatch();}",'function toggleSearchHighlight(){var enable=$("searchHighlight").checked;if(enable){addClass(logMainContainer,"searchhighlight");}else{removeClass(logMainContainer,"searchhighlight");}}','function clearSearch(){$("searchBox").value="";doSearch();}','function searchNext(){if(currentSearch!==null&&currentMatchIndex!==null){currentSearch.matches[currentMatchIndex].setNotCurrent();var nextMatchIndex=currentSearch.getNextVisibleMatchIndex();if(nextMatchIndex>currentMatchIndex||confirm("Reached the end of the page. Start from the top?")){setCurrentMatchIndex(nextMatchIndex);}}}','function searchPrevious(){if(currentSearch!==null&&currentMatchIndex!==null){currentSearch.matches[currentMatchIndex].setNotCurrent();var previousMatchIndex=currentSearch.getPreviousVisibleMatchIndex();if(previousMatchIndex<currentMatchIndex||confirm("Reached the start of the page. Continue from the bottom?")){setCurrentMatchIndex(previousMatchIndex);}}}',"function setCurrentMatchIndex(index){currentMatchIndex=index;currentSearch.matches[currentMatchIndex].setCurrent();}",'function addClass(el,cssClass){if(!hasClass(el,cssClass)){if(el.className){el.className+=" "+cssClass;}else{el.className=cssClass;}}}','function hasClass(el,cssClass){if(el.className){var classNames=el.className.split(" ");return array_contains(classNames,cssClass);}',"return false;}",'function removeClass(el,cssClass){if(hasClass(el,cssClass)){var existingClasses=el.className.split(" ");var newClasses=[];for(var i=0,len=existingClasses.length;i<len;i++){if(existingClasses[i]!=cssClass){newClasses[newClasses.length]=existingClasses[i];}}','el.className=newClasses.join(" ");}}',"function replaceClass(el,newCssClass,oldCssClass){removeClass(el,oldCssClass);addClass(el,newCssClass);}","function getElementsByClass(el,cssClass,tagName){var elements=el.getElementsByTagName(tagName);var matches=[];for(var i=0,len=elements.length;i<len;i++){if(hasClass(elements[i],cssClass)){matches.push(elements[i]);}}","return matches;}","function $(id){return document.getElementById(id);}","function isDescendant(node,ancestorNode){while(node!=null){if(node===ancestorNode){return true;}","node=node.parentNode;}","return false;}","function isOrphan(node){var currentNode=node;while(currentNode){if(currentNode==document.body){return false;}","currentNode=currentNode.parentNode;}","return true;}",'function escapeHtml(str){return str.replace(/&/g,"&amp;").replace(/[<]/g,"&lt;").replace(/>/g,"&gt;");}',"function getWindowWidth(){if(window.innerWidth){return window.innerWidth;}else if(document.documentElement&&document.documentElement.clientWidth){return document.documentElement.clientWidth;}else if(document.body){return document.body.clientWidth;}","return 0;}","function getWindowHeight(){if(window.innerHeight){return window.innerHeight;}else if(document.documentElement&&document.documentElement.clientHeight){return document.documentElement.clientHeight;}else if(document.body){return document.body.clientHeight;}","return 0;}",'function getToolBarsHeight(){return $("switches").offsetHeight;}','function getChromeHeight(){var height=getToolBarsHeight();if(showCommandLine){height+=$("commandLine").offsetHeight;}',"return height;}",'function setLogContainerHeight(){if(logMainContainer){var windowHeight=getWindowHeight();$("body").style.height=getWindowHeight()+"px";logMainContainer.style.height=""+','Math.max(0,windowHeight-getChromeHeight())+"px";}}','function setCommandInputWidth(){if(showCommandLine){$("command").style.width=""+Math.max(0,$("commandLineContainer").offsetWidth-','($("evaluateButton").offsetWidth+13))+"px";}}',"window.onresize=function(){setCommandInputWidth();setLogContainerHeight();};if(!Array.prototype.push){Array.prototype.push=function(){for(var i=0,len=arguments.length;i<len;i++){this[this.length]=arguments[i];}","return this.length;};}","if(!Array.prototype.pop){Array.prototype.pop=function(){if(this.length>0){var val=this[this.length-1];this.length=this.length-1;return val;}};}","if(!Array.prototype.shift){Array.prototype.shift=function(){if(this.length>0){var firstItem=this[0];for(var i=0,len=this.length-1;i<len;i++){this[i]=this[i+1];}","this.length=this.length-1;return firstItem;}};}","if(!Array.prototype.splice){Array.prototype.splice=function(startIndex,deleteCount){var itemsAfterDeleted=this.slice(startIndex+deleteCount);var itemsDeleted=this.slice(startIndex,startIndex+deleteCount);this.length=startIndex;var argumentsArray=[];for(var i=0,len=arguments.length;i<len;i++){argumentsArray[i]=arguments[i];}","var itemsToAppend=(argumentsArray.length>2)?itemsAfterDeleted=argumentsArray.slice(2).concat(itemsAfterDeleted):itemsAfterDeleted;for(i=0,len=itemsToAppend.length;i<len;i++){this.push(itemsToAppend[i]);}","return itemsDeleted;};}","function array_remove(arr,val){var index=-1;for(var i=0,len=arr.length;i<len;i++){if(arr[i]===val){index=i;break;}}","if(index>=0){arr.splice(index,1);return index;}else{return false;}}","function array_removeFromStart(array,numberToRemove){if(Array.prototype.splice){array.splice(0,numberToRemove);}else{for(var i=numberToRemove,len=array.length;i<len;i++){array[i-numberToRemove]=array[i];}","array.length=array.length-numberToRemove;}","return array;}","function array_contains(arr,val){for(var i=0,len=arr.length;i<len;i++){if(arr[i]==val){return true;}}","return false;}","function getErrorMessage(ex){if(ex.message){return ex.message;}else if(ex.description){return ex.description;}",'return""+ex;}',"function moveCaretToEnd(input){if(input.setSelectionRange){input.focus();var length=input.value.length;input.setSelectionRange(length,length);}else if(input.createTextRange){var range=input.createTextRange();range.collapse(false);range.select();}","input.focus();}",'function stopPropagation(evt){if(evt.stopPropagation){evt.stopPropagation();}else if(typeof evt.cancelBubble!="undefined"){evt.cancelBubble=true;}}',"function getEvent(evt){return evt?evt:event;}","function getTarget(evt){return evt.target?evt.target:evt.srcElement;}",'function getRelatedTarget(evt){if(evt.relatedTarget){return evt.relatedTarget;}else if(evt.srcElement){switch(evt.type){case"mouseover":return evt.fromElement;case"mouseout":return evt.toElement;default:return evt.srcElement;}}}',"function cancelKeyEvent(evt){evt.returnValue=false;stopPropagation(evt);}",'function evalCommandLine(){var expr=$("command").value;evalCommand(expr);$("command").value="";}',"function evalLastCommand(){if(lastCommand!=null){evalCommand(lastCommand);}}",'var lastCommand=null;var commandHistory=[];var currentCommandIndex=0;function evalCommand(expr){if(appender){appender.evalCommandAndAppend(expr);}else{var prefix=">>> "+expr+"\\r\\n";try{log("INFO",prefix+eval(expr));}catch(ex){log("ERROR",prefix+"Error: "+getErrorMessage(ex));}}',"if(expr!=commandHistory[commandHistory.length-1]){commandHistory.push(expr);if(appender){appender.storeCommandHistory(commandHistory);}}","currentCommandIndex=(expr==commandHistory[currentCommandIndex])?currentCommandIndex+1:commandHistory.length;lastCommand=expr;}","//]]>","<\/script>",'<style type="text/css">',"body{background-color:white;color:black;padding:0;margin:0;font-family:tahoma,verdana,arial,helvetica,sans-serif;overflow:hidden}div#switchesContainer input{margin-bottom:0}div.toolbar{border-top:solid #ffffff 1px;border-bottom:solid #aca899 1px;background-color:#f1efe7;padding:3px 5px;font-size:68.75%}div.toolbar,div#search input{font-family:tahoma,verdana,arial,helvetica,sans-serif}div.toolbar input.button{padding:0 5px;font-size:100%}div.toolbar input.hidden{display:none}div#switches input#clearButton{margin-left:20px}div#levels label{font-weight:bold}div#levels label,div#options label{margin-right:5px}div#levels label#wrapLabel{font-weight:normal}div#search label{margin-right:10px}div#search label.searchboxlabel{margin-right:0}div#search input{font-size:100%}div#search input.validregex{color:green}div#search input.invalidregex{color:red}div#search input.nomatches{color:white;background-color:#ff6666}div#search input.nomatches{color:white;background-color:#ff6666}div#searchNav{display:none}div#commandLine{display:none}div#commandLine input#command{font-size:100%;font-family:Courier New,Courier}div#commandLine input#evaluateButton{}*.greyedout{color:gray !important;border-color:gray !important}*.greyedout *.alwaysenabled{color:black}*.unselectable{-khtml-user-select:none;-moz-user-select:none;user-select:none}div#log{font-family:Courier New,Courier;font-size:75%;width:100%;overflow:auto;clear:both;position:relative}div.group{border-color:#cccccc;border-style:solid;border-width:1px 0 1px 1px;overflow:visible}div.oldIe div.group,div.oldIe div.group *,div.oldIe *.logentry{height:1%}div.group div.groupheading span.expander{border:solid black 1px;font-family:Courier New,Courier;font-size:0.833em;background-color:#eeeeee;position:relative;top:-1px;color:black;padding:0 2px;cursor:pointer;cursor:hand;height:1%}div.group div.groupcontent{margin-left:10px;padding-bottom:2px;overflow:visible}div.group div.expanded{display:block}div.group div.collapsed{display:none}*.logentry{overflow:visible;display:none;white-space:pre}span.pre{white-space:pre}pre.unwrapped{display:inline !important}pre.unwrapped pre.pre,div.wrapped pre.pre{display:inline}div.wrapped pre.pre{white-space:normal}div.wrapped{display:none}body.searching *.logentry span.currentmatch{color:white !important;background-color:green !important}body.searching div.searchhighlight *.logentry span.searchterm{color:black;background-color:yellow}div.wrap *.logentry{white-space:normal !important;border-width:0 0 1px 0;border-color:#dddddd;border-style:dotted}div.wrap #log_wrapped,#log_unwrapped{display:block}div.wrap #log_unwrapped,#log_wrapped{display:none}div.wrap *.logentry span.pre{overflow:visible;white-space:normal}div.wrap *.logentry pre.unwrapped{display:none}div.wrap *.logentry span.wrapped{display:inline}div.searchfilter *.searchnonmatch{display:none !important}div#log *.TRACE,label#label_TRACE{color:#666666}div#log *.DEBUG,label#label_DEBUG{color:green}div#log *.INFO,label#label_INFO{color:#000099}div#log *.WARN,label#label_WARN{color:#999900}div#log *.ERROR,label#label_ERROR{color:red}div#log *.FATAL,label#label_FATAL{color:#660066}div.TRACE#log *.TRACE,div.DEBUG#log *.DEBUG,div.INFO#log *.INFO,div.WARN#log *.WARN,div.ERROR#log *.ERROR,div.FATAL#log *.FATAL{display:block}div#log div.separator{background-color:#cccccc;margin:5px 0;line-height:1px}","</style>","</head>",'<body id="body">','<div id="switchesContainer">','<div id="switches">','<div id="levels" class="toolbar">',"Filters:",'<input type="checkbox" id="switch_TRACE" onclick="applyFilters(); checkAllLevels()" checked="checked" title="Show/hide trace messages" /><label for="switch_TRACE" id="label_TRACE">trace</label>','<input type="checkbox" id="switch_DEBUG" onclick="applyFilters(); checkAllLevels()" checked="checked" title="Show/hide debug messages" /><label for="switch_DEBUG" id="label_DEBUG">debug</label>','<input type="checkbox" id="switch_INFO" onclick="applyFilters(); checkAllLevels()" checked="checked" title="Show/hide info messages" /><label for="switch_INFO" id="label_INFO">info</label>','<input type="checkbox" id="switch_WARN" onclick="applyFilters(); checkAllLevels()" checked="checked" title="Show/hide warn messages" /><label for="switch_WARN" id="label_WARN">warn</label>','<input type="checkbox" id="switch_ERROR" onclick="applyFilters(); checkAllLevels()" checked="checked" title="Show/hide error messages" /><label for="switch_ERROR" id="label_ERROR">error</label>','<input type="checkbox" id="switch_FATAL" onclick="applyFilters(); checkAllLevels()" checked="checked" title="Show/hide fatal messages" /><label for="switch_FATAL" id="label_FATAL">fatal</label>','<input type="checkbox" id="switch_ALL" onclick="toggleAllLevels(); applyFilters()" checked="checked" title="Show/hide all messages" /><label for="switch_ALL" id="label_ALL">all</label>',"</div>",'<div id="search" class="toolbar">','<label for="searchBox" class="searchboxlabel">Search:</label> <input type="text" id="searchBox" onclick="toggleSearchEnabled(true)" onkeyup="scheduleSearch()" size="20" />','<input type="button" id="searchReset" disabled="disabled" value="Reset" onclick="clearSearch()" class="button" title="Reset the search" />','<input type="checkbox" id="searchRegex" onclick="doSearch()" title="If checked, search is treated as a regular expression" /><label for="searchRegex">Regex</label>','<input type="checkbox" id="searchCaseSensitive" onclick="doSearch()" title="If checked, search is case sensitive" /><label for="searchCaseSensitive">Match case</label>','<input type="checkbox" id="searchDisable" onclick="toggleSearchEnabled()" title="Enable/disable search" /><label for="searchDisable" class="alwaysenabled">Disable</label>','<div id="searchNav">','<input type="button" id="searchNext" disabled="disabled" value="Next" onclick="searchNext()" class="button" title="Go to the next matching log entry" />','<input type="button" id="searchPrevious" disabled="disabled" value="Previous" onclick="searchPrevious()" class="button" title="Go to the previous matching log entry" />','<input type="checkbox" id="searchFilter" onclick="toggleSearchFilter()" title="If checked, non-matching log entries are filtered out" /><label for="searchFilter">Filter</label>','<input type="checkbox" id="searchHighlight" onclick="toggleSearchHighlight()" title="Highlight matched search terms" /><label for="searchHighlight" class="alwaysenabled">Highlight all</label>',"</div>","</div>",'<div id="options" class="toolbar">',"Options:",'<input type="checkbox" id="enableLogging" onclick="toggleLoggingEnabled()" checked="checked" title="Enable/disable logging" /><label for="enableLogging" id="enableLoggingLabel">Log</label>','<input type="checkbox" id="wrap" onclick="toggleWrap()" title="Enable / disable word wrap" /><label for="wrap" id="wrapLabel">Wrap</label>','<input type="checkbox" id="newestAtTop" onclick="toggleNewestAtTop()" title="If checked, causes newest messages to appear at the top" /><label for="newestAtTop" id="newestAtTopLabel">Newest at the top</label>','<input type="checkbox" id="scrollToLatest" onclick="toggleScrollToLatest()" checked="checked" title="If checked, window automatically scrolls to a new message when it is added" /><label for="scrollToLatest" id="scrollToLatestLabel">Scroll to latest</label>','<input type="button" id="clearButton" value="Clear" onclick="clearLog()" class="button" title="Clear all log messages"  />','<input type="button" id="hideButton" value="Hide" onclick="hide()" class="hidden button" title="Hide the console" />','<input type="button" id="closeButton" value="Close" onclick="closeWindow()" class="hidden button" title="Close the window" />',"</div>","</div>","</div>",'<div id="log" class="TRACE DEBUG INFO WARN ERROR FATAL"></div>','<div id="commandLine" class="toolbar">','<div id="commandLineContainer">','<input type="text" id="command" title="Enter a JavaScript command here and hit return or press \'Evaluate\'" />','<input type="button" id="evaluateButton" value="Evaluate" class="button" title="Evaluate the command" onclick="evalCommandLine()" />',"</div>","</div>","</body>","</html>",""]};var defaultCommandLineFunctions=[];ConsoleAppender=function(){};var consoleAppenderIdCounter=1;ConsoleAppender.prototype=new Appender();ConsoleAppender.prototype.create=function(inPage,container,lazyInit,initiallyMinimized,useDocumentWrite,width,height,focusConsoleWindow){var appender=this;var initialized=false;var consoleWindowCreated=false;var consoleWindowLoaded=false;var consoleClosed=false;var queuedLoggingEvents=[];var isSupported=true;var consoleAppenderId=consoleAppenderIdCounter++;initiallyMinimized=extractBooleanFromParam(initiallyMinimized,this.defaults.initiallyMinimized);lazyInit=extractBooleanFromParam(lazyInit,this.defaults.lazyInit);useDocumentWrite=extractBooleanFromParam(useDocumentWrite,this.defaults.useDocumentWrite);var newestMessageAtTop=this.defaults.newestMessageAtTop;var scrollToLatestMessage=this.defaults.scrollToLatestMessage;width=width?width:this.defaults.width;height=height?height:this.defaults.height;var maxMessages=this.defaults.maxMessages;var showCommandLine=this.defaults.showCommandLine;var commandLineObjectExpansionDepth=this.defaults.commandLineObjectExpansionDepth;var showHideButton=this.defaults.showHideButton;var showCloseButton=this.defaults.showCloseButton;var showLogEntryDeleteButtons=this.defaults.showLogEntryDeleteButtons;this.setLayout(this.defaults.layout);var init,createWindow,safeToAppend,getConsoleWindow,open;var appenderName=inPage?"InPageAppender":"PopUpAppender";var checkCanConfigure=function(configOptionName){if(consoleWindowCreated){handleError(appenderName+": configuration option '"+configOptionName+"' may not be set after the appender has been initialized");return false}return true};var consoleWindowExists=function(){return(consoleWindowLoaded&&isSupported&&!consoleClosed)};this.isNewestMessageAtTop=function(){return newestMessageAtTop};this.setNewestMessageAtTop=function(newestMessageAtTopParam){newestMessageAtTop=bool(newestMessageAtTopParam);if(consoleWindowExists()){getConsoleWindow().setNewestAtTop(newestMessageAtTop)}};this.isScrollToLatestMessage=function(){return scrollToLatestMessage};this.setScrollToLatestMessage=function(scrollToLatestMessageParam){scrollToLatestMessage=bool(scrollToLatestMessageParam);if(consoleWindowExists()){getConsoleWindow().setScrollToLatest(scrollToLatestMessage)}};this.getWidth=function(){return width};this.setWidth=function(widthParam){if(checkCanConfigure("width")){width=extractStringFromParam(widthParam,width)}};this.getHeight=function(){return height};this.setHeight=function(heightParam){if(checkCanConfigure("height")){height=extractStringFromParam(heightParam,height)}};this.getMaxMessages=function(){return maxMessages};this.setMaxMessages=function(maxMessagesParam){maxMessages=extractIntFromParam(maxMessagesParam,maxMessages);if(consoleWindowExists()){getConsoleWindow().setMaxMessages(maxMessages)}};this.isShowCommandLine=function(){return showCommandLine};this.setShowCommandLine=function(showCommandLineParam){showCommandLine=bool(showCommandLineParam);if(consoleWindowExists()){getConsoleWindow().setShowCommandLine(showCommandLine)}};this.isShowHideButton=function(){return showHideButton};this.setShowHideButton=function(showHideButtonParam){showHideButton=bool(showHideButtonParam);if(consoleWindowExists()){getConsoleWindow().setShowHideButton(showHideButton)}};this.isShowCloseButton=function(){return showCloseButton};this.setShowCloseButton=function(showCloseButtonParam){showCloseButton=bool(showCloseButtonParam);if(consoleWindowExists()){getConsoleWindow().setShowCloseButton(showCloseButton)}};this.getCommandLineObjectExpansionDepth=function(){return commandLineObjectExpansionDepth};this.setCommandLineObjectExpansionDepth=function(commandLineObjectExpansionDepthParam){commandLineObjectExpansionDepth=extractIntFromParam(commandLineObjectExpansionDepthParam,commandLineObjectExpansionDepth)};var minimized=initiallyMinimized;this.isInitiallyMinimized=function(){return initiallyMinimized};this.setInitiallyMinimized=function(initiallyMinimizedParam){if(checkCanConfigure("initiallyMinimized")){initiallyMinimized=bool(initiallyMinimizedParam);minimized=initiallyMinimized}};this.isUseDocumentWrite=function(){return useDocumentWrite};this.setUseDocumentWrite=function(useDocumentWriteParam){if(checkCanConfigure("useDocumentWrite")){useDocumentWrite=bool(useDocumentWriteParam)}};function QueuedLoggingEvent(loggingEvent,formattedMessage){this.loggingEvent=loggingEvent;this.levelName=loggingEvent.level.name;this.formattedMessage=formattedMessage}QueuedLoggingEvent.prototype.append=function(){getConsoleWindow().log(this.levelName,this.formattedMessage)};function QueuedGroup(name,initiallyExpanded){this.name=name;this.initiallyExpanded=initiallyExpanded}QueuedGroup.prototype.append=function(){getConsoleWindow().group(this.name,this.initiallyExpanded)};function QueuedGroupEnd(){}QueuedGroupEnd.prototype.append=function(){getConsoleWindow().groupEnd()};var checkAndAppend=function(){safeToAppend();if(!initialized){init()}else{if(consoleClosed&&reopenWhenClosed){createWindow()}}if(safeToAppend()){appendQueuedLoggingEvents()}};this.append=function(loggingEvent){if(isSupported){var formattedMessage=appender.getLayout().formatWithException(loggingEvent);queuedLoggingEvents.push(new QueuedLoggingEvent(loggingEvent,formattedMessage));checkAndAppend()}};this.group=function(name,initiallyExpanded){if(isSupported){queuedLoggingEvents.push(new QueuedGroup(name,initiallyExpanded));checkAndAppend()}};this.groupEnd=function(){if(isSupported){queuedLoggingEvents.push(new QueuedGroupEnd());checkAndAppend()}};var appendQueuedLoggingEvents=function(){var currentLoggingEvent;while(queuedLoggingEvents.length>0){queuedLoggingEvents.shift().append()}if(focusConsoleWindow){getConsoleWindow().focus()}};this.setAddedToLogger=function(logger){this.loggers.push(logger);if(enabled&&!lazyInit){init()}};this.clear=function(){if(consoleWindowExists()){getConsoleWindow().clearLog()}queuedLoggingEvents.length=0};this.focus=function(){if(consoleWindowExists()){getConsoleWindow().focus()}};this.focusCommandLine=function(){if(consoleWindowExists()){getConsoleWindow().focusCommandLine()}};this.focusSearch=function(){if(consoleWindowExists()){getConsoleWindow().focusSearch()}};var commandWindow=window;this.getCommandWindow=function(){return commandWindow};this.setCommandWindow=function(commandWindowParam){commandWindow=commandWindowParam};this.executeLastCommand=function(){if(consoleWindowExists()){getConsoleWindow().evalLastCommand()}};var commandLayout=new PatternLayout("%m");this.getCommandLayout=function(){return commandLayout};this.setCommandLayout=function(commandLayoutParam){commandLayout=commandLayoutParam};this.evalCommandAndAppend=function(expr){var commandReturnValue={appendResult:true,isError:false};var commandOutput="";try{var result,i;if(!commandWindow.eval&&commandWindow.execScript){commandWindow.execScript("null")}var commandLineFunctionsHash={};for(i=0,len=commandLineFunctions.length;i<len;i++){commandLineFunctionsHash[commandLineFunctions[i][0]]=commandLineFunctions[i][1]}var objectsToRestore=[];var addObjectToRestore=function(name){objectsToRestore.push([name,commandWindow[name]])};addObjectToRestore("appender");commandWindow.appender=appender;addObjectToRestore("commandReturnValue");commandWindow.commandReturnValue=commandReturnValue;addObjectToRestore("commandLineFunctionsHash");commandWindow.commandLineFunctionsHash=commandLineFunctionsHash;var addFunctionToWindow=function(name){addObjectToRestore(name);commandWindow[name]=function(){return this.commandLineFunctionsHash[name](appender,arguments,commandReturnValue)}};for(i=0,len=commandLineFunctions.length;i<len;i++){addFunctionToWindow(commandLineFunctions[i][0])}if(commandWindow===window&&commandWindow.execScript){addObjectToRestore("evalExpr");addObjectToRestore("result");window.evalExpr=expr;commandWindow.execScript("window.result=eval(window.evalExpr);");result=window.result}else{result=commandWindow.eval(expr)}commandOutput=isUndefined(result)?result:formatObjectExpansion(result,commandLineObjectExpansionDepth);for(i=0,len=objectsToRestore.length;i<len;i++){commandWindow[objectsToRestore[i][0]]=objectsToRestore[i][1]}}catch(ex){commandOutput="Error evaluating command: "+getExceptionStringRep(ex);commandReturnValue.isError=true}if(commandReturnValue.appendResult){var message=">>> "+expr;if(!isUndefined(commandOutput)){message+=newLine+commandOutput}var level=commandReturnValue.isError?Level.ERROR:Level.INFO;var loggingEvent=new LoggingEvent(null,new Date(),level,[message],null);var mainLayout=this.getLayout();this.setLayout(commandLayout);this.append(loggingEvent);this.setLayout(mainLayout)}};var commandLineFunctions=defaultCommandLineFunctions.concat([]);this.addCommandLineFunction=function(functionName,commandLineFunction){commandLineFunctions.push([functionName,commandLineFunction])};var commandHistoryCookieName="log4javascriptCommandHistory";this.storeCommandHistory=function(commandHistory){setCookie(commandHistoryCookieName,commandHistory.join(","))};var writeHtml=function(doc){var lines=getConsoleHtmlLines();doc.open();for(var i=0,len=lines.length;i<len;i++){doc.writeln(lines[i])}doc.close()};this.setEventTypes(["load","unload"]);var consoleWindowLoadHandler=function(){var win=getConsoleWindow();win.setAppender(appender);win.setNewestAtTop(newestMessageAtTop);win.setScrollToLatest(scrollToLatestMessage);win.setMaxMessages(maxMessages);win.setShowCommandLine(showCommandLine);win.setShowHideButton(showHideButton);win.setShowCloseButton(showCloseButton);win.setMainWindow(window);var storedValue=getCookie(commandHistoryCookieName);if(storedValue){win.commandHistory=storedValue.split(",");win.currentCommandIndex=win.commandHistory.length}appender.dispatchEvent("load",{win:win})};this.unload=function(){logLog.debug("unload "+this+", caller: "+this.unload.caller);if(!consoleClosed){logLog.debug("really doing unload "+this);consoleClosed=true;consoleWindowLoaded=false;consoleWindowCreated=false;appender.dispatchEvent("unload",{})}};var pollConsoleWindow=function(windowTest,interval,successCallback,errorMessage){function doPoll(){try{if(consoleClosed){clearInterval(poll)}if(windowTest(getConsoleWindow())){clearInterval(poll);successCallback()}}catch(ex){clearInterval(poll);isSupported=false;handleError(errorMessage,ex)}}var poll=setInterval(doPoll,interval)};var getConsoleUrl=function(){var documentDomainSet=(document.domain!=location.hostname);return useDocumentWrite?"":getBaseUrl()+"console.html"+(documentDomainSet?"?log4javascript_domain="+escape(document.domain):"")};if(inPage){var containerElement=null;var cssProperties=[];this.addCssProperty=function(name,value){if(checkCanConfigure("cssProperties")){cssProperties.push([name,value])}};var windowCreationStarted=false;var iframeContainerDiv;var iframeId=uniqueId+"_InPageAppender_"+consoleAppenderId;this.hide=function(){if(initialized&&consoleWindowCreated){if(consoleWindowExists()){getConsoleWindow().$("command").blur()}iframeContainerDiv.style.display="none";minimized=true}};this.show=function(){if(initialized){if(consoleWindowCreated){iframeContainerDiv.style.display="block";this.setShowCommandLine(showCommandLine);minimized=false}else{if(!windowCreationStarted){createWindow(true)}}}};this.isVisible=function(){return !minimized&&!consoleClosed};this.close=function(fromButton){if(!consoleClosed&&(!fromButton||confirm("This will permanently remove the console from the page. No more messages will be logged. Do you wish to continue?"))){iframeContainerDiv.parentNode.removeChild(iframeContainerDiv);this.unload()}};open=function(){var initErrorMessage="InPageAppender.open: unable to create console iframe";function finalInit(){try{if(!initiallyMinimized){appender.show()}consoleWindowLoadHandler();consoleWindowLoaded=true;appendQueuedLoggingEvents()}catch(ex){isSupported=false;handleError(initErrorMessage,ex)}}function writeToDocument(){try{var windowTest=function(win){return isLoaded(win)};if(useDocumentWrite){writeHtml(getConsoleWindow().document)}if(windowTest(getConsoleWindow())){finalInit()}else{pollConsoleWindow(windowTest,100,finalInit,initErrorMessage)}}catch(ex){isSupported=false;handleError(initErrorMessage,ex)}}minimized=false;iframeContainerDiv=containerElement.appendChild(document.createElement("div"));iframeContainerDiv.style.width=width;iframeContainerDiv.style.height=height;iframeContainerDiv.style.border="solid gray 1px";for(var i=0,len=cssProperties.length;i<len;i++){iframeContainerDiv.style[cssProperties[i][0]]=cssProperties[i][1]}var iframeSrc=useDocumentWrite?"":" src='"+getConsoleUrl()+"'";iframeContainerDiv.innerHTML="<iframe id='"+iframeId+"' name='"+iframeId+"' width='100%' height='100%' frameborder='0'"+iframeSrc+" scrolling='no'></iframe>";consoleClosed=false;var iframeDocumentExistsTest=function(win){try{return bool(win)&&bool(win.document)}catch(ex){return false}};if(iframeDocumentExistsTest(getConsoleWindow())){writeToDocument()}else{pollConsoleWindow(iframeDocumentExistsTest,100,writeToDocument,initErrorMessage)}consoleWindowCreated=true};createWindow=function(show){if(show||!initiallyMinimized){var pageLoadHandler=function(){if(!container){containerElement=document.createElement("div");containerElement.style.position="fixed";containerElement.style.left="0";containerElement.style.right="0";containerElement.style.bottom="0";document.body.appendChild(containerElement);appender.addCssProperty("borderWidth","1px 0 0 0");appender.addCssProperty("zIndex",1000000);open()}else{try{var el=document.getElementById(container);if(el.nodeType==1){containerElement=el}open()}catch(ex){handleError("InPageAppender.init: invalid container element '"+container+"' supplied",ex)}}};if(pageLoaded&&container&&container.appendChild){containerElement=container;open()}else{if(pageLoaded){pageLoadHandler()}else{log4javascript.addEventListener("load",pageLoadHandler)}}windowCreationStarted=true}};init=function(){createWindow();initialized=true};getConsoleWindow=function(){var iframe=window.frames[iframeId];if(iframe){return iframe}};safeToAppend=function(){if(isSupported&&!consoleClosed){if(consoleWindowCreated&&!consoleWindowLoaded&&getConsoleWindow()&&isLoaded(getConsoleWindow())){consoleWindowLoaded=true}return consoleWindowLoaded}return false}}else{var useOldPopUp=appender.defaults.useOldPopUp;var complainAboutPopUpBlocking=appender.defaults.complainAboutPopUpBlocking;var reopenWhenClosed=this.defaults.reopenWhenClosed;this.isUseOldPopUp=function(){return useOldPopUp};this.setUseOldPopUp=function(useOldPopUpParam){if(checkCanConfigure("useOldPopUp")){useOldPopUp=bool(useOldPopUpParam)}};this.isComplainAboutPopUpBlocking=function(){return complainAboutPopUpBlocking};this.setComplainAboutPopUpBlocking=function(complainAboutPopUpBlockingParam){if(checkCanConfigure("complainAboutPopUpBlocking")){complainAboutPopUpBlocking=bool(complainAboutPopUpBlockingParam)}};this.isFocusPopUp=function(){return focusConsoleWindow};this.setFocusPopUp=function(focusPopUpParam){focusConsoleWindow=bool(focusPopUpParam)};this.isReopenWhenClosed=function(){return reopenWhenClosed};this.setReopenWhenClosed=function(reopenWhenClosedParam){reopenWhenClosed=bool(reopenWhenClosedParam)};this.close=function(){logLog.debug("close "+this);try{popUp.close();this.unload()}catch(ex){}};this.hide=function(){logLog.debug("hide "+this);if(consoleWindowExists()){this.close()}};this.show=function(){logLog.debug("show "+this);if(!consoleWindowCreated){open()}};this.isVisible=function(){return safeToAppend()};var popUp;open=function(){var windowProperties="width="+width+",height="+height+",status,resizable";var frameInfo="";try{var frameEl=window.frameElement;if(frameEl){frameInfo="_"+frameEl.tagName+"_"+(frameEl.name||frameEl.id||"")}}catch(e){frameInfo="_inaccessibleParentFrame"}var windowName="PopUp_"+location.host.replace(/[^a-z0-9]/gi,"_")+"_"+consoleAppenderId+frameInfo;if(!useOldPopUp||!useDocumentWrite){windowName=windowName+"_"+uniqueId}var checkPopUpClosed=function(win){if(consoleClosed){return true}else{try{return bool(win)&&win.closed}catch(ex){}}return false};var popUpClosedCallback=function(){if(!consoleClosed){appender.unload()}};function finalInit(){getConsoleWindow().setCloseIfOpenerCloses(!useOldPopUp||!useDocumentWrite);consoleWindowLoadHandler();consoleWindowLoaded=true;appendQueuedLoggingEvents();pollConsoleWindow(checkPopUpClosed,500,popUpClosedCallback,"PopUpAppender.checkPopUpClosed: error checking pop-up window")}try{popUp=window.open(getConsoleUrl(),windowName,windowProperties);consoleClosed=false;consoleWindowCreated=true;if(popUp&&popUp.document){if(useDocumentWrite&&useOldPopUp&&isLoaded(popUp)){popUp.mainPageReloaded();finalInit()}else{if(useDocumentWrite){writeHtml(popUp.document)}var popUpLoadedTest=function(win){return bool(win)&&isLoaded(win)};if(isLoaded(popUp)){finalInit()}else{pollConsoleWindow(popUpLoadedTest,100,finalInit,"PopUpAppender.init: unable to create console window")}}}else{isSupported=false;logLog.warn("PopUpAppender.init: pop-ups blocked, please unblock to use PopUpAppender");if(complainAboutPopUpBlocking){handleError("log4javascript: pop-up windows appear to be blocked. Please unblock them to use pop-up logging.")}}}catch(ex){handleError("PopUpAppender.init: error creating pop-up",ex)}};createWindow=function(){if(!initiallyMinimized){open()}};init=function(){createWindow();initialized=true};getConsoleWindow=function(){return popUp};safeToAppend=function(){if(isSupported&&!isUndefined(popUp)&&!consoleClosed){if(popUp.closed||(consoleWindowLoaded&&isUndefined(popUp.closed))){appender.unload();logLog.debug("PopUpAppender: pop-up closed");return false}if(!consoleWindowLoaded&&isLoaded(popUp)){consoleWindowLoaded=true}}return isSupported&&consoleWindowLoaded&&!consoleClosed}}this.getConsoleWindow=getConsoleWindow};ConsoleAppender.addGlobalCommandLineFunction=function(functionName,commandLineFunction){defaultCommandLineFunctions.push([functionName,commandLineFunction])};function PopUpAppender(lazyInit,initiallyMinimized,useDocumentWrite,width,height){this.create(false,null,lazyInit,initiallyMinimized,useDocumentWrite,width,height,this.defaults.focusPopUp)}PopUpAppender.prototype=new ConsoleAppender();PopUpAppender.prototype.defaults={layout:new PatternLayout("%d{HH:mm:ss} %-5p - %m{1}%n"),initiallyMinimized:false,focusPopUp:false,lazyInit:true,useOldPopUp:true,complainAboutPopUpBlocking:true,newestMessageAtTop:false,scrollToLatestMessage:true,width:"600",height:"400",reopenWhenClosed:false,maxMessages:null,showCommandLine:true,commandLineObjectExpansionDepth:1,showHideButton:false,showCloseButton:true,showLogEntryDeleteButtons:true,useDocumentWrite:true};PopUpAppender.prototype.toString=function(){return"PopUpAppender"};log4javascript.PopUpAppender=PopUpAppender;function InPageAppender(container,lazyInit,initiallyMinimized,useDocumentWrite,width,height){this.create(true,container,lazyInit,initiallyMinimized,useDocumentWrite,width,height,false)}InPageAppender.prototype=new ConsoleAppender();InPageAppender.prototype.defaults={layout:new PatternLayout("%d{HH:mm:ss} %-5p - %m{1}%n"),initiallyMinimized:false,lazyInit:true,newestMessageAtTop:false,scrollToLatestMessage:true,width:"100%",height:"220px",maxMessages:null,showCommandLine:true,commandLineObjectExpansionDepth:1,showHideButton:false,showCloseButton:false,showLogEntryDeleteButtons:true,useDocumentWrite:true};InPageAppender.prototype.toString=function(){return"InPageAppender"};log4javascript.InPageAppender=InPageAppender;log4javascript.InlineAppender=InPageAppender})();function padWithSpaces(str,len){if(str.length<len){var spaces=[];var numberOfSpaces=Math.max(0,len-str.length);for(var i=0;i<numberOfSpaces;i++){spaces[i]=" "}str+=spaces.join("")}return str}(function(){function dir(obj){var maxLen=0;for(var p in obj){maxLen=Math.max(toStr(p).length,maxLen)}var propList=[];for(p in obj){var propNameStr="  "+padWithSpaces(toStr(p),maxLen+2);var propVal;try{propVal=splitIntoLines(toStr(obj[p])).join(padWithSpaces(newLine,maxLen+6))}catch(ex){propVal="[Error obtaining property. Details: "+getExceptionMessage(ex)+"]"}propList.push(propNameStr+propVal)}return propList.join(newLine)}var nodeTypes={ELEMENT_NODE:1,ATTRIBUTE_NODE:2,TEXT_NODE:3,CDATA_SECTION_NODE:4,ENTITY_REFERENCE_NODE:5,ENTITY_NODE:6,PROCESSING_INSTRUCTION_NODE:7,COMMENT_NODE:8,DOCUMENT_NODE:9,DOCUMENT_TYPE_NODE:10,DOCUMENT_FRAGMENT_NODE:11,NOTATION_NODE:12};var preFormattedElements=["script","pre"];var emptyElements=["br","img","hr","param","link","area","input","col","base","meta"];var indentationUnit="  ";function getXhtml(rootNode,includeRootNode,indentation,startNewLine,preformatted){includeRootNode=(typeof includeRootNode=="undefined")?true:!!includeRootNode;if(typeof indentation!="string"){indentation=""}startNewLine=!!startNewLine;preformatted=!!preformatted;var xhtml;function isWhitespace(node){return((node.nodeType==nodeTypes.TEXT_NODE)&&/^[ \t\r\n]*$/.test(node.nodeValue))}function fixAttributeValue(attrValue){return attrValue.toString().replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/"/g,"&quot;")}function getStyleAttributeValue(el){var stylePairs=el.style.cssText.split(";");var styleValue="";var isFirst=true;for(var j=0,len=stylePairs.length;j<len;j++){var nameValueBits=stylePairs[j].split(":");var props=[];if(!/^\s*$/.test(nameValueBits[0])){props.push(trim(nameValueBits[0]).toLowerCase()+":"+trim(nameValueBits[1]))}styleValue=props.join(";")}return styleValue}function getNamespace(el){if(el.prefix){return el.prefix}else{if(el.outerHTML){var regex=new RegExp("<([^:]+):"+el.tagName+"[^>]*>","i");if(regex.test(el.outerHTML)){return RegExp.$1.toLowerCase()}}}return""}var lt="<";var gt=">";if(includeRootNode&&rootNode.nodeType!=nodeTypes.DOCUMENT_FRAGMENT_NODE){switch(rootNode.nodeType){case nodeTypes.ELEMENT_NODE:var tagName=rootNode.tagName.toLowerCase();xhtml=startNewLine?newLine+indentation:"";xhtml+=lt;var prefix=getNamespace(rootNode);var hasPrefix=!!prefix;if(hasPrefix){xhtml+=prefix+":"}xhtml+=tagName;for(i=0,len=rootNode.attributes.length;i<len;i++){var currentAttr=rootNode.attributes[i];if(!currentAttr.specified||currentAttr.nodeValue===null||currentAttr.nodeName.toLowerCase()==="style"||typeof currentAttr.nodeValue!=="string"||currentAttr.nodeName.indexOf("_moz")===0){continue}xhtml+=" "+currentAttr.nodeName.toLowerCase()+'="';xhtml+=fixAttributeValue(currentAttr.nodeValue);xhtml+='"'}if(rootNode.style.cssText){var styleValue=getStyleAttributeValue(rootNode);if(styleValue!==""){xhtml+=' style="'+getStyleAttributeValue(rootNode)+'"'}}if(array_contains(emptyElements,tagName)||(hasPrefix&&!rootNode.hasChildNodes())){xhtml+="/"+gt}else{xhtml+=gt;var childStartNewLine=!(rootNode.childNodes.length===1&&rootNode.childNodes[0].nodeType===nodeTypes.TEXT_NODE);var childPreformatted=array_contains(preFormattedElements,tagName);for(var i=0,len=rootNode.childNodes.length;i<len;i++){xhtml+=getXhtml(rootNode.childNodes[i],true,indentation+indentationUnit,childStartNewLine,childPreformatted)}var endTag=lt+"/"+tagName+gt;xhtml+=childStartNewLine?newLine+indentation+endTag:endTag}return xhtml;case nodeTypes.TEXT_NODE:if(isWhitespace(rootNode)){xhtml=""}else{if(preformatted){xhtml=rootNode.nodeValue}else{var lines=splitIntoLines(trim(rootNode.nodeValue));var trimmedLines=[];for(var i=0,len=lines.length;i<len;i++){trimmedLines[i]=trim(lines[i])}xhtml=trimmedLines.join(newLine+indentation)}if(startNewLine){xhtml=newLine+indentation+xhtml}}return xhtml;case nodeTypes.CDATA_SECTION_NODE:return"<![CDATA["+rootNode.nodeValue+"]]>"+newLine;case nodeTypes.DOCUMENT_NODE:xhtml="";for(var i=0,len=rootNode.childNodes.length;i<len;i++){xhtml+=getXhtml(rootNode.childNodes[i],true,indentation)}return xhtml;default:return""}}else{xhtml="";for(var i=0,len=rootNode.childNodes.length;i<len;i++){xhtml+=getXhtml(rootNode.childNodes[i],true,indentation+indentationUnit)}return xhtml}}function createCommandLineFunctions(){ConsoleAppender.addGlobalCommandLineFunction("$",function(appender,args,returnValue){return document.getElementById(args[0])});ConsoleAppender.addGlobalCommandLineFunction("dir",function(appender,args,returnValue){var lines=[];for(var i=0,len=args.length;i<len;i++){lines[i]=dir(args[i])}return lines.join(newLine+newLine)});ConsoleAppender.addGlobalCommandLineFunction("dirxml",function(appender,args,returnValue){var lines=[];for(var i=0,len=args.length;i<len;i++){var win=appender.getCommandWindow();lines[i]=getXhtml(args[i])}return lines.join(newLine+newLine)});ConsoleAppender.addGlobalCommandLineFunction("cd",function(appender,args,returnValue){var win,message;if(args.length===0||args[0]===""){win=window;message="Command line set to run in main window"}else{if(args[0].window==args[0]){win=args[0];message="Command line set to run in frame '"+args[0].name+"'"}else{win=window.frames[args[0]];if(win){message="Command line set to run in frame '"+args[0]+"'"}else{returnValue.isError=true;message="Frame '"+args[0]+"' does not exist";win=appender.getCommandWindow()}}}appender.setCommandWindow(win);return message});ConsoleAppender.addGlobalCommandLineFunction("clear",function(appender,args,returnValue){returnValue.appendResult=false;appender.clear()});ConsoleAppender.addGlobalCommandLineFunction("keys",function(appender,args,returnValue){var keys=[];for(var k in args[0]){keys.push(k)}return keys});ConsoleAppender.addGlobalCommandLineFunction("values",function(appender,args,returnValue){var values=[];for(var k in args[0]){try{values.push(args[0][k])}catch(ex){logLog.warn("values(): Unable to obtain value for key "+k+". Details: "+getExceptionMessage(ex))}}return values});ConsoleAppender.addGlobalCommandLineFunction("expansionDepth",function(appender,args,returnValue){var expansionDepth=parseInt(args[0],10);if(isNaN(expansionDepth)||expansionDepth<0){returnValue.isError=true;return""+args[0]+" is not a valid expansion depth"}else{appender.setCommandLineObjectExpansionDepth(expansionDepth);return"Object expansion depth set to "+expansionDepth}})}function init(){createCommandLineFunctions()}init()})();log4javascript.setDocumentReady=function(){pageLoaded=true;log4javascript.dispatchEvent("load",{})};if(window.addEventListener){window.addEventListener("load",log4javascript.setDocumentReady,false)}else{if(window.attachEvent){window.attachEvent("onload",log4javascript.setDocumentReady)}else{var oldOnload=window.onload;if(typeof window.onload!="function"){window.onload=log4javascript.setDocumentReady}else{window.onload=function(evt){if(oldOnload){oldOnload(evt)}log4javascript.setDocumentReady()}}}}window.log4javascript=log4javascript;return log4javascript})();

/*! jstz - v1.0.4 - 2012-12-18 */
(function(e){var t=function(){"use strict";var e="s",n=function(e){var t=-e.getTimezoneOffset();return t!==null?t:0},r=function(e,t,n){var r=new Date;return e!==undefined&&r.setFullYear(e),r.setDate(n),r.setMonth(t),r},i=function(e){return n(r(e,0,2))},s=function(e){return n(r(e,5,2))},o=function(e){var t=e.getMonth()>7?s(e.getFullYear()):i(e.getFullYear()),r=n(e);return t-r!==0},u=function(){var t=i(),n=s(),r=i()-s();return r<0?t+",1":r>0?n+",1,"+e:t+",0"},a=function(){var e=u();return new t.TimeZone(t.olson.timezones[e])},f=function(e){var t=new Date(2010,6,15,1,0,0,0),n={"America/Denver":new Date(2011,2,13,3,0,0,0),"America/Mazatlan":new Date(2011,3,3,3,0,0,0),"America/Chicago":new Date(2011,2,13,3,0,0,0),"America/Mexico_City":new Date(2011,3,3,3,0,0,0),"America/Asuncion":new Date(2012,9,7,3,0,0,0),"America/Santiago":new Date(2012,9,3,3,0,0,0),"America/Campo_Grande":new Date(2012,9,21,5,0,0,0),"America/Montevideo":new Date(2011,9,2,3,0,0,0),"America/Sao_Paulo":new Date(2011,9,16,5,0,0,0),"America/Los_Angeles":new Date(2011,2,13,8,0,0,0),"America/Santa_Isabel":new Date(2011,3,5,8,0,0,0),"America/Havana":new Date(2012,2,10,2,0,0,0),"America/New_York":new Date(2012,2,10,7,0,0,0),"Asia/Beirut":new Date(2011,2,27,1,0,0,0),"Europe/Helsinki":new Date(2011,2,27,4,0,0,0),"Europe/Istanbul":new Date(2011,2,28,5,0,0,0),"Asia/Damascus":new Date(2011,3,1,2,0,0,0),"Asia/Jerusalem":new Date(2011,3,1,6,0,0,0),"Asia/Gaza":new Date(2009,2,28,0,30,0,0),"Africa/Cairo":new Date(2009,3,25,0,30,0,0),"Pacific/Auckland":new Date(2011,8,26,7,0,0,0),"Pacific/Fiji":new Date(2010,11,29,23,0,0,0),"America/Halifax":new Date(2011,2,13,6,0,0,0),"America/Goose_Bay":new Date(2011,2,13,2,1,0,0),"America/Miquelon":new Date(2011,2,13,5,0,0,0),"America/Godthab":new Date(2011,2,27,1,0,0,0),"Europe/Moscow":t,"Asia/Yekaterinburg":t,"Asia/Omsk":t,"Asia/Krasnoyarsk":t,"Asia/Irkutsk":t,"Asia/Yakutsk":t,"Asia/Vladivostok":t,"Asia/Kamchatka":t,"Europe/Minsk":t,"Australia/Perth":new Date(2008,10,1,1,0,0,0)};return n[e]};return{determine:a,date_is_dst:o,dst_start_for:f}}();t.TimeZone=function(e){"use strict";var n={"America/Denver":["America/Denver","America/Mazatlan"],"America/Chicago":["America/Chicago","America/Mexico_City"],"America/Santiago":["America/Santiago","America/Asuncion","America/Campo_Grande"],"America/Montevideo":["America/Montevideo","America/Sao_Paulo"],"Asia/Beirut":["Asia/Beirut","Europe/Helsinki","Europe/Istanbul","Asia/Damascus","Asia/Jerusalem","Asia/Gaza"],"Pacific/Auckland":["Pacific/Auckland","Pacific/Fiji"],"America/Los_Angeles":["America/Los_Angeles","America/Santa_Isabel"],"America/New_York":["America/Havana","America/New_York"],"America/Halifax":["America/Goose_Bay","America/Halifax"],"America/Godthab":["America/Miquelon","America/Godthab"],"Asia/Dubai":["Europe/Moscow"],"Asia/Dhaka":["Asia/Yekaterinburg"],"Asia/Jakarta":["Asia/Omsk"],"Asia/Shanghai":["Asia/Krasnoyarsk","Australia/Perth"],"Asia/Tokyo":["Asia/Irkutsk"],"Australia/Brisbane":["Asia/Yakutsk"],"Pacific/Noumea":["Asia/Vladivostok"],"Pacific/Tarawa":["Asia/Kamchatka"],"Africa/Johannesburg":["Asia/Gaza","Africa/Cairo"],"Asia/Baghdad":["Europe/Minsk"]},r=e,i=function(){var e=n[r],i=e.length,s=0,o=e[0];for(;s<i;s+=1){o=e[s];if(t.date_is_dst(t.dst_start_for(o))){r=o;return}}},s=function(){return typeof n[r]!="undefined"};return s()&&i(),{name:function(){return r}}},t.olson={},t.olson.timezones={"-720,0":"Etc/GMT+12","-660,0":"Pacific/Pago_Pago","-600,1":"America/Adak","-600,0":"Pacific/Honolulu","-570,0":"Pacific/Marquesas","-540,0":"Pacific/Gambier","-540,1":"America/Anchorage","-480,1":"America/Los_Angeles","-480,0":"Pacific/Pitcairn","-420,0":"America/Phoenix","-420,1":"America/Denver","-360,0":"America/Guatemala","-360,1":"America/Chicago","-360,1,s":"Pacific/Easter","-300,0":"America/Bogota","-300,1":"America/New_York","-270,0":"America/Caracas","-240,1":"America/Halifax","-240,0":"America/Santo_Domingo","-240,1,s":"America/Santiago","-210,1":"America/St_Johns","-180,1":"America/Godthab","-180,0":"America/Argentina/Buenos_Aires","-180,1,s":"America/Montevideo","-120,0":"Etc/GMT+2","-120,1":"Etc/GMT+2","-60,1":"Atlantic/Azores","-60,0":"Atlantic/Cape_Verde","0,0":"Etc/UTC","0,1":"Europe/London","60,1":"Europe/Berlin","60,0":"Africa/Lagos","60,1,s":"Africa/Windhoek","120,1":"Asia/Beirut","120,0":"Africa/Johannesburg","180,0":"Asia/Baghdad","180,1":"Europe/Moscow","210,1":"Asia/Tehran","240,0":"Asia/Dubai","240,1":"Asia/Baku","270,0":"Asia/Kabul","300,1":"Asia/Yekaterinburg","300,0":"Asia/Karachi","330,0":"Asia/Kolkata","345,0":"Asia/Kathmandu","360,0":"Asia/Dhaka","360,1":"Asia/Omsk","390,0":"Asia/Rangoon","420,1":"Asia/Krasnoyarsk","420,0":"Asia/Jakarta","480,0":"Asia/Shanghai","480,1":"Asia/Irkutsk","525,0":"Australia/Eucla","525,1,s":"Australia/Eucla","540,1":"Asia/Yakutsk","540,0":"Asia/Tokyo","570,0":"Australia/Darwin","570,1,s":"Australia/Adelaide","600,0":"Australia/Brisbane","600,1":"Asia/Vladivostok","600,1,s":"Australia/Sydney","630,1,s":"Australia/Lord_Howe","660,1":"Asia/Kamchatka","660,0":"Pacific/Noumea","690,0":"Pacific/Norfolk","720,1,s":"Pacific/Auckland","720,0":"Pacific/Tarawa","765,1,s":"Pacific/Chatham","780,0":"Pacific/Tongatapu","780,1,s":"Pacific/Apia","840,0":"Pacific/Kiritimati"},typeof exports!="undefined"?exports.jstz=t:e.jstz=t})(this);

var livePolling = angular.module('live-polling-V2_2');

livePolling.config(['$stateProvider', function($stateProvider) {
    $stateProvider
        .state('event.live-polling-V2_2', {
            abstract: true
        })
        .state('event.live-polling-V2_2.poll', {
            views: {
                'fullscreen@event': {
                    templateUrl: '/asset/component/live-polling/2.2/webapp/html/live-polling-poll.html',
                    controller: 'LivePollingPollController'
                }
            },
            params: {
                eventId: null,
                lumiSessionId: null
            }
        })
        .state('event.live-polling-V2_2.discussion', {
            abstract: true
        })
        .state('event.live-polling-V2_2.discussion.list', {
            views: {
                'component@event': {
                    templateUrl: '/asset/component/live-polling/2.2/webapp/html/live-polling-discussion-list.html',
                    controller: 'LivePollingDiscussionListController'
                },
                'header@event': {
                    controller: 'LivePollingDiscussionListHeaderController'
                }
            },
            params: {
                eventId: null,
                lumiSessionId: null
            }
        })
        .state('event.live-polling-V2_2.discussion.detail', {
            views: {
                'component@event': {
                    templateUrl: '/asset/component/live-polling/2.2/webapp/html/live-polling-discussion-detail.html',
                    controller: 'LivePollingDiscussionDetailController'
                },
                'header@event': {
                    controller: 'LivePollingDiscussionDetailHeaderController'
                }
            },
            params: {
                eventId: null,
                lumiSessionId: null,
                messageBoard: null
            }
        });
}]);

var livePolling = angular.module('live-polling-V2_2');

livePolling.constant('qmLumiApi', window.ReactorApi);

livePolling.constant('LUMI', {
    init: {
        jstzUnsupported: 1,
        cookieUnsupported: 2,
        socketUnsupported: 3,
        success: 4
    },
    joinStatus: {
        joining: 'Joining',
        joined: 'Joined',
        failed: 'Failed'
    },
    pollState: {
        open: 'open',
        closing: 'closing',
        closed: 'closed',
        results: 'results'
    },
    viewStatus: {
        viewing: 'Viewing',
        viewed: 'Viewed',
        failed: 'Failed'
    }
});

var livePolling = angular.module('live-polling-V2_2');
/**
 * @ngdoc controller
 * @name live-polling-V2_2.controller:LivePollingLeavingPollBaseController
 * @description
 * Base controller that handles leaving a poll that has been joined
 */
livePolling.controller('LivePollingLeavingPollBaseController',
['$scope', 'qmLumiService', 'qmLocalization', '$window', 'qmUtilities', '$state', 'qmLocalStorage',
function($scope, qmLumiService, qmLocalization, $window, qmUtilities, $state, qmLocalStorage) {
    $scope.leavingPollModalScope = {
        title: qmLocalization.getString('ALERT_LUMI_LEAVE_SESSION_TITLE'),
        content: qmLocalization.getString('ALERT_LUMI_LEAVE_SESSION_MESSAGE'),
        noLabel: qmLocalization.getString('BUTTON_CANCEL'),
        yesLabel: qmLocalization.getString('BUTTON_OK'),
        isSubmitting: false
    };

    var checkBeforeLeaving = function(event, toState, toParams) {

        if (!$scope.leavingPollModalScope.isSubmitting) {
            event.preventDefault();
            qmUtilities.openConfirmModal($scope.leavingPollModalScope).then(function() {
                $scope.leavingPollModalScope.isSubmitting = true;
                // Leave session before navigating away
                qmLumiService.leaveSession($state.params.lumiSessionId);
                qmLocalStorage.removeItem('lumiSessionId');
                $state.go(toState, toParams);
            });
        }
    };

    $window.addEventListener('beforeunload', function() {
        event.returnValue = $scope.leavingPollModalScope.content;
    });
    $scope.$on('$stateChangeStart', function(event, toState, toParams) {

        var parentId = qmLocalStorage.getItem('parentId');
        if (parentId !== null && parentId !== undefined) {
            checkBeforeLeaving(event, toState, toParams);
        } else if (!toState.name.match(/^event\.live-polling(-V[0-9]+_[0-9]+)|^event\.events(-V[0-9]+_[0-9]+)\.detail/gm)) {
            checkBeforeLeaving(event, toState, toParams);
        }
    });
}]);

/**
 * @ngdoc controller
 * @name live-polling-V2_2.controller:LivePollingPollSyncBaseController
 * @description
 * Base controller that handles syncing the UI to the current state of a live poll
 */
livePolling.controller('LivePollingPollSyncBaseController',
['$scope', 'qmLumiService', '$state', '$modalStack', 'qmUtilities',
function($scope, qmLumiService, $state, $modalStack, qmUtilities) {
    $scope.hasPollStarted = false;
    // Check if there is an active poll and navigate to it if there is
    var checkLumiPollState = function() {
        var data = qmLumiService.getPollStateData();
        if (data && data.currentPoll) {
            if ($scope.leavingPollModalScope) {
                // Prevent confirmation dialog from showing when navigating to live polling state
                $scope.leavingPollModalScope.isSubmitting = function() {
                    return true;
                };
            }
            // Make sure we get rid of any modal or page loading indicator that is there before navigating to poll
            $modalStack.dismissAll();
            qmUtilities.removePageLoad();
            $scope.hasPollStarted = true;
            $state.go('event.live-polling.poll', $state.params, {
                location: false
            });
        }
    };

    $scope.$on('lumiPollState', function() {
        checkLumiPollState();
    });
    checkLumiPollState();
}]);


var livePolling = angular.module('live-polling-V2_2');

/**
 * @ngdoc controller
 * @name live-polling-V2_2.controller:LivePollingPollController
 * @description
 * Controller for live polling view
 */
livePolling.controller('LivePollingPollController',
['$scope', '$controller', 'qmLumiService', 'qmLocalization', 'LUMI', 'qmUtilities', '$state', 'qmHistory',
function($scope, $controller, qmLumiService, qmLocalization, LUMI, qmUtilities, $state, qmHistory) {
    $controller('LivePollingLeavingPollBaseController', {
        $scope: $scope
    });

    $scope.$parent.isFullScreenView = true;
    $scope.headerTitle = qmLocalization.getString('LABEL_LUMI_POLL_OPEN_TITLE');
    $scope.pollInstructions = null;

    $scope.isPollClosing = false;
    $scope.isPollClosed = false;
    $scope.isPollEnded = false;

    var pollStateData = {};

    $scope.updateLumiPollState = function() {
        pollStateData = qmLumiService.getPollStateData();
        if (pollStateData.currentPoll) {
            $scope.isPollEnded = false;
            if (pollStateData.currentPoll.pollState === LUMI.pollState.open) {
                $scope.question = pollStateData.currentPoll.question;
                $scope.syncPollAnswers();
                if ($scope.question.maxNumSel > 1) {
                    $scope.pollInstructions = qmLocalization.getString('LABEL_LUMI_POLL_OPEN_INSTRUCTIONS', $scope.question.maxNumSel);
                }
            }

            if (pollStateData.currentPoll.pollState === LUMI.pollState.closing) {
                // Show loading screen when poll is closing
                qmUtilities.addPageLoad();
                $scope.isPollClosing = true;
            } else {
                // Remove loading screen if it is showing since we are no longer in closing state
                if ($scope.isPollClosing) {
                    qmUtilities.removePageLoad();
                    $scope.isPollClosing = false;
                }
            }

            if (pollStateData.currentPoll.pollState === LUMI.pollState.closed) {
                $scope.pollInstructions = null;
                $scope.isPollClosed = true;
                if (!$scope.question) {
                    $scope.question = pollStateData.currentPoll.question;
                    $scope.syncPollAnswers();
                }
                $scope.headerTitle = qmLocalization.getString('LABEL_LUMI_POLL_CLOSED_TITLE');
            } else {
                $scope.isPollClosed = false;
            }

            if (pollStateData.currentPoll.pollState === LUMI.pollState.results) {
                if (!$scope.question) {
                    $scope.question = pollStateData.currentPoll.question;
                }
                $scope.headerTitle = qmLocalization.getString('LABEL_LUMI_POLL_RESULTS_TITLE');
                $scope.pollResults = pollStateData.currentPoll.pollResult;
                var questionObj = pollStateData.currentPoll.question;
                // Add text to pollResults object so it can be easily displayed in view
                for (var i = 0; i < $scope.pollResults.choices.length; i++) {
                    for (var j = 0; j < questionObj.choices.length; j++) {
                        if ($scope.pollResults.choices[i].choiceId === questionObj.choices[j].id) {
                            $scope.pollResults.choices[i].text = questionObj.choices[j].text;
                            break;
                        }
                    }
                }
            } else {
                $scope.pollResults = null;
            }
        } else {
            $scope.leavingPollModalScope.isSubmitting = function() {
                return true;
            };
            $scope.isPollEnded = true;
            var previousState = qmHistory.getPrevious();
            if (previousState && previousState.state) {
                var goOptions = {};
                if (previousState.state.match('/^event\.live-polling(-V[0-9]+_[0-9]+)/')) {
                    goOptions.location = true;
                } else {
                    goOptions.reload = true;
                }
                $state.go(previousState.state, previousState.params, goOptions);
            }
        }
    };

    $scope.syncPollAnswers = function() {
        var answers = qmLumiService.getPollAnswerData();
        if (answers) {
            $scope.isPollSubmitted = answers.hasAnswered;
            if (answers.pollAnswer && answers.pollAnswer.choices) {
                $scope.numSelected = 0;
                for (var i = 0; i < $scope.question.choices.length; i++) {
                    var answerSelected = false;
                    for (var j = 0; j < answers.pollAnswer.choices.length; j++) {
                        if ($scope.question.choices[i].id === answers.pollAnswer.choices[j]) {
                            answerSelected = true;
                            $scope.question.choices[i].selected = true;
                            $scope.numSelected++;
                            break;
                        }
                    }
                    if (!answerSelected) {
                        $scope.question.choices[i].selected = false;
                    }
                }
            }
        }
    };

    $scope.numSelected = 0;
    $scope.selectChoice = function(choice) {
        if ($scope.isPollClosed || $scope.isPollSubmitted) {
            return;
        }
        // Do not let user select more than the max number of selections defined by Lumi
        if ($scope.question.maxNumSel && $scope.numSelected >= $scope.question.maxNumSel && !choice.selected) {
            return;
        }
        choice.selected = !choice.selected;
        if (choice.selected) {
            $scope.numSelected++;
        } else {
            $scope.numSelected--;
        }
        if ($scope.question.maxNumSel === 1 && $scope.numSelected === 1) {
            $scope.submitAnswer();
        }
    };

    $scope.isPollSubmitted = false;
    $scope.submitAnswer = function() {
        if (!$scope.isPollSubmitted) {
            $scope.isPollSubmitted = true;
            var answerChoices = [];
            for (var i = 0; i < $scope.question.choices.length; i++) {
                if ($scope.question.choices[i].selected) {
                    answerChoices.push($scope.question.choices[i].id);
                }
            }
            qmUtilities.addPageLoad();
            qmLumiService.sendPollAnswer({
                sessionId: pollStateData.sessionId,
                pollId: pollStateData.currentPoll.pollId,
                answerChoices: answerChoices
            }).then(function() {
                $scope.isPollSubmitted = true;
            }, function() {
                $scope.isPollSubmitted = false;
            }).finally(function() {
                qmUtilities.removePageLoad();
            });
        }
    };

    // Revoke answer if poll has been submitted, if not just clear selections
    $scope.clearAnswer = function() {
        if ($scope.isPollSubmitted) {
            qmUtilities.addPageLoad();
            qmLumiService.revokePollAnswer({
                sessionId: pollStateData.sessionId,
                pollId: pollStateData.currentPoll.pollId
            }).then(function() {
                $scope.isPollSubmitted = false;
                $scope.clearSelections();
            }).finally(function() {
                qmUtilities.removePageLoad();
            });
        } else {
            $scope.clearSelections();
        }
    };

    // Clear all user selections
    $scope.clearSelections = function() {
        if ($scope.question && $scope.question.choices) {
            for (var i = 0; i < $scope.question.choices.length; i++) {
                $scope.question.choices[i].selected = false;
                $scope.numSelected = 0;
            }
        }
    };

    $scope.$on('lumiPollState', function() {
        $scope.updateLumiPollState();
        if (!$scope.isPollEnded) {
            $scope.$digest();
        }
    });
    $scope.updateLumiPollState();
}]);

var livePolling = angular.module('live-polling-V2_2');
/**
 * @ngdoc controller
 * @name live-polling-V2_2.controller:LivePollingDiscussionListController
 * @description
 * Controller for live discussions list view
 */
livePolling.controller('LivePollingDiscussionListController',
['$scope', '$controller', 'qmLumiService', 'qmLocalization', 'qmList', '$state',
function($scope, $controller, qmLumiService, qmLocalization, qmList, $state) {
    $controller('LivePollingLeavingPollBaseController', {
        $scope: $scope
    });
    $controller('LivePollingPollSyncBaseController', {
        $scope: $scope
    });

    $scope.emptyListIcon = '/asset/component/live-polling/2.2/webapp/images/bg_lumi_discussion_boards.png';

    var liveDiscussionList = qmLumiService.getMessageBoardListData();
    if (liveDiscussionList) {
        $scope.liveDiscussionList = qmList.transformList(liveDiscussionList);
    }
    $scope.emptyText = qmLocalization.getString('LABEL_LUMI_EMPTY_DISCUSSIONS_LIST_TITLE');
    $scope.emptySubText = qmLocalization.getString('LABEL_LUMI_EMPTY_DISCUSSIONS_LIST_MESSAGE');

    $scope.goToDetail = function(messageBoard) {
        $state.params.messageBoard = messageBoard;
        $state.go('event.live-polling.discussion.detail', $state.params, {
            location: false
        });
    };

    // Update the discussion boards
    $scope.$on('lumiMessageBoardUpdateBoards', function() {
        var liveDiscussionList = qmLumiService.getMessageBoardListData();
        if (liveDiscussionList) {
            $scope.liveDiscussionList = qmList.transformList(liveDiscussionList);
        }
        $scope.$digest();
    });
}]);

/**
 * @ngdoc controller
 * @name live-polling-V2_2.controller:LivePollingDiscussionListHeaderController
 * @description
 * Controller for live discussion list header view
 */
livePolling.controller('LivePollingDiscussionListHeaderController', ['$scope', 'qmLocalization', function($scope, qmLocalization) {
    $scope.headerTitle = qmLocalization.getString('LABEL_LUMI_DISCUSSIONS_LIST_TITLE');
    $scope.headerBackState = true;
    $scope.backState = 'event.events.detail';
}]);

var livePolling = angular.module('live-polling-V2_2');
/**
 * @ngdoc controller
 * @name live-polling-V2_2.controller:LivePollingDiscussionDetailController
 * @description
 * Controller for live discussions detail view
 */
livePolling.controller('LivePollingDiscussionDetailController',
['$scope', '$controller', 'qmLumiService', 'qmList', 'qmLocalization', '$q', '$state', 'qmUtilities',
function($scope, $controller, qmLumiService, qmList, qmLocalization, $q, $state, qmUtilities) {
    $controller('LivePollingLeavingPollBaseController', {
        $scope: $scope
    });
    $controller('LivePollingPollSyncBaseController', {
        $scope: $scope
    });

    $scope.emptyListIcon = '/asset/component/live-polling/2.2/webapp/images/bg_lumi_discussion_boards.png';
    $scope.emptyText = qmLocalization.getString('LABEL_LUMI_EMPTY_DISCUSSION_BOARD_TITLE');
    $scope.emptySubText = qmLocalization.getString('LABEL_LUMI_EMPTY_DISCUSSION_BOARD_MESSAGE');

    $scope.liveDiscussionPosts = [];
    // loadMoreDiscussionPosts will get called by the list view directive infinite scrolling logic
    $scope.loadMoreDiscussionPosts = function(filter, itemOffset) {
        var defer = $q.defer();
        if (itemOffset === 0) {
            // Load the first bunch of message board posts
            var unregisterGetListener = $scope.$on('lumiMessageBoardPosts', function(event, data) {
                defer.resolve(qmList.transformList(data));
                unregisterGetListener();
            });
            qmLumiService.joinMessageBoard($state.params.messageBoard).catch(function() {
                defer.reject();
            });
        } else {
            // Load the next bunch of message board posts and list view directive will append them
            var unregisterGetMoreListener = $scope.$on('lumiMessageBoardMorePosts', function(event, data) {
                defer.resolve(qmList.transformList(data));
                unregisterGetMoreListener();
            });
            qmLumiService.fetchMoreMessageBoardPosts($state.params.messageBoard).catch(function() {
                defer.reject();
            });
        }
        return defer.promise;
    };

    // Prepend new posts to top of list or replace old post with updated post
    $scope.$on('lumiMessageBoardUpdatePosts', function(event, data) {
        var toBeUpdated = [];
        var toBePrepended = [];
        if ($scope.liveDiscussionPosts) {
            if ($scope.liveDiscussionPosts.length === 0) {
                $scope.liveDiscussionPosts = qmList.transformList(data);
            } else {
                var needEmptyListCheck = false;
                for (var i = 0; i < data.length; i++) {
                    if (data[i].isNew) {
                        toBePrepended.push(data[i]);
                    } else {
                        toBeUpdated.push(data[i]);
                        if (data[i].deleted) {
                            needEmptyListCheck = true;
                        }
                    }
                }
                qmList.prependToList($scope.liveDiscussionPosts, qmList.transformList(toBePrepended));
                qmList.updateListItems($scope.liveDiscussionPosts, qmList.transformList(toBeUpdated), 'postId');
                if (needEmptyListCheck) {
                    $scope.checkEmptyList();
                }
            }
            $scope.$digest();
        }
    });

    // Check if list is empty. This also includes if all posts are marked as deleted
    $scope.checkEmptyList = function() {
        var isEmptyList = true;
        var listData = qmList.getListData($scope.liveDiscussionPosts);
        for (var i = 0; i < listData.length; i++) {
            if (listData[i].deleted !== true) {
                isEmptyList = false;
                break;
            }
        }
        if (isEmptyList) {
            $scope.liveDiscussionPosts = [];
        }
    };

    var isLiking = false;
    $scope.likeDiscussionPost = function(postId) {
        if (!isLiking) {
            isLiking = true;
            qmLumiService.likePost($state.params.messageBoard, postId).catch(function() {
                qmUtilities.showUnknownErrorToast();
            }).finally(function() {
                isLiking = false;
            });
        }
    };

    var isUnliking = false;
    $scope.unlikeDiscussionPost = function(postId) {
        if (!isUnliking) {
            isUnliking = true;
            qmLumiService.unlikePost($state.params.messageBoard, postId).catch(function() {
                qmUtilities.showUnknownErrorToast();
            }).finally(function() {
                isUnliking = false;
            });
        }
    };

    $scope.postDiscussionMessage = function() {
        qmLumiService.postMessage($state.params.messageBoard, $scope.newDiscussionMessage).then(function() {
            qmUtilities.showSuccessToast(
                qmLocalization.getString('ALERT_LUMI_DISCUSSION_SENT_TITLE'),
                qmLocalization.getString('ALERT_LUMI_DISCUSSION_SENT_MESSAGE')
            );
        }, function() {
            qmUtilities.showUnknownErrorToast();
        });
    };

    // Use qmTextFieldWidget and hiding the preview mode for the create new discussion post view
    $scope.textFieldWidgetPreviewMode = true;
    $scope.newDiscussionMessagePlaceholder = qmLocalization.getString('LABEL_LUMI_SHARE_YOUR_THOUGHTS');
    $scope.newDiscussionMessageDoneLabel = qmLocalization.getString('BUTTON_POST');
    $scope.allowEmptyDiscussionPost = false;
    $scope.$on('createDiscussionPost', function() {
        $scope.newDiscussionMessage = '';
        $scope.textFieldWidgetPreviewMode = false;
    });

    $scope.$on('$stateChangeStart', function() {
        qmLumiService.leaveMessageBoard($state.params.messageBoard);
    });
}]);

/**
 * @ngdoc controller
 * @name live-polling-V2_2.controller:LivePollingDiscussionDetailHeaderController
 * @description
 * Controller for live discussion detail header view
 */
livePolling.controller('LivePollingDiscussionDetailHeaderController',
['$scope', 'qmLumiService', 'qmLocalization', '$rootScope', '$state', function($scope, qmLumiService, qmLocalization, $rootScope, $state) {
    $scope.headerTitle = '';
    if ($state.params.messageBoard.messageBoardName) {
        $scope.headerTitle = $state.params.messageBoard.messageBoardName;
    }
    $scope.headerBackState = true;
    if (qmLumiService.getMessageBoardListData().length === 1) {
        $scope.backState = 'event.events.detail';
    } else {
        $scope.backState = 'event.live-polling.discussion.list';
    }
    $scope.backStateOpts = {location: false};
    $scope.headerRightElements = [];
    var createPostElement = {};
    createPostElement.type = 'icon';
    createPostElement.class = 'fa fa-pencil-square-o';
    createPostElement.dataTestId = 'discussionPost';
    createPostElement.clickHandler = function() {
        $rootScope.$broadcast('createDiscussionPost');
    };
    $scope.headerRightElements.push(createPostElement);
}]);

var livePolling = angular.module('live-polling-V2_2');

/**
 * @ngdoc service
 * @name live-polling-V2_2.service:qmLumiService
 * @description
 * Manages Lumi Api
 */
livePolling.factory('qmLumiService', ['LUMI', 'qmLumiApi', '$q', '$rootScope', 'qmLogin', 'qmUtilities', 'qmEventConfig',
function(LUMI, qmLumiApi, $q, $rootScope, qmLogin, qmUtilities, qmEventConfig) {
    var isInitialized = false;
    var isProjectConnected = false;
    var isSessionConnected = false;
    var isPollConnected = false;
    var isMessageBoardsConnected = false;

    var projectProfile = null;
    var pollStateData = null;
    var pollAnswerData = null;
    var messageBoardListData = null;
    var messageBoardPostsData = null;

    var pollStateHandler = function(data) {
        // Don't broadcast if data hasn't changed, otherwise it view will refresh and have weird flicker
        if (!angular.equals(pollStateData, data)) {
            pollStateData = data;
            $rootScope.$broadcast('lumiPollState');
        }
        // Make sure we don't keep old answer data from another poll
        if (data && data.currentPoll === null) {
            pollAnswerData = null;
        }
    };

    var pollAnswerStatusHandler = function(data) {
        pollAnswerData = data;
    };

    var messageBoardListHandler = function(data) {
        messageBoardListData = data;

        // Broadcast update & also update list on message boards (discussion boards)
        $rootScope.$broadcast('lumiMessageBoardUpdateBoards', messageBoardListData);
    };

    var messageBoardPostsHandler = function(data) {
        // After joining session we get the first bunch of posts. After that we just get updates or fetch responses
        if (messageBoardPostsData) {
            if (data && data.length) {
                // Lumi says they have guaranteed incremented postIds and we should use that for ordering
                // Lumi API knows which posts have been fetched and will not send updates of posts that have not been fetched
                if (messageBoardPostsData.length && messageBoardPostsData[messageBoardPostsData.length - 1].postId > data[0].postId) {
                    $rootScope.$broadcast('lumiMessageBoardMorePosts', data);
                } else {
                    for (var i = 0; i < data.length; i++) {
                        if (messageBoardPostsData.length === 0 || messageBoardPostsData[0].postId < data[i].postId) {
                            // Update messageBoardPostsData so changes to new message are not marked as new later
                            messageBoardPostsData.unshift(data[i]);
                            data[i].isNew = true;
                        } else {
                            data[i].isNew = false;
                        }
                    }
                    // This can be new or updates to posts we have fetched
                    $rootScope.$broadcast('lumiMessageBoardUpdatePosts', data);
                }
            } else {
                $rootScope.$broadcast('lumiMessageBoardMorePosts', []);
            }
        } else {
            messageBoardPostsData = angular.copy(data);
            $rootScope.$broadcast('lumiMessageBoardPosts', data);
        }
    };

    return {
        /**
         * @ngdoc method
         * @name init
         * @methodOf live-polling-V2_2.service:qmLumiService
         * @description
         * Initialize Lumi API
         *
         */
        init: function() {
            if (qmLumiApi) {
                var initResult = qmLumiApi.init({
                    serverUrl: 'partner-htmlproxy.lumireactor.com',
                    appId: 'ec353227-faf8-4ac6-92d4-b36ab012c19f',
                    appVersion: '1.5.0',
                    secret: 'aNm4uI5pk+WyyWweK17wILoDcMwO9TrKLQWHDYItyuI=',
                    key: 'NwXQUQwXMxrYZqMY'
                });
                if (initResult === LUMI.init.success) {
                    isInitialized = true;
                }
            }
        },
        /**
         * @ngdoc method
         * @name getStatus
         * @methodOf live-polling-V2_2.service:qmLumiService
         * @description
         * Get status of lumi api service
         *
         * @returns {object} Status object
         */
        getStatus: function() {
            return {
                isInitialized: isInitialized,
                isProjectConnected: isProjectConnected,
                isSessionConnected: isSessionConnected,
                isPollConnected: isPollConnected,
                isMessageBoardsConnected: isMessageBoardsConnected
            };
        },
        /**
         * @ngdoc method
         * @name joinProject
         * @methodOf live-polling-V2_2.service:qmLumiService
         * @description
         * Join Lumi project
         *
         * @returns {promise} Promise that resolves when Lumi project has been joined
         */
        joinProject: function() {
            var livePollingConfig = qmEventConfig.getComponentConfig('live-polling');
            var projectId = livePollingConfig['@attributes'].projectId;
            var defer = $q.defer();

            if (!isInitialized) {
                this.init();
            }

            if (isInitialized) {
                if (isProjectConnected) {
                    defer.resolve();
                } else {
                    var projectJoinStatusHandler = function(status) {
                        if (status === LUMI.joinStatus.failed) {
                            isProjectConnected = false;
                            defer.reject();
                        }
                    };
                    qmLumiApi.registerEventListener('ProjectJoinStatus', projectJoinStatusHandler);

                    qmLumiApi.registerEventListener('ProjectJoin', function projectJoinHandler(data) {
                        qmLumiApi.removeEventListener('ProjectJoin', projectJoinHandler);
                        qmLumiApi.removeEventListener('ProjectJoinStatus', projectJoinStatusHandler);
                        isProjectConnected = true;

                        projectProfile = data.profile;
                        defer.resolve();
                    });
                    var thirdPartyUserId;
                    if (qmLogin.isLoggedIn()) {
                        var userInfo = qmLogin.getUserInfo();
                        thirdPartyUserId = userInfo.attendeeId;
                    } else {
                        thirdPartyUserId = qmUtilities.generateUUID();
                    }
                    qmLumiApi.Project.joinProject(projectId, thirdPartyUserId);
                }
            } else {
                defer.reject();
            }
            return defer.promise;
        },
        /**
         * @ngdoc method
         * @name leaveProject
         * @methodOf live-polling-V2_2.service:qmLumiService
         * @description
         * Leaves Lumi project
         *
         * @returns {promise} Promise that resolves when successfully leaving Lumi project
         */
        leaveProject: function() {
            var defer = $q.defer();
            if (isInitialized && isProjectConnected) {
                qmLumiApi.registerEventListener('ProjectLeave', function projectLeaveHandler() {
                    qmLumiApi.removeEventListener('ProjectLeave', projectLeaveHandler);
                    isProjectConnected = false;
                    defer.resolve();
                });
                qmLumiApi.Project.leaveProject();
            } else {
                defer.reject();
            }
            return defer.promise;
        },
        /**
         * @ngdoc method
         * @name joinSession
         * @methodOf live-polling-V2_2.service:qmLumiService
         * @description
         * Join Lumi session
         *
         * @param {string} sessionId Lumi session id
         * @param {boolean} isAnonymous Is user anonymous?
         * @returns {promise} Promise that resolves when Lumi session has been joined
         */
        joinSession: function(sessionId, isAnonymous) {
            var mainDefer = $q.defer();
            var that = this;
            // Make sure we're connected to a project first
            this.joinProject().then(function() {
                var joinSessionDefer = $q.defer();
                if (isSessionConnected) {
                    joinSessionDefer.resolve();
                } else {
                    var sessionJoinStatusHandler = function(status) {
                        if (status === LUMI.joinStatus.failed) {
                            isSessionConnected = false;
                            joinSessionDefer.reject();
                        }
                    };
                    qmLumiApi.registerEventListener('SessionJoinStatus', sessionJoinStatusHandler);

                    qmLumiApi.registerEventListener('SessionJoin', function sessionJoinHandler() {
                        qmLumiApi.removeEventListener('SessionJoin', sessionJoinHandler);
                        qmLumiApi.removeEventListener('SessionJoinStatus', sessionJoinStatusHandler);

                        isSessionConnected = true;
                        joinSessionDefer.resolve();
                    });
                    // We don't support pass code right now so send null
                    qmLumiApi.Session.joinSession(sessionId, null);
                    // No need to wait for profile to successfully save or not.
                    // If it fails it's not a big deal so no need to block success of joinSession
                    that.saveProfile(isAnonymous);
                }

                return joinSessionDefer.promise;
            }).then(function() {
                mainDefer.resolve();
            }, function() {
                mainDefer.reject();
            });

            return mainDefer.promise;
        },
        /**
         * @ngdoc method
         * @name leaveSession
         * @methodOf live-polling-V2_2.service:qmLumiService
         * @description
         * Leave Lumi session
         *
         * @param {string} sessionId Lumi session id
         * @returns {promise} Promise that resolves when successfully leaving Lumi session
         */
        leaveSession: function(sessionId) {
            var defer = $q.defer();

            if (isProjectConnected && isSessionConnected) {
                var that = this;
                qmLumiApi.registerEventListener('SessionLeave', function sessionLeaveHandler() {
                    qmLumiApi.removeEventListener('SessionLeave', sessionLeaveHandler);
                    isSessionConnected = false;

                    // Make sure we also leave polls and message boards
                    $q.all([that.leavePoll(), that.leaveMessageBoards()]).then(function() {
                        defer.resolve();
                    }, function() {
                        defer.reject();
                    });
                });
                qmLumiApi.Session.leaveSession(sessionId);
            } else {
                defer.reject();
            }
            return defer.promise;
        },
        /**
         * @ngdoc method
         * @name joinPoll
         * @methodOf live-polling-V2_2.service:qmLumiService
         * @description
         * Join Lumi live poll
         *
         * @param {string} sessionId Lumi session id
         * @param {boolean} isAnonymous Is user anonymous?
         * @returns {promise} Promise that resolves when Lumi poll has been joined
         */
        joinPoll: function(sessionId, isAnonymous) {
            var defer = $q.defer();
            if (!isPollConnected) {
                // Listen to PollState event before joining session because it gets fired right away before success callback
                qmLumiApi.registerEventListener('PollState', pollStateHandler);
                qmLumiApi.registerEventListener('PollAnswerStatus', pollAnswerStatusHandler);
                isPollConnected = true;
            }

            this.joinSession(sessionId, isAnonymous).then(function() {
                defer.resolve();
            }, function() {
                defer.reject();
            });

            return defer.promise;
        },
        /**
         * @ngdoc method
         * @name leavePoll
         * @methodOf live-polling-V2_2.service:qmLumiService
         * @description
         * Leave Lumi live poll
         *
         * @returns {promise} Promise that resolves when successfully leaving Lumi poll
         */
        leavePoll: function() {
            var defer = $q.defer();

            qmLumiApi.removeEventListener('PollState', pollStateHandler);
            qmLumiApi.removeEventListener('PollAnswerStatus', pollAnswerStatusHandler);
            isPollConnected = false;
            pollStateData = null;
            defer.resolve();

            return defer.promise;
        },
        /**
         * @ngdoc method
         * @name joinMessageBoards
         * @methodOf live-polling-V2_2.service:qmLumiService
         * @description
         * Join Lumi message boards
         *
         * @param {string} sessionId Lumi session id
         * @param {boolean} isAnonymous Is user anonymous?
         * @returns {promise} Promise that resolves when Lumi messages boards have been joined
         */
        joinMessageBoards: function(sessionId, isAnonymous) {
            var defer = $q.defer();

            if (!isMessageBoardsConnected) {
                // Listen to MessageBoardList event before joining session because it gets fired right away before success callback
                qmLumiApi.registerEventListener('MessageBoardList', messageBoardListHandler);
                isMessageBoardsConnected = true;
            }
            this.joinSession(sessionId, isAnonymous).then(function() {
                qmLumiApi.registerEventListener('SessionViewStatus', function sessionViewStatusHandler(status) {
                    if (status === LUMI.viewStatus.viewed) {
                        qmLumiApi.removeEventListener('SessionViewStatus', sessionViewStatusHandler);
                        defer.resolve();
                    } else if (status === LUMI.viewStatus.failed) {
                        qmLumiApi.removeEventListener('SessionViewStatus', sessionViewStatusHandler);
                        defer.reject();
                    }
                });
                // View session so that it triggers a MessageBoardList event
                qmLumiApi.Session.viewSession(sessionId);
            }, function() {
                defer.reject();
            });

            return defer.promise;
        },
        /**
         * @ngdoc method
         * @name leaveMessageBoards
         * @methodOf live-polling-V2_2.service:qmLumiService
         * @description
         * Leave Lumi message boards
         *
         * @returns {promise} Promise that resolves when successfully leaving Lumi message boards
         */
        leaveMessageBoards: function() {
            var defer = $q.defer();

            qmLumiApi.removeEventListener('MessageBoardList', messageBoardListHandler);
            qmLumiApi.removeEventListener('MessageBoardPosts', messageBoardPostsHandler);
            messageBoardListData = null;
            messageBoardPostsData = null;
            isMessageBoardsConnected = false;
            defer.resolve();

            return defer.promise;
        },
        /**
         * @ngdoc method
         * @name joinMessageBoard
         * @methodOf live-polling-V2_2.service:qmLumiService
         * @description
         * Join a Lumi message board
         *
         * @param {object} messageBoard Lumi message board object
         * @returns {promise} Promise that resolves when successfully joining a Lumi message board
         */
        joinMessageBoard: function(messageBoard) {
            var defer = $q.defer();
            qmLumiApi.registerEventListener('MessageBoardPosts', messageBoardPostsHandler);
            qmLumiApi.registerEventListener('MessageBoardJoinStatus', function messageBoardJoinStatusHandler(status) {
                if (status === LUMI.joinStatus.joined) {
                    qmLumiApi.removeEventListener('MessageBoardJoinStatus', messageBoardJoinStatusHandler);
                    defer.resolve();
                } else if (status === LUMI.joinStatus.failed) {
                    qmLumiApi.removeEventListener('MessageBoardJoinStatus', messageBoardJoinStatusHandler);
                    defer.reject();
                }
            });
            qmLumiApi.Discussion.joinMessageBoard(messageBoard);
            return defer.promise;
        },
        /**
         * @ngdoc method
         * @name leaveMessageBoard
         * @methodOf live-polling-V2_2.service:qmLumiService
         * @description
         * Leave a Lumi message board
         *
         * @param {object} messageBoard Lumi message board object
         * @returns {promise} Promise that resolves when successfully leaving a Lumi message board
         */
        leaveMessageBoard: function(messageBoard) {
            var defer = $q.defer();

            qmLumiApi.registerEventListener('MessageBoardLeave', function messageBoardLeaveHandler() {
                qmLumiApi.removeEventListener('MessageBoardPosts', messageBoardPostsHandler);
                qmLumiApi.removeEventListener('MessageBoardLeave', messageBoardLeaveHandler);
                defer.resolve();
                // Lumi doesn't have any leaving status so we can only succeed
            });
            qmLumiApi.Discussion.leaveMessageBoard(messageBoard);
            messageBoardPostsData = null;
            return defer.promise;
        },
        /**
         * @ngdoc method
         * @name fetchMoreMessageBoardPosts
         * @methodOf live-polling-V2_2.service:qmLumiService
         * @description
         * Get more posts in a message board
         *
         * @param {object} messageBoard Lumi message board object
         * @returns {promise} Promise that resolves when successfully fetching more message board posts
         */
        fetchMoreMessageBoardPosts: function(messageBoard) {
            var defer = $q.defer();
            qmLumiApi.Discussion.fetchMorePosts(messageBoard, function(status) {
                if (status === true) {
                    defer.resolve();
                } else {
                    defer.reject();
                }
            });
            return defer.promise;
        },
        /**
         * @ngdoc method
         * @name postMessage
         * @methodOf live-polling-V2_2.service:qmLumiService
         * @description
         * Post message in message board
         *
         * @param {object} messageBoard Lumi message board object
         * @param {object} message Message to post
         * @returns {promise} Promise that resolves when successfully posting message
         */
        postMessage: function(messageBoard, message) {
            var defer = $q.defer();
            qmLumiApi.Discussion.postMessage(messageBoard, message, function(status) {
                if (status === true) {
                    defer.resolve();
                } else {
                    defer.reject();
                }
            });
            return defer.promise;
        },
        /**
         * @ngdoc method
         * @name likePost
         * @methodOf live-polling-V2_2.service:qmLumiService
         * @description
         * Like a post in message board
         *
         * @param {object} messageBoard Lumi message board object
         * @param {object} postId Post Id to like
         * @returns {promise} Promise that resolves when successfully posting message
         */
        likePost: function(messageBoard, postId) {
            var defer = $q.defer();
            qmLumiApi.Discussion.likePost(messageBoard, postId, function(status) {
                if (status === true) {
                    defer.resolve();
                } else {
                    defer.reject();
                }
            });
            return defer.promise;
        },
        /**
         * @ngdoc method
         * @name unlikePost
         * @methodOf live-polling-V2_2.service:qmLumiService
         * @description
         * Unlike a post in message board
         *
         * @param {object} messageBoard Lumi message board object
         * @param {object} postId Post Id to unlike
         * @returns {promise} Promise that resolves when successfully posting message
         */
        unlikePost: function(messageBoard, postId) {
            var defer = $q.defer();
            qmLumiApi.Discussion.unlikePost(messageBoard, postId, function(status) {
                if (status === true) {
                    defer.resolve();
                } else {
                    defer.reject();
                }
            });
            return defer.promise;
        },
        /**
         * @ngdoc method
         * @name saveProfile
         * @methodOf live-polling-V2_2.service:qmLumiService
         * @description
         * Save Lumi profile
         *
         * @param {boolean} isAnonymous Is anonymous?
         * @returns {promise} Promise that resolves when successfully saving Lumi profile
         */
        saveProfile: function(isAnonymous) {
            var defer = $q.defer();
            var profile = {};
            profile.userId = projectProfile.userId;
            profile.properties = {};
            if (!isAnonymous && qmLogin.isLoggedIn()) {
                var userInfo = qmLogin.getUserInfo();
                // jscs:disable requireCamelCaseOrUpperCaseIdentifiers
                profile.properties.first_name = userInfo.firstName;
                profile.properties.last_name = userInfo.lastName;
                // jscs:enable requireCamelCaseOrUpperCaseIdentifiers
            } else {
                // jscs:disable requireCamelCaseOrUpperCaseIdentifiers
                profile.properties.first_name = 'Anonymous';
                profile.properties.last_name = '';
                // jscs:enable requireCamelCaseOrUpperCaseIdentifiers
            }
            qmLumiApi.Profile.saveProfile(profile, function(response) {
                if (response === true) {
                    defer.resolve();
                } else {
                    defer.reject();
                    qmUtilities.showUnknownErrorToast();
                }
            });
            return defer.promise;
        },
        /**
         * @ngdoc method
         * @name sendPollAnswer
         * @methodOf live-polling-V2_2.service:qmLumiService
         * @description
         * Send a poll answer
         *
         * @param {Object} pollAnswer Object containing poll answers
         * @returns {Promise} Promise that resolves when answer has successfully sent
         */
        sendPollAnswer: function(pollAnswer) {
            var defer = $q.defer();
            qmLumiApi.Poll.sendPollAnswer(pollAnswer, function(response) {
                if (response === true) {
                    defer.resolve();
                } else {
                    defer.reject();
                }
            });
            return defer.promise;
        },
        /**
         * @ngdoc method
         * @name revokePollAnswer
         * @methodOf live-polling-V2_2.service:qmLumiService
         * @description
         * Revoke a poll answer
         *
         * @param {Object} pollInfo Object containing poll info
         * @returns {Promise} Promise that resolves when revoke is successfully processed
         */
        revokePollAnswer: function(pollInfo) {
            var defer = $q.defer();
            qmLumiApi.Poll.revokePollAnswer(pollInfo, function(response) {
                if (response === true) {
                    defer.resolve();
                } else {
                    defer.reject();
                }
            });
            return defer.promise;
        },
        /**
         * @ngdoc method
         * @name getPollStateData
         * @methodOf live-polling-V2_2.service:qmLumiService
         * @description
         * Get poll state data
         *
         * @returns {object} Poll state data
         */
        getPollStateData: function() {
            return angular.copy(pollStateData);
        },
        /**
         * @ngdoc method
         * @name getPollAnswerStatusData
         * @methodOf live-polling-V2_2.service:qmLumiService
         * @description
         * Get poll answer data
         *
         * @returns {object} Poll answer data
         */
        getPollAnswerData: function() {
            return angular.copy(pollAnswerData);
        },
        /**
         * @ngdoc method
         * @name getMessageBoardListData
         * @methodOf live-polling-V2_2.service:qmLumiService
         * @description
         * Get list of message boards
         *
         * @returns {object} Message board list data
         */
        getMessageBoardListData: function() {
            return angular.copy(messageBoardListData);
        },
        /**
         * @ngdoc method
         * @name getMessageBoardPostsData
         * @methodOf live-polling-V2_2.service:qmLumiService
         * @description
         * Get message board posts
         *
         * @returns {object} Message board posts data
         */
        getMessageBoardPostsData: function() {
            return angular.copy(messageBoardPostsData);
        }
    };
}]);

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

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

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

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

        var idleTimeTimeout = null;

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

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

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

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

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

    var prompLogin = function() {

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

            if (qmStateChange.didCompleteEventTransition) {

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

    $scope.logout = function() {

        qmLogin.reset();

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

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

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

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

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

    $scope.refreshSession = function(password) {

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

        qmRpc.post(loginUrl, {method: 'login', params: [username, password]}).then(function(response) {
            if (response.token) {
                $scope.incorrectLogin = false;
                $scope.unknownError = false;
                qmLogin.setUserToken(response.token);
                $modalInstance.close(response.token);
                defer.resolve();
            } else {
                $scope.incorrectLogin = false;
                $scope.unknownError = true;
                defer.reject();
            }
        }, function(err) {
            if (err.error) {
                $scope.alertLoginIncorrectMessage = qmLogin.getLocalizedStringFromErrorMsg(err.error);
                $scope.incorrectLogin = true;
                $scope.unknownError = false;
                defer.reject();
            } else {
                $scope.incorrectLogin = false;
                $scope.unknownError = true;
                defer.reject();
            }
        }).finally(function() {
            $scope.isSubmitting = false;
        });

        return defer.promise;
    };

    $scope.login = function(password) {

        var defer = $q.defer();

        try {

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

            if (factory.isContainerLogin()) {

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

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

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

                }, function() {

                    defer.reject();
                });

            } else {

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

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

        return defer.promise;
    };
}]);

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

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

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

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

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

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

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

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

            return parseInt(idleTime, 10);
        },

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

        $rootScope.$on('$stateChangeSuccess', function(ev, to, toParams, from) {
            qmMessagingService.setPreviousStateName(from.name);
        });
    }]);

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

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

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

        var previousStateName = '';

        return {

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

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

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

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

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

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

                });

                return defer.promise;
            },

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

        var attendeeId = $state.params.attendeeId;

        $scope.attendeeId = attendeeId;

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

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

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

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

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

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

                return false;
            }

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

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

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

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

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

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

        $scope.cancel = function() {

            qmNavigation.goBack();
        };

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

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

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

    }]);

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

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

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

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

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

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

        $scope.cancel = function() {

            qmNavigation.goBack();
        };

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

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

            $scope.save = function() {

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

                    return false;
                }

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

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

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

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

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

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

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

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

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

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

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

        });

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    $scope.headerRightElements = [];

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

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

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

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

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

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

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

            return defer.promise;
        },

        /**
         * @ngdoc method
         * @name deleteMyExhibitor
         * @methodOf my-exhibitors-V2_0.service:qmMyExhibitorsService
         * @description
         * Delete attendee exhibitor link
         *
         * @param {string} exhibitorId Exhibitor Id
         * @returns {promise} Promise that resolves when my exhibitor has been removed
         */
        deleteMyExhibitor: function(exhibitorId) {
            var qmExhibitorsService = getExhibitorsService();
            var defer = $q.defer();
            qmExhibitorsService.deleteMyExhibitor(exhibitorId).then(function(response) {
                qmAnalytics.markEvent('MyExhibitorsDelete', exhibitorId);
                defer.resolve(response);
            }, function(err) {
                defer.reject(err);
            });

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

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

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

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

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

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

            return text;
        };

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

            return array;
        };

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

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

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

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

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

    $scope.headerRightElements = [];

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

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

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

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

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

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

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

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

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

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

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

            return defer.promise;
        },

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

                qmNavigation.goBack();

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

                defer.resolve(allowDelete);

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

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

                return setting;
            }
        };
    }]);

