<commit_before>requirejs(["rAppid"], function (rAppid) {
    rAppid.defineClass("js.core.History", ["js.core.Bindable"], function (Bindable) {

        var routeStripper = /^#\/?/,
            undef,
            emptyCallback = function () {};

        return Bindable.inherit({

            ctor: function () {
                this.callBase();
                this.$routers = [];
                this.processUrl = true;

                this.$history = [];
            },

            defaults: {
                interval: 50
            },

            getFragment: function () {
                var fragment;

                if (this.runsInBrowser()) {
                    fragment = decodeURIComponent(window.location.hash);
                } else {
                    fragment = this.$history[this.$history.length-1] || "";
                }

                return fragment.replace(routeStripper, '');
            },

            start: function (callback, initialHash) {

                var self = this;
                this.$checkUrlFn = function () {
                    self.checkUrl.apply(self, arguments);
                };

                if (this.runsInBrowser()) {
                    // we're on a browser
                    if ("onhashchange" in window) {
                        if (window.addEventListener) {
                            window.addEventListener('hashchange',
                                this.$checkUrlFn, false);
                        } else {
                            window.attachEvent('onhashchange', this.$checkUrlFn);
                        }
                    } else {
                        // polling
                        this.$checkUrlInterval = setInterval(this.$checkUrlFn, this.$.interval);
                    }
                } else {
                    // rendering on node
                    this.$history.push(initialHash || "");
                }

                this.fragment = this.getFragment();
                this.navigate(this.fragment, true, true, callback);
                this.processUrl = true;

            },

            stop: function () {
                if (typeof window !== "undefined") {
                    if ("onhashchange" in window) {
                        if (window.removeEventListener) {
                            window.removeEventListener('hashchange',
                                this.$checkUrlFn, false);
                        } else {
                            window.detachEvent('onhashchange', this.$checkUrlFn);
                        }
                    } else {
                        // polling
                        clearInterval(this.$checkUrlInterval);
                    }
                }
            },

            addRouter: function (router) {
                this.$routers.push(router);
            },

            checkUrl: function (e) {

                if (this.processUrl) {
                    var currentFragment = this.getFragment();
                    if (currentFragment == this.fragment) {
                        return false;
                    }

                    this.navigate(currentFragment, true, true, emptyCallback);
                }

                this.processUrl = true;

            },

            triggerRoute: function (fragment, callback) {

                for (var i = 0; i < this.$routers.length; i++) {
                    if (this.$routers[i].executeRoute(fragment, callback)) {
                        return true;
                    }
                }

                console.log("no route for '" + fragment + "' found.");
            },

            navigate: function (fragment, createHistoryEntry, triggerRoute, callback) {

                if (!callback && createHistoryEntry instanceof Function) {
                    callback = createHistoryEntry;
                    createHistoryEntry = null;
                }

                if (!callback && triggerRoute instanceof Function) {
                    callback = triggerRoute;
                    triggerRoute = null;
                }

                if (createHistoryEntry == undef || createHistoryEntry == null) {
                    createHistoryEntry = true;
                }

                if (triggerRoute == undef || triggerRoute == null) {
                    triggerRoute = true;
                }

                this.processUrl = false;

                if (createHistoryEntry) {
                    if (this.runsInBrowser()) {
                        window.location.hash = "/" + fragment;
                    } else {
                        this.$history.push(fragment);
                        this.checkUrl(null);
                    }
                } else {
                    if (this.runsInBrowser()) {
                        // replace hash
                        window.location.replace("#/" + fragment);
                    } else {
                        this.$history[this.$history.length-1] = fragment;
                    }

                }

                this.fragment = fragment;

                if (triggerRoute) {
                    return this.triggerRoute(fragment, callback);
                }

            }
        });
    });
});<commit_msg>execute callback during start of history or navigation, if no route is found.<commit_after>requirejs(["rAppid"], function (rAppid) {
    rAppid.defineClass("js.core.History", ["js.core.Bindable"], function (Bindable) {

        var routeStripper = /^#\/?/,
            undef,
            emptyCallback = function () {};

        return Bindable.inherit({

            ctor: function () {
                this.callBase();
                this.$routers = [];
                this.processUrl = true;

                this.$history = [];
            },

            defaults: {
                interval: 50
            },

            getFragment: function () {
                var fragment;

                if (this.runsInBrowser()) {
                    fragment = decodeURIComponent(window.location.hash);
                } else {
                    fragment = this.$history[this.$history.length-1] || "";
                }

                return fragment.replace(routeStripper, '');
            },

            start: function (callback, initialHash) {

                var self = this;
                this.$checkUrlFn = function () {
                    self.checkUrl.apply(self, arguments);
                };

                if (this.runsInBrowser()) {
                    // we're on a browser
                    if ("onhashchange" in window) {
                        if (window.addEventListener) {
                            window.addEventListener('hashchange',
                                this.$checkUrlFn, false);
                        } else {
                            window.attachEvent('onhashchange', this.$checkUrlFn);
                        }
                    } else {
                        // polling
                        this.$checkUrlInterval = setInterval(this.$checkUrlFn, this.$.interval);
                    }
                } else {
                    // rendering on node
                    this.$history.push(initialHash || "");
                }

                this.fragment = this.getFragment();
                this.navigate(this.fragment, true, true, callback);
                this.processUrl = true;

            },

            stop: function () {
                if (typeof window !== "undefined") {
                    if ("onhashchange" in window) {
                        if (window.removeEventListener) {
                            window.removeEventListener('hashchange',
                                this.$checkUrlFn, false);
                        } else {
                            window.detachEvent('onhashchange', this.$checkUrlFn);
                        }
                    } else {
                        // polling
                        clearInterval(this.$checkUrlInterval);
                    }
                }
            },

            addRouter: function (router) {
                this.$routers.push(router);
            },

            checkUrl: function (e) {

                if (this.processUrl) {
                    var currentFragment = this.getFragment();
                    if (currentFragment == this.fragment) {
                        return false;
                    }

                    this.navigate(currentFragment, true, true, emptyCallback);
                }

                this.processUrl = true;

            },

            triggerRoute: function (fragment, callback) {

                for (var i = 0; i < this.$routers.length; i++) {
                    if (this.$routers[i].executeRoute(fragment, callback)) {
                        return true;
                    }
                }

                console.log("no route for '" + fragment + "' found.");
            },

            navigate: function (fragment, createHistoryEntry, triggerRoute, callback) {

                if (!callback && createHistoryEntry instanceof Function) {
                    callback = createHistoryEntry;
                    createHistoryEntry = null;
                }

                if (!callback && triggerRoute instanceof Function) {
                    callback = triggerRoute;
                    triggerRoute = null;
                }

                if (createHistoryEntry == undef || createHistoryEntry == null) {
                    createHistoryEntry = true;
                }

                if (triggerRoute == undef || triggerRoute == null) {
                    triggerRoute = true;
                }

                this.processUrl = false;

                if (createHistoryEntry) {
                    if (this.runsInBrowser()) {
                        window.location.hash = "/" + fragment;
                    } else {
                        this.$history.push(fragment);
                        this.checkUrl(null);
                    }
                } else {
                    if (this.runsInBrowser()) {
                        // replace hash
                        window.location.replace("#/" + fragment);
                    } else {
                        this.$history[this.$history.length-1] = fragment;
                    }

                }

                this.fragment = fragment;

                var ret;
                if (triggerRoute) {
                    ret = this.triggerRoute(fragment, callback);
                }

                if (!ret && callback) {
                    callback();
                }

            }
        });
    });
});<|endoftext|>


