<commit_before>'use strict';

// Main application
angular.module('MatchCalendar', ['ui.bootstrap', 'ngCookies', 'ngSanitize', 'btford.markdown', 'ui.router', 'ngClipboard'])

    .run(['$rootScope', '$cookieStore', 'DateTimeService', function($rootScope, $cookieStore, DateTimeService) {
        DateTimeService.resync();
        $rootScope.currentTime = function() {
            return moment().add('ms', $rootScope.offset);
        };

        $rootScope.settings = {
            time_formats: ['12h', '24h'],
            time_zones: moment.tz.names(),
            time_zone: $cookieStore.get('time_zone'),
            time_format: $cookieStore.get('time_format'),
            subreddits: $cookieStore.get('subreddits')
        };

        $rootScope.$watch('settings.time_zone', function(newValue) {
            $cookieStore.put('time_zone', newValue);
        });
        if(null == $rootScope.settings.time_zone)
            $rootScope.settings.time_zone = 'Etc/UTC';

        $rootScope.$watch('settings.time_format', function(newValue) {
            $cookieStore.put('time_format', newValue);
        });
        if(null == $rootScope.settings.time_format)
            $rootScope.settings.time_format = '24h';

        $rootScope.$watchCollection('settings.subreddits', function(newValue) {
            $cookieStore.put('subreddits', newValue);
        });
        if(null == $rootScope.settings.subreddits)
            $rootScope.settings.subreddits = ['ultrahardcore', 'ghowden'];
    }])

    //configuration
    .config(['$stateProvider', '$urlRouterProvider', function($stateProvider, $urlRouterProvider) {
        $stateProvider
            .state('list', {
                url: '/list',
                templateUrl: 'partials/list.html'
            })

            .state('generate', {
                url: '/generate',
                templateUrl: 'partials/generator.html',
                controller: 'HeaderGeneratorCtrl'
            })

            .state('settings', {
                url: '/settings',
                templateUrl: 'partials/settings.html',
                controller: 'SettingsCtrl'
            });

        $urlRouterProvider.otherwise('/list');
    }])

    //controller for the application
    .controller('AppCtrl', ['$scope', 'RedditPostsService', '$cookieStore', '$timeout', 'HtmlNotifications', function($scope, RedditPostsService, $cookieStore, $timeout, HtmlNotifications) {

        $scope.updatingPosts = false;

        $scope.requestPermissions = function() {
            HtmlNotifications.requestPermission().then(function() {
                HtmlNotifications.notify('Notifications Enabled!');
            });
        };
        $scope.currentPermission = function() {
            return HtmlNotifications.currentPermission();
        };

        $scope.posts = [];
        $scope.updatePosts = function() {
            $scope.updatingPosts = true;
            RedditPostsService.query($scope.settings.subreddits).then(function(data) {
                $scope.posts = data;
                $scope.updatingPosts = false;
                $scope.lastUpdated = $scope.currentTime();
            });
        };

        $scope.$watchCollection('settings.subreddits', $scope.updatePosts);

        (function tick() {
            $scope.current_time = $scope.currentTime();
            $timeout(tick, 1000);
         })();

        (function tick() {
            $scope.updatePosts();
            if(HtmlNotifications.currentPermission() === 'granted') {
                angular.forEach($scope.posts, function (post) {
                    if(post.opens == null) return;

                    var timeLeft = post.opens.diff($scope.current_time);
                    if(timeLeft < 1000 * 60 * 15) {
                        HtmlNotifications.notify('Game opening ' + post.opens.from($scope.currentTime()), post.title);
                    }
                });
            }
            $timeout(tick, 1000 * 60);
        })();
    }])

    .controller('SettingsCtrl', ['$scope', '$rootScope', function($scope, $rootScope) {
        $scope.addSubreddit = function(name) {
            if(name === '' || name === null || name === undefined) {
                return;
            }
            if($rootScope.settings.subreddits.indexOf(name) === -1) {
                $rootScope.settings.subreddits.push(name);
            }
        };
        $scope.removeSubreddit = function(index) {
            $rootScope.settings.subreddits.splice(index, 1);
        };
    }])

    .controller('HeaderGeneratorCtrl', ['$scope', function($scope) {
        $scope.regions = {
            'AF': 'Africa',
            'AN': 'Antartica',
            'AS': 'Asia',
            'EU': 'Europe',
            'NA': 'North America',
            'OC': 'Oceania',
            'SA': 'South America'
        };

        $scope.generated = {
            opens: '',
            starts: '',
            address: '',
            title: '',
            region: ''
        };

        $scope.$watch('opens', function(newValue) {
            $scope.generated.opens = newValue.utc().format('YYYY-MM-DDTHH:mm:ssZ');
            $scope.simpleUtcOpens = newValue.utc().format('YYYY-MM-DD HH:mm UTC');
        });
        $scope.$watch('starts', function(newValue) {
            $scope.generated.starts = newValue.utc().format('YYYY-MM-DDTHH:mm:ssZ');
            $scope.simpleUtcStarts = newValue.utc().format('YYYY-MM-DD HH:mm UTC');
        });
        $scope.$watch('address', function(newValue) {
            $scope.generated.address = newValue.replace(/\[/g, '[').replace(/\]/g, ']');
        });
        $scope.$watch('post_title', function(newValue) {
            $scope.generated.title = newValue.replace(/\[/g, '[').replace(/\]/g, ']');
        });
        $scope.$watch('region', function(newValue) {
            $scope.generated.region = newValue;
        });

        $scope.$watch('generated', function(newValue) {
            $scope.generatedLink = '[' + JSON.stringify(newValue) + '](/matchpost)';
        }, true);

        $scope.opens = $scope.currentTime();
        $scope.starts = $scope.currentTime();
        $scope.address = '192.168.0.1';
        $scope.post_title = 'Game Title';
        $scope.region = 'NA';
    }])

    //a match post model
    .factory('MatchPost', ['MarkdownLinkDataService', '$rootScope', function (MarkdownLinkDataService, $rootScope) {

        function MatchPost(id, region, title, selftext, author, opens, starts, permalink) {
            this.id = id;
            this.region = region;
            this.title = title;
            this.selftext = selftext;
            this.author = author;
            this.opens = opens;
            this.starts = starts;
            this.permalink = permalink;
        }

        /**
         * @param element the raw post element from the JSON api
         * @returns {MatchPost}
         */
        MatchPost.parseData = function (element) {
            var linkData = MarkdownLinkDataService.fetch('/matchpost', element.selftext);

            var opens, starts, title, region;

            var parsedLink = false;
            if(linkData != null) {
                try {
                    var json = JSON.parse(linkData);

                    opens = moment(json.opens, 'YYYY-MM-DDTHH:mm:ssZ');
                    starts = moment(json.starts, 'YYYY-MM-DDTHH:mm:ssZ');
                    region = json.region;
                    title = element.title;

                    parsedLink = true;
                } catch (err) {}
            }

            if(!parsedLink) {
                //fall back to old style title parsing

                //attempt to parse the date from the post title
                starts = moment.utc(/[\w]+ [\d]+ [\d]+:[\d]+/.exec(element.title), 'MMM DD HH:mm', 'en');
                opens = starts;

                //get everything after the first '- ' in the title as the actual title
                title = element.title.substring(element.title.indexOf('-') + 2);

                //set a default region to show
                region = 'Earth';
            }

            //if it's invalid (no parsable date) read as unparsed
            if(!starts.isValid()) {
                starts = null;
            } else if(starts.diff($rootScope.currentTime()) < 0) {
                //if it's in the past don't show it at all
                return null;
            }

            //if it's invalid (no parsable date) read as unparsed
            if(!opens.isValid()) {
                opens = null;
            }

            var link = 'http://reddit.com/' + element.permalink;

            return new MatchPost(element.id, region, title, element.selftext, element.author, opens, starts, link);
        };

        //Return the constructor function
        return MatchPost;
    }])

    //service for matching markdown links to specific URL path
    .factory( 'MarkdownLinkDataService', [function() {
        return {
            /**
             * Returns the raw string for the markdown link in format [data](link)
             * @param path {string} the URL that was linked to
             * @param markdown {string} the markdown
             * @returns {string} data for the link
             */
            fetch: function(path, markdown) {
                //simple regex for [data](/link) type links
                var regex = new RegExp('\\[([^\\[\\]]+)\\]\\('+path+'\\)', 'g');
                var matches = regex.exec(markdown);
                if(matches == null) {
                    return null;
                } else {
                    return matches[1];
                }
            }
        }
    }])

    //service for fetching reddit posts from the JSON api
    .factory( 'RedditPostsService', ['$http', '$q', '$filter', 'MatchPost', function( $http, $q, $filter, MatchPost ) {
        return {
            //fetch all
            query: function (subreddits, limit, sort) {
                //set defaults
                limit = limit || 100;
                sort = sort || 'new';

                var deferreds = [];
                angular.forEach(subreddits, function(subreddit) {
                    var deferred = $q.defer();

                    var parsed = [];
                    var unparsed = [];
                    //get the posts
                    $http.get('https://www.reddit.com/r/' + subreddit + '/search.json?q=flair%3AUpcoming_Match&restrict_sr=on&limit=' + limit + '&sort=' + sort).then(
                        function(data) {
                            angular.forEach(data.data.data.children, function(element) {
                                //parse the post
                                var matchPost = MatchPost.parseData(element.data);

                                if(null == matchPost) {
                                    return;
                                }

                                //if time was invalid push to the invalid stack
                                matchPost.starts == null ? unparsed.push(matchPost) : parsed.push(matchPost);
                            });
                            deferred.resolve({
                                parsed: parsed,
                                unparsed: unparsed
                            });
                        },
                        function() {
                            deferred.resolve({
                                parsed: parsed,
                                unparsed: unparsed
                            });
                        }
                    );
                    deferreds.push(deferred.promise);
                });

                var deferred = $q.defer();
                $q.all(deferreds).then(function(data) {
                    var parsed = [];
                    var unparsed = [];
                    angular.forEach(data, function(element) {
                        parsed.push.apply(parsed, element.parsed);
                        unparsed.push.apply(unparsed, element.unparsed);
                    });

                    //filter the parsed ones in time order
                    var filtered = $filter('orderBy')(parsed, function(element) {
                        return element.starts.format('X');
                    });

                    //add the unparsed matches to the end
                    filtered.push.apply(filtered, unparsed);
                    deferred.resolve(filtered);
                });

                return deferred.promise;
            }
        };
    }])

    .factory('HtmlNotifications', ['$q', function($q) {
        return {
            /**
             * @returns boolean true if notification available, false otherwise
             */
            supports: function() {
                return "Notification" in window;
            },
            currentPermission: function() {
                if(!Notification.permission) {
                    Notification.permission = 'default';
                }
                return Notification.permission;
            },
            /**
             * @returns {promise} resolves on granted, rejects on not
             */
            requestPermission: function() {
                var def = $q.defer();
                if(Notification.permission !== 'granted') {
                    //request the permission and update the permission value
                    Notification.requestPermission(function (status) {
                        if (Notification.permission !== status) {
                            Notification.permission = status;
                        }
                        status === 'granted' ? def.resolve() : def.reject();
                    });
                } else {
                    def.resolve();
                }
                return def.promise;
            },
            /**
             * @param title the title for the notification
             * @param options
             * @param body the body of the notification
             */
            notify: function(title, body, options) {
                this.requestPermission().then(function() {
                    options = options || [];
                    options.icon = options.icon || 'images/favicon.png';
                    options.body = body || '';

                    new Notification(title, options);
                });
            }
        };
    }])

    .factory('DateTimeService', ['$http', '$rootScope', function($http, $rootScope) {
        var resyncURL = 'sync.php';

        return {
            resync: function() {
                $http.get(resyncURL).then(
                    function(data) {
                        //this isn't really that accurate but within ping time so close enough
                        $rootScope.offset = data.data.time - moment().valueOf();
                    }
                );
            }
        }
    }])

    .directive('dateTimePicker', [function() {
        return {
            restrict: 'AE',
            scope: {
                minDate: '=?',
                pickedDate: '=',
                meridian: '=',
                timeZone: '='
            },
            templateUrl: 'partials/dateTimePicker.html',
            link: function($scope, $element, $attr) {
                $scope.opened = false;

                $scope.internalJSDate = $scope.pickedDate.toDate();
                $scope.internalMinDate = $scope.minDate.toDate();

                $scope.$watch('internalJSDate', function() {
                    $scope.updatePickedDate();
                });
                $scope.$watch('timeZone', function() {
                    $scope.updatePickedDate();
                });

                $scope.updatePickedDate = function() {
                    var pickedMoment = moment($scope.internalJSDate);
                    var formattedMoment = pickedMoment.format('MMM DD HH:mm');
                    $scope.pickedDate = moment.tz(formattedMoment, 'MMM DD HH:mm', $scope.timeZone);
                };

                $scope.toggle = function($event) {
                    $event.preventDefault();
                    $event.stopPropagation();
                    $scope.opened = !$scope.opened;
                }
            }
        }
    }])

    //directive with keybind="expression()" key=13
    .directive('keybind', function() {
        return function(scope, element, attrs) {
            element.bind("keydown keypress", function(event) {
                if(event.which === Number(attrs.key)) {
                    scope.$apply(function(){
                        scope.$eval(attrs.keybind);
                    });

                    event.preventDefault();
                }
            });
        };
    });<commit_msg>store favorite hosts in the global + cookie<commit_after>'use strict';

// Main application
angular.module('MatchCalendar', ['ui.bootstrap', 'ngCookies', 'ngSanitize', 'btford.markdown', 'ui.router', 'ngClipboard'])

    .run(['$rootScope', '$cookieStore', 'DateTimeService', function($rootScope, $cookieStore, DateTimeService) {
        DateTimeService.resync();
        $rootScope.currentTime = function() {
            return moment().add('ms', $rootScope.offset);
        };

        $rootScope.settings = {
            time_formats: ['12h', '24h'],
            time_zones: moment.tz.names(),
            time_zone: $cookieStore.get('time_zone'),
            time_format: $cookieStore.get('time_format'),
            subreddits: $cookieStore.get('subreddits'),
            favorite_hosts: $cookieStore.get('favorite_hosts')
        };

        $rootScope.$watchCollection('settings.favorite_hosts', function(newValue) {
            $cookieStore.put('favorite_hosts', newValue);
        });
        if(null == $rootScope.settings.favorite_hosts)
            $rootScope.settings.favorite_hosts = ['Elllzman619'];

        $rootScope.$watch('settings.time_zone', function(newValue) {
            $cookieStore.put('time_zone', newValue);
        });
        if(null == $rootScope.settings.time_zone)
            $rootScope.settings.time_zone = 'Etc/UTC';

        $rootScope.$watch('settings.time_format', function(newValue) {
            $cookieStore.put('time_format', newValue);
        });
        if(null == $rootScope.settings.time_format)
            $rootScope.settings.time_format = '24h';

        $rootScope.$watchCollection('settings.subreddits', function(newValue) {
            $cookieStore.put('subreddits', newValue);
        });
        if(null == $rootScope.settings.subreddits)
            $rootScope.settings.subreddits = ['ultrahardcore', 'ghowden'];
    }])

    //configuration
    .config(['$stateProvider', '$urlRouterProvider', function($stateProvider, $urlRouterProvider) {
        $stateProvider
            .state('list', {
                url: '/list',
                templateUrl: 'partials/list.html'
            })

            .state('generate', {
                url: '/generate',
                templateUrl: 'partials/generator.html',
                controller: 'HeaderGeneratorCtrl'
            })

            .state('settings', {
                url: '/settings',
                templateUrl: 'partials/settings.html',
                controller: 'SettingsCtrl'
            });

        $urlRouterProvider.otherwise('/list');
    }])

    //controller for the application
    .controller('AppCtrl', ['$scope', 'RedditPostsService', '$cookieStore', '$timeout', 'HtmlNotifications', function($scope, RedditPostsService, $cookieStore, $timeout, HtmlNotifications) {

        $scope.updatingPosts = false;

        $scope.requestPermissions = function() {
            HtmlNotifications.requestPermission().then(function() {
                HtmlNotifications.notify('Notifications Enabled!');
            });
        };
        $scope.currentPermission = function() {
            return HtmlNotifications.currentPermission();
        };

        $scope.posts = [];
        $scope.updatePosts = function() {
            $scope.updatingPosts = true;
            RedditPostsService.query($scope.settings.subreddits).then(function(data) {
                $scope.posts = data;
                $scope.updatingPosts = false;
                $scope.lastUpdated = $scope.currentTime();
            });
        };

        $scope.$watchCollection('settings.subreddits', $scope.updatePosts);

        (function tick() {
            $scope.current_time = $scope.currentTime();
            $timeout(tick, 1000);
         })();

        (function tick() {
            $scope.updatePosts();
            if(HtmlNotifications.currentPermission() === 'granted') {
                angular.forEach($scope.posts, function (post) {
                    if(post.opens == null) return;

                    var timeLeft = post.opens.diff($scope.current_time);
                    if(timeLeft < 1000 * 60 * 15) {
                        HtmlNotifications.notify('Game opening ' + post.opens.from($scope.currentTime()), post.title);
                    }
                });
            }
            $timeout(tick, 1000 * 60);
        })();
    }])

    .controller('SettingsCtrl', ['$scope', '$rootScope', function($scope, $rootScope) {
        $scope.addSubreddit = function(name) {
            if(name === '' || name === null || name === undefined) {
                return;
            }
            if($rootScope.settings.subreddits.indexOf(name) === -1) {
                $rootScope.settings.subreddits.push(name);
            }
        };
        $scope.removeSubreddit = function(index) {
            $rootScope.settings.subreddits.splice(index, 1);
        };
    }])

    .controller('HeaderGeneratorCtrl', ['$scope', function($scope) {
        $scope.regions = {
            'AF': 'Africa',
            'AN': 'Antartica',
            'AS': 'Asia',
            'EU': 'Europe',
            'NA': 'North America',
            'OC': 'Oceania',
            'SA': 'South America'
        };

        $scope.generated = {
            opens: '',
            starts: '',
            address: '',
            title: '',
            region: ''
        };

        $scope.$watch('opens', function(newValue) {
            $scope.generated.opens = newValue.utc().format('YYYY-MM-DDTHH:mm:ssZ');
            $scope.simpleUtcOpens = newValue.utc().format('YYYY-MM-DD HH:mm UTC');
        });
        $scope.$watch('starts', function(newValue) {
            $scope.generated.starts = newValue.utc().format('YYYY-MM-DDTHH:mm:ssZ');
            $scope.simpleUtcStarts = newValue.utc().format('YYYY-MM-DD HH:mm UTC');
        });
        $scope.$watch('address', function(newValue) {
            $scope.generated.address = newValue.replace(/\[/g, '[').replace(/\]/g, ']');
        });
        $scope.$watch('post_title', function(newValue) {
            $scope.generated.title = newValue.replace(/\[/g, '[').replace(/\]/g, ']');
        });
        $scope.$watch('region', function(newValue) {
            $scope.generated.region = newValue;
        });

        $scope.$watch('generated', function(newValue) {
            $scope.generatedLink = '[' + JSON.stringify(newValue) + '](/matchpost)';
        }, true);

        $scope.opens = $scope.currentTime();
        $scope.starts = $scope.currentTime();
        $scope.address = '192.168.0.1';
        $scope.post_title = 'Game Title';
        $scope.region = 'NA';
    }])

    //a match post model
    .factory('MatchPost', ['MarkdownLinkDataService', '$rootScope', function (MarkdownLinkDataService, $rootScope) {

        function MatchPost(id, region, title, selftext, author, opens, starts, permalink) {
            this.id = id;
            this.region = region;
            this.title = title;
            this.selftext = selftext;
            this.author = author;
            this.opens = opens;
            this.starts = starts;
            this.permalink = permalink;
        }

        /**
         * @param element the raw post element from the JSON api
         * @returns {MatchPost}
         */
        MatchPost.parseData = function (element) {
            var linkData = MarkdownLinkDataService.fetch('/matchpost', element.selftext);

            var opens, starts, title, region;

            var parsedLink = false;
            if(linkData != null) {
                try {
                    var json = JSON.parse(linkData);

                    opens = moment(json.opens, 'YYYY-MM-DDTHH:mm:ssZ');
                    starts = moment(json.starts, 'YYYY-MM-DDTHH:mm:ssZ');
                    region = json.region;
                    title = element.title;

                    parsedLink = true;
                } catch (err) {}
            }

            if(!parsedLink) {
                //fall back to old style title parsing

                //attempt to parse the date from the post title
                starts = moment.utc(/[\w]+ [\d]+ [\d]+:[\d]+/.exec(element.title), 'MMM DD HH:mm', 'en');
                opens = starts;

                //get everything after the first '- ' in the title as the actual title
                title = element.title.substring(element.title.indexOf('-') + 2);

                //set a default region to show
                region = 'Earth';
            }

            //if it's invalid (no parsable date) read as unparsed
            if(!starts.isValid()) {
                starts = null;
            } else if(starts.diff($rootScope.currentTime()) < 0) {
                //if it's in the past don't show it at all
                return null;
            }

            //if it's invalid (no parsable date) read as unparsed
            if(!opens.isValid()) {
                opens = null;
            }

            var link = 'http://reddit.com/' + element.permalink;

            return new MatchPost(element.id, region, title, element.selftext, element.author, opens, starts, link);
        };

        //Return the constructor function
        return MatchPost;
    }])

    //service for matching markdown links to specific URL path
    .factory( 'MarkdownLinkDataService', [function() {
        return {
            /**
             * Returns the raw string for the markdown link in format [data](link)
             * @param path {string} the URL that was linked to
             * @param markdown {string} the markdown
             * @returns {string} data for the link
             */
            fetch: function(path, markdown) {
                //simple regex for [data](/link) type links
                var regex = new RegExp('\\[([^\\[\\]]+)\\]\\('+path+'\\)', 'g');
                var matches = regex.exec(markdown);
                if(matches == null) {
                    return null;
                } else {
                    return matches[1];
                }
            }
        }
    }])

    //service for fetching reddit posts from the JSON api
    .factory( 'RedditPostsService', ['$http', '$q', '$filter', 'MatchPost', function( $http, $q, $filter, MatchPost ) {
        return {
            //fetch all
            query: function (subreddits, limit, sort) {
                //set defaults
                limit = limit || 100;
                sort = sort || 'new';

                var deferreds = [];
                angular.forEach(subreddits, function(subreddit) {
                    var deferred = $q.defer();

                    var parsed = [];
                    var unparsed = [];
                    //get the posts
                    $http.get('https://www.reddit.com/r/' + subreddit + '/search.json?q=flair%3AUpcoming_Match&restrict_sr=on&limit=' + limit + '&sort=' + sort).then(
                        function(data) {
                            angular.forEach(data.data.data.children, function(element) {
                                //parse the post
                                var matchPost = MatchPost.parseData(element.data);

                                if(null == matchPost) {
                                    return;
                                }

                                //if time was invalid push to the invalid stack
                                matchPost.starts == null ? unparsed.push(matchPost) : parsed.push(matchPost);
                            });
                            deferred.resolve({
                                parsed: parsed,
                                unparsed: unparsed
                            });
                        },
                        function() {
                            deferred.resolve({
                                parsed: parsed,
                                unparsed: unparsed
                            });
                        }
                    );
                    deferreds.push(deferred.promise);
                });

                var deferred = $q.defer();
                $q.all(deferreds).then(function(data) {
                    var parsed = [];
                    var unparsed = [];
                    angular.forEach(data, function(element) {
                        parsed.push.apply(parsed, element.parsed);
                        unparsed.push.apply(unparsed, element.unparsed);
                    });

                    //filter the parsed ones in time order
                    var filtered = $filter('orderBy')(parsed, function(element) {
                        return element.starts.format('X');
                    });

                    //add the unparsed matches to the end
                    filtered.push.apply(filtered, unparsed);
                    deferred.resolve(filtered);
                });

                return deferred.promise;
            }
        };
    }])

    .factory('HtmlNotifications', ['$q', function($q) {
        return {
            /**
             * @returns boolean true if notification available, false otherwise
             */
            supports: function() {
                return "Notification" in window;
            },
            currentPermission: function() {
                if(!Notification.permission) {
                    Notification.permission = 'default';
                }
                return Notification.permission;
            },
            /**
             * @returns {promise} resolves on granted, rejects on not
             */
            requestPermission: function() {
                var def = $q.defer();
                if(Notification.permission !== 'granted') {
                    //request the permission and update the permission value
                    Notification.requestPermission(function (status) {
                        if (Notification.permission !== status) {
                            Notification.permission = status;
                        }
                        status === 'granted' ? def.resolve() : def.reject();
                    });
                } else {
                    def.resolve();
                }
                return def.promise;
            },
            /**
             * @param title the title for the notification
             * @param options
             * @param body the body of the notification
             */
            notify: function(title, body, options) {
                this.requestPermission().then(function() {
                    options = options || [];
                    options.icon = options.icon || 'images/favicon.png';
                    options.body = body || '';

                    new Notification(title, options);
                });
            }
        };
    }])

    .factory('DateTimeService', ['$http', '$rootScope', function($http, $rootScope) {
        var resyncURL = 'sync.php';

        return {
            resync: function() {
                $http.get(resyncURL).then(
                    function(data) {
                        //this isn't really that accurate but within ping time so close enough
                        $rootScope.offset = data.data.time - moment().valueOf();
                    }
                );
            }
        }
    }])

    .directive('dateTimePicker', [function() {
        return {
            restrict: 'AE',
            scope: {
                minDate: '=?',
                pickedDate: '=',
                meridian: '=',
                timeZone: '='
            },
            templateUrl: 'partials/dateTimePicker.html',
            link: function($scope, $element, $attr) {
                $scope.opened = false;

                $scope.internalJSDate = $scope.pickedDate.toDate();
                $scope.internalMinDate = $scope.minDate.toDate();

                $scope.$watch('internalJSDate', function() {
                    $scope.updatePickedDate();
                });
                $scope.$watch('timeZone', function() {
                    $scope.updatePickedDate();
                });

                $scope.updatePickedDate = function() {
                    var pickedMoment = moment($scope.internalJSDate);
                    var formattedMoment = pickedMoment.format('MMM DD HH:mm');
                    $scope.pickedDate = moment.tz(formattedMoment, 'MMM DD HH:mm', $scope.timeZone);
                };

                $scope.toggle = function($event) {
                    $event.preventDefault();
                    $event.stopPropagation();
                    $scope.opened = !$scope.opened;
                }
            }
        }
    }])

    //directive with keybind="expression()" key=13
    .directive('keybind', function() {
        return function(scope, element, attrs) {
            element.bind("keydown keypress", function(event) {
                if(event.which === Number(attrs.key)) {
                    scope.$apply(function(){
                        scope.$eval(attrs.keybind);
                    });

                    event.preventDefault();
                }
            });
        };
    });<|endoftext|>