<commit_before>var gnames = gNames();
for(var i=0; i<gnames.length; i++){
	$(".result").append('
'+gnames[i].city+'
');
	$(".result").append('<gcse:searchresults-only gname="'+gnames[i].gname+'"></gcse:searchresults-only>');
}
var QueryString = queryString();
if(jQuery.isEmptyObject(QueryString) === false) {
	for(var t in QueryString) {
		if(t == "q") $("#search_string").val(QueryString[t]);
		if(t == "zipcode") {
			console.log(jQuery.isEmptyObject(QueryString));
			$("#location").val(QueryString[t]);
			if(jQuery.isEmptyObject(QueryString) === true) {
				$(".result").html('
No Result found
');
			}
		}
		if(t == "max_dist") $("#max_dist").val(QueryString[t]);
		if(t == "min_price") $("#minAsk8").val(QueryString[t]);
		if(t == "max_price") $("#maxAsk8").val(QueryString[t]);
		if(t == "min_meter") $("#minYear8").val(QueryString[t]);
		if(t == "max_meter") $("#maxYear8").val(QueryString[t]);
	}

	window.__gcse = {
			callback: googleCSELoaded
	};

	// CSE Code. This is a free version, so please don't make too many requests.
	(function() {
		//var cx = '001386805071419863133:cb1vfab8b4y';
		var cx = '014940883816157527715:_1dsxowig5w';
		var gcse = document.createElement('script');
		gcse.type = 'text/javascript';
		gcse.async = true;
		gcse.src = 'https://cse.google.com/cse.js?cx=' + cx;
		var s = document.getElementsByTagName('script')[0];
		s.parentNode.insertBefore(gcse, s);
	})();
}
<commit_msg>test<commit_after>var gnames = gNames();
for(var i=0; i<gnames.length; i++){
	$(".result").append('
'+gnames[i].city+'
');
	$(".result").append('<gcse:searchresults-only gname="'+gnames[i].gname+'"></gcse:searchresults-only>');
}
var QueryString = queryString();
if(jQuery.isEmptyObject(QueryString) === false) {
	for(var t in QueryString) {
		if(t == "q") $("#search_string").val(QueryString[t]);
		if(t == "zipcode") {
			console.log(QueryString.length);
			$("#location").val(QueryString[t]);
			if(jQuery.isEmptyObject(QueryString) === true) {
				$(".result").html('
No Result found
');
			}
		}
		if(t == "max_dist") $("#max_dist").val(QueryString[t]);
		if(t == "min_price") $("#minAsk8").val(QueryString[t]);
		if(t == "max_price") $("#maxAsk8").val(QueryString[t]);
		if(t == "min_meter") $("#minYear8").val(QueryString[t]);
		if(t == "max_meter") $("#maxYear8").val(QueryString[t]);
	}

	window.__gcse = {
			callback: googleCSELoaded
	};

	// CSE Code. This is a free version, so please don't make too many requests.
	(function() {
		//var cx = '001386805071419863133:cb1vfab8b4y';
		var cx = '014940883816157527715:_1dsxowig5w';
		var gcse = document.createElement('script');
		gcse.type = 'text/javascript';
		gcse.async = true;
		gcse.src = 'https://cse.google.com/cse.js?cx=' + cx;
		var s = document.getElementsByTagName('script')[0];
		s.parentNode.insertBefore(gcse, s);
	})();
}
<|endoftext|>


<commit_before>/*
*  Directives
*/
// Sidenav
angular.module('app.directives', [])
    .directive('siteSidenav', function () {
        var SidenavController = function ($scope, $mdSidenav) {
            console.info('Successfully loaded!');

            $scope.main = [
                { url: 'https://chan4077.github.io', title: 'Main', icon: 'home' },
                { url: 'https://chan4077.github.io/about', title: 'About Me', icon: 'account-box' },
                { url: 'https://chan4077.github.io/blog', title: 'Blog', icon: 'blogger' },
                { url: 'https://chan4077.github.io/media', title: 'Social Media', icon: 'google-plus' },
                { url: 'https://chan4077.github.io/angular', title: 'Angular', icon: 'angular' },
            ];
            $scope.other = [
                { url: 'https://github.com/Chan4077/chan4077.github.io', title: 'Project Page', icon: 'github-circle' },
                { url: 'https://chan4077.github.io/preferences', title: 'Preferences', icon: 'settings' },
                { url: 'https://chan4077.github.io/help', title: 'Get Help', icon: 'help-circle' }
            ];
            $scope.projects = [
                { url: 'https://chan4077.github.io/projects', title: 'Projects', icon: '' },
                { url: 'https://chan4077.github.io/MyFirstApp', title: 'MyFirstApp', icon: '' },
                { url: 'https://chan4077.github.io/icons', title: 'Icons', icon: '' }
            ]
            $scope.openLeftMenu = function () {
                $mdSidenav('left').toggle();
            }
        };
        return {
            restrict: 'E',
            templateUrl: '/templates/sidenav.html',
            transclude: true,
            controller: SidenavController
        };
    })
    // Toolbar
    .directive('siteToolbar', function () {
        return {
            restrict: 'E',
            templateUrl: '/templates/toolbar.html',
            transclude: true,
            controller: function ToolbarController($scope, $location) {
                console.info('Loaded!');
                if ($location.path().indexOf('beta') > -1) {
                    $scope.title = 'Beta';
                } else if ($location.path().indexOf('beta/blog.html') > -1) {
                    $scope.title = 'Beta Blog';
                } else if ($location.path().indexOf('help') > -1) {
                    $scope.title = 'Help';
                } else if ($location.path().indexOf('about') > -1) {
                    $scope.title = 'About';
                } else if ($location.path().indexOf('angular/material.html') > -1) {
                    $scope.title = 'Anglar Material Demo';
                } else if ($location.path().indexOf('media') > -1) {
                    $scope.title = 'Social Media';
                } else if ($location.path().indexOf('preferences') > -1) {
                    $scope.title = 'Preferences';
                } else if ($location.path().indexOf('404.html') > -1) {
                    $scope.title = 'Error 404';
                } else if ($location.path().indexOf('') > -1) {
                    $scope.title = 'Home';
                } else if ($location.path().indexOf('blog') > -1) {
                    $scope.title = 'Blog';
                } else if ($location.path().indexOf('projects') > -1) {
                    $scope.title = 'Projects';
                } else {
                    $scope.title = '{{title}}';
                }
                console.log('url visited: ' + $location.path());
                $scope.refreshPage = function () {
                    window.location.reload(true);
                };
                // $mdDialog (start)
                $scope.whats_new = function (ev) {
                    $mdDialog.show({
                        controller: WhatNewController,
                        templateUrl: '/templates/whats_new_tmpl.html',
                        parent: angular.element(document.body),
                        targetEvent: ev,
                        clickOutsideToClose: true,
                    });
                };
                $scope.settings = function (ev) {
                    $mdDialog.show({
                        controller: SettingsController,
                        templateUrl: '/templates/settings_tmpl.html',
                        parent: angular.element(document.body),
                        targetEvent: ev,
                        clickOutsideToClose: true,
                    });
                };
                $scope.sendFeedback = function (ev) {
                    $mdDialog.show({
                        controller: FeedbackController,
                        templateUrl: '/templates/feedbackhelp.html',
                        parent: angular.element(document.body),
                        targetEvent: ev,
                        clickOutsideToClose: true,
                    });
                };
                // More menu
                var originatorEv;

                $scope.openMenu = function ($mdOpenMenu, ev) {
                    originatorEv = ev;
                    $mdOpenMenu(ev);
                };
                function WhatNewController($scope, $mdDialog, $mdToast) {
                    $scope.hide = function () {
                        $mdDialog.hide();
                    };
                    $scope.cancel = function () {
                        $mdDialog.cancel();
                    };
                    $scope.help = function () {
                        window.location.href = "https://chan4077.github.io/help";
                        console.info('Redirecting to Help site...');
                    }
                };
                function FeedbackController($scope, $mdDialog, $mdToast) {
                    $scope.hide = function () {
                        $mdDialog.hide();
                    };
                    $scope.cancel = function () {
                        $mdDialog.cancel();
                    };
                    $scope.help = function () {
                        window.location.href = "https://chan4077.github.io/help/about_feedback.html";
                        console.info('Redirecting to Help site...');
                    }
                    $scope.oriFeedback = {
                        name: "",
                        email: "",
                        sendFeedback: ""
                    };
                    // Declare a variable called oriFeedback
                    var oriFeedback = angular.copy($scope.oriFeedback);

                    // On initial, set feedback to original
                    $scope.feedback = angular.copy(oriFeedback);

                    // Reset form
                    $scope.reset = function () {
                        $scope.feedback = angular.copy(oriFeedback);
                        // Reset form
                        $scope.feedbackhelp.$setPristine();
                        // Reset validation errors
                        $scope.feedbackhelp.$setUntouched();
                    };
                    $scope.submitForm = function () {
                        $mdDialog.cancel();
                        $mdToast.show(
                            $mdToast.simple()
                                .textContent('You submitted the form!')
                                .position('bottom left')
                                .hideDelay(3000)
                        );
                    };
                };
                function SettingsController($scope, $mdDialog) {
                    $scope.hide = function () {
                        $mdDialog.hide();
                    };
                    $scope.cancel = function () {
                        $mdDialog.cancel();
                    };
                    $scope.help = function () {
                        window.location.href = "https://chan4077.github.io/help";
                        console.info('Redirecting to Help site...');
                    }
                    $scope.general_settings = {
                        proBar: true,
                        enableSound: true
                    };
                    $scope.developer_settings = {
                        consoleLog: false,
                        showExtraOptions: false
                    };
                    $scope.extra_settings = {
                        showCredits: true,
                        enableOffline: true
                    };
                };
                // $mdDialog (end)

                $scope.about_site = function () {
                    window.location.href = "https://chan4077.github.io/about#angular";
                };
            }
        };
    })
    // Speed Dial
    .directive('siteSpeedDial', function () {
        return {
            restrict: 'E',
            templateUrl: '/templates/speeddial.html',
            transclude: true
        };
    })
    // Controllers
    .controller('SideNavController', function ($scope, $mdSidenav) {

    })
angular.module('app.config', [])
    .config(function ($mdIconProvider, $mdToastProvider) {
        $mdIconProvider
            .defaultIconSet('/img/mdi.svg')
            .icon('website-logo', '/img/website.svg');
        console.info('Successfully initialized!');
    });<commit_msg>fix(dependencies) - Add inputs<commit_after>/*
*  Directives
*/
// Sidenav
angular.module('app.directives', [])
    .directive('siteSidenav', function () {
        var SidenavController = function ($scope, $mdSidenav) {
            console.info('Successfully loaded!');

            $scope.main = [
                { url: 'https://chan4077.github.io', title: 'Main', icon: 'home' },
                { url: 'https://chan4077.github.io/about', title: 'About Me', icon: 'account-box' },
                { url: 'https://chan4077.github.io/blog', title: 'Blog', icon: 'blogger' },
                { url: 'https://chan4077.github.io/media', title: 'Social Media', icon: 'google-plus' },
                { url: 'https://chan4077.github.io/angular', title: 'Angular', icon: 'angular' },
            ];
            $scope.other = [
                { url: 'https://github.com/Chan4077/chan4077.github.io', title: 'Project Page', icon: 'github-circle' },
                { url: 'https://chan4077.github.io/preferences', title: 'Preferences', icon: 'settings' },
                { url: 'https://chan4077.github.io/help', title: 'Get Help', icon: 'help-circle' }
            ];
            $scope.projects = [
                { url: 'https://chan4077.github.io/projects', title: 'Projects', icon: '' },
                { url: 'https://chan4077.github.io/MyFirstApp', title: 'MyFirstApp', icon: '' },
                { url: 'https://chan4077.github.io/icons', title: 'Icons', icon: '' }
            ]
            $scope.openLeftMenu = function () {
                $mdSidenav('left').toggle();
            }
        };
        return {
            restrict: 'E',
            templateUrl: '/templates/sidenav.html',
            transclude: true,
            controller: SidenavController
        };
    })
    // Toolbar
    .directive('siteToolbar', function () {
        return {
            restrict: 'E',
            templateUrl: '/templates/toolbar.html',
            transclude: true,
            controller: function ToolbarController($scope, $location, $mdDialog, $mdToast, $mdOpenMenu) {
                console.info('Loaded!');
                if ($location.path().indexOf('beta') > -1) {
                    $scope.title = 'Beta';
                } else if ($location.path().indexOf('beta/blog.html') > -1) {
                    $scope.title = 'Beta Blog';
                } else if ($location.path().indexOf('help') > -1) {
                    $scope.title = 'Help';
                } else if ($location.path().indexOf('about') > -1) {
                    $scope.title = 'About';
                } else if ($location.path().indexOf('angular/material.html') > -1) {
                    $scope.title = 'Anglar Material Demo';
                } else if ($location.path().indexOf('media') > -1) {
                    $scope.title = 'Social Media';
                } else if ($location.path().indexOf('preferences') > -1) {
                    $scope.title = 'Preferences';
                } else if ($location.path().indexOf('404.html') > -1) {
                    $scope.title = 'Error 404';
                } else if ($location.path().indexOf('') > -1) {
                    $scope.title = 'Home';
                } else if ($location.path().indexOf('blog') > -1) {
                    $scope.title = 'Blog';
                } else if ($location.path().indexOf('projects') > -1) {
                    $scope.title = 'Projects';
                } else {
                    $scope.title = '{{title}}';
                }
                console.log('url visited: ' + $location.path());
                $scope.refreshPage = function () {
                    window.location.reload(true);
                };
                // $mdDialog (start)
                $scope.whats_new = function (ev) {
                    $mdDialog.show({
                        controller: WhatNewController,
                        templateUrl: '/templates/whats_new_tmpl.html',
                        parent: angular.element(document.body),
                        targetEvent: ev,
                        clickOutsideToClose: true,
                    });
                };
                $scope.settings = function (ev) {
                    $mdDialog.show({
                        controller: SettingsController,
                        templateUrl: '/templates/settings_tmpl.html',
                        parent: angular.element(document.body),
                        targetEvent: ev,
                        clickOutsideToClose: true,
                    });
                };
                $scope.sendFeedback = function (ev) {
                    $mdDialog.show({
                        controller: FeedbackController,
                        templateUrl: '/templates/feedbackhelp.html',
                        parent: angular.element(document.body),
                        targetEvent: ev,
                        clickOutsideToClose: true,
                    });
                };
                // More menu
                var originatorEv;

                $scope.openMenu = function ($mdOpenMenu, ev) {
                    originatorEv = ev;
                    $mdOpenMenu(ev);
                };
                function WhatNewController($scope, $mdDialog, $mdToast) {
                    $scope.hide = function () {
                        $mdDialog.hide();
                    };
                    $scope.cancel = function () {
                        $mdDialog.cancel();
                    };
                    $scope.help = function () {
                        window.location.href = "https://chan4077.github.io/help";
                        console.info('Redirecting to Help site...');
                    }
                };
                function FeedbackController($scope, $mdDialog, $mdToast) {
                    $scope.hide = function () {
                        $mdDialog.hide();
                    };
                    $scope.cancel = function () {
                        $mdDialog.cancel();
                    };
                    $scope.help = function () {
                        window.location.href = "https://chan4077.github.io/help/about_feedback.html";
                        console.info('Redirecting to Help site...');
                    }
                    $scope.oriFeedback = {
                        name: "",
                        email: "",
                        sendFeedback: ""
                    };
                    // Declare a variable called oriFeedback
                    var oriFeedback = angular.copy($scope.oriFeedback);

                    // On initial, set feedback to original
                    $scope.feedback = angular.copy(oriFeedback);

                    // Reset form
                    $scope.reset = function () {
                        $scope.feedback = angular.copy(oriFeedback);
                        // Reset form
                        $scope.feedbackhelp.$setPristine();
                        // Reset validation errors
                        $scope.feedbackhelp.$setUntouched();
                    };
                    $scope.submitForm = function () {
                        $mdDialog.cancel();
                        $mdToast.show(
                            $mdToast.simple()
                                .textContent('You submitted the form!')
                                .position('bottom left')
                                .hideDelay(3000)
                        );
                    };
                };
                function SettingsController($scope, $mdDialog) {
                    $scope.hide = function () {
                        $mdDialog.hide();
                    };
                    $scope.cancel = function () {
                        $mdDialog.cancel();
                    };
                    $scope.help = function () {
                        window.location.href = "https://chan4077.github.io/help";
                        console.info('Redirecting to Help site...');
                    }
                    $scope.general_settings = {
                        proBar: true,
                        enableSound: true
                    };
                    $scope.developer_settings = {
                        consoleLog: false,
                        showExtraOptions: false
                    };
                    $scope.extra_settings = {
                        showCredits: true,
                        enableOffline: true
                    };
                };
                // $mdDialog (end)

                $scope.about_site = function () {
                    window.location.href = "https://chan4077.github.io/about#angular";
                };
            }
        };
    })
    // Speed Dial
    .directive('siteSpeedDial', function () {
        return {
            restrict: 'E',
            templateUrl: '/templates/speeddial.html',
            transclude: true
        };
    })
    // Controllers
    .controller('SideNavController', function ($scope, $mdSidenav) {

    })
angular.module('app.config', [])
    .config(function ($mdIconProvider, $mdToastProvider) {
        $mdIconProvider
            .defaultIconSet('/img/mdi.svg')
            .icon('website-logo', '/img/website.svg');
        console.info('Successfully initialized!');
    });<|endoftext|>


<commit_before>function GameManager(size, InputManager, Actuator, StorageManager) {
    this.size = size; // Size of the grid
    this.actuator = new Actuator;
    this.inputManager = new InputManager;
    this.storageManager = new StorageManager;

    this.inputManager.on("click", this.performClickAction.bind(this));
    this.inputManager.on("undo", this.undoAction.bind(this));
    this.inputManager.on("moveToNextLevel", this.moveToNextLevel.bind(this));
    this.inputManager.on("restart", this.restart.bind(this));

    this.firstSelected = false;
    this.firstPosition = null;

    this.storedValues = [];

    this.setup();
}

GameManager.prototype.setup = function () {

    var previousState = this.storageManager.getGameState();

    // Reload the game from a previous game if present
    if (previousState) {
        this.grid        = new Grid(previousState.grid.size,
            previousState.grid.cells); // Reload grid
        this.moves       = previousState.moves;
        this.level       = previousState.level;
        this.won         = previousState.won;
        this.newTiles    = false;
        this.storedValues = previousState.storedValues;
    } else {
        this.grid = new Grid(this.size);
        this.moves = 0;
        this.level = 1;
        this.won = false;
        this.newTiles = false;

        this.addStartTiles();
    }

    this.actuate();
};

// Represent the current game as an object
GameManager.prototype.serialize = function () {
    return {
        grid:        this.grid.serialize(),
        moves:       this.moves,
        level:       this.level,
        won:         this.won,
        newTiles:    this.newTiles,
        storedValues:this.storedValues
    };
};

GameManager.prototype.addStartTiles = function () {
    for (var i=0; i < this.size; i++) {
        for (var j=0; j < this.size; j++) {

            if (i === 1 && j === 1) {
                this.addTile({ x: i, y: j}, 0);
            } else {
                this.addTile({ x: i, y: j}, 1)
            }
        }
    }
};

GameManager.prototype.addTile = function (position, value) {
    var tile = new Tile(position, value);
    this.grid.insertTile(tile);
};

GameManager.prototype.actuate = function () {

    this.storageManager.setGameState(this.serialize());

    if (this.alertObject == null) {
        this.createAlert("info", "Merge the tiles to make them equal to " + this.level);
    }

    this.actuator.actuate(this.grid, {
        moves: this.moves,
        level: this.level,
        won: this.won,
        newTiles: this.newTiles,
        inputManager: this.inputManager,
        alertObject: this.alertObject
    });
};

GameManager.prototype.performClickAction = function (position) {
    if (this.firstSelected === false) {
        this.firstSelected = true;
        this.firstPosition = position;
        this.grid.cells[position.x][position.y].selected = true;

        this.createAlert("info", "Merge the tiles to make them equal to " + this.level);

    } else {
        this.firstSelected = false;

        if (this.equalPosition(this.firstPosition, position)) {
            // cannot combine tile with itself
            this.createAlert("danger", "You cannot merge a tile with itself!");
        } else {

            var firstTileOldValue = this.grid.tileAtPosition(this.firstPosition).value;
            var secondTileOldValue = this.grid.tileAtPosition(position).value;

            var firstTileNewValue = firstTileOldValue + 1;
            var secondTileNewValue = secondTileOldValue + firstTileOldValue;

            if ((firstTileNewValue > this.level) || (secondTileNewValue > this.level)) {
                // tiles cannot have value greater than level
                this.createAlert("danger", "The tiles cannot be greater than the current level!");
            } else {
                this.grid.tileAtPosition(position).setValue(secondTileNewValue);
                this.grid.tileAtPosition(this.firstPosition).setValue(firstTileNewValue);

                this.grid.tileAtPosition(position).merged = true;
                this.grid.tileAtPosition(this.firstPosition).merged = true;

                this.grid.tileAtPosition(position).saveOldValue(secondTileOldValue);
                this.grid.tileAtPosition(this.firstPosition).saveOldValue(firstTileOldValue);

                this.storedValues.push({
                    firstTile: {
                        position: this.firstPosition,
                        value: firstTileOldValue
                    },
                    secondTile: {
                        position: position,
                        value: secondTileOldValue
                    }
                });

                this.moves = this.moves + 1;
                this.createAlert("info", "Merge the tiles to make them equal to " + this.level);
            }
        }

        this.grid.resetTilesSelectedState();
    }

    this.followUp();
};

GameManager.prototype.followUp = function () {
    if (this.userWon() === true) {
        // user has won
        this.createAlert("success", "You have completed the current level in " + this.moves + " move(s)!");
    }

    this.actuate();
};

GameManager.prototype.equalPosition = function (first, second) {
    return first.x === second.x && first.y === second.y;
};

GameManager.prototype.userWon = function () {
    for (var x = 0; x < this.size; x++) {
        for (var y = 0; y < this.size; y++) {
            var tile = this.grid.cells[x][y];

            if (tile.value !== this.level) {
                this.won = false;
                return false;
            }
        }
    }

    this.won = true;
    return true;
};

GameManager.prototype.undoAction = function () {

    var length = this.storedValues.length;

    if (length > 0) {
        if (this.firstSelected == true) {
            this.firstSelected = false;
            this.grid.cells[this.firstPosition.x][this.firstPosition.y].selected = false;
        }

        var firstTile = this.storedValues[length - 1].firstTile;
        var secondTile = this.storedValues[length - 1].secondTile;

        var firstTileOldValue = this.grid.tileAtPosition(firstTile.position).value;
        var secondTileOldValue = this.grid.tileAtPosition(secondTile.position).value;

        this.grid.tileAtPosition(firstTile.position).saveOldValue(firstTileOldValue);
        this.grid.tileAtPosition(secondTile.position).saveOldValue(secondTileOldValue);

        this.grid.tileAtPosition(firstTile.position).setValue(firstTile.value);
        this.grid.tileAtPosition(secondTile.position).setValue(secondTile.value);

        this.grid.tileAtPosition(firstTile.position).merged = true;
        this.grid.tileAtPosition(secondTile.position).merged = true;

        this.moves = this.moves + 1;

        this.storedValues.pop();
    }

    this.followUp();
};

GameManager.prototype.newGame = function(level) {
    this.won = false;

    this.moves = 0;
    this.level = level;

    for (var x = 0; x < this.size; x++) {
        for (var y = 0; y < this.size; y++) {
            this.grid.cells[x][y].value = 1;

            if (x == 1 && y == 1) {
                this.grid.cells[x][y].value = 0;
            }
        }
    }

    this.createAlert("info", "Merge the tiles to make them equal to " + level);

    this.newTiles = true;
    this.grid.resetTilesSelectedState();
    this.actuate();

    this.newTiles = false;
    this.storedValues.length = 0;
};

GameManager.prototype.restart = function () {

    this.storageManager.clearGameState();
    this.actuator.continueGame(); // clear the game won message
    this.newGame(1);
    this.grid.resetTilesSelectedState();
};

// Move to next level
GameManager.prototype.moveToNextLevel = function () {
    this.actuator.continueGame(); // Clear the game won message
    this.newGame(this.level + 1);
};

GameManager.prototype.createAlert = function (severity, details) {
    this.alertObject = {
        severity: severity,
        details: details
    };
};<commit_msg>Improvement to CL message 2<commit_after>function GameManager(size, InputManager, Actuator, StorageManager) {
    this.size = size; // Size of the grid
    this.actuator = new Actuator;
    this.inputManager = new InputManager;
    this.storageManager = new StorageManager;

    this.inputManager.on("click", this.performClickAction.bind(this));
    this.inputManager.on("undo", this.undoAction.bind(this));
    this.inputManager.on("moveToNextLevel", this.moveToNextLevel.bind(this));
    this.inputManager.on("restart", this.restart.bind(this));

    this.firstSelected = false;
    this.firstPosition = null;

    this.storedValues = [];

    this.setup();
}

GameManager.prototype.setup = function () {

    var previousState = this.storageManager.getGameState();

    // Reload the game from a previous game if present
    if (previousState) {
        this.grid        = new Grid(previousState.grid.size,
            previousState.grid.cells); // Reload grid
        this.moves       = previousState.moves;
        this.level       = previousState.level;
        this.won         = previousState.won;
        this.newTiles    = false;
        this.storedValues = previousState.storedValues;
    } else {
        this.grid = new Grid(this.size);
        this.moves = 0;
        this.level = 1;
        this.won = false;
        this.newTiles = false;

        this.addStartTiles();
    }

    this.actuate();
};

// Represent the current game as an object
GameManager.prototype.serialize = function () {
    return {
        grid:        this.grid.serialize(),
        moves:       this.moves,
        level:       this.level,
        won:         this.won,
        newTiles:    this.newTiles,
        storedValues:this.storedValues
    };
};

GameManager.prototype.addStartTiles = function () {
    for (var i=0; i < this.size; i++) {
        for (var j=0; j < this.size; j++) {

            if (i === 1 && j === 1) {
                this.addTile({ x: i, y: j}, 0);
            } else {
                this.addTile({ x: i, y: j}, 1)
            }
        }
    }
};

GameManager.prototype.addTile = function (position, value) {
    var tile = new Tile(position, value);
    this.grid.insertTile(tile);
};

GameManager.prototype.actuate = function () {

    this.storageManager.setGameState(this.serialize());

    if (this.alertObject == null) {
        this.createAlert("info", "Merge the tiles to make them equal to " + this.level);
    }

    this.actuator.actuate(this.grid, {
        moves: this.moves,
        level: this.level,
        won: this.won,
        newTiles: this.newTiles,
        inputManager: this.inputManager,
        alertObject: this.alertObject
    });
};

GameManager.prototype.performClickAction = function (position) {
    if (this.firstSelected === false) {
        this.firstSelected = true;
        this.firstPosition = position;
        this.grid.cells[position.x][position.y].selected = true;

        this.createAlert("info", "Merge the tiles to make them equal to " + this.level);

    } else {
        this.firstSelected = false;

        if (this.equalPosition(this.firstPosition, position)) {
            // cannot combine tile with itself
            this.createAlert("danger", "You cannot merge a tile with itself!");
        } else {

            var firstTileOldValue = this.grid.tileAtPosition(this.firstPosition).value;
            var secondTileOldValue = this.grid.tileAtPosition(position).value;

            var firstTileNewValue = firstTileOldValue + 1;
            var secondTileNewValue = secondTileOldValue + firstTileOldValue;

            if ((firstTileNewValue > this.level) || (secondTileNewValue > this.level)) {
                // tiles cannot have value greater than level
                this.createAlert("danger", "The tiles cannot be greater than the current level!");
            } else {
                this.grid.tileAtPosition(position).setValue(secondTileNewValue);
                this.grid.tileAtPosition(this.firstPosition).setValue(firstTileNewValue);

                this.grid.tileAtPosition(position).merged = true;
                this.grid.tileAtPosition(this.firstPosition).merged = true;

                this.grid.tileAtPosition(position).saveOldValue(secondTileOldValue);
                this.grid.tileAtPosition(this.firstPosition).saveOldValue(firstTileOldValue);

                this.storedValues.push({
                    firstTile: {
                        position: this.firstPosition,
                        value: firstTileOldValue
                    },
                    secondTile: {
                        position: position,
                        value: secondTileOldValue
                    }
                });

                this.moves = this.moves + 1;
                this.createAlert("info", "Merge the tiles to make them equal to " + this.level);
            }
        }

        this.grid.resetTilesSelectedState();
    }

    this.followUp();
};

GameManager.prototype.followUp = function () {
    if (this.userWon() === true) {
        // user has won
        this.createAlert("success", "You completed the current level in " + this.moves + " move(s)!");
    }

    this.actuate();
};

GameManager.prototype.equalPosition = function (first, second) {
    return first.x === second.x && first.y === second.y;
};

GameManager.prototype.userWon = function () {
    for (var x = 0; x < this.size; x++) {
        for (var y = 0; y < this.size; y++) {
            var tile = this.grid.cells[x][y];

            if (tile.value !== this.level) {
                this.won = false;
                return false;
            }
        }
    }

    this.won = true;
    return true;
};

GameManager.prototype.undoAction = function () {

    var length = this.storedValues.length;

    if (length > 0) {
        if (this.firstSelected == true) {
            this.firstSelected = false;
            this.grid.cells[this.firstPosition.x][this.firstPosition.y].selected = false;
        }

        var firstTile = this.storedValues[length - 1].firstTile;
        var secondTile = this.storedValues[length - 1].secondTile;

        var firstTileOldValue = this.grid.tileAtPosition(firstTile.position).value;
        var secondTileOldValue = this.grid.tileAtPosition(secondTile.position).value;

        this.grid.tileAtPosition(firstTile.position).saveOldValue(firstTileOldValue);
        this.grid.tileAtPosition(secondTile.position).saveOldValue(secondTileOldValue);

        this.grid.tileAtPosition(firstTile.position).setValue(firstTile.value);
        this.grid.tileAtPosition(secondTile.position).setValue(secondTile.value);

        this.grid.tileAtPosition(firstTile.position).merged = true;
        this.grid.tileAtPosition(secondTile.position).merged = true;

        this.moves = this.moves + 1;

        this.storedValues.pop();
    }

    this.followUp();
};

GameManager.prototype.newGame = function(level) {
    this.won = false;

    this.moves = 0;
    this.level = level;

    for (var x = 0; x < this.size; x++) {
        for (var y = 0; y < this.size; y++) {
            this.grid.cells[x][y].value = 1;

            if (x == 1 && y == 1) {
                this.grid.cells[x][y].value = 0;
            }
        }
    }

    this.createAlert("info", "Merge the tiles to make them equal to " + level);

    this.newTiles = true;
    this.grid.resetTilesSelectedState();
    this.actuate();

    this.newTiles = false;
    this.storedValues.length = 0;
};

GameManager.prototype.restart = function () {

    this.storageManager.clearGameState();
    this.actuator.continueGame(); // clear the game won message
    this.newGame(1);
    this.grid.resetTilesSelectedState();
};

// Move to next level
GameManager.prototype.moveToNextLevel = function () {
    this.actuator.continueGame(); // Clear the game won message
    this.newGame(this.level + 1);
};

GameManager.prototype.createAlert = function (severity, details) {
    this.alertObject = {
        severity: severity,
        details: details
    };
};<|endoftext|>


<commit_before>// Simulate config options from your production environment by
// customising the .env file in your project's root folder.
require('dotenv').load();

// Require keystone
var keystone = require('keystone');
var keystoneMultilingual = require('keystone-multilingual');

// Initialise Keystone with your project's configuration.
// See http://keystonejs.com/guide/config for available options
// and documentation.

keystone.init({
	'name': 'Our School Liberia',
	'brand': 'Our School Liberia',

	'sass': 'public',
	'static': 'public',
	'favicon': 'public/favicon.ico',
	'views': 'templates/views',
	'view engine': 'jade',

	'emails': 'templates/emails',

	'auto update': true,
	'session': true,
	'auth': true,
	'user model': 'User',
	'cookie secret': 'XW)Kb1t54s_MnM@"*`8`_gulw/*.LW94s_C>{NkG@ixc<-!i"X)/ox]:nVAb6"gb',
});

//must initialise before importing models
keystoneMultilingual.init(keystone);

// Load your project's Models
keystone.import('models');

// Setup common locals for your templates. The following are required for the
// bundled templates and layouts. Any runtime locals (that should be set uniquely
// for each request) should be added to ./routes/middleware.js

keystone.set('locals', {
	_: require('underscore'),
	env: keystone.get('env'),
	utils: keystone.utils,
	editable: keystone.content.editable
});

// Load your project's Routes
keystone.set('routes', require('./routes'));

// Setup common locals for your emails. The following are required by Keystone's
// default email templates, you may remove them if you're using your own.

keystone.set('email locals', {
	logo_src: '/images/logo-email.gif',
	logo_width: 194,
	logo_height: 76,
	theme: {
		email_bg: '#f9f9f9',
		link_color: '#2697de',
		buttons: {
			color: '#fff',
			background_color: '#2697de',
			border_color: '#1a7cb7'
		}
	}
});

// Setup replacement rules for emails, to automate the handling of differences
// between development a production.

// Be sure to update this rule to include your site's actual domain, and add
// other rules your email templates require.

keystone.set('email rules', [{
	find: '/images/',
	replace: (keystone.get('env') == 'production') ? 'http://www.your-server.com/images/' : 'http://localhost:3000/images/'
}, {
	find: '/keystone/',
	replace: (keystone.get('env') == 'production') ? 'http://www.your-server.com/keystone/' : 'http://localhost:3000/keystone/'
}]);

// Load your project's email test routes

keystone.set('email tests', require('./routes/emails'));

// Configure the navigation bar in Keystone's Admin UI

keystone.set('nav', {
	'news': [
		'updates',
		'videos',
		'press-releases',
		'categories',
		'galleries',
	],
	'get-involved': [
		'events'
	],
	'about': [
		'students',
		'team-members',
		'supporter-individuals',
		'supporter-companies',
		'financial-reports',
	],
	'enquiries': 'enquiries',
	'users': 'users'
});

// Start Keystone to connect to your database and initialise the web server

keystone.start();
<commit_msg>removing updated keystone conflict<commit_after>// Simulate config options from your production environment by
// customising the .env file in your project's root folder.
require('dotenv').load();

// Require keystone
var keystone = require('keystone');
var keystoneMultilingual = require('keystone-multilingual');

// Initialise Keystone with your project's configuration.
// See http://keystonejs.com/guide/config for available options
// and documentation.

keystone.init({
	'name': 'Our School Liberia',
	'brand': 'Our School Liberia',

	'sass': 'public',
	'static': 'public',
	'favicon': 'public/favicon.ico',
	'views': 'templates/views',
	'view engine': 'jade',

	'emails': 'templates/emails',

	'auto update': true,
	'session': true,
	'auth': true,
	'user model': 'User',
	'cookie secret': 'XW)Kb1t54s_MnM@"*`8`_gulw/*.LW94s_C>{NkG@ixc<-!i"X)/ox]:nVAb6"gb',
});

//must initialise before importing models
keystoneMultilingual.init(keystone);

// Load your project's Models
keystone.import('models');

// Setup common locals for your templates. The following are required for the
// bundled templates and layouts. Any runtime locals (that should be set uniquely
// for each request) should be added to ./routes/middleware.js

keystone.set('locals', {
	_: require('underscore'),
	env: keystone.get('env'),
	utils: keystone.utils,
	editable: keystone.content.editable
});

// Load your project's Routes
keystone.set('routes', require('./routes'));

// Setup common locals for your emails. The following are required by Keystone's
// default email templates, you may remove them if you're using your own.

keystone.set('email locals', {
	logo_src: '/images/logo-email.gif',
	logo_width: 194,
	logo_height: 76,
	theme: {
		email_bg: '#f9f9f9',
		link_color: '#2697de',
		buttons: {
			color: '#fff',
			background_color: '#2697de',
			border_color: '#1a7cb7'
		}
	}
});

// Setup replacement rules for emails, to automate the handling of differences
// between development a production.

// Be sure to update this rule to include your site's actual domain, and add
// other rules your email templates require.

// keystone.set('email rules', [{
// 	find: '/images/',
// 	replace: (keystone.get('env') == 'production') ? 'http://www.your-server.com/images/' : 'http://localhost:3000/images/'
// }, {
// 	find: '/keystone/',
// 	replace: (keystone.get('env') == 'production') ? 'http://www.your-server.com/keystone/' : 'http://localhost:3000/keystone/'
// }]);

// Load your project's email test routes

keystone.set('email tests', require('./routes/emails'));

// Configure the navigation bar in Keystone's Admin UI

keystone.set('nav', {
	'news': [
		'updates',
		'videos',
		'press-releases',
		'categories',
		'galleries',
	],
	'get-involved': [
		'events'
	],
	'about': [
		'students',
		'team-members',
		'supporter-individuals',
		'supporter-companies',
		'financial-reports',
	],
	'enquiries': 'enquiries',
	'users': 'users'
});

// Start Keystone to connect to your database and initialise the web server

keystone.start();
<|endoftext|>


<commit_before>function GameManager(size, InputManager, Actuator, ScoreManager) {
  this.size         = size; // Size of the grid
  this.inputManager = new InputManager;
  this.scoreManager = new ScoreManager;
  this.actuator     = new Actuator;

  this.startTiles   = 2;

  this.inputManager.on("move", this.move.bind(this));
  this.inputManager.on("restart", this.restart.bind(this));
  this.inputManager.on("keepPlaying", this.keepPlaying.bind(this));

  this.setup();
}

// Restart the game
GameManager.prototype.restart = function () {
  this.actuator.continue();
  this.setup();
};

// Keep playing after winning
GameManager.prototype.keepPlaying = function () {
  this.keepPlaying = true;
  this.actuator.continue();
};

GameManager.prototype.isGameTerminated = function () {
  if (this.over || (this.won && !this.keepPlaying)) {
    return true;
  } else {
    return false;
  }
};

// Set up the game
GameManager.prototype.setup = function () {
  this.grid        = new Grid(this.size);

  this.score       = 0;
  this.over        = false;
  this.won         = false;
  this.keepPlaying = false;

  // Add the initial tiles
  this.addStartTiles();

  // Update the actuator
  this.actuate();
};

// Set up the initial tiles to start the game with
GameManager.prototype.addStartTiles = function () {
  for (var i = 0; i < this.startTiles; i++) {
    this.addRandomTile();
  }
};

// Adds a tile in a random position
GameManager.prototype.addRandomTile = function () {
  if (this.grid.cellsAvailable()) {
    var value = Math.random() < 0.9 ? 2 : 4;
    //var tile = new Tile(this.grid.randomAvailableCell(), value);
    var tile = new Tile(this.grid.randomAvailableCell(), value);

    this.grid.insertTile(tile);
  }
};

// Sends the updated grid to the actuator
GameManager.prototype.actuate = function () {
  if (this.scoreManager.get() < this.score) {
    this.scoreManager.set(this.score);
  }

  this.actuator.actuate(this.grid, {
    score:      this.score,
    over:       this.over,
    won:        this.won,
    bestScore:  this.scoreManager.get(),
    terminated: this.isGameTerminated()
  });

};

// Save all tile positions and remove merger info
GameManager.prototype.prepareTiles = function () {
  this.grid.eachCell(function (x, y, tile) {
    if (tile) {
      tile.mergedFrom = null;
      tile.savePosition();
    }
  });
};

// Move a tile and its representation
GameManager.prototype.moveTile = function (tile, cell) {
  this.grid.cells[tile.x][tile.y] = null;
  this.grid.cells[cell.x][cell.y] = tile;
  tile.updatePosition(cell);
};

// Move tiles on the grid in the specified direction
GameManager.prototype.move = function (direction) {
  // 0: up, 1: right, 2:down, 3: left
  var self = this;

  if (this.isGameTerminated()) return; // Don't do anything if the game's over

  var cell, tile;

  var vector     = this.getVector(direction);
  var traversals = this.buildTraversals(vector);
  var moved      = false;

  // Save the current tile positions and remove merger information
  this.prepareTiles();

  // Traverse the grid in the right direction and move tiles
  traversals.x.forEach(function (x) {
    traversals.y.forEach(function (y) {
      cell = { x: x, y: y };
      tile = self.grid.cellContent(cell);

      if (tile) {
        var positions = self.findFarthestPosition(cell, vector);
        var next      = self.grid.cellContent(positions.next);

        // Only one merger per row traversal?
        if (next && next.value === tile.value && !next.mergedFrom) {
          //var merged = new Tile(positions.next, tile.value * 2);
          var merged = new Tile(positions.next, 2048);
          merged.mergedFrom = [tile, next];

          self.grid.insertTile(merged);
          self.grid.removeTile(tile);

          // Converge the two tiles' positions
          tile.updatePosition(positions.next);

          // Update the score
          self.score += merged.value;

          // The mighty 2048 tile
          if (merged.value === 2048) self.won = true;
        } else {
          self.moveTile(tile, positions.farthest);
        }

        if (!self.positionsEqual(cell, tile)) {
          moved = true; // The tile moved from its original cell!
        }
      }
    });
  });

  if (moved) {
    this.addRandomTile();

    if (!this.movesAvailable()) {
      this.over = true; // Game over!
    }

    this.actuate();
  }
};

// Get the vector representing the chosen direction
GameManager.prototype.getVector = function (direction) {
  // Vectors representing tile movement
  var map = {
    0: { x: 0,  y: -1 }, // up
    1: { x: 1,  y: 0 },  // right
    2: { x: 0,  y: 1 },  // down
    3: { x: -1, y: 0 }   // left
  };

  return map[direction];
};

// Build a list of positions to traverse in the right order
GameManager.prototype.buildTraversals = function (vector) {
  var traversals = { x: [], y: [] };

  for (var pos = 0; pos < this.size; pos++) {
    traversals.x.push(pos);
    traversals.y.push(pos);
  }

  // Always traverse from the farthest cell in the chosen direction
  if (vector.x === 1) traversals.x = traversals.x.reverse();
  if (vector.y === 1) traversals.y = traversals.y.reverse();

  return traversals;
};

GameManager.prototype.findFarthestPosition = function (cell, vector) {
  var previous;

  // Progress towards the vector direction until an obstacle is found
  do {
    previous = cell;
    cell     = { x: previous.x + vector.x, y: previous.y + vector.y };
  } while (this.grid.withinBounds(cell) &&
           this.grid.cellAvailable(cell));

  return {
    farthest: previous,
    next: cell // Used to check if a merge is required
  };
};

GameManager.prototype.movesAvailable = function () {
  return this.grid.cellsAvailable() || this.tileMatchesAvailable();
};

// Check for available matches between tiles (more expensive check)
GameManager.prototype.tileMatchesAvailable = function () {
  var self = this;

  var tile;

  for (var x = 0; x < this.size; x++) {
    for (var y = 0; y < this.size; y++) {
      tile = this.grid.cellContent({ x: x, y: y });

      if (tile) {
        for (var direction = 0; direction < 4; direction++) {
          var vector = self.getVector(direction);
          var cell   = { x: x + vector.x, y: y + vector.y };

          var other  = self.grid.cellContent(cell);

          if (other && other.value === tile.value) {
            return true; // These two tiles can be merged
          }
        }
      }
    }
  }

  return false;
};

GameManager.prototype.positionsEqual = function (first, second) {
  return first.x === second.x && first.y === second.y;
};
<commit_msg>Update game_manager.js<commit_after>function GameManager(size, InputManager, Actuator, ScoreManager) {
  this.size         = size; // Size of the grid
  this.inputManager = new InputManager;
  this.scoreManager = new ScoreManager;
  this.actuator     = new Actuator;

  this.startTiles   = 2;

  this.inputManager.on("move", this.move.bind(this));
  this.inputManager.on("restart", this.restart.bind(this));
  this.inputManager.on("keepPlaying", this.keepPlaying.bind(this));

  this.setup();
}

// Restart the game
GameManager.prototype.restart = function () {
  this.actuator.continue();
  this.setup();
};

// Keep playing after winning
GameManager.prototype.keepPlaying = function () {
  this.keepPlaying = true;
  this.actuator.continue();
};

GameManager.prototype.isGameTerminated = function () {
  if (this.over || (this.won && !this.keepPlaying)) {
    return true;
  } else {
    return false;
  }
};

// Set up the game
GameManager.prototype.setup = function () {
  this.grid        = new Grid(this.size);

  this.score       = 0;
  this.over        = false;
  this.won         = false;
  this.keepPlaying = false;

  // Add the initial tiles
  this.addStartTiles();

  // Update the actuator
  this.actuate();
};

// Set up the initial tiles to start the game with
GameManager.prototype.addStartTiles = function () {
  for (var i = 0; i < this.startTiles; i++) {
    this.addRandomTile();
  }
};

// Adds a tile in a random position
GameManager.prototype.addRandomTile = function () {
  if (this.grid.cellsAvailable()) {
    var value = Math.random() < 0.9 ? 2 : 4;
    //var tile = new Tile(this.grid.randomAvailableCell(), value);
    var tile = new Tile(this.grid.randomAvailableCell(), .5);

    this.grid.insertTile(tile);
  }
};

// Sends the updated grid to the actuator
GameManager.prototype.actuate = function () {
  if (this.scoreManager.get() < this.score) {
    this.scoreManager.set(this.score);
  }

  this.actuator.actuate(this.grid, {
    score:      this.score,
    over:       this.over,
    won:        this.won,
    bestScore:  this.scoreManager.get(),
    terminated: this.isGameTerminated()
  });

};

// Save all tile positions and remove merger info
GameManager.prototype.prepareTiles = function () {
  this.grid.eachCell(function (x, y, tile) {
    if (tile) {
      tile.mergedFrom = null;
      tile.savePosition();
    }
  });
};

// Move a tile and its representation
GameManager.prototype.moveTile = function (tile, cell) {
  this.grid.cells[tile.x][tile.y] = null;
  this.grid.cells[cell.x][cell.y] = tile;
  tile.updatePosition(cell);
};

// Move tiles on the grid in the specified direction
GameManager.prototype.move = function (direction) {
  // 0: up, 1: right, 2:down, 3: left
  var self = this;

  if (this.isGameTerminated()) return; // Don't do anything if the game's over

  var cell, tile;

  var vector     = this.getVector(direction);
  var traversals = this.buildTraversals(vector);
  var moved      = false;

  // Save the current tile positions and remove merger information
  this.prepareTiles();

  // Traverse the grid in the right direction and move tiles
  traversals.x.forEach(function (x) {
    traversals.y.forEach(function (y) {
      cell = { x: x, y: y };
      tile = self.grid.cellContent(cell);

      if (tile) {
        var positions = self.findFarthestPosition(cell, vector);
        var next      = self.grid.cellContent(positions.next);

        // Only one merger per row traversal?
        if (next && next.value === tile.value && !next.mergedFrom) {
          //var merged = new Tile(positions.next, tile.value * 2);
          var merged = new Tile(positions.next, 2048);
          merged.mergedFrom = [tile, next];

          self.grid.insertTile(merged);
          self.grid.removeTile(tile);

          // Converge the two tiles' positions
          tile.updatePosition(positions.next);

          // Update the score
          self.score += merged.value;

          // The mighty 2048 tile
          if (merged.value === 2048) self.won = true;
        } else {
          self.moveTile(tile, positions.farthest);
        }

        if (!self.positionsEqual(cell, tile)) {
          moved = true; // The tile moved from its original cell!
        }
      }
    });
  });

  if (moved) {
    this.addRandomTile();

    if (!this.movesAvailable()) {
      this.over = true; // Game over!
    }

    this.actuate();
  }
};

// Get the vector representing the chosen direction
GameManager.prototype.getVector = function (direction) {
  // Vectors representing tile movement
  var map = {
    0: { x: 0,  y: -1 }, // up
    1: { x: 1,  y: 0 },  // right
    2: { x: 0,  y: 1 },  // down
    3: { x: -1, y: 0 }   // left
  };

  return map[direction];
};

// Build a list of positions to traverse in the right order
GameManager.prototype.buildTraversals = function (vector) {
  var traversals = { x: [], y: [] };

  for (var pos = 0; pos < this.size; pos++) {
    traversals.x.push(pos);
    traversals.y.push(pos);
  }

  // Always traverse from the farthest cell in the chosen direction
  if (vector.x === 1) traversals.x = traversals.x.reverse();
  if (vector.y === 1) traversals.y = traversals.y.reverse();

  return traversals;
};

GameManager.prototype.findFarthestPosition = function (cell, vector) {
  var previous;

  // Progress towards the vector direction until an obstacle is found
  do {
    previous = cell;
    cell     = { x: previous.x + vector.x, y: previous.y + vector.y };
  } while (this.grid.withinBounds(cell) &&
           this.grid.cellAvailable(cell));

  return {
    farthest: previous,
    next: cell // Used to check if a merge is required
  };
};

GameManager.prototype.movesAvailable = function () {
  return this.grid.cellsAvailable() || this.tileMatchesAvailable();
};

// Check for available matches between tiles (more expensive check)
GameManager.prototype.tileMatchesAvailable = function () {
  var self = this;

  var tile;

  for (var x = 0; x < this.size; x++) {
    for (var y = 0; y < this.size; y++) {
      tile = this.grid.cellContent({ x: x, y: y });

      if (tile) {
        for (var direction = 0; direction < 4; direction++) {
          var vector = self.getVector(direction);
          var cell   = { x: x + vector.x, y: y + vector.y };

          var other  = self.grid.cellContent(cell);

          if (other && other.value === tile.value) {
            return true; // These two tiles can be merged
          }
        }
      }
    }
  }

  return false;
};

GameManager.prototype.positionsEqual = function (first, second) {
  return first.x === second.x && first.y === second.y;
};
<|endoftext|>


<commit_before>IconEnum = {
	ARROW: 0,
	NUMBER: 1,
	FIRE: 2,
	GIFT: 3
	// Future enhancements - new icons: reveal area, moves, shuffle
};

var Icon = Backbone.Model.extend({});

var Tile = Backbone.Model.extend({

	initialize: function() {

		this.revealed = false;
		this.dead = false;
	}
});

var TileCollection = Backbone.Collection.extend({

	model: Tile
});

var Grid = Backbone.Model.extend({

	grid: new TileCollection(),

	initialize: function() {

		for (var y = 7; y >= 1; y--)
			for (var x = 1; x <= 9; x++)
				this.grid.add(new Tile({ x: x, y: y }));

		this.populateIcons();
	},

	populateIcons: function() {

		var self = this;

		var tiles = this.grid.models.slice();

		var giftTile = addIconToRandomTile(IconEnum.GIFT, tiles);

		for (var i = 0; i < this.get("fires"); i++)
			addIconToRandomTile(IconEnum.FIRE, tiles);

		for (var i = 0; i < this.get("numbers"); i++)
			addNumberIconToRandomSuitableTile(tiles);

		for (var i = 0; i < this.get("arrows"); i++)
			addIconToRandomTile(IconEnum.ARROW, tiles);

		function addNumberIconToRandomSuitableTile(tiles) {

			// Limit the distance from the gift that number tiles can be.
			var suitableTiles = _.filter(tiles, function(tile) { return (self.getDistanceFromGiftTile(tile, giftTile) <= Math.round(Math.sqrt(self.grid.models.length))) });

			var tile = addIconToRandomTile(IconEnum.NUMBER, suitableTiles);

			tiles.splice(tiles.indexOf(tile), 1);
		}

		function addIconToRandomTile(icon, tiles) {

			var randomLocation = Math.floor(Math.random() * (tiles.length - 1));
			var tile = tiles[randomLocation];

			var value;
			switch(icon) {

				case IconEnum.ARROW:  value = self.getDirectionToGift(tile, giftTile); break;
				case IconEnum.NUMBER: value = self.getDistanceFromGiftTile(tile, giftTile); break;
				default:              value = null;
			}

			tile.set("icon", new Icon({ type: icon, value: value }));
			tiles.splice(randomLocation, 1);

			return tile;
		}
	},

	getDirectionToGift: function(tile, giftTile) {

		var useLeftRight;

		// Determine whether this arrow will be left/right or up/down (sometimes there's no choice)
		if (tile.get("x") == giftTile.get("x")) {

			useLeftRight = false;
		}
		else if (tile.get("y") == giftTile.get("y")) {

			useLeftRight = true;
		}
		else {

			useLeftRight = (Math.random() > 0.5);
		}

		if (useLeftRight) {

			return (tile.get("x") > giftTile.get("x") ? "left" : "right");
		}
		else {

			return (tile.get("y") > giftTile.get("y") ? "down" : "up");
		}
	},

	getDistanceFromGiftTile: function(tile, giftTile) {

		return (Math.abs(tile.get("x") - giftTile.get("x")) + Math.abs(tile.get("y") - giftTile.get("y")));
	}
});

var TileView = Backbone.Marionette.ItemView.extend({

	className: "grid-cell",

	events: {
		"click": "tileClicked",
		"tap": "tileClicked"
	},

	render: function() {

		if (this.model.get("revealed")) {

			this.$el.addClass("revealed");
			this.addContent();
		}
		else if (this.model.get("dead")) {

			this.$el.addClass("dead");
		}
		else {

			this.$el.removeClass("revealed");

			// Remove content
			this.$el.removeClass("bad neutral good");
			this.$el.html("");
		}

		return this;
	},

	tileClicked: function() {

		if (!this.model.get("revealed")) {

			this.reveal();
			this.performAction();
			this.trigger("tile:revealed");

		}
	},

	reveal: function() {

		this.model.set("revealed", true);
		this.render();
	},

	hide: function() {

		if (this.model.get("revealed")) {

			this.model.set("revealed", false);
			this.render();
		}
	},

	addContent: function() {},

	performAction: function() {}
});

var EmptyTileView = TileView.extend({

	addContent: function() {

		this.$el.addClass("neutral");
	}
});

var GiftTileView = TileView.extend({

	addContent: function() {

		this.$el.addClass("gift");
		this.$el.html('');
	},

	reveal: function() {

		TileView.prototype.reveal.call(this);

		this.trigger("win");
	},

	performAction: function() {

		this.$(".glyphicon").addClass("animated flash");
	}
});

var FireTileView = TileView.extend({

	addContent: function() {

		this.$el.addClass("bad");
		this.$el.html('');
	},

	hide: function() {

		TileView.prototype.hide.call(this);

		this.$el.removeClass("animated pulse");
	},

	performAction: function() {

		var self = this;

		this.$el.addClass("animated pulse");

		this.collection.each(function(tileView) {

			// Hide all tiles except this one
			if (tileView != self) tileView.hide();
		});
	}
});

var ArrowTileView = TileView.extend({

	addContent: function() {

		this.$el.addClass("good");
		this.$el.html('');
	}
});

var NumberTileView = TileView.extend({

	addContent: function() {

		this.$el.addClass("good");
		this.$el.html(this.model.get("icon").get("value"));
	}
});

var GridView = Backbone.Marionette.CollectionView.extend({

	el: "#game-grid",

	childView: TileView,

	events: {
		"click .grid-cell": "tileClicked"
	},

	onRender: function() {

		// Wrap cells in a .grid-row every 9 cells
		var cells = $("#game-grid > .grid-cell");

		for(var i = 0; i < cells.length; i += 9)
			cells.slice(i, i + 9).wrapAll('
');
	},

	buildChildView: function(child, ChildViewClass, childViewOptions) {

		var icon = child.get("icon") ? child.get("icon").get("type") : null;

		var options = _.extend({ model: child, collection: this.children }, childViewOptions);

		switch (icon) {

			case IconEnum.FIRE:   return new FireTileView(options);
			case IconEnum.ARROW:  return new ArrowTileView(options);
			case IconEnum.NUMBER: return new NumberTileView(options);
			case IconEnum.GIFT:   return new GiftTileView(options);
			default:              return new EmptyTileView(options);
		}
	},

	show: function() {

		this.$el.addClass("animated fadeIn");
		this.render();
	},

	revealAll: function() {

		var self = this;

		var delay = 0;

		this.children.each(function(tile) {

			if(!tile.model.get("revealed")) {

				setTimeout(function() {

					tile.reveal();

					if ((tile.model.get("x") === 9) && (tile.model.get("y") === 1))
						self.trigger("end");

				}, delay);

				delay += 75;
			}
		});
	},

	onChildviewTileRevealed: function() {

		this.trigger("tile:revealed");
	},

	onChildviewWin: function() {

		this.trigger("win");
	}
});

var StatsView = Backbone.View.extend({

	el: "#stats",

	template: _.template($("#template-stats").html()),

	render: function() {

		this.$el.html(this.template(this.model.attributes));
		return this;
	},

	show: function() {

		this.$el.addClass("animated fadeIn");
		this.render();
	}
});
var App = Backbone.Model.extend({

	initialize: function() {

		var self = this;

		this.set({
			won: false,
			moves: 25,
			fires: 4,
			arrows: 5,
			numbers: 5
		});

		this.set({ movesRemaining: self.get("moves") });
	}
});

var EndDialogView = Backbone.View.extend({

	id: "end-dialog",

	className: "animated fadeIn",

	template: _.template($("#template-end-dialog").html()),

	render: function() {

		this.$el.html(this.template(this.model.attributes));
		return this;
	}
});

var AppView = Backbone.View.extend({

	el: "#game",

	model: new App(),

	initialize: function() {

		this.start();
	},

	start: function() {

		var self = this;

		this.grid = new GridView({ collection: new Grid({ fires: this.model.get("fires"), arrows: this.model.get("arrows"), numbers: this.model.get("numbers") }).grid })
		this.listenTo(this.grid, "tile:revealed", this.tileRevealed);
		this.listenTo(this.grid, "win", this.win);

		this.stats = new StatsView({ model: this.model });

		this.stats.render();
		this.grid.show();

		$(setTimeout(function() { self.stats.show() }, 1000));
	},

	tileRevealed: function() {

		// Just in case, prevent moves remaining from ever displaying below 0
		if (this.model.get("movesRemaining") > 0)
			this.model.set("movesRemaining", this.model.get("movesRemaining") - 1);

		this.stats.render();

		if (this.model.get("movesRemaining") == 0)
			this.lose();
	},

	lose: function() {

		this.grid.children.each(function(child) {

			child.model.set("dead", true);
			child.render();
		});

		this.reveal();
	},

	win: function() {

		this.model.set("won", true);

		this.reveal();
	},

	reveal: function(result) {

		var self = this;

		this.disableBoard();

		this.listenTo(this.grid, "end", this.showEndDialog);

		setTimeout(function() { self.grid.revealAll() }, 1000);
	},

	disableBoard: function() {

		this.stopListening();
		this.grid.children.each(function(child) { child.undelegateEvents() });
	},

	showEndDialog: function() {

		var self = this;

		setTimeout(function() {

			$(".grid-row").fadeTo("slow", 0.5);

			var end = new EndDialogView({ model: self.model });
			self.grid.$el.append(end.$el);
			end.render();

		}, 1000);
	}
});

new AppView();<commit_msg>fade stats on game end<commit_after>IconEnum = {
	ARROW: 0,
	NUMBER: 1,
	FIRE: 2,
	GIFT: 3
	// Future enhancements - new icons: reveal area, moves, shuffle
};

var Icon = Backbone.Model.extend({});

var Tile = Backbone.Model.extend({

	initialize: function() {

		this.revealed = false;
		this.dead = false;
	}
});

var TileCollection = Backbone.Collection.extend({

	model: Tile
});

var Grid = Backbone.Model.extend({

	grid: new TileCollection(),

	initialize: function() {

		for (var y = 7; y >= 1; y--)
			for (var x = 1; x <= 9; x++)
				this.grid.add(new Tile({ x: x, y: y }));

		this.populateIcons();
	},

	populateIcons: function() {

		var self = this;

		var tiles = this.grid.models.slice();

		var giftTile = addIconToRandomTile(IconEnum.GIFT, tiles);

		for (var i = 0; i < this.get("fires"); i++)
			addIconToRandomTile(IconEnum.FIRE, tiles);

		for (var i = 0; i < this.get("numbers"); i++)
			addNumberIconToRandomSuitableTile(tiles);

		for (var i = 0; i < this.get("arrows"); i++)
			addIconToRandomTile(IconEnum.ARROW, tiles);

		function addNumberIconToRandomSuitableTile(tiles) {

			// Limit the distance from the gift that number tiles can be.
			var suitableTiles = _.filter(tiles, function(tile) { return (self.getDistanceFromGiftTile(tile, giftTile) <= Math.round(Math.sqrt(self.grid.models.length))) });

			var tile = addIconToRandomTile(IconEnum.NUMBER, suitableTiles);

			tiles.splice(tiles.indexOf(tile), 1);
		}

		function addIconToRandomTile(icon, tiles) {

			var randomLocation = Math.floor(Math.random() * (tiles.length - 1));
			var tile = tiles[randomLocation];

			var value;
			switch(icon) {

				case IconEnum.ARROW:  value = self.getDirectionToGift(tile, giftTile); break;
				case IconEnum.NUMBER: value = self.getDistanceFromGiftTile(tile, giftTile); break;
				default:              value = null;
			}

			tile.set("icon", new Icon({ type: icon, value: value }));
			tiles.splice(randomLocation, 1);

			return tile;
		}
	},

	getDirectionToGift: function(tile, giftTile) {

		var useLeftRight;

		// Determine whether this arrow will be left/right or up/down (sometimes there's no choice)
		if (tile.get("x") == giftTile.get("x")) {

			useLeftRight = false;
		}
		else if (tile.get("y") == giftTile.get("y")) {

			useLeftRight = true;
		}
		else {

			useLeftRight = (Math.random() > 0.5);
		}

		if (useLeftRight) {

			return (tile.get("x") > giftTile.get("x") ? "left" : "right");
		}
		else {

			return (tile.get("y") > giftTile.get("y") ? "down" : "up");
		}
	},

	getDistanceFromGiftTile: function(tile, giftTile) {

		return (Math.abs(tile.get("x") - giftTile.get("x")) + Math.abs(tile.get("y") - giftTile.get("y")));
	}
});

var TileView = Backbone.Marionette.ItemView.extend({

	className: "grid-cell",

	events: {
		"click": "tileClicked",
		"tap": "tileClicked"
	},

	render: function() {

		if (this.model.get("revealed")) {

			this.$el.addClass("revealed");
			this.addContent();
		}
		else if (this.model.get("dead")) {

			this.$el.addClass("dead");
		}
		else {

			this.$el.removeClass("revealed");

			// Remove content
			this.$el.removeClass("bad neutral good");
			this.$el.html("");
		}

		return this;
	},

	tileClicked: function() {

		if (!this.model.get("revealed")) {

			this.reveal();
			this.performAction();
			this.trigger("tile:revealed");

		}
	},

	reveal: function() {

		this.model.set("revealed", true);
		this.render();
	},

	hide: function() {

		if (this.model.get("revealed")) {

			this.model.set("revealed", false);
			this.render();
		}
	},

	addContent: function() {},

	performAction: function() {}
});

var EmptyTileView = TileView.extend({

	addContent: function() {

		this.$el.addClass("neutral");
	}
});

var GiftTileView = TileView.extend({

	addContent: function() {

		this.$el.addClass("gift");
		this.$el.html('');
	},

	reveal: function() {

		TileView.prototype.reveal.call(this);

		this.trigger("win");
	},

	performAction: function() {

		this.$(".glyphicon").addClass("animated flash");
	}
});

var FireTileView = TileView.extend({

	addContent: function() {

		this.$el.addClass("bad");
		this.$el.html('');
	},

	hide: function() {

		TileView.prototype.hide.call(this);

		this.$el.removeClass("animated pulse");
	},

	performAction: function() {

		var self = this;

		this.$el.addClass("animated pulse");

		this.collection.each(function(tileView) {

			// Hide all tiles except this one
			if (tileView != self) tileView.hide();
		});
	}
});

var ArrowTileView = TileView.extend({

	addContent: function() {

		this.$el.addClass("good");
		this.$el.html('');
	}
});

var NumberTileView = TileView.extend({

	addContent: function() {

		this.$el.addClass("good");
		this.$el.html(this.model.get("icon").get("value"));
	}
});

var GridView = Backbone.Marionette.CollectionView.extend({

	el: "#game-grid",

	childView: TileView,

	events: {
		"click .grid-cell": "tileClicked"
	},

	onRender: function() {

		// Wrap cells in a .grid-row every 9 cells
		var cells = $("#game-grid > .grid-cell");

		for(var i = 0; i < cells.length; i += 9)
			cells.slice(i, i + 9).wrapAll('
');
	},

	buildChildView: function(child, ChildViewClass, childViewOptions) {

		var icon = child.get("icon") ? child.get("icon").get("type") : null;

		var options = _.extend({ model: child, collection: this.children }, childViewOptions);

		switch (icon) {

			case IconEnum.FIRE:   return new FireTileView(options);
			case IconEnum.ARROW:  return new ArrowTileView(options);
			case IconEnum.NUMBER: return new NumberTileView(options);
			case IconEnum.GIFT:   return new GiftTileView(options);
			default:              return new EmptyTileView(options);
		}
	},

	show: function() {

		this.$el.addClass("animated fadeIn");
		this.render();
	},

	revealAll: function() {

		var self = this;

		var delay = 0;

		this.children.each(function(tile) {

			if(!tile.model.get("revealed")) {

				setTimeout(function() {

					tile.reveal();

					if ((tile.model.get("x") === 9) && (tile.model.get("y") === 1))
						self.trigger("end");

				}, delay);

				delay += 75;
			}
		});
	},

	onChildviewTileRevealed: function() {

		this.trigger("tile:revealed");
	},

	onChildviewWin: function() {

		this.trigger("win");
	}
});

var StatsView = Backbone.View.extend({

	el: "#stats",

	template: _.template($("#template-stats").html()),

	render: function() {

		this.$el.html(this.template(this.model.attributes));
		return this;
	},

	show: function() {

		this.$el.addClass("animated fadeIn");
		this.render();
	}
});
var App = Backbone.Model.extend({

	initialize: function() {

		var self = this;

		this.set({
			won: false,
			moves: 25,
			fires: 4,
			arrows: 5,
			numbers: 5
		});

		this.set({ movesRemaining: self.get("moves") });
	}
});

var EndDialogView = Backbone.View.extend({

	id: "end-dialog",

	className: "animated fadeIn",

	template: _.template($("#template-end-dialog").html()),

	render: function() {

		this.$el.html(this.template(this.model.attributes));
		return this;
	}
});

var AppView = Backbone.View.extend({

	el: "#game",

	model: new App(),

	initialize: function() {

		this.start();
	},

	start: function() {

		var self = this;

		this.grid = new GridView({ collection: new Grid({ fires: this.model.get("fires"), arrows: this.model.get("arrows"), numbers: this.model.get("numbers") }).grid })
		this.listenTo(this.grid, "tile:revealed", this.tileRevealed);
		this.listenTo(this.grid, "win", this.win);

		this.stats = new StatsView({ model: this.model });

		this.stats.render();
		this.grid.show();

		$(setTimeout(function() { self.stats.show() }, 1000));
	},

	tileRevealed: function() {

		// Just in case, prevent moves remaining from ever displaying below 0
		if (this.model.get("movesRemaining") > 0)
			this.model.set("movesRemaining", this.model.get("movesRemaining") - 1);

		this.stats.render();

		if (this.model.get("movesRemaining") == 0)
			this.lose();
	},

	lose: function() {

		this.grid.children.each(function(child) {

			child.model.set("dead", true);
			child.render();
		});

		this.reveal();
	},

	win: function() {

		this.model.set("won", true);

		this.reveal();
	},

	reveal: function(result) {

		var self = this;

		this.disableBoard();

		this.listenTo(this.grid, "end", this.showEndDialog);

		setTimeout(function() { self.grid.revealAll() }, 1000);
	},

	disableBoard: function() {

		this.stopListening();
		this.grid.children.each(function(child) { child.undelegateEvents() });
	},

	showEndDialog: function() {

		var self = this;

		setTimeout(function() {

			$(".stats-row, .grid-row").fadeTo("slow", 0.5);

			var end = new EndDialogView({ model: self.model });
			self.grid.$el.append(end.$el);
			end.render();

		}, 1000);
	}
});

new AppView();<|endoftext|>