<commit_before>'use strict';

const cellStates = {
	x: 'x',
	o: 'o',
	_: '_'
};

const getDefaultMatrix = function () {
	return [
				[cellStates._, cellStates._, cellStates._],
				[cellStates._, cellStates._, cellStates._],
				[cellStates._, cellStates._, cellStates._]
			];
	};

const Box = React.createClass({
	getInitialState: function () {
		return {
			size: 3,
			player: cellStates.x,
			matrix: getDefaultMatrix()
		};
	},
	changePlayer: function () {
		const player = (this.state.player === cellStates.x)? cellStates.o : cellStates.x;
		this.setState({player: player});
	},
	changeStateOfCell: function (x, y) {
		const playerSimbol = (this.state.player == cellStates.x) ? cellStates.x : cellStates.o;
		var matrix = this.state.matrix;
		matrix[x][y] = playerSimbol;
		this.setState({matrix: matrix});
	},
	restartAll: function () {
		const matrix = getDefaultMatrix();
		const player = cellStates.x;
		this.setState({matrix: matrix, player: player});
	},
	render: function () {
		// todo: add button Layer
		const matrix = this.state.matrix;
		return (

						{matrix.map(function(row, index){
							return <Row cells={row} changeStateOfCell={this.changeStateOfCell} rowindex={index} />;
						}, this)}

				<Player changePlayer={this.changePlayer} player={this.state.player} />
				<Restart restart={this.restartAll} />

			);
	}
});

const Row = React.createClass({
	rowCellOnClick: function (x, y) {
		this.props.changeStateOfCell(x, y);
	},
	render: function () {
		const cells = this.props.cells;
		const rowIndex = this.props.rowindex;
		return (

				{cells.map(function(cell, index){
					return <Cell item={cell} rowCellOnClick={this.rowCellOnClick} rowindex={rowIndex} cellindex={index} />;
				}, this)}

);
	}
});

const Cell = React.createClass({
	handlerOnClick: function () {
		const x = this.props.rowindex;
		const y = this.props.cellindex;
		this.props.rowCellOnClick(x, y);
	},
	render: function () {
		const item = this.props.item;
		return ({item}	);
	}
});

const Player = React.createClass({
	render: function () {
		const player = this.props.player;
		return (

Player: {player}

			<button className="btn-lg btn-default" onClick={this.props.changePlayer}>Change Player</ button>
			);
	}
});

const Restart = React.createClass({
	render: function () {
		return (<button className="btn-sm btn-default" onClick={this.props.restart}>Restart</button>);
	}
});

ReactDOM.render(
	<Box />,
  	document.getElementById('xo')
);
<commit_msg>added changing state of the cell in two ways<commit_after>'use strict';

const cellStates = {
	x: 'x',
	o: 'o',
	_: '_'
};

const getDefaultMatrix = function () {
	return [
				[cellStates._, cellStates._, cellStates._],
				[cellStates._, cellStates._, cellStates._],
				[cellStates._, cellStates._, cellStates._]
			];
	};

const Box = React.createClass({
	getInitialState: function () {
		return {
			size: 3,
			player: cellStates.x,
			matrix: getDefaultMatrix()
		};
	},
	changePlayer: function () {
		const player = (this.state.player === cellStates.x)? cellStates.o : cellStates.x;
		this.setState({player: player});
	},
	changeStateOfCell: function (x, y) {
		const playerSimbol = (this.state.player == cellStates.x)
								? cellStates.x
								: cellStates.o;
		var matrix = this.state.matrix;
		console.log(matrix[x][y], playerSimbol, matrix);
		matrix[x][y] = (matrix[x][y] == cellStates._)
							? playerSimbol
							: cellStates._;
		console.log(matrix[x][y], playerSimbol, matrix);
		this.setState({matrix: matrix});
	},
	restartAll: function () {
		const matrix = getDefaultMatrix();
		const player = cellStates.x;
		this.setState({matrix: matrix, player: player});
	},
	render: function () {
		// todo: add buttons Layer
		const matrix = this.state.matrix;
		return (

						{matrix.map(function(row, index){
							return <Row cells={row} changeStateOfCell={this.changeStateOfCell} rowindex={index} />;
						}, this)}

				<Player changePlayer={this.changePlayer} player={this.state.player} />
				<Restart restart={this.restartAll} />

			);
	}
});

const Row = React.createClass({
	rowCellOnClick: function (x, y) {
		this.props.changeStateOfCell(x, y);
	},
	render: function () {
		const cells = this.props.cells;
		const rowIndex = this.props.rowindex;
		return (

				{cells.map(function(cell, index){
					return <Cell item={cell} rowCellOnClick={this.rowCellOnClick} rowindex={rowIndex} cellindex={index} />;
				}, this)}

);
	}
});

const Cell = React.createClass({
	handlerOnClick: function () {
		const x = this.props.rowindex;
		const y = this.props.cellindex;
		this.props.rowCellOnClick(x, y);
	},
	render: function () {
		const item = this.props.item;
		return ({item}	);
	}
});

const Player = React.createClass({
	render: function () {
		const player = this.props.player;
		return (

Player: {player}

			<button className="btn-lg btn-default" onClick={this.props.changePlayer}>Change Player</ button>
			);
	}
});

const Restart = React.createClass({
	render: function () {
		return (<button className="btn-sm btn-default" onClick={this.props.restart}>Restart</button>);
	}
});

ReactDOM.render(
	<Box />,
  	document.getElementById('xo')
);
<|endoftext|>


<commit_before>var map;
var chart;
var puma_layer;
var leaflet_features = {};
var chart_series = {};
var feature_color = '';
var puma_lookup = {};

$(window).resize(function () {
  var h = $(window).height(),
    offsetTop = 240; // Calculate the top offset

  $('#chart').css('height', (h - offsetTop - 40));
  $('#map').css('height', (h - offsetTop));
}).resize();

$(function () {

    // logic for iframe view
    var is_iframe = false;
    if (window.location.href.indexOf("iframe=1") >= 0) {
      is_iframe = true;
      $("#topNav").hide();
      $("#chart").css("top", "0");
      $("#map").css("top", "0");
    }

    init_chart();

});

function init_chart(){

  $.when($.get('/data/cook_puma_trend_by_quarter.csv')).then(
    function(data){
      var puma_data = $.csv.toObjects(data);
      var series_data = [];

      $.each(puma_data, function(k, v){
        puma_lookup[v['PumaID']] = v;
        var data = [];
        for (var year = 2000; year < 2015; year++) {
          for (var quarter = 1; quarter < 5; quarter++) {
            data.push(parseFloat(v[ year + 'Q' + quarter ]));
          }
        }

        chart_series[v['PumaID']] = k;
        series_data.push({name: v['PumaID'], data: data, lineWidth: 2});

      });

      init_map();
      init_table();

      // initialize chart
      chart = new Highcharts.Chart({
        chart: {
            renderTo: 'chart'
        },
        title: {
            text: null,
            x: -20 //center
        },
        credits: { enabled: false },
        xAxis: { type: 'datetime' },
        yAxis: {
            title: {
                text: 'Price Change Since 2000 (%)'
            }
        },
        tooltip: {
          crosshairs: true,
          formatter: function() {
            var val = this.y;
            if (this.y >= 0)
              val = "+" + this.y;
            var s = "" + puma_lookup[this.series.name].Name + "
" + Highcharts.dateFormat("%B %Y", this.x) + "
Price change since 2000: " + val + "%";

            return s;
          }
        },
        legend: {
          enabled: false
        },
        plotOptions: {
          series: {
            marker: {
              radius: 0,
              states: {
                hover: {
                  enabled: true,
                  radius: 5
                }
              }
            },
            pointInterval: (3 * 30.4 * 24 * 3600 * 1000),
            pointStart: Date.UTC(2000, 2, 15),
            shadow: false,
            states: {
               hover: {
                  lineWidth: 10
               }
            },
            events: {
              mouseOver: function () {
                chart.series[this.index].group.toFront();
                map._layers[leaflet_features[this.name]].fireEvent('mouseover');
                map._layers[leaflet_features[this.name]].setStyle({fillColor: this.color});
              },
              mouseOut: function () {
                map._layers[leaflet_features[this.name]].fireEvent('mouseout');
              }
            }
          }
        },
        series: series_data
      });
    });
}

function init_map() {

    map = L.map('map', { scrollWheelZoom: false }).setView([41.79998325207397, -87.87277221679688], 9);

    L.tileLayer('https://{s}.tiles.mapbox.com/v3/datamade.hn83a654/{z}/{x}/{y}.png', {
        maxZoom: 18,
        attribution: 'Map data © OpenStreetMap contributors, CC-BY-SA',
        id: 'datamade.hn83a654'
    }).addTo(map);

    // control that shows state info on hover
    var info = L.control();

    info.onAdd = function (map) {
      this._div = L.DomUtil.create('div', 'info');
      this.update();
      return this._div;
    };

    info.update = function (props) {
      this._div.innerHTML = (props ?
        '' + puma_lookup[props.PUMACE10].Name + ''
        : 'Hover over an area');
    };

    info.addTo(map);

    function puma_style(feature){
        var style = {
            "color": "white",
            "fillColor": "#0855CD",
            "opacity": 1,
            "weight": 1,
            "fillOpacity": 0.5,
        }
        return style;
    }

    function resetHighlight(e) {
      var layer = e.target;
      puma_layer.resetStyle(layer);
      if (layer.feature.properties.PUMACE10 == "03525")
        layer.setStyle({fillColor: '#ccc'});
      else
        chart.series[chart_series[layer.feature.properties.PUMACE10]].setState();
      info.update();
    }

    function highlightFeature(e) {
      var layer = e.target;
      var color = '#ccc';
      if (chart.series[chart_series[layer.feature.properties.PUMACE10]] != undefined) {
        color = chart.series[chart_series[layer.feature.properties.PUMACE10]].color;
        chart.series[chart_series[layer.feature.properties.PUMACE10]].setState('hover');
        chart.series[chart_series[layer.feature.properties.PUMACE10]].group.toFront();
      }

      layer.setStyle({
        weight: 5,
        color: '#fff',
        dashArray: '',
        fillOpacity: 0.7,
        fillColor: color
      });

      if (!L.Browser.ie && !L.Browser.opera) {
        layer.bringToFront();
      }

      info.update(layer.feature.properties);
    }

    function onEachFeature(feature, layer) {
      layer.on({
        mouseover: highlightFeature,
        mouseout: resetHighlight
      });
    }

    $.when($.getJSON('data/pumas.geojson')).then(
      function(pumas){
        puma_layer = L.geoJson(pumas, {
            style: puma_style,
            onEachFeature: onEachFeature
        }).addTo(map);

        puma_layer.eachLayer(function (layer) {
          if (layer.feature.properties.PUMACE10 == "03525")
            layer.setStyle({fillColor: '#ccc'});
          leaflet_features[layer.feature.properties.PUMACE10] = layer._leaflet_id;
        });
    });
}

function init_table() {
  $.each(puma_lookup, function(k,v){
    var row = "\

\
        " + v.Name + "	\
        " + v['Change Since 2000'] + "%	\
        " + v['Change Peak to Current'] + "%	\
        " + v['Change Bottom to Current'] + "%	\

";

    $('#indicators_table tbody').append(row);

  });

  $('#indicators_table').dataTable( {
    "aaSorting": [ [1,'desc'] ],
    "aoColumns": [
        null,
        null,
        null,
        null
    ],
    "bInfo": false,
    "bPaginate": false,
    "bFilter": false
} );
}

function table_style(number) {
  if (number <= 0 )
    return 'danger';
  else if (number > 0)
    return 'success';
}<commit_msg>formatting for y axis<commit_after>var map;
var chart;
var puma_layer;
var leaflet_features = {};
var chart_series = {};
var feature_color = '';
var puma_lookup = {};

$(window).resize(function () {
  var h = $(window).height(),
    offsetTop = 240; // Calculate the top offset

  $('#chart').css('height', (h - offsetTop - 40));
  $('#map').css('height', (h - offsetTop));
}).resize();

$(function () {

    // logic for iframe view
    var is_iframe = false;
    if (window.location.href.indexOf("iframe=1") >= 0) {
      is_iframe = true;
      $("#topNav").hide();
      $("#chart").css("top", "0");
      $("#map").css("top", "0");
    }

    init_chart();

});

function init_chart(){

  $.when($.get('/data/cook_puma_trend_by_quarter.csv')).then(
    function(data){
      var puma_data = $.csv.toObjects(data);
      var series_data = [];

      $.each(puma_data, function(k, v){
        puma_lookup[v['PumaID']] = v;
        var data = [];
        for (var year = 2000; year < 2015; year++) {
          for (var quarter = 1; quarter < 5; quarter++) {
            data.push(parseFloat(v[ year + 'Q' + quarter ]));
          }
        }

        chart_series[v['PumaID']] = k;
        series_data.push({name: v['PumaID'], data: data, lineWidth: 2});

      });

      init_map();
      init_table();

      // initialize chart
      chart = new Highcharts.Chart({
        chart: {
            renderTo: 'chart'
        },
        title: {
            text: null,
            x: -20 //center
        },
        credits: { enabled: false },
        xAxis: { type: 'datetime' },
        yAxis: {
            title: {
                text: 'Price Change Since 2000'
            },
            labels: {
                formatter: function() {
                    return this.value + ' %';
                }
            },
        },
        tooltip: {
          crosshairs: true,
          formatter: function() {
            var val = this.y;
            if (this.y >= 0)
              val = "+" + this.y;
            var s = "" + puma_lookup[this.series.name].Name + "
" + Highcharts.dateFormat("%B %Y", this.x) + "
Price change since 2000: " + val + "%";

            return s;
          }
        },
        legend: {
          enabled: false
        },
        plotOptions: {
          series: {
            marker: {
              radius: 0,
              states: {
                hover: {
                  enabled: true,
                  radius: 5
                }
              }
            },
            pointInterval: (3 * 30.4 * 24 * 3600 * 1000),
            pointStart: Date.UTC(2000, 2, 15),
            shadow: false,
            states: {
               hover: {
                  lineWidth: 10
               }
            },
            events: {
              mouseOver: function () {
                chart.series[this.index].group.toFront();
                map._layers[leaflet_features[this.name]].fireEvent('mouseover');
                map._layers[leaflet_features[this.name]].setStyle({fillColor: this.color});
              },
              mouseOut: function () {
                map._layers[leaflet_features[this.name]].fireEvent('mouseout');
              }
            }
          }
        },
        series: series_data
      });
    });
}

function init_map() {

    map = L.map('map', { scrollWheelZoom: false }).setView([41.79998325207397, -87.87277221679688], 9);

    L.tileLayer('https://{s}.tiles.mapbox.com/v3/datamade.hn83a654/{z}/{x}/{y}.png', {
        maxZoom: 18,
        attribution: 'Map data © OpenStreetMap contributors, CC-BY-SA',
        id: 'datamade.hn83a654'
    }).addTo(map);

    // control that shows state info on hover
    var info = L.control();

    info.onAdd = function (map) {
      this._div = L.DomUtil.create('div', 'info');
      this.update();
      return this._div;
    };

    info.update = function (props) {
      this._div.innerHTML = (props ?
        '' + puma_lookup[props.PUMACE10].Name + ''
        : 'Hover over an area');
    };

    info.addTo(map);

    function puma_style(feature){
        var style = {
            "color": "white",
            "fillColor": "#0855CD",
            "opacity": 1,
            "weight": 1,
            "fillOpacity": 0.5,
        }
        return style;
    }

    function resetHighlight(e) {
      var layer = e.target;
      puma_layer.resetStyle(layer);
      if (layer.feature.properties.PUMACE10 == "03525")
        layer.setStyle({fillColor: '#ccc'});
      else
        chart.series[chart_series[layer.feature.properties.PUMACE10]].setState();
      info.update();
    }

    function highlightFeature(e) {
      var layer = e.target;
      var color = '#ccc';
      if (chart.series[chart_series[layer.feature.properties.PUMACE10]] != undefined) {
        color = chart.series[chart_series[layer.feature.properties.PUMACE10]].color;
        chart.series[chart_series[layer.feature.properties.PUMACE10]].setState('hover');
        chart.series[chart_series[layer.feature.properties.PUMACE10]].group.toFront();
      }

      layer.setStyle({
        weight: 5,
        color: '#fff',
        dashArray: '',
        fillOpacity: 0.7,
        fillColor: color
      });

      if (!L.Browser.ie && !L.Browser.opera) {
        layer.bringToFront();
      }

      info.update(layer.feature.properties);
    }

    function onEachFeature(feature, layer) {
      layer.on({
        mouseover: highlightFeature,
        mouseout: resetHighlight
      });
    }

    $.when($.getJSON('data/pumas.geojson')).then(
      function(pumas){
        puma_layer = L.geoJson(pumas, {
            style: puma_style,
            onEachFeature: onEachFeature
        }).addTo(map);

        puma_layer.eachLayer(function (layer) {
          if (layer.feature.properties.PUMACE10 == "03525")
            layer.setStyle({fillColor: '#ccc'});
          leaflet_features[layer.feature.properties.PUMACE10] = layer._leaflet_id;
        });
    });
}

function init_table() {
  $.each(puma_lookup, function(k,v){
    var row = "\

\
        " + v.Name + "	\
        " + v['Change Since 2000'] + "%	\
        " + v['Change Peak to Current'] + "%	\
        " + v['Change Bottom to Current'] + "%	\

";

    $('#indicators_table tbody').append(row);

  });

  $('#indicators_table').dataTable( {
    "aaSorting": [ [1,'desc'] ],
    "aoColumns": [
        null,
        null,
        null,
        null
    ],
    "bInfo": false,
    "bPaginate": false,
    "bFilter": false
} );
}

function table_style(number) {
  if (number <= 0 )
    return 'danger';
  else if (number > 0)
    return 'success';
}<|endoftext|>


<commit_before>/* ****** MODEL ******** */

var neighborhoodSpots = [
		{
			title: "Saggio",
			location: {lat: 40.851473, lng: -73.939588},
			type: "Restaurant"
		},
    	{
    		title: "Mambi Steakhouse",
    		location: {lat: 40.84772, lng: -73.938251},
    		type: "Restaurant"
    	},
    	{
    		title: "Planet Fitness",
    		location: {lat: 40.847326, lng: -73.937638},
    		type: "Gym"
    	},
    	{
    		title: "Le Cheile",
    		location: {lat: 40.851495, lng: -73.939988},
    		type: "Restaurant"
    	},
    	{
    		title: "Elite Cleaners",
    		location: {lat: 40.843097, lng: -73.939438},
    		type: "Dry Cleaners"
    	},
    	{
    		title: "Columbia Wine Co",
    		location: {lat: 40.842125, lng: -73.939272},
    		type: "Liquor Store"
    	}
	]

// ****** VIEWMODEL ******** //

var ViewModel = function() {
	var self = this;

	this.places = ko.observableArray([]);

	neighborhoodSpots.forEach(function(locationItem) {
		self.places.push(new listView(locationItem));
	});

	this.selectedType = ko.observable();
	this.clearFilter = function() {
		this.selectedType(null)
	}
}

ko.applyBindings(new ViewModel());

//****** VIEW ******** //

var listView = function(data) {
	this.title = ko.observable(data.title);
	this.location = ko.observable(data.location);
	this.type = ko.observable(data.type);
}

// ******** MAP ******** //

var map;

// Create empty arry to push markers into.
var markers = [];

// Get that map going in the map div!
// Thanks to Google Maps API for the super helpful documentation, course, and instructions!
var initMap = function() {
	map = new google.maps.Map(document.getElementById('map'), {
		center: {lat: 40.843436, lng: -73.934794},
		zoom: 14
	});

	var mapInfoWindow = new google.maps.InfoWindow();

	var defaultIcon = makeMarkerIcon('ff2d00');

	var highlightedIcon = makeMarkerIcon('631504');

	// loop through neighborhoodSpots array to make markers.
	for (var i = 0; i < neighborhoodSpots.length; i++) {
		var title = neighborhoodSpots[i].title;
		var position = neighborhoodSpots[i].location;

		var marker = new google.maps.Marker({
			map: map,
			position: position,
			title: title,
			icon: defaultIcon,
			id: i
		});

	// Push markers to markers[] array.
	markers.push(marker);

	//Open info window on click.
	marker.addListener("click", function() {
		fillInfoWindow(this, mapInfoWindow);
	});

	// Change marker color when mouse hovers over marker.
	marker.addListener("mouseover", function() {
		this.setIcon(highlightedIcon);
	});

	// Use default color when not hovering over marker.
	marker.addListener("mouseout", function() {
		this.setIcon(defaultIcon);
	});
	}

function fillInfoWindow(marker, infowindow) {
	// Check if marker's window is already open.
	if (infowindow.marker != marker) {
		// Clear infowindow
		infowindow.setContent('');
		infowindow.marker = marker;
		// Open  infowinow
		infowindow.open(map, marker)

		// Close infowindow on click
		infowindow.addListener('closeclick', function() {
			infowindow.marker(null);
		});

		infowindow.setContent("
" + marker.title + "
")

	}
}

		// This function takes in a color, and then creates a new marker
       // icon of that color. The icon will be 21px wide by 34px high, have an
      // origin of 0, 0 and be anchored at 10, 34. Again, thank you Google API for the help.
      function makeMarkerIcon(markerColor) {
        var markerImage = new google.maps.MarkerImage(
          "https://chart.googleapis.com/chart?chst=d_map_spin&chld=1.15|0|" + markerColor +
          "|40|_|%E2%80%A2",
          new google.maps.Size(21, 34),
          new google.maps.Point(0, 0),
          new google.maps.Point(10, 34),
          new google.maps.Size(21,34));
        return markerImage;
      }

};

/*function TicketsViewModel() {
	this.tickets = [
	{name: "Economy", price: 199},
	{name: "Business", price: 449},
	{name: "First Class", price: 11199}
	];
	this.chosenTicket = ko.observable();
	this.resetTicket = function() {
		this.chosenTicket(null)
	}
}
ko.applyBindings(new TicketsViewModel());*/<commit_msg>move listview above viewmodel<commit_after>/* ****** MODEL ******** */

var neighborhoodSpots = [
		{
			title: "Saggio",
			location: {lat: 40.851473, lng: -73.939588},
			type: "Restaurant"
		},
    	{
    		title: "Mambi Steakhouse",
    		location: {lat: 40.84772, lng: -73.938251},
    		type: "Restaurant"
    	},
    	{
    		title: "Planet Fitness",
    		location: {lat: 40.847326, lng: -73.937638},
    		type: "Gym"
    	},
    	{
    		title: "Le Cheile",
    		location: {lat: 40.851495, lng: -73.939988},
    		type: "Restaurant"
    	},
    	{
    		title: "Elite Cleaners",
    		location: {lat: 40.843097, lng: -73.939438},
    		type: "Dry Cleaners"
    	},
    	{
    		title: "Columbia Wine Co",
    		location: {lat: 40.842125, lng: -73.939272},
    		type: "Liquor Store"
    	}
	]

	//****** VIEW ******** //

var listView = function(data) {
	this.title = ko.observable(data.title);
	this.location = ko.observable(data.location);
	this.type = ko.observable(data.type);
}

// ****** VIEWMODEL ******** //

var ViewModel = function() {
	var self = this;

	this.places = ko.observableArray([]);

	neighborhoodSpots.forEach(function(locationItem) {
		self.places.push(new listView(locationItem));
	});

	this.selectedType = ko.observable();
	this.clearFilter = function() {
		this.selectedType(null)
	}
}

ko.applyBindings(new ViewModel());

// ******** MAP ******** //

var map;

// Create empty arry to push markers into.
var markers = [];

// Get that map going in the map div!
// Thanks to Google Maps API for the super helpful documentation, course, and instructions!
var initMap = function() {
	map = new google.maps.Map(document.getElementById('map'), {
		center: {lat: 40.843436, lng: -73.934794},
		zoom: 14
	});

	var mapInfoWindow = new google.maps.InfoWindow();

	var defaultIcon = makeMarkerIcon('ff2d00');

	var highlightedIcon = makeMarkerIcon('631504');

	// loop through neighborhoodSpots array to make markers.
	for (var i = 0; i < neighborhoodSpots.length; i++) {
		var title = neighborhoodSpots[i].title;
		var position = neighborhoodSpots[i].location;

		var marker = new google.maps.Marker({
			map: map,
			position: position,
			title: title,
			icon: defaultIcon,
			id: i
		});

	// Push markers to markers[] array.
	markers.push(marker);

	//Open info window on click.
	marker.addListener("click", function() {
		fillInfoWindow(this, mapInfoWindow);
	});

	// Change marker color when mouse hovers over marker.
	marker.addListener("mouseover", function() {
		this.setIcon(highlightedIcon);
	});

	// Use default color when not hovering over marker.
	marker.addListener("mouseout", function() {
		this.setIcon(defaultIcon);
	});
	}

function fillInfoWindow(marker, infowindow) {
	// Check if marker's window is already open.
	if (infowindow.marker != marker) {
		// Clear infowindow
		infowindow.setContent('');
		infowindow.marker = marker;
		// Open  infowinow
		infowindow.open(map, marker)

		// Close infowindow on click
		infowindow.addListener('closeclick', function() {
			infowindow.marker(null);
		});

		infowindow.setContent("
" + marker.title + "
")

	}
}

		// This function takes in a color, and then creates a new marker
       // icon of that color. The icon will be 21px wide by 34px high, have an
      // origin of 0, 0 and be anchored at 10, 34. Again, thank you Google API for the help.
      function makeMarkerIcon(markerColor) {
        var markerImage = new google.maps.MarkerImage(
          "https://chart.googleapis.com/chart?chst=d_map_spin&chld=1.15|0|" + markerColor +
          "|40|_|%E2%80%A2",
          new google.maps.Size(21, 34),
          new google.maps.Point(0, 0),
          new google.maps.Point(10, 34),
          new google.maps.Size(21,34));
        return markerImage;
      }

};

/*function TicketsViewModel() {
	this.tickets = [
	{name: "Economy", price: 199},
	{name: "Business", price: 449},
	{name: "First Class", price: 11199}
	];
	this.chosenTicket = ko.observable();
	this.resetTicket = function() {
		this.chosenTicket(null)
	}
}
ko.applyBindings(new TicketsViewModel());*/<|endoftext|>


<commit_before>(function(){
    //initiate the client
    var oh = Ohmage("/app", "campaign-manager")

    //refresh the page when the hash changes
    window.onhashchange = function(){
        window.location.reload();
    }

    var class_names = {};

    //debugging
    window.oh = oh;

    //global data table
    var table;

    //attach global callbacks
    oh.callback("done", function(x, status, req){
        //for debugging only
        //console.log(x);
    })

    //global error handler. In ohmage 200 means unauthenticated
    oh.callback("error", function(msg, code, req){
        (code == 200) ? window.location.replace("/#login") : message("Error! " + msg);
    });

    //prevent timeout
    oh.keepalive();

    //get data
    oh.user.info().done(function(userdata){
        var username = Object.keys(userdata)[0];
        var user_is_admin = userdata[username].permissions.is_admin;
        class_names = userdata[username].classes;
        $.each( Object.keys(class_names).sort(), function( i, class_urn ) {
            $("#class_select").append($("<option />").text(class_names[class_urn]).val(class_urn));
        });
        updateProgress(10)
        oh.campaign.readall().done(function(data){
            updateProgress(15)
            $("#progressdiv").removeClass("hidden");
            var urns = Object.keys(data);
            var progress = 0;
            var total = 0;
            var requests = $.map(urns.sort(), function(urn, i){
                data[urn].urn = urn;
                var roles = data[urn]["user_roles"];

                //if(roles.length == 0) return;

                var count = -1;
                total++;

                var tr = $("
").appendTo("#campaigntablebody").data("campaigndata", data[urn]);
                var td1 = $("").appendTo(tr).text(data[urn].name);
                var td2 = $("").appendTo(tr).text(data[urn].creation_timestamp);

                $("").appendTo(tr).text(data[urn].author_list);

                var td3 = $("").appendTo(tr).text(data[urn].running_state);
                var td4 = $("").appendTo(tr);
                var td5 = $("").addClass("buttontd").appendTo(tr);
                var td6 = $("").appendTo(tr).text("" + data[urn].classes)

                var btn = $("
").addClass("btn-group").append('\
                    <button type="button" class="btn btn-default btn-sm" data-toggle="dropdown"> \
                    </button>').appendTo(td5);

                var ul = $("
").addClass("dropdown-menu").attr("role", "menu").appendTo(btn);

                var a2 = $("").appendTo($("
").appendTo(ul)).append(' Edit Campaign').attr("href", '../surveytool/#' + urn).click(function(e){
                    if(count < 0){
                        e.preventDefault();
                        message("Loading campaign info, please be patient.", "info")
                    } else if(!user_is_admin && $.inArray("author", roles) < 0 && $.inArray("supervisor", roles) < 0) {
                        e.preventDefault()
                        message("You do not have permission to modify campaign " + urn + ".")
                    } else if(count > 0){
                        e.preventDefault();
                        message("Campaign " + urn + " has existing responses and can therefore not be modified.")
                    }
                });

                var a4 = $("").appendTo($("
").appendTo(ul)).append(' Download XML').attr("href", "#").click(function(e){
                    e.preventDefault();
                    $("#hiddenurn").val(urn)
                    $("#hiddenform").submit()
                });

                ul.append($("
").addClass('divider'))

                var responselink = $("").appendTo($("
").appendTo(ul)).append(' Responses').attr("href", '../responsetool/#' + urn).click(function(e){
                    if(count < 0){
                        e.preventDefault();
                        message("Loading campaign info, please be patient.", "info")
                    } else if(count === 0){
                        e.preventDefault();
                        message("Campaign " + urn + " has no existing responses.")
                    }
                });

                var a3 = $("").appendTo($("
").appendTo(ul)).append(' Dashboard').attr("href", '../dashboard/#' + urn).click(function(e){
                    if(count < 0){
                        e.preventDefault();
                        message("Loading campaign info, please be patient.", "info")
                    } else if(count === 0){
                        e.preventDefault();
                        message("Campaign " + urn + " has no existing responses. Nothing to visualize.")
                    }
                });

                var a7 = $("").appendTo($("
").appendTo(ul)).append(' PlotApp').attr("href", '../plotapp/#' + urn).click(function(e){
                    if(count < 0){
                        e.preventDefault();
                        message("Loading campaign info, please be patient.", "info")
                    } else if(count === 0){
                        e.preventDefault();
                        message("Campaign " + urn + " has no existing responses. Nothing to plot.")
                    }
                });

                var a5 = $("").appendTo($("
").appendTo(ul)).append(' Export Data')
                    .attr("href", "../../app/survey_response/read?campaign_urn=" + urn + "&privacy_state=shared&client=manager&user_list=urn:ohmage:special:all&prompt_id_list=urn:ohmage:special:all&output_format=csv&sort_oder=timestamp&column_list=urn:ohmage:user:id,urn:ohmage:context:timestamp,urn:ohmage:prompt:response,urn:ohmage:context:location:latitude,urn:ohmage:context:location:longitude&suppress_metadata=true")

                ul.append($("
").addClass('divider'));

                var a6 = $("").appendTo($("
").appendTo(ul)).append(' Monitor').attr("href", "../monitor/#" + urn);

                var a1 = $("").appendTo($("
").appendTo(ul)).append(' Settings').attr("href", "#").click(function(e){
                    e.preventDefault();
                    populateModal();
                });

                //maps the url hash to a default app
                var appmap = {
                    settings : a1,
                    edit : a2,
                    dashboard : a3,
                    download : a4,
                    export : a5,
                    monitor: a6,
                    plotapp : a7,
                    responses : responselink
                }

                //add a default action
                var hashval = window.location.hash.replace(/^[#]/, "");
                var link = appmap[hashval] || responselink;
                var defaultbtn = $('<button type="button" class="btn btn-default btn-sm" />').html(link.html()).click(function(e){
                    link[0].click();
                }).prependTo(btn);
                $("#subtitle").text(hashval)

                function populateModal(){
                    oh.campaign.readall({
                        campaign_urn_list : urn,
                        output_format:"long"
                    }).done(function(x){
                        var longdata = x[urn];
                        $("#campaign_name").val(longdata.name);
                        $("#campaign_description").val(longdata.description);
                        $("#campaign_urn").val(urn);

                        //$("#campaign_running")[0].checked = (data["running_state"] == "running");
                        //$("#campaign_privacy")[0].checked = (data["privacy_state"] == "shared");

                        $("#campaign_running").bootstrapSwitch({size: "small", onColor: "success", offColor: "danger", onText:"running", offText:"stopped"})
                        $("#campaign_privacy").bootstrapSwitch({size: "small", onColor: "success", offColor: "danger", onText:"enabled", offText:"disabled"})
                        $("#campaign_privacy").bootstrapSwitch("state", longdata["privacy_state"] == "shared");
                        $("#campaign_running").bootstrapSwitch("state", longdata["running_state"] == "running");

                        $("#campaign_class option").each(function(i){
                            $(this).prop("selected", $.inArray($(this).attr("value"), longdata.classes) > -1);
                        });
                        $("#campaign_class").trigger("chosen:updated");

                        if(count){
                            $(".xml-upload-form").hide();
                        }

                        $('#myModal').modal("show").on("shown.bs.modal", function(){
                            $("#campaign_class").chosen({search_contains:true, no_results_text: "Class not found."});
                            $(".xml-upload-form").fileinput();
                        });

                        var xmldata;
                        $("input.xml-file-input").on("change", function(){
                            var f = this.files[0];
                            if(f){
                                var r = new FileReader();
                                r.onload = function(e) {
                                    xmldata = r.result;
                                }
                                r.readAsText(f);
                            } else {
                                xmldata = null;
                            }
                        })

                        $("#campaign_delete_button").unbind("click").click(function(e){
                            e.preventDefault();

                            // test for existing responses
                            if(count && !confirm("This campaign has " + count + " responses. Are you sure?")){
                                return;
                            }

                            var btn = $(this).attr("disabled", "disabled");
                            oh.campaign.delete({campaign_urn:urn}).done(function(){
                                $('#myModal').modal("hide");
                                tr.hide("slow", function(){
                                    alert("Campaign " + urn + " deleted!")
                                });
                            }).always(function(){
                                btn.removeAttr("disabled");
                            });
                        });

                        $("#campaign_save_button").unbind("click").click(function(e){
                            e.preventDefault();
                            var btn = $(this).attr("disabled", "disabled");
                            var running_state = $("#campaign_running")[0].checked ? "running" : "stopped";
                            var privacy_state = $("#campaign_privacy")[0].checked ? "shared" : "private";
                            var args = {
                                campaign_urn : urn,
                                running_state : running_state,
                                privacy_state : privacy_state,
                                description : $("#campaign_description").val(),
                                class_list_remove : longdata.classes,
                                class_list_add : $("#campaign_class").val()
                            }

                            //update xml only if selected
                            if(xmldata){
                                args.xml = fixxml(xmldata, longdata.name ,urn);
                            }

                            oh.campaign.update(args).done(function(){
                                td3.text(running_state);
                                $('#myModal').modal("hide")
                            }).always(function(){
                                btn.removeAttr("disabled");
                            });
                        });
                    });
                }

                return oh.survey.count(urn).done(function(counts){
                    if(!Object.keys(counts).length){
                        //no existing responses found
                        count = 0;
                    } else {
                        count = $.map(counts, function(val, key) {
                            return val[0].count;
                        }).reduce(function(previousValue, currentValue) {
                            return previousValue + currentValue;
                        });
                        var sharedcount = counts.shared ? counts.shared[0].count : 0;
                    }
                    td4.text(count);
                }).always(function(){
                    updateProgress((progress++/total) * 75 + 25);
                });
            });

            //data tables widget
            function initTable(){
                table = $('#campaigntable').DataTable( {
                    "dom" : '<"pull-right"l><"pull-left"f>tip',
                    "lengthMenu": [[25, 50, 100, -1], [25, 50, 100, "All"]],
                    "aoColumnDefs": [
                       { 'bSortable': false, 'aTargets' : [ 5 ] },
                       { 'bSearchable': false, 'aTargets': [ 4, 5 ] },
                       { 'bVisible' : false, 'aTargets' : [ 6 ] }
                    ]
                });

                /* Custom filtering by date range */
                $.fn.dataTable.ext.search.push(
                    function( settings, data, dataIndex ) {
                        var time = Date.parse(data[1].replace(" ", "T"));
                        if(!time) return true;

                        var min = Date.parse($("#mindate").val());
                        if (min && time < min) return false;

                        var maxtxt = $("#maxdate").val();
                        var max = Date.parse(maxtxt + "T23:55");
                        if (maxtxt && max && time > max) return false;

                        return true;
                    }
                );

                /* Custom filtering by class */
                $.fn.dataTable.ext.search.push(
                    function( settings, data, dataIndex ) {
                        var selected_class = $("#class_select").val();
                        if(!selected_class) return true;
                        var classes = data[6].split(",");
                        return (classes.indexOf(selected_class) >= 0)
                    }
                );

                $('.datepicker').text("").datepicker({
                    format: 'yyyy-mm-dd',
                    autoclose: true,
                    clearBtn: true
                }).change( function() {
                    table.draw();
                });

                $("#class_select").change(function(){
                    table.draw();
                })
            }

            //init temporary datatable
            initTable()

            //reinit final datatable after counts have been updated
            $.when.apply($, requests).always(function() {
                $("#progressdiv").addClass("hidden");
                $('#campaigntable').dataTable().fnDestroy();
                initTable();
            });

            //resetters
            $("#myModal").on("hidden.bs.modal", function(){
                $(".xml-upload-form").show();
                //$(".fileinput").fileinput("clear");
            })

            //expand function
            $('#campaigntable').on('click', "tbody td:not('.buttontd')", function () {
                var tr = $(this).parent()
                var row = table.row(tr);
                if(tr.attr("role") != "row") return;

                if ( row.child.isShown() ) {
                    // This row is already open - close it
                    row.child.hide();
                    tr.removeClass('shown');
                } else {
                    // Open this row
                    row.child( makerow(row.data(), tr.data("campaigndata"))).show();
                    tr.addClass('shown');
                }
            });
        });

        //get users
        oh.user.read().done(function(x){
            $.each(Object.keys(x).sort(), function( i, name ) {
                //$("#campaign_author").append($("<option />").text(name));
            });
        });

        //get classes
        oh.class.read().done(function(x){
            $.each(Object.keys(x).sort(), function( i, urn ) {
                $("#campaign_class").append($("<option />").text(urn).attr("value", urn));
            });
        });
    });

    updateProgress = _.throttle(function(pct){
        $(".progress-bar").css("width", + pct + "%");
    }, 300);

    function message(msg, type){
        // type must be one of success, info, warning, danger
        type = type || "danger"
        $("#errordiv").empty().append('
×' + msg + '
');
        $('html, body').animate({
           scrollTop: 100
        }, 200);
    }

    //XML parser for mixed case tag names (HTML only supports lowercase tags)
    var parse = (function(parser, jQuery){
        return function(str) {
            return jQuery(parser.parseFromString(str, "text/xml").documentElement)
        }
    })(new DOMParser(), jQuery)

    function fixxml(input, name, urn){
        var xml = $.parseXML(input);
        var campaign = $("campaign", xml);
        campaign.children("campaignName").remove();
        campaign.children("campaignUrn").remove();
        campaign.prepend(parse("<campaignName/>").text(name))
        campaign.prepend(parse("<campaignUrn/>").text(urn))
        return (new XMLSerializer()).serializeToString(xml);
    }

    function makerow(rowdata, campaign) {
        var row = $('
').addClass('row').addClass("response-row");
        $("
").text(campaign.urn).appendTo(row);
        var p = $("
").appendTo(row).html("" + (campaign.description || "No description.") + "")
        var p2 = $("
").appendTo(row).html("" + (campaign.classes || "No Classes.") + "")
        return row;
    }

})();
<commit_msg>hide author column as per HT<commit_after>(function(){
    //initiate the client
    var oh = Ohmage("/app", "campaign-manager")

    //refresh the page when the hash changes
    window.onhashchange = function(){
        window.location.reload();
    }

    var class_names = {};

    //debugging
    window.oh = oh;

    //global data table
    var table;

    //attach global callbacks
    oh.callback("done", function(x, status, req){
        //for debugging only
        //console.log(x);
    })

    //global error handler. In ohmage 200 means unauthenticated
    oh.callback("error", function(msg, code, req){
        (code == 200) ? window.location.replace("/#login") : message("Error! " + msg);
    });

    //prevent timeout
    oh.keepalive();

    //get data
    oh.user.info().done(function(userdata){
        var username = Object.keys(userdata)[0];
        var user_is_admin = userdata[username].permissions.is_admin;
        class_names = userdata[username].classes;
        $.each( Object.keys(class_names).sort(), function( i, class_urn ) {
            $("#class_select").append($("<option />").text(class_names[class_urn]).val(class_urn));
        });
        updateProgress(10)
        oh.campaign.readall().done(function(data){
            updateProgress(15)
            $("#progressdiv").removeClass("hidden");
            var urns = Object.keys(data);
            var progress = 0;
            var total = 0;
            var requests = $.map(urns.sort(), function(urn, i){
                data[urn].urn = urn;
                var roles = data[urn]["user_roles"];

                //if(roles.length == 0) return;

                var count = -1;
                total++;

                var tr = $("
").appendTo("#campaigntablebody").data("campaigndata", data[urn]);
                var td1 = $("").appendTo(tr).text(data[urn].name);
                var td2 = $("").appendTo(tr).text(data[urn].creation_timestamp);

                $("").appendTo(tr).text(data[urn].author_list.join(", "));

                var td3 = $("").appendTo(tr).text(data[urn].running_state);
                var td4 = $("").appendTo(tr);
                var td5 = $("").addClass("buttontd").appendTo(tr);
                var td6 = $("").appendTo(tr).text("" + data[urn].classes)

                var btn = $("
").addClass("btn-group").append('\
                    <button type="button" class="btn btn-default btn-sm" data-toggle="dropdown"> \
                    </button>').appendTo(td5);

                var ul = $("
").addClass("dropdown-menu").attr("role", "menu").appendTo(btn);

                var a2 = $("").appendTo($("
").appendTo(ul)).append(' Edit Campaign').attr("href", '../surveytool/#' + urn).click(function(e){
                    if(count < 0){
                        e.preventDefault();
                        message("Loading campaign info, please be patient.", "info")
                    } else if(!user_is_admin && $.inArray("author", roles) < 0 && $.inArray("supervisor", roles) < 0) {
                        e.preventDefault()
                        message("You do not have permission to modify campaign " + urn + ".")
                    } else if(count > 0){
                        e.preventDefault();
                        message("Campaign " + urn + " has existing responses and can therefore not be modified.")
                    }
                });

                var a4 = $("").appendTo($("
").appendTo(ul)).append(' Download XML').attr("href", "#").click(function(e){
                    e.preventDefault();
                    $("#hiddenurn").val(urn)
                    $("#hiddenform").submit()
                });

                ul.append($("
").addClass('divider'))

                var responselink = $("").appendTo($("
").appendTo(ul)).append(' Responses').attr("href", '../responsetool/#' + urn).click(function(e){
                    if(count < 0){
                        e.preventDefault();
                        message("Loading campaign info, please be patient.", "info")
                    } else if(count === 0){
                        e.preventDefault();
                        message("Campaign " + urn + " has no existing responses.")
                    }
                });

                var a3 = $("").appendTo($("
").appendTo(ul)).append(' Dashboard').attr("href", '../dashboard/#' + urn).click(function(e){
                    if(count < 0){
                        e.preventDefault();
                        message("Loading campaign info, please be patient.", "info")
                    } else if(count === 0){
                        e.preventDefault();
                        message("Campaign " + urn + " has no existing responses. Nothing to visualize.")
                    }
                });

                var a7 = $("").appendTo($("
").appendTo(ul)).append(' PlotApp').attr("href", '../plotapp/#' + urn).click(function(e){
                    if(count < 0){
                        e.preventDefault();
                        message("Loading campaign info, please be patient.", "info")
                    } else if(count === 0){
                        e.preventDefault();
                        message("Campaign " + urn + " has no existing responses. Nothing to plot.")
                    }
                });

                var a5 = $("").appendTo($("
").appendTo(ul)).append(' Export Data')
                    .attr("href", "../../app/survey_response/read?campaign_urn=" + urn + "&privacy_state=shared&client=manager&user_list=urn:ohmage:special:all&prompt_id_list=urn:ohmage:special:all&output_format=csv&sort_oder=timestamp&column_list=urn:ohmage:user:id,urn:ohmage:context:timestamp,urn:ohmage:prompt:response,urn:ohmage:context:location:latitude,urn:ohmage:context:location:longitude&suppress_metadata=true")

                ul.append($("
").addClass('divider'));

                var a6 = $("").appendTo($("
").appendTo(ul)).append(' Monitor').attr("href", "../monitor/#" + urn);

                var a1 = $("").appendTo($("
").appendTo(ul)).append(' Settings').attr("href", "#").click(function(e){
                    e.preventDefault();
                    populateModal();
                });

                //maps the url hash to a default app
                var appmap = {
                    settings : a1,
                    edit : a2,
                    dashboard : a3,
                    download : a4,
                    export : a5,
                    monitor: a6,
                    plotapp : a7,
                    responses : responselink
                }

                //add a default action
                var hashval = window.location.hash.replace(/^[#]/, "");
                var link = appmap[hashval] || responselink;
                var defaultbtn = $('<button type="button" class="btn btn-default btn-sm" />').html(link.html()).click(function(e){
                    link[0].click();
                }).prependTo(btn);
                $("#subtitle").text(hashval)

                function populateModal(){
                    oh.campaign.readall({
                        campaign_urn_list : urn,
                        output_format:"long"
                    }).done(function(x){
                        var longdata = x[urn];
                        $("#campaign_name").val(longdata.name);
                        $("#campaign_description").val(longdata.description);
                        $("#campaign_urn").val(urn);

                        //$("#campaign_running")[0].checked = (data["running_state"] == "running");
                        //$("#campaign_privacy")[0].checked = (data["privacy_state"] == "shared");

                        $("#campaign_running").bootstrapSwitch({size: "small", onColor: "success", offColor: "danger", onText:"running", offText:"stopped"})
                        $("#campaign_privacy").bootstrapSwitch({size: "small", onColor: "success", offColor: "danger", onText:"enabled", offText:"disabled"})
                        $("#campaign_privacy").bootstrapSwitch("state", longdata["privacy_state"] == "shared");
                        $("#campaign_running").bootstrapSwitch("state", longdata["running_state"] == "running");

                        $("#campaign_class option").each(function(i){
                            $(this).prop("selected", $.inArray($(this).attr("value"), longdata.classes) > -1);
                        });
                        $("#campaign_class").trigger("chosen:updated");

                        if(count){
                            $(".xml-upload-form").hide();
                        }

                        $('#myModal').modal("show").on("shown.bs.modal", function(){
                            $("#campaign_class").chosen({search_contains:true, no_results_text: "Class not found."});
                            $(".xml-upload-form").fileinput();
                        });

                        var xmldata;
                        $("input.xml-file-input").on("change", function(){
                            var f = this.files[0];
                            if(f){
                                var r = new FileReader();
                                r.onload = function(e) {
                                    xmldata = r.result;
                                }
                                r.readAsText(f);
                            } else {
                                xmldata = null;
                            }
                        })

                        $("#campaign_delete_button").unbind("click").click(function(e){
                            e.preventDefault();

                            // test for existing responses
                            if(count && !confirm("This campaign has " + count + " responses. Are you sure?")){
                                return;
                            }

                            var btn = $(this).attr("disabled", "disabled");
                            oh.campaign.delete({campaign_urn:urn}).done(function(){
                                $('#myModal').modal("hide");
                                tr.hide("slow", function(){
                                    alert("Campaign " + urn + " deleted!")
                                });
                            }).always(function(){
                                btn.removeAttr("disabled");
                            });
                        });

                        $("#campaign_save_button").unbind("click").click(function(e){
                            e.preventDefault();
                            var btn = $(this).attr("disabled", "disabled");
                            var running_state = $("#campaign_running")[0].checked ? "running" : "stopped";
                            var privacy_state = $("#campaign_privacy")[0].checked ? "shared" : "private";
                            var args = {
                                campaign_urn : urn,
                                running_state : running_state,
                                privacy_state : privacy_state,
                                description : $("#campaign_description").val(),
                                class_list_remove : longdata.classes,
                                class_list_add : $("#campaign_class").val()
                            }

                            //update xml only if selected
                            if(xmldata){
                                args.xml = fixxml(xmldata, longdata.name ,urn);
                            }

                            oh.campaign.update(args).done(function(){
                                td3.text(running_state);
                                $('#myModal').modal("hide")
                            }).always(function(){
                                btn.removeAttr("disabled");
                            });
                        });
                    });
                }

                return oh.survey.count(urn).done(function(counts){
                    if(!Object.keys(counts).length){
                        //no existing responses found
                        count = 0;
                    } else {
                        count = $.map(counts, function(val, key) {
                            return val[0].count;
                        }).reduce(function(previousValue, currentValue) {
                            return previousValue + currentValue;
                        });
                        var sharedcount = counts.shared ? counts.shared[0].count : 0;
                    }
                    td4.text(count);
                }).always(function(){
                    updateProgress((progress++/total) * 75 + 25);
                });
            });

            //data tables widget
            function initTable(){
                table = $('#campaigntable').DataTable( {
                    "dom" : '<"pull-right"l><"pull-left"f>tip',
                    "lengthMenu": [[25, 50, 100, -1], [25, 50, 100, "All"]],
                    "aoColumnDefs": [
                       { 'bSortable': false, 'aTargets' : [ 5 ] },
                       { 'bSearchable': false, 'aTargets': [ 4, 5 ] },
                       { 'bVisible' : false, 'aTargets' : [ 2, 6 ] }
                    ]
                });

                /* Custom filtering by date range */
                $.fn.dataTable.ext.search.push(
                    function( settings, data, dataIndex ) {
                        var time = Date.parse(data[1].replace(" ", "T"));
                        if(!time) return true;

                        var min = Date.parse($("#mindate").val());
                        if (min && time < min) return false;

                        var maxtxt = $("#maxdate").val();
                        var max = Date.parse(maxtxt + "T23:55");
                        if (maxtxt && max && time > max) return false;

                        return true;
                    }
                );

                /* Custom filtering by class */
                $.fn.dataTable.ext.search.push(
                    function( settings, data, dataIndex ) {
                        var selected_class = $("#class_select").val();
                        if(!selected_class) return true;
                        var classes = data[6].split(",");
                        return (classes.indexOf(selected_class) >= 0)
                    }
                );

                $('.datepicker').text("").datepicker({
                    format: 'yyyy-mm-dd',
                    autoclose: true,
                    clearBtn: true
                }).change( function() {
                    table.draw();
                });

                $("#class_select").change(function(){
                    table.draw();
                })
            }

            //init temporary datatable
            initTable()

            //reinit final datatable after counts have been updated
            $.when.apply($, requests).always(function() {
                $("#progressdiv").addClass("hidden");
                $('#campaigntable').dataTable().fnDestroy();
                initTable();
            });

            //resetters
            $("#myModal").on("hidden.bs.modal", function(){
                $(".xml-upload-form").show();
                //$(".fileinput").fileinput("clear");
            })

            //expand function
            $('#campaigntable').on('click', "tbody td:not('.buttontd')", function () {
                var tr = $(this).parent()
                var row = table.row(tr);
                if(tr.attr("role") != "row") return;

                if ( row.child.isShown() ) {
                    // This row is already open - close it
                    row.child.hide();
                    tr.removeClass('shown');
                } else {
                    // Open this row
                    row.child( makerow(row.data(), tr.data("campaigndata"))).show();
                    tr.addClass('shown');
                }
            });
        });

        //get users
        oh.user.read().done(function(x){
            $.each(Object.keys(x).sort(), function( i, name ) {
                //$("#campaign_author").append($("<option />").text(name));
            });
        });

        //get classes
        oh.class.read().done(function(x){
            $.each(Object.keys(x).sort(), function( i, urn ) {
                $("#campaign_class").append($("<option />").text(urn).attr("value", urn));
            });
        });
    });

    updateProgress = _.throttle(function(pct){
        $(".progress-bar").css("width", + pct + "%");
    }, 300);

    function message(msg, type){
        // type must be one of success, info, warning, danger
        type = type || "danger"
        $("#errordiv").empty().append('
×' + msg + '
');
        $('html, body').animate({
           scrollTop: 100
        }, 200);
    }

    //XML parser for mixed case tag names (HTML only supports lowercase tags)
    var parse = (function(parser, jQuery){
        return function(str) {
            return jQuery(parser.parseFromString(str, "text/xml").documentElement)
        }
    })(new DOMParser(), jQuery)

    function fixxml(input, name, urn){
        var xml = $.parseXML(input);
        var campaign = $("campaign", xml);
        campaign.children("campaignName").remove();
        campaign.children("campaignUrn").remove();
        campaign.prepend(parse("<campaignName/>").text(name))
        campaign.prepend(parse("<campaignUrn/>").text(urn))
        return (new XMLSerializer()).serializeToString(xml);
    }

    function makerow(rowdata, campaign) {
        var row = $('
').addClass('row').addClass("response-row");
        $("
").text(campaign.urn).appendTo(row);
        var p = $("
").appendTo(row).html("" + (campaign.description || "No description.") + "")
        var p2 = $("
").appendTo(row).html("" + (campaign.classes || "No Classes.") + "")
        return row;
    }

})();
<|endoftext|>


<commit_before>function genSur() {
    surnames = [];
    var pickIt = Math.floor(Math.random() * surnames.length);
    return surnames[pickIt];
}

function genMale(final) {
    maleNames = [];
    surname = genSur();
    var pickIt = Math.floor(Math.random() * maleNames.length);
    if (final) {
        document.getElementById('result').innerHTML = maleNames[pickIt] + " " + surname;
    } else {
        return maleNames[pickIt] + " " + surname;
    }
}

function genFemale(final) {
    femaleNames = [];
    surname = genSur();
    var pickIt = Math.floor(Math.random() * femaleNames.length);
    if (final) {
        document.getElementById('result').innerHTML = femaleNames[pickIt] + " " + surname;
    } else {
        return femaleNames[pickIt] + " " + surname;
    }
}

function doesntMatter() {
    var newGen = Math.floor((Math.random() * 2) + 1);
    if (newGen === 1) {
        genMale(true);
    } else if (newGen === 2) {
        genFemale(true);
    } else {
        document.getElementById("result").innerHTML = "Sorry, something went wrong. yell at me";
    }
}
<commit_msg>Symbols<commit_after>function genSur() {
    surnames = [];
    var pickIt = Math.floor(Math.random() * surnames.length);
    return surnames[pickIt];
}

function genMale(final) {
    maleNames = [];
    surname = genSur();
    var pickIt = Math.floor(Math.random() * maleNames.length);
    if (final) {
        document.getElementById('result').innerHTML = maleNames[pickIt] + " " + surname + " ♂";
    } else {
        return maleNames[pickIt] + " " + surname + " (male)";
    }
}

function genFemale(final) {
    femaleNames = [];
    surname = genSur();
    var pickIt = Math.floor(Math.random() * femaleNames.length);
    if (final) {
        document.getElementById('result').innerHTML = femaleNames[pickIt] + " " + surname + " ♀";
    } else {
        return femaleNames[pickIt] + " " + surname + " (female)";
    }
}

function doesntMatter() {
    var newGen = Math.floor((Math.random() * 2) + 1);
    if (newGen === 1) {
        genMale(true);
    } else if (newGen === 2) {
        genFemale(true);
    } else {
        document.getElementById("result").innerHTML = "Sorry, something went wrong. yell at me";
    }
}
<|endoftext|>