<commit_before>angular.module("materialCalendar", ["ngMaterial", "ngSanitize"]);

angular.module("materialCalendar").constant("materialCalendar.config", {
    version: "0.2.13",
    debug: document.domain.indexOf("localhost") > -1
});

angular.module("materialCalendar").config(["materialCalendar.config", "$logProvider", "$compileProvider", function (config, $logProvider, $compileProvider) {
    if (config.debug) {
        $logProvider.debugEnabled(false);
        $compileProvider.debugInfoEnabled(false);
    }
}]);

angular.module("materialCalendar").service("materialCalendar.Calendar", [function () {

    function Calendar(year, month, options) {

        var now = new Date();

        this.setWeekStartsOn = function (i) {
            var d = parseInt(i || 0, 10);
            if (!isNaN(d) && d >= 0 && d <= 6) {
                this.weekStartsOn = d;
            } else {
                this.weekStartsOn = 0;
            }
            return this.weekStartsOn;
        };

        this.setStartDateOfMonth = function (i) {
            var d = parseInt(i || 1, 10);
            if (!isNaN(d) && d >= 1 && d <= 31) {
                this.startDateOfMonth = d;
            } else {
                this.startDateOfMonth = 1;
            }
            return this.startDateOfMonth;
        };

        this.setNoOfDays = function (i) {
            var d = parseInt(i || 0, 10);
            if (!isNaN(d) && d > 0 ) {
                this.noOfDays = d;
            } else {
                this.noOfDays = 0;
            }
            return this.noOfDays;
        };

        this.options = angular.isObject(options) ? options : {};
        this.year = now.getFullYear();
        this.month = now.getMonth();
        this.weeks = [];
        this.weekStartsOn = this.setWeekStartsOn(this.options.weekStartsOn);
        this.startDateOfMonth = this.setStartDateOfMonth(this.options.startDateOfMonth);
        this.noOfDays = this.setNoOfDays(this.options.noOfDays);

        this.next = function () {
            if (this.start.getMonth() < 11) {
                this.init(this.start.getFullYear(), this.start.getMonth() + 1);
                return;
            }
            this.init(this.start.getFullYear() + 1, 0);
        };

        this.prev = function () {
            if (this.month) {
                this.init(this.start.getFullYear(), this.start.getMonth() - 1);
                return;
            }
            this.init(this.start.getFullYear() - 1, 11);
        };

        // Month should be the javascript indexed month, 0 is January, etc.
        this.init = function (year, month) {

            var now = new Date();
            this.year = angular.isDefined(year) ? year : now.getFullYear();
            this.month = angular.isDefined(month) ? month : now.getMonth();

            var daysInMonth = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
            var monthLength = daysInMonth[this.month];

            // Figure out if is a leap year.
            if (this.month === 1) {
                if ((this.year % 4 === 0 && this.year % 100 !== 0) || this.year % 400 === 0) {
                    monthLength = 29;
                }
            }

            // First day of calendar month.
            if ( angular.isDefined(this.options.startDateOfMonth) ) {
                this.start = new Date(this.year, this.month, this.startDateOfMonth);
            } else {
                this.start = new Date(this.year, this.month, 1);
            }

            var date = angular.copy(this.start);
            if ( date.getDate() === 1) {
                while ( date.getDay() !== this.weekStartsOn) {
                    date.setDate(date.getDate() - 1);
                    monthLength++;
                }
            }

            if ( this.noOfDays !== 0) {
                while (this.noOfDays % 7 !== 0) {
                    this.noOfDays++;
                }
                monthLength = this.noOfDays;
            } else {
                // Last day of calendar month.
                while (monthLength % 7 !== 0) {
                    monthLength++;
                }
            }

            // Last day of calendar month.
            while (monthLength % 7 !== 0) {
                monthLength++;
            }

            this.weeks = [];
            for (var i = 0; i < monthLength; ++i) {

                // Let's start a new week.
                if (i % 7 === 0) {
                    this.weeks.push([]);
                }

                // Add copy of the date. If not a copy,
                // it will get updated shortly.
                this.weeks[this.weeks.length - 1].push(angular.copy(date));

                // Increment it.
                date.setDate(date.getDate() + 1);

            }

        };

        this.init(year, month);

    }

    return Calendar;

}]);

angular.module("materialCalendar").service("MaterialCalendarData", [function () {
    function CalendarData() {

        this.data = {};

        this.getDayKey = function(date) {
            return [date.getFullYear(), date.getMonth() + 1, date.getDate()].join("-");
        };

        this.setDayContent = function(date, content) {
            this.data[this.getDayKey(date)] = content || this.data[this.getDayKey(date)] || "";
        };
    }
    return new CalendarData();
}]);

angular.module("materialCalendar").directive("calendarMd", ["$compile", "$parse", "$templateRequest", "$q", "materialCalendar.Calendar", "MaterialCalendarData", function ($compile, $parse, $templateRequest, $q, Calendar, CalendarData) {

    var defaultTemplate = "/* angular-material-calendar.html */";

    var injectCss = function () {
        var styleId = "calendarMdCss";
        if (!document.getElementById(styleId)) {
            var head = document.getElementsByTagName("head")[0];
            var css = document.createElement("style");
            css.type = "text/css";
            css.id = styleId;
            css.innerHTML = "/* angular-material-calendar.css */";
            head.insertBefore(css, head.firstChild);
        }
    };

    return {
        restrict: "E",
        scope: {
            ngModel: "=?",
            template: "&",
            templateUrl: "=?",
            onDayClick: "=?",
            onPrevMonth: "=?",
            onNextMonth: "=?",
            calendarDirection: "=?",
            dayContent: "&?",
            timezone: "=?",
            titleFormat: "=?",
            dayFormat: "=?",
            dayLabelFormat: "=?",
            dayLabelTooltipFormat: "=?",
            dayTooltipFormat: "=?",
            weekStartsOn: "=?",
            tooltips: "&?",
            startDateOfMonth: "=?",
            noOfDays: "=?",
            clearDataCacheOnLoad: "=?",
            disableFutureSelection: "=?",
            disableSelection: "=?"
        },
        link: function ($scope, $element, $attrs) {

            // Add the CSS here.
            injectCss();

            var date = new Date();
            var month = parseInt($attrs.startMonth || date.getMonth());
            var year = parseInt($attrs.startYear || date.getFullYear());

            $scope.columnWeekLayout = "column";
            $scope.weekLayout = "row";
            $scope.timezone = $scope.timezone || null;
            $scope.noCache = $attrs.clearDataCacheOnLoad || false;

            // Parse the parent model to determine if it's an array.
            // If it is an array, than we'll automatically be able to select
            // more than one date.
            if ($attrs.ngModel) {
                $scope.active = $scope.$parent.$eval($attrs.ngModel);
                if ($attrs.ngModel) {
                    $scope.$watch("$parent." + $attrs.ngModel, function (val) {
                        $scope.active = val;
                    });
                }
            } else {
                $scope.active = null;
            }

            // Set the defaults here.
            $scope.titleFormat = $scope.titleFormat || "LLLL yyyy";
            $scope.dayLabelFormat = $scope.dayLabelFormat || "EEE";
            $scope.dayLabelTooltipFormat = $scope.dayLabelTooltipFormat || "EEEE";
            $scope.dayFormat = $scope.dayFormat || "d";
            $scope.dayTooltipFormat = $scope.dayTooltipFormat || "fullDate";
            $scope.dayIdFormat = "dd-MM-yy";
            $scope.disableFutureSelection = $scope.disableFutureSelection || false;
            $scope.disableSelection = $scope.disableSelection || false;

            $scope.sameMonth = function (date) {
                var d = angular.copy(date);
                return d.getFullYear() === $scope.calendar.year &&
                    d.getMonth() === $scope.calendar.month;
            };

            $scope.isDisabled = function (date,startDateOfMonth,noOfDays) {
                if (noOfDays!=0 && angular.isDefined(noOfDays)) {
                    var dateStart = new Date($scope.calendar.year,$scope.calendar.month,startDateOfMonth);
                    var dateEnd = angular.copy(dateStart);
                    dateEnd.setDate(dateStart.getDate()+parseInt(noOfDays));
                    if (date.getDate() <= dateStart && date.getDate() >= dateEnd) { return true; }
                }
                if ($scope.disableSelection) { return true; }
                if ($scope.disableFutureSelection && date > new Date()) { return true; }
                return !$scope.sameMonth(date);
            };

            $scope.calendarDirection = $scope.calendarDirection || "horizontal";

            $scope.$watch("calendarDirection", function (val) {
                $scope.weekLayout = val === "horizontal" ? "row" : "column";
            });

            $scope.$watch("weekLayout", function () {
                year = $scope.calendar.year;
                month = $scope.calendar.month;
                bootstrap();
            });

            var handleCb = function (cb, data) {
                (cb || angular.noop)(data);
            };

            var dateFind = function (arr, date) {
                var index = -1;
                angular.forEach(arr, function (d, k) {
                    if (index < 0) {
                        if (angular.equals(date, d)) {
                            index = k;
                        }
                    }
                });
                return index;
            };

            $scope.isActive = function (date) {
                var match;
                var active = angular.copy($scope.active);
                if (!angular.isArray(active)) {
                    if (active && angular.equals(active.getYear(), date.getYear()) && angular.equals(active.getMonth(), date.getMonth()) && angular.equals(active.getDate(), date.getDate())) {
                        match = true;
                    }
                } else {
                    match = dateFind(active, date) > -1;
                }
                return match;
            };

            $scope.hasEvents = function (date) {
                var data = CalendarData.data[$scope.dayKey(date)];
                return (data && data.length > 0);
            };

            $scope.prev = function () {
                $scope.calendar.prev();
                var data = {
                    year: $scope.calendar.year,
                    month: $scope.calendar.month + 1
                };
                setData();
                handleCb($scope.onPrevMonth, data);
            };

            $scope.next = function () {
                $scope.calendar.next();
                var data = {
                    year: $scope.calendar.year,
                    month: $scope.calendar.month + 1
                };
                setData();
                handleCb($scope.onNextMonth, data);
            };

            $scope.handleDayClick = function (date) {

                if($scope.disableFutureSelection && date > new Date()) {
                    return;
                }

                if($scope.disableSelection) {
                    return;
                }

                var active = angular.copy($scope.active);
                if (angular.isArray(active)) {
                    var idx = dateFind(active, date);
                    if (idx > -1) {
                        active.splice(idx, 1);
                    } else {
                        active.push(date);
                    }
                } else {
                    if (angular.equals(active, date)) {
                        active = null;
                    } else {
                        active = date;
                    }
                }

                $scope.active = active;
                if ($attrs.ngModel) {
                    $parse($attrs.ngModel).assign($scope.$parent, angular.copy($scope.active));
                }

                handleCb($scope.onDayClick, angular.copy(date));

            };

            // Small helper function to set the contents of the template.
            var setTemplate = function (contents) {
                $element.html(contents);
                $compile($element.contents())($scope);
            };

            var init = function () {

                $scope.calendar = new Calendar(year, month, {
                    weekStartsOn: $scope.weekStartsOn || 0,
                    startDateOfMonth: $scope.startDateOfMonth || 1,
                    noOfDays: $scope.noOfDays || 0
                });

                // Allows fetching of dynamic templates via $templateCache.
                if ($scope.templateUrl) {
                    return $templateRequest($scope.templateUrl);
                }

                return $q.resolve($scope.template() || defaultTemplate);
            };

            $scope.dataService = CalendarData;

            // Set the html contents of each date.
            var getDayKey = function (date) {
                return $scope.dataService.getDayKey(date);
            };
            $scope.dayKey = getDayKey;

            var getDayContent = function (date) {

                // Initialize the data in the data array.
                if ($scope.noCache) {
                    $scope.dataService.setDayContent(date, "");
                } else {
                    $scope.dataService.setDayContent(date, ($scope.dataService.data[getDayKey(date)] || ""));
                }

                var cb = ($scope.dayContent || angular.noop)();
                var result = (cb || angular.noop)(date);

                // Check for async function. This should support $http.get() and also regular $q.defer() functions.
                if (angular.isObject(result) && "function" === typeof result.success) {
                    result.success(function (html) {
                        $scope.dataService.setDayContent(date, html);
                    });
                } else if (angular.isObject(result) && "function" === typeof result.then) {
                    result.then(function (html) {
                        $scope.dataService.setDayContent(date, html);
                    });
                } else {
                    $scope.dataService.setDayContent(date, result);
                }

            };

            var setData = function () {
                angular.forEach($scope.calendar.weeks, function (week) {
                    angular.forEach(week, getDayContent);
                });
            };

            window.data = $scope.data;

            var bootstrap = function () {
                init().then(function (contents) {
                    setTemplate(contents);
                    setData();
                });
            };

            $scope.$watchGroup(["weekStartsOn","startDateOfMonth","noOfDays"], init);
            bootstrap();

            // These are for tests, don't remove them..
            $scope._$$init = init;
            $scope._$$setTemplate = setTemplate;
            $scope._$$bootstrap = bootstrap;

        }
    };

}]);
<commit_msg>fix #81<commit_after>angular.module("materialCalendar", ["ngMaterial", "ngSanitize"]);

angular.module("materialCalendar").constant("materialCalendar.config", {
    version: "0.2.13",
    debug: document.domain.indexOf("localhost") > -1
});

angular.module("materialCalendar").config(["materialCalendar.config", "$logProvider", "$compileProvider", function (config, $logProvider, $compileProvider) {
    if (config.debug) {
        $logProvider.debugEnabled(false);
        $compileProvider.debugInfoEnabled(false);
    }
}]);

angular.module("materialCalendar").service("materialCalendar.Calendar", [function () {

    function Calendar(year, month, options) {

        var now = new Date();

        this.setWeekStartsOn = function (i) {
            var d = parseInt(i || 0, 10);
            if (!isNaN(d) && d >= 0 && d <= 6) {
                this.weekStartsOn = d;
            } else {
                this.weekStartsOn = 0;
            }
            return this.weekStartsOn;
        };

        this.setStartDateOfMonth = function (i) {
            var d = parseInt(i || 1, 10);
            if (!isNaN(d) && d >= 1 && d <= 31) {
                this.startDateOfMonth = d;
            } else {
                this.startDateOfMonth = 1;
            }
            return this.startDateOfMonth;
        };

        this.setNoOfDays = function (i) {
            var d = parseInt(i || 0, 10);
            if (!isNaN(d) && d > 0 ) {
                this.noOfDays = d;
            } else {
                this.noOfDays = 0;
            }
            return this.noOfDays;
        };

        this.options = angular.isObject(options) ? options : {};
        this.year = now.getFullYear();
        this.month = now.getMonth();
        this.weeks = [];
        this.weekStartsOn = this.setWeekStartsOn(this.options.weekStartsOn);
        this.startDateOfMonth = this.setStartDateOfMonth(this.options.startDateOfMonth);
        this.noOfDays = this.setNoOfDays(this.options.noOfDays);

        this.next = function () {
            if (this.start.getMonth() < 11) {
                this.init(this.start.getFullYear(), this.start.getMonth() + 1);
                return;
            }
            this.init(this.start.getFullYear() + 1, 0);
        };

        this.prev = function () {
            if (this.month) {
                this.init(this.start.getFullYear(), this.start.getMonth() - 1);
                return;
            }
            this.init(this.start.getFullYear() - 1, 11);
        };

        // Month should be the javascript indexed month, 0 is January, etc.
        this.init = function (year, month) {

            var now = new Date();
            this.year = angular.isDefined(year) ? year : now.getFullYear();
            this.month = angular.isDefined(month) ? month : now.getMonth();

            var daysInMonth = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
            var monthLength = daysInMonth[this.month];

            // Figure out if is a leap year.
            if (this.month === 1) {
                if ((this.year % 4 === 0 && this.year % 100 !== 0) || this.year % 400 === 0) {
                    monthLength = 29;
                }
            }

            // First day of calendar month.
            if ( angular.isDefined(this.options.startDateOfMonth) ) {
                this.start = new Date(this.year, this.month, this.startDateOfMonth);
            } else {
                this.start = new Date(this.year, this.month, 1);
            }

            var date = angular.copy(this.start);
            if ( date.getDate() === 1) {
                while ( date.getDay() !== this.weekStartsOn) {
                    date.setDate(date.getDate() - 1);
                    monthLength++;
                }
            }

            if ( this.noOfDays !== 0) {
                while (this.noOfDays % 7 !== 0) {
                    this.noOfDays++;
                }
                monthLength = this.noOfDays;
            } else {
                // Last day of calendar month.
                while (monthLength % 7 !== 0) {
                    monthLength++;
                }
            }

            // Last day of calendar month.
            while (monthLength % 7 !== 0) {
                monthLength++;
            }

            this.weeks = [];
            for (var i = 0; i < monthLength; ++i) {

                // Let's start a new week.
                if (i % 7 === 0) {
                    this.weeks.push([]);
                }

                // Add copy of the date. If not a copy,
                // it will get updated shortly.
                this.weeks[this.weeks.length - 1].push(angular.copy(date));

                // Increment it.
                date.setDate(date.getDate() + 1);

            }

        };

        this.init(year, month);

    }

    return Calendar;

}]);

angular.module("materialCalendar").service("MaterialCalendarData", [function () {
    function CalendarData() {

        this.data = {};

        this.getDayKey = function(date) {
            return [date.getFullYear(), date.getMonth() + 1, date.getDate()].join("-");
        };

        this.setDayContent = function(date, content) {
            this.data[this.getDayKey(date)] = content || this.data[this.getDayKey(date)] || "";
        };
    }
    return new CalendarData();
}]);

angular.module("materialCalendar").directive("calendarMd", ["$compile", "$parse", "$templateRequest", "$q", "materialCalendar.Calendar", "MaterialCalendarData", function ($compile, $parse, $templateRequest, $q, Calendar, CalendarData) {

    var defaultTemplate = "/* angular-material-calendar.html */";

    var injectCss = function () {
        var styleId = "calendarMdCss";
        if (!document.getElementById(styleId)) {
            var head = document.getElementsByTagName("head")[0];
            var css = document.createElement("style");
            css.type = "text/css";
            css.id = styleId;
            css.innerHTML = "/* angular-material-calendar.css */";
            head.insertBefore(css, head.firstChild);
        }
    };

    return {
        restrict: "E",
        scope: {
            ngModel: "=?",
            template: "&",
            templateUrl: "=?",
            onDayClick: "=?",
            onPrevMonth: "=?",
            onNextMonth: "=?",
            calendarDirection: "=?",
            dayContent: "&?",
            timezone: "=?",
            titleFormat: "=?",
            dayFormat: "=?",
            dayLabelFormat: "=?",
            dayLabelTooltipFormat: "=?",
            dayTooltipFormat: "=?",
            weekStartsOn: "=?",
            tooltips: "&?",
            startDateOfMonth: "=?",
            noOfDays: "=?",
            clearDataCacheOnLoad: "=?",
            disableFutureSelection: "=?",
            disableSelection: "=?"
        },
        link: function ($scope, $element, $attrs) {

            // Add the CSS here.
            injectCss();

            var date = new Date();
            var month = parseInt($attrs.startMonth || date.getMonth());
            var year = parseInt($attrs.startYear || date.getFullYear());

            $scope.columnWeekLayout = "column";
            $scope.weekLayout = "row";
            $scope.timezone = $scope.timezone || null;
            $scope.noCache = $attrs.clearDataCacheOnLoad || false;

            // Parse the parent model to determine if it's an array.
            // If it is an array, than we'll automatically be able to select
            // more than one date.
            if ($attrs.ngModel) {
                $scope.active = $scope.$parent.$eval($attrs.ngModel);
                if ($attrs.ngModel) {
                    $scope.$watch("$parent." + $attrs.ngModel, function (val) {
                        $scope.active = val;
                    });
                }
            } else {
                $scope.active = null;
            }

            // Set the defaults here.
            $scope.titleFormat = $scope.titleFormat || "LLLL yyyy";
            $scope.dayLabelFormat = $scope.dayLabelFormat || "EEE";
            $scope.dayLabelTooltipFormat = $scope.dayLabelTooltipFormat || "EEEE";
            $scope.dayFormat = $scope.dayFormat || "d";
            $scope.dayTooltipFormat = $scope.dayTooltipFormat || "fullDate";
            $scope.dayIdFormat = "dd-MM-yy";
            $scope.disableFutureSelection = $scope.disableFutureSelection || false;
            $scope.disableSelection = $scope.disableSelection || false;

            $scope.sameMonth = function (date) {
                var d = angular.copy(date);
                return d.getFullYear() === $scope.calendar.year &&
                    d.getMonth() === $scope.calendar.month;
            };

            $scope.isDisabled = function (date,startDateOfMonth,noOfDays) {
                if (noOfDays!=0 && angular.isDefined(noOfDays)) {
                    var dateStart = new Date($scope.calendar.year,$scope.calendar.month,startDateOfMonth);
                    var dateEnd = angular.copy(dateStart);
                    dateEnd.setDate(dateStart.getDate()+parseInt(noOfDays));
                    if (date.getDate() <= dateStart && date.getDate() >= dateEnd) { return true; }
                }
                if ($scope.disableSelection) { return true; }
                if ($scope.disableFutureSelection && date > new Date()) { return true; }
                return !$scope.sameMonth(date);
            };

            $scope.calendarDirection = $scope.calendarDirection || "horizontal";

            $scope.$watch("calendarDirection", function (val) {
                $scope.weekLayout = val === "horizontal" ? "row" : "column";
            });

            $scope.$watch("weekLayout", function () {
                year = $scope.calendar.year;
                month = $scope.calendar.month;
                bootstrap();
            });

            var handleCb = function (cb, data) {
                (cb || angular.noop)(data);
            };

            var dateFind = function (arr, date) {
                var index = -1;
                angular.forEach(arr, function (d, k) {
                    if (index < 0) {
                        if (angular.equals(date, d)) {
                            index = k;
                        }
                    }
                });
                return index;
            };

            $scope.isActive = function (date) {
                var match;
                var active = angular.copy($scope.active);
                if (!angular.isArray(active)) {
                    if (active && angular.equals(active.getYear(), date.getYear()) && angular.equals(active.getMonth(), date.getMonth()) && angular.equals(active.getDate(), date.getDate())) {
                        match = true;
                    }
                } else {
                    match = dateFind(active, date) > -1;
                }
                return match;
            };

            $scope.hasEvents = function (date) {
                var data = CalendarData.data[$scope.dayKey(date)];
                return (data && data.length > 0);
            };

            $scope.prev = function () {
                $scope.calendar.prev();
                var data = {
                    year: $scope.calendar.year,
                    month: $scope.calendar.month + 1
                };
                setData();
                handleCb($scope.onPrevMonth, data);
            };

            $scope.next = function () {
                $scope.calendar.next();
                var data = {
                    year: $scope.calendar.year,
                    month: $scope.calendar.month + 1
                };
                setData();
                handleCb($scope.onNextMonth, data);
            };

            $scope.handleDayClick = function (date) {

                if($scope.disableFutureSelection && date > new Date()) {
                    return;
                }

                if($scope.disableSelection) {
                    return;
                }

                var active = angular.copy($scope.active);
                if (angular.isArray(active)) {
                    var idx = dateFind(active, date);
                    if (idx > -1) {
                        active.splice(idx, 1);
                    } else {
                        active.push(date);
                    }
                } else {
                    if (angular.equals(active, date)) {
                        active = null;
                    } else {
                        active = date;
                    }
                }

                $scope.active = active;
                if ($attrs.ngModel) {
                    $parse($attrs.ngModel).assign($scope.$parent, angular.copy($scope.active));
                }

                handleCb($scope.onDayClick, angular.copy(date));

            };

            // Small helper function to set the contents of the template.
            var setTemplate = function (contents) {
                $element.html(contents);
                $compile($element.contents())($scope);
            };

            var init = function () {

                $scope.calendar = new Calendar(year, month, {
                    weekStartsOn: $scope.weekStartsOn || 0,
                    startDateOfMonth: $scope.startDateOfMonth || 1,
                    noOfDays: $scope.noOfDays || 0
                });

                // Allows fetching of dynamic templates via $templateCache.
                if ($scope.templateUrl) {
                    return $templateRequest($scope.templateUrl);
                }

                return $q.when($scope.template() || defaultTemplate);
            };

            $scope.dataService = CalendarData;

            // Set the html contents of each date.
            var getDayKey = function (date) {
                return $scope.dataService.getDayKey(date);
            };
            $scope.dayKey = getDayKey;

            var getDayContent = function (date) {

                // Initialize the data in the data array.
                if ($scope.noCache) {
                    $scope.dataService.setDayContent(date, "");
                } else {
                    $scope.dataService.setDayContent(date, ($scope.dataService.data[getDayKey(date)] || ""));
                }

                var cb = ($scope.dayContent || angular.noop)();
                var result = (cb || angular.noop)(date);

                // Check for async function. This should support $http.get() and also regular $q.defer() functions.
                if (angular.isObject(result) && "function" === typeof result.success) {
                    result.success(function (html) {
                        $scope.dataService.setDayContent(date, html);
                    });
                } else if (angular.isObject(result) && "function" === typeof result.then) {
                    result.then(function (html) {
                        $scope.dataService.setDayContent(date, html);
                    });
                } else {
                    $scope.dataService.setDayContent(date, result);
                }

            };

            var setData = function () {
                angular.forEach($scope.calendar.weeks, function (week) {
                    angular.forEach(week, getDayContent);
                });
            };

            window.data = $scope.data;

            var bootstrap = function () {
                init().then(function (contents) {
                    setTemplate(contents);
                    setData();
                });
            };

            $scope.$watchGroup(["weekStartsOn","startDateOfMonth","noOfDays"], init);
            bootstrap();

            // These are for tests, don't remove them..
            $scope._$$init = init;
            $scope._$$setTemplate = setTemplate;
            $scope._$$bootstrap = bootstrap;

        }
    };

}]);
<|endoftext|>


<commit_before>/**
 * @license base62.js
 * http://github.com/sasaplus1/base62.js
 *
 * Copyright(c) 2012 sasa+1
 * Released under the MIT license
 * http://github.com/sasaplus1/base62.js/blob/master/LICENSE
 */
;(function(){

  var Base62 = (function(){

    /**
     * @const
     * @type {string}
     */
    var BASE62_TABLE =
          '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';

    /**
     * 62進数を10進数に変換します。
     * @param {string} str 62進数の文字列
     * @return {number} 10進数に変換された数値
     */
    function decode_(str) {
      var decodedNum = 0,
          column, len, isNegative;

      if (typeof str !== 'string' || !/^-?[\da-zA-Z]+$/.test(str)) {
        return NaN;
      }

      isNegative = (str[0] === '-');
      if (isNegative) {
        str = str.slice(1);
      }

      column = len = str.length;
      for (; column--;) {
        decodedNum +=
          BASE62_TABLE.indexOf(str[column]) * Math.pow(62, len - column - 1);
      }

      if (isNegative) {
        decodedNum *= -1;
      }

      return decodedNum;
    }

    /**
     * 10進数を62進数に変換します。
     * @param {number} num 10進数の数値
     * @return {string} 62進数に変換された文字列
     */
    function encode_(num) {
      var encodedStr,
          isNegative;

      if (typeof num !== 'number' || !isFinite(num)) {
        return '';
      }

      isNegative = (num < 0);
      if (isNegative) {
        num *= -1;
      }

      encodedStr = '';
      while (num > 0) {
        encodedStr = BASE62_TABLE.charAt(num % 62) + encodedStr;
        num = Math.floor(num / 62);
      }

      if (isNegative) {
        encodedStr = '-' + encodedStr;
      }

      return encodedStr;
    }

    return {
      decode: decode_,
      encode: encode_
    };

  }());

  if (typeof window === 'undefined') {
    module.exports = Base62;
  } else {
    window.Base62 = Base62;
  }

}());
<commit_msg>encode関数に0を渡したときの不具合を修正<commit_after>/**
 * @license base62.js
 * http://github.com/sasaplus1/base62.js
 *
 * Copyright(c) 2012 sasa+1
 * Released under the MIT license
 * http://github.com/sasaplus1/base62.js/blob/master/LICENSE
 */
;(function(){

  var Base62 = (function(){

    /**
     * @const
     * @type {string}
     */
    var BASE62_TABLE =
          '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';

    /**
     * 62進数を10進数に変換します。
     * @param {string} str 62進数の文字列
     * @return {number} 10進数に変換された数値
     */
    function decode_(str) {
      var decodedNum = 0,
          column, len, isNegative;

      if (typeof str !== 'string' || !/^-?[\da-zA-Z]+$/.test(str)) {
        return NaN;
      }

      isNegative = (str[0] === '-');
      if (isNegative) {
        str = str.slice(1);
      }

      column = len = str.length;
      for (; column--;) {
        decodedNum +=
          BASE62_TABLE.indexOf(str[column]) * Math.pow(62, len - column - 1);
      }

      if (isNegative) {
        decodedNum *= -1;
      }

      return decodedNum;
    }

    /**
     * 10進数を62進数に変換します。
     * @param {number} num 10進数の数値
     * @return {string} 62進数に変換された文字列
     */
    function encode_(num) {
      var encodedStr,
          isNegative;

      if (typeof num !== 'number' || !isFinite(num)) {
        return '';
      }

      if (num === 0) {
        return '0';
      }

      isNegative = (num < 0);
      if (isNegative) {
        num *= -1;
      }

      encodedStr = '';
      while (num > 0) {
        encodedStr = BASE62_TABLE.charAt(num % 62) + encodedStr;
        num = Math.floor(num / 62);
      }

      if (isNegative) {
        encodedStr = '-' + encodedStr;
      }

      return encodedStr;
    }

    return {
      decode: decode_,
      encode: encode_
    };

  }());

  if (typeof window === 'undefined') {
    module.exports = Base62;
  } else {
    window.Base62 = Base62;
  }

}());
<|endoftext|>


<commit_before>/**
 * @license base62.js
 * http://github.com/sasaplus1/base62.js
 *
 * Copyright(c) 2012 sasa+1
 * Released under the MIT license
 * http://github.com/sasaplus1/base62.js/blob/master/LICENSE
 */
;(function(){

  var Base62 = (function(){

    /**
     * @const
     * @type {string}
     */
    var BASE62_TABLE =
          '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';

    /**
     * 62進数を10進数に変換します。
     * @param {string} str 62進数の文字列
     * @return {number} 10進数に変換された数値
     */
    function decode_(str) {
    }

    /**
     * 10進数を62進数に変換します。
     * @param {number} num 10進数の数値
     * @return {string} 62進数に変換された文字列
     */
    function encode_(num) {
      var encodedStr,
          isNegative;

      if (typeof num !== 'number' || !isFinite(num)) {
        return '';
      }

      isNegative = (num < 0);
      if (isNegative) {
        num *= -1;
      }

      encodedStr = '';
      while (num > 0) {
        encodedStr = BASE62_TABLE.charAt(num % 62) + encodedStr;
        num = Math.floor(num / 62);
      }

      if (isNegative) {
        encodedStr = '-' + encodedStr;
      }

      return encodedStr;
    }

    return {
      decode: decode_,
      encode: encode_
    };

  }());

  if (typeof window === 'undefined') {
    module.exports = Base62;
  } else {
    window.Base62 = Base62;
  }

}());
<commit_msg>decode関数を書いた<commit_after>/**
 * @license base62.js
 * http://github.com/sasaplus1/base62.js
 *
 * Copyright(c) 2012 sasa+1
 * Released under the MIT license
 * http://github.com/sasaplus1/base62.js/blob/master/LICENSE
 */
;(function(){

  var Base62 = (function(){

    /**
     * @const
     * @type {string}
     */
    var BASE62_TABLE =
          '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';

    /**
     * 62進数を10進数に変換します。
     * @param {string} str 62進数の文字列
     * @return {number} 10進数に変換された数値
     */
    function decode_(str) {
      var decodedNum = 0,
          column, len, isNegative;

      if (typeof str !== 'string' || !/^-?[\da-zA-Z]+$/.test(str)) {
        return NaN;
      }

      isNegative = (str[0] === '-');
      if (isNegative) {
        str = str.slice(1);
      }

      column = len = str.length;
      for (; column--;) {
        decodedNum +=
          BASE62_TABLE.indexOf(str[column]) * Math.pow(62, len - column - 1);
      }

      if (isNegative) {
        decodedNum *= -1;
      }

      return decodedNum;
    }

    /**
     * 10進数を62進数に変換します。
     * @param {number} num 10進数の数値
     * @return {string} 62進数に変換された文字列
     */
    function encode_(num) {
      var encodedStr,
          isNegative;

      if (typeof num !== 'number' || !isFinite(num)) {
        return '';
      }

      isNegative = (num < 0);
      if (isNegative) {
        num *= -1;
      }

      encodedStr = '';
      while (num > 0) {
        encodedStr = BASE62_TABLE.charAt(num % 62) + encodedStr;
        num = Math.floor(num / 62);
      }

      if (isNegative) {
        encodedStr = '-' + encodedStr;
      }

      return encodedStr;
    }

    return {
      decode: decode_,
      encode: encode_
    };

  }());

  if (typeof window === 'undefined') {
    module.exports = Base62;
  } else {
    window.Base62 = Base62;
  }

}());
<|endoftext|>


<commit_before>(function(angular, undefined) {

  function defineActionDescriptors(Combatant) {
    return [{
      label: 'Finish Turn',
      isApplicable: function(combatant) {
        return combatant.turnStatus === Combatant.TURN_STATUS.active ||
          combatant.turnStatus === Combatant.TURN_STATUS.delaying;
      },
      generateActionFor: function(combatant) {
        return function() {
          combatant.turnStatus = Combatant.TURN_STATUS.complete;
        };
      }
    }, {
      label: 'Delay Turn',
      isApplicable: function(combatant) {
        return combatant.turnStatus === Combatant.TURN_STATUS.active;
      },
      generateActionFor: function(combatant, position, combatants) {
        return function() {
          combatant.turnStatus = Combatant.TURN_STATUS.delaying;
        };
      }
    }, {
      label: 'Remove Combatant',
      isApplicable: function() {
        return true;
      },
      generateActionFor: function(combatant, position, combatants) {
        return function() {
          combatants.splice(position, 1);
        };
      }
    }];
  }

  angular.module('ngBattleTracker.battleView', [
    'ui.router',
    'ui.route',
    'ui.bootstrap.modal',
    'ui.bootstrap.tabs',
    'ngBattleTracker.campaign',
    'ngBattleTracker.battleView.model'
  ])

  .config(function battleViewConfig($stateProvider) {
    $stateProvider.state('battleView', {
      url: '/battleView/:campaignId?surprise',
      views: {
        "main": {
          controller: 'BattleViewCtrl',
          templateUrl: 'battleview/battleview.tpl.html'
        }
      },
      data: {
        pageTitle: 'Battle'
      }
    });
  })

  .filter('orderByTurn', function() {
    return function(combatants) {
      return combatants.slice().sort(function(a, b) {
        if (a.turnStatus === b.turnStatus) {
          return b.initiative - a.initiative;
        } else {
          return b.turnStatus.ordinal - a.turnStatus.ordinal;
        }
      });
    };
  })

  .controller('BattleViewCtrl', function BattleViewCtrl($scope, $modal, $stateParams, campaignService, BattleViewModel) {
    var ctrl = this,
      campaignId = $stateParams.campaignId,
      surpriseMode = !! $stateParams.surprise,
      campaign;

    function loadCampaign() {
      campaign = campaignService.lookupCampaignById(campaignId);
    }

    function init() {
      loadCampaign();
      campaign.combatants.forEach(function(combatantName) {
        $scope.combatants.push(
          new BattleViewModel.Combatant(combatantName, undefined, true));
      });
      resetBattle();
    }

    function filterCombatantsOfTurnStatus(turnStatus) {
      return $scope.combatants.filter(function(combatant) {
        return combatant.turnStatus === turnStatus;
      });
    }

    function sortCombatantList() {
      $scope.combatants.sort(function(a, b) {
        if (a.turnStatus === b.turnStatus) {
          return b.initiative - a.initiative;
        } else {
          return b.turnStatus.ordinal - a.turnStatus.ordinal;
        }
      });
    }

    function refreshCombatantList() {
      //clear active status
      filterCombatantsOfTurnStatus(BattleViewModel.Combatant.TURN_STATUS.active).forEach(function(combatant) {
        combatant.turnStatus = BattleViewModel.Combatant.TURN_STATUS.waiting;
      });
      //set active status
      (filterCombatantsOfTurnStatus(BattleViewModel.Combatant.TURN_STATUS.waiting)[0] || {}).turnStatus = BattleViewModel.Combatant.TURN_STATUS.active;
      calculateActions();
    }
    ctrl.refreshCombatantList = refreshCombatantList;

    var actionDescriptors = defineActionDescriptors(BattleViewModel.Combatant);

    function calculateActions() {
      $scope.combatants.forEach(function(combatant, index) {
        var actions = [];

        function buildAction(actionDescriptor, combatant) {
          var actionFn = actionDescriptor.generateActionFor(combatant, index, $scope.combatants);
          return {
            label: actionDescriptor.label,
            apply: function() {
              actionFn();
              refreshCombatantList();
            }
          };
        }

        actionDescriptors.forEach(function(actionDescriptor) {
          if (actionDescriptor.isApplicable(combatant, index, $scope.combatants)) {
            actions.push(buildAction(actionDescriptor, combatant));
          }
        });
        combatant.actions = actions;
      });
    }

    function extractAndCopyPartyMembers(combatants) {
      return combatants.filter(function(combatant) {
        return combatant.partyMember;
      }).map(function(partyMemberCabatant) {
        return new BattleViewModel.Combatant(partyMemberCabatant.name, undefined, true);
      }).sort(function(a, b) {
        return a.name.localeCompare(b.name);
      });
    }

    function resetBattle() {
      var partyMemberCopy = extractAndCopyPartyMembers($scope.combatants);
      if (partyMemberCopy.length > 0) {
        var modalInstance = $modal.open({
          templateUrl: 'battleview/resetmodal.tpl.html',
          controller: 'ResetModalInstanceCtrl',
          resolve: {
            combatants: function() {
              return partyMemberCopy;
            }
          }
        });

        modalInstance.result.then(function(combatants) {
          $scope.combatants = combatants.sort(function(a, b) {
            return b.initiative - a.initiative;
          });
          if (surpriseMode) {
            $scope.combatants.forEach(function(combatant) {
              combatant.turnStatus = BattleViewModel.Combatant.TURN_STATUS.complete;
            });
          }
          refreshCombatantList();
        });
      } else {
        $scope.combatants = [];
        refreshCombatantList();
      }
    }

    function determineInsertPosition(combatants, combatant) {
      var len = combatants.length,
        i;

      for (i = 0; i < len; i++) {
        if (combatants[i].initiative < combatant.initiative) {
          return i;
        }
      }

      return len;
    }

    function addCombatant(combatant) {
      var insertPosition = determineInsertPosition($scope.combatants, combatant);
      $scope.combatants.splice(insertPosition, 0, combatant);
      refreshCombatantList();
    }

    $scope.resetBattle = resetBattle;
    $scope.addCombatant = addCombatant;
    $scope.combatants = [];
    refreshCombatantList();
    init();
  })

  .controller('BattleRoundCtrl', function($scope, $stateParams, $location, BattleViewModel) {
    var campaignId = $stateParams.campaignId;

    function isRoundComplete() {
      var hasCombatants = $scope.combatants.length > 0,
        allComplete = $scope.combatants.every(function(combatant) {
          return combatant.turnStatus === BattleViewModel.Combatant.TURN_STATUS.complete;
        });
      return hasCombatants && allComplete;
    }

    function startNextRound() {
      if ($scope.isRoundComplete()) {
        $scope.combatants.forEach(function(combatant) {
          combatant.turnStatus = BattleViewModel.Combatant.TURN_STATUS.waiting;
        });
        $scope.$parent.refreshCombatantList();
      }
    }

    function endBattle() {
      $location.path("/campaignView/" + campaignId);
    }

    $scope.isRoundComplete = isRoundComplete;
    $scope.startNextRound = startNextRound;

    $scope.endBattle = endBattle;
  })

  .controller('AddNewCombatantCtrl', function($scope, BattleViewModel) {

    function generateNewCombatant() {
      return {
        name: undefined,
        initiative: undefined,
        partyMember: false
      };
    }

    function isValidCombatant(combatant) {
      return combatant && combatant.name && combatant.initiative !== undefined;
    }

    function addCombatant() {
      if (!isValidCombatant($scope.newCombatant)) {
        return;
      }
      var combatant = new BattleViewModel.Combatant($scope.newCombatant.name, $scope.newCombatant.initiative, $scope.newCombatant.partyMember);
      $scope.newCombatant = generateNewCombatant();
      $scope.$parent.addCombatant(combatant);
    }

    $scope.newCombatant = generateNewCombatant();
    $scope.addCombatant = addCombatant;
  })

  .controller('AddMultipleNonPartyCombatantsCtrl', function($scope, BattleViewModel) {

    function generateNewNonPartyCombatants() {
      return {
        nameBase: undefined,
        count: undefined,
        combatants: []
      };
    }

    function isValidCombatant(combatant) {
      return combatant && combatant.name && combatant.initiative !== undefined;
    }

    function refreshNewMultipleNonPartyCombatants() {
      var nameBase = $scope.newMultipleNonPartyCombatants.nameBase,
        count = $scope.newMultipleNonPartyCombatants.count,
        newCombatants = $scope.newMultipleNonPartyCombatants.combatants,
        i;

      if (count > 0) {
        newCombatants = newCombatants.slice(0, count);
        for (i = 0; i < count; i++) {
          newCombatants[i] = newCombatants[i] || {};
          newCombatants[i].name = nameBase + " #" + i;
        }
      }
      $scope.newMultipleNonPartyCombatants.combatants = newCombatants;
    }

    function addMultipleNonPartyCombatants() {
      if (!$scope.newMultipleNonPartyCombatants.combatants.every(isValidCombatant)) {
        return;
      }

      $scope.newMultipleNonPartyCombatants.combatants.forEach(function(c) {
        var combatant = new BattleViewModel.Combatant(c.name, c.initiative, false);
        $scope.$parent.addCombatant(combatant);
      });

      $scope.newMultipleNonPartyCombatants = generateNewNonPartyCombatants();
    }

    $scope.newMultipleNonPartyCombatants = generateNewNonPartyCombatants();
    $scope.refreshNewMultipleNonPartyCombatants = refreshNewMultipleNonPartyCombatants;
    $scope.addMultipleNonPartyCombatants = addMultipleNonPartyCombatants;
  })

  .controller('ResetModalInstanceCtrl', function($scope, $modalInstance, combatants) {
    function ok() {
      $modalInstance.close($scope.combatants);
    }

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

    $scope.ok = ok;
    $scope.cancel = cancel;
    $scope.combatants = combatants;
  });
}(angular));<commit_msg>bug(battleView): player becomes active after start of next round<commit_after>(function(angular, undefined) {

  function defineActionDescriptors(Combatant) {
    return [{
      label: 'Finish Turn',
      isApplicable: function(combatant) {
        return combatant.turnStatus === Combatant.TURN_STATUS.active ||
          combatant.turnStatus === Combatant.TURN_STATUS.delaying;
      },
      generateActionFor: function(combatant) {
        return function() {
          combatant.turnStatus = Combatant.TURN_STATUS.complete;
        };
      }
    }, {
      label: 'Delay Turn',
      isApplicable: function(combatant) {
        return combatant.turnStatus === Combatant.TURN_STATUS.active;
      },
      generateActionFor: function(combatant, position, combatants) {
        return function() {
          combatant.turnStatus = Combatant.TURN_STATUS.delaying;
        };
      }
    }, {
      label: 'Remove Combatant',
      isApplicable: function() {
        return true;
      },
      generateActionFor: function(combatant, position, combatants) {
        return function() {
          combatants.splice(position, 1);
        };
      }
    }];
  }

  angular.module('ngBattleTracker.battleView', [
    'ui.router',
    'ui.route',
    'ui.bootstrap.modal',
    'ui.bootstrap.tabs',
    'ngBattleTracker.campaign',
    'ngBattleTracker.battleView.model'
  ])

  .config(function battleViewConfig($stateProvider) {
    $stateProvider.state('battleView', {
      url: '/battleView/:campaignId?surprise',
      views: {
        "main": {
          controller: 'BattleViewCtrl',
          templateUrl: 'battleview/battleview.tpl.html'
        }
      },
      data: {
        pageTitle: 'Battle'
      }
    });
  })

  .filter('orderByTurn', function() {
    return function(combatants) {
      return combatants.slice().sort(function(a, b) {
        if (a.turnStatus === b.turnStatus) {
          return b.initiative - a.initiative;
        } else {
          return b.turnStatus.ordinal - a.turnStatus.ordinal;
        }
      });
    };
  })

  .controller('BattleViewCtrl', function BattleViewCtrl($scope, $modal, $stateParams, campaignService, BattleViewModel) {
    var ctrl = this,
      campaignId = $stateParams.campaignId,
      surpriseMode = !! $stateParams.surprise,
      campaign;

    function loadCampaign() {
      campaign = campaignService.lookupCampaignById(campaignId);
    }

    function init() {
      loadCampaign();
      campaign.combatants.forEach(function(combatantName) {
        $scope.combatants.push(
          new BattleViewModel.Combatant(combatantName, undefined, true));
      });
      resetBattle();
    }

    function filterCombatantsOfTurnStatus(turnStatus) {
      return $scope.combatants.filter(function(combatant) {
        return combatant.turnStatus === turnStatus;
      });
    }

    function sortCombatantList() {
      $scope.combatants.sort(function(a, b) {
        if (a.turnStatus === b.turnStatus) {
          return b.initiative - a.initiative;
        } else {
          return b.turnStatus.ordinal - a.turnStatus.ordinal;
        }
      });
    }

    function refreshCombatantList() {
      //clear active status
      filterCombatantsOfTurnStatus(BattleViewModel.Combatant.TURN_STATUS.active).forEach(function(combatant) {
        combatant.turnStatus = BattleViewModel.Combatant.TURN_STATUS.waiting;
      });
      //set active status
      (filterCombatantsOfTurnStatus(BattleViewModel.Combatant.TURN_STATUS.waiting)[0] || {}).turnStatus = BattleViewModel.Combatant.TURN_STATUS.active;
      calculateActions();
    }
    ctrl.refreshCombatantList = refreshCombatantList;

    var actionDescriptors = defineActionDescriptors(BattleViewModel.Combatant);

    function calculateActions() {
      $scope.combatants.forEach(function(combatant, index) {
        var actions = [];

        function buildAction(actionDescriptor, combatant) {
          var actionFn = actionDescriptor.generateActionFor(combatant, index, $scope.combatants);
          return {
            label: actionDescriptor.label,
            apply: function() {
              actionFn();
              refreshCombatantList();
            }
          };
        }

        actionDescriptors.forEach(function(actionDescriptor) {
          if (actionDescriptor.isApplicable(combatant, index, $scope.combatants)) {
            actions.push(buildAction(actionDescriptor, combatant));
          }
        });
        combatant.actions = actions;
      });
    }

    function extractAndCopyPartyMembers(combatants) {
      return combatants.filter(function(combatant) {
        return combatant.partyMember;
      }).map(function(partyMemberCabatant) {
        return new BattleViewModel.Combatant(partyMemberCabatant.name, undefined, true);
      }).sort(function(a, b) {
        return a.name.localeCompare(b.name);
      });
    }

    function resetBattle() {
      var partyMemberCopy = extractAndCopyPartyMembers($scope.combatants);
      if (partyMemberCopy.length > 0) {
        var modalInstance = $modal.open({
          templateUrl: 'battleview/resetmodal.tpl.html',
          controller: 'ResetModalInstanceCtrl',
          resolve: {
            combatants: function() {
              return partyMemberCopy;
            }
          }
        });

        modalInstance.result.then(function(combatants) {
          $scope.combatants = combatants.sort(function(a, b) {
            return b.initiative - a.initiative;
          });
          if (surpriseMode) {
            $scope.combatants.forEach(function(combatant) {
              combatant.turnStatus = BattleViewModel.Combatant.TURN_STATUS.complete;
            });
          }
          refreshCombatantList();
        });
      } else {
        $scope.combatants = [];
        refreshCombatantList();
      }
    }

    function determineInsertPosition(combatants, combatant) {
      var len = combatants.length,
        i;

      for (i = 0; i < len; i++) {
        if (combatants[i].initiative < combatant.initiative) {
          return i;
        }
      }

      return len;
    }

    function addCombatant(combatant) {
      var insertPosition = determineInsertPosition($scope.combatants, combatant);
      $scope.combatants.splice(insertPosition, 0, combatant);
      refreshCombatantList();
    }

    $scope.resetBattle = resetBattle;
    $scope.addCombatant = addCombatant;
    $scope.refreshCombatantList = refreshCombatantList;
    $scope.combatants = [];
    refreshCombatantList();
    init();
  })

  .controller('BattleRoundCtrl', function($scope, $stateParams, $location, BattleViewModel) {
    var campaignId = $stateParams.campaignId;

    function isRoundComplete() {
      var hasCombatants = $scope.combatants.length > 0,
        allComplete = $scope.combatants.every(function(combatant) {
          return combatant.turnStatus === BattleViewModel.Combatant.TURN_STATUS.complete;
        });
      return hasCombatants && allComplete;
    }

    function startNextRound() {
      if ($scope.isRoundComplete()) {
        $scope.combatants.forEach(function(combatant) {
          combatant.turnStatus = BattleViewModel.Combatant.TURN_STATUS.waiting;
        });
        $scope.$parent.refreshCombatantList();
      }
    }

    function endBattle() {
      $location.path("/campaignView/" + campaignId);
    }

    $scope.isRoundComplete = isRoundComplete;
    $scope.startNextRound = startNextRound;

    $scope.endBattle = endBattle;
  })

  .controller('AddNewCombatantCtrl', function($scope, BattleViewModel) {

    function generateNewCombatant() {
      return {
        name: undefined,
        initiative: undefined,
        partyMember: false
      };
    }

    function isValidCombatant(combatant) {
      return combatant && combatant.name && combatant.initiative !== undefined;
    }

    function addCombatant() {
      if (!isValidCombatant($scope.newCombatant)) {
        return;
      }
      var combatant = new BattleViewModel.Combatant($scope.newCombatant.name, $scope.newCombatant.initiative, $scope.newCombatant.partyMember);
      $scope.newCombatant = generateNewCombatant();
      $scope.$parent.addCombatant(combatant);
    }

    $scope.newCombatant = generateNewCombatant();
    $scope.addCombatant = addCombatant;
  })

  .controller('AddMultipleNonPartyCombatantsCtrl', function($scope, BattleViewModel) {

    function generateNewNonPartyCombatants() {
      return {
        nameBase: undefined,
        count: undefined,
        combatants: []
      };
    }

    function isValidCombatant(combatant) {
      return combatant && combatant.name && combatant.initiative !== undefined;
    }

    function refreshNewMultipleNonPartyCombatants() {
      var nameBase = $scope.newMultipleNonPartyCombatants.nameBase,
        count = $scope.newMultipleNonPartyCombatants.count,
        newCombatants = $scope.newMultipleNonPartyCombatants.combatants,
        i;

      if (count > 0) {
        newCombatants = newCombatants.slice(0, count);
        for (i = 0; i < count; i++) {
          newCombatants[i] = newCombatants[i] || {};
          newCombatants[i].name = nameBase + " #" + i;
        }
      }
      $scope.newMultipleNonPartyCombatants.combatants = newCombatants;
    }

    function addMultipleNonPartyCombatants() {
      if (!$scope.newMultipleNonPartyCombatants.combatants.every(isValidCombatant)) {
        return;
      }

      $scope.newMultipleNonPartyCombatants.combatants.forEach(function(c) {
        var combatant = new BattleViewModel.Combatant(c.name, c.initiative, false);
        $scope.$parent.addCombatant(combatant);
      });

      $scope.newMultipleNonPartyCombatants = generateNewNonPartyCombatants();
    }

    $scope.newMultipleNonPartyCombatants = generateNewNonPartyCombatants();
    $scope.refreshNewMultipleNonPartyCombatants = refreshNewMultipleNonPartyCombatants;
    $scope.addMultipleNonPartyCombatants = addMultipleNonPartyCombatants;
  })

  .controller('ResetModalInstanceCtrl', function($scope, $modalInstance, combatants) {
    function ok() {
      $modalInstance.close($scope.combatants);
    }

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

    $scope.ok = ok;
    $scope.cancel = cancel;
    $scope.combatants = combatants;
  });
}(angular));<|endoftext|>


<commit_before>angular.module( 'ngbps.categories', [
  'ui.router'
])

.config(function config( $stateProvider ) {
  $stateProvider.state( 'categories', {
    url: '/categories/:category',
    views: {
      "main": {
        controller: 'CategoryCtrl',
        templateUrl: 'categories/category.tpl.html'
      }
    }
  });
})

.controller( 'CategoriesCtrl', function CategoriesController( $scope, Categories ) {
  Categories.queryCategories('level', 1).then(function(categories){
    $scope.categories = categories;
  });
})

.controller( 'CategoryCtrl', function CategoryController( $stateParams, $scope, Categories ) {
  Categories.getCategory('url', $stateParams.category).then(function(category){
    $scope.category = category;
  });
})

;
<commit_msg>fix(categories): update head title<commit_after>angular.module( 'ngbps.categories', [
  'ui.router'
])

.config(function config( $stateProvider ) {
  $stateProvider.state( 'categories', {
    url: '/categories/:category',
    views: {
      "main": {
        controller: 'CategoryCtrl',
        templateUrl: 'categories/category.tpl.html'
      }
    }
  });
})

.controller( 'CategoriesCtrl', function CategoriesController( $scope, Categories ) {
  Categories.queryCategories('level', 1).then(function(categories){
    $scope.categories = categories;
  });
})

.controller( 'CategoryCtrl', function CategoryController( $stateParams, $scope, Categories ) {
  Categories.getCategory('url', $stateParams.category).then(function(category){
    $scope.category = category;
    $scope.app.title = category.title;
  });
})

;
<|endoftext|>


<commit_before>var fs = require('fs'),
    http = require('http'),
    path = require('path'),
    util = require('util'),
    exec = require('child_process').exec,
    child_process = require('child_process'),
    utils = require(__dirname+'/lib/client-utils.js'),
    serialport = require('serialport'),
    SerialPort = serialport.SerialPort,
    sendIv = 0,
    rebootIv,
    tty,
    config =  {
        client:'beagle',
        nodeVersion:0.9,
        cloudHost: 'zendo.ninja.is',
        cloudStream: 'stream.ninja.is',
        cloudStreamPort: 443,
        cloudPort: 443,
        devtty: "/dev/ttyO1",
        locksDir: "/etc/opt/ninja",
        serialFile: "/etc/opt/ninja/serial.conf",
        tokenFile: "/etc/opt/ninja/token.conf",
        updateLock: '/etc/opt/ninja/.has_updated',
        heartbeat_interval: 5000,
        secure:true
    };
    config.id=fs.readFileSync(config.serialFile).toString().replace(/\n/g,'');
    config.utilitiesVersion=(path.existsSync('/opt/utilities/version'))
        ? parseFloat(fs.readFileSync('/opt/utilities/version'))
        : 0.4;
    config.systemVersion=(path.existsSync('/opt/utilities/sys_version'))
        ? parseFloat(fs.readFileSync('/opt/utilities/sys_version'))
        : 0.4;

console.log(utils.timestamp()+' Ninja Block Starting Up');

/*
    Fetch the arduino model and version
 */
child_process.execFile('/opt/utilities/bin/fetch_arduino_version',function(code,stdout,stderr) {
    if (stdout && stdout.indexOf('_')>-1) {
        var parts = stdout.split('_');
        config.arduinoModel = parts[0];
        config.arduinoVersion = parseFloat(parts[1]);
    } else if (stdout && stdout.length>0) {
        config.arduinoModel = 'V11';
        config.arduinoVersion = 0.36
    }
});
// We give 3 seconds to try and grab the arduino version
setTimeout(function() {
    // Setup the TTY serial port
    tty = new SerialPort(config.devtty, {
        parser: serialport.parsers.readline("\n")
    });
    utils.configure(config,tty);
    var up = upnode(clientHandlers).connect(connectionParams);
    up.on('up',function (remote) {
        utils.initRemote(remote);
        utils.changeLEDColor('green');
        console.log(utils.timestamp()+' All Systems Go');
    });
    up.on('reconnect',function() {
        utils.changeLEDColor('cyan');
        console.log(utils.timestamp()+' Reconnecting');
    });
},5000);

// Development overwrites
if (process.argv[2] == 'local') {
    config.cloudHost = process.argv[3];
    config.cloudPort = 3001;
    config.cloudStream = process.argv[3];
    config.cloudStreamPort = 3003;
    config.secure = false;
};

// Connect
// up down reconnect reconnect reconnect up
var upnode = require('upnode');
var proto = (config.secure) ? require('tls') : require('net');
var connectionParams = {
    ping:10000,
    timeout:5000,
    reconnect:2000,
    createStream:function () {
        return proto.connect(config.cloudPort, config.cloudHost);
    },
    block: function (remote, conn) {
        var params = utils.fetchBlockParams();
        var token = utils.fetchBlockToken();
        console.log(params);
        if (token) {
            remote.handshake(params, token, function (err, res) {
                if (err) console.error(utils.timestamp()+" "+err);
                else {
                    conn.emit('up',res);
                }
            });
            console.log(utils.timestamp()+' Connecting');
        } else {
            remote.activate(params,function(err,auth) {
                if (err||!auth) {
                    console.log(utils.timestamp()+" Error, Restarting");
                    process.exit(1);
                    return;
                }
                console.log(utils.timestamp()+" Received Authorisation, Confirming");
                fs.writeFile(config.tokenFile, auth.token, 'utf8',function(err) {
                    exec('sync');   // TODO: fsyncSync
                    if (err) throw err;
                    params.token=auth.token;
                    remote.confirmActivation(params,function(err) {
                        if (err) {
                            console.log(utils.timestamp()+" Error pairing block.")
                            console.log(utils.timestamp()+" "+err.error);
                            if (err.id===409) utils.changeLEDColor('blue');
                            fs.unlinkSync(config.tokenFile);
                        } else {
                            console.log(utils.timestamp()+" Confirmed Authorisation, Restarting");
                        }
                        process.exit(1);
                    });
                });
            });
            utils.changeLEDColor('purple');
            console.log(utils.timestamp()+' Awaiting Activation');
        }
    }
};
var clientHandlers = {
    revokeCredentials: function() {
        console.log(utils.timestamp()+" Invalid Token, Restarting");
        // Delete token
        fs.unlinkSync(config.tokenFile);
        // Restart
        process.exit(1);
    },
    execute: function(command,fn) {
        if (utils.executeCommand(command)) {
            fn(null);   // Executed successfully
        } else {
            fn(true);   // Error executing
        }
    },
    update: function(toUpdate) {
        console.log(utils.timestamp()+" Updating");
        if (typeof toUpdate !== "object"
            || !(toUpdate instanceof Array)) return false;
        else utils.updateCode(toUpdate);
    }
};

 /*
// Process event handlers
process.on('exit',function() {
    utils.changeLEDColor('yellow');
});
process.on('SIGINT',function() {
    // Ctrl + C
});
process.on('uncaughtException',function(err) {
    // Unknown error
    console.log(err);
    process.exit(1);
});
*/
<commit_msg>Bump version<commit_after>var fs = require('fs'),
    http = require('http'),
    path = require('path'),
    util = require('util'),
    exec = require('child_process').exec,
    child_process = require('child_process'),
    utils = require(__dirname+'/lib/client-utils.js'),
    serialport = require('serialport'),
    SerialPort = serialport.SerialPort,
    sendIv = 0,
    rebootIv,
    tty,
    config =  {
        client:'beagle',
        nodeVersion:0.91,
        cloudHost: 'zendo.ninja.is',
        cloudStream: 'stream.ninja.is',
        cloudStreamPort: 443,
        cloudPort: 443,
        devtty: "/dev/ttyO1",
        locksDir: "/etc/opt/ninja",
        serialFile: "/etc/opt/ninja/serial.conf",
        tokenFile: "/etc/opt/ninja/token.conf",
        updateLock: '/etc/opt/ninja/.has_updated',
        heartbeat_interval: 5000,
        secure:true
    };
    config.id=fs.readFileSync(config.serialFile).toString().replace(/\n/g,'');
    config.utilitiesVersion=(path.existsSync('/opt/utilities/version'))
        ? parseFloat(fs.readFileSync('/opt/utilities/version'))
        : 0.4;
    config.systemVersion=(path.existsSync('/opt/utilities/sys_version'))
        ? parseFloat(fs.readFileSync('/opt/utilities/sys_version'))
        : 0.4;

console.log(utils.timestamp()+' Ninja Block Starting Up');

/*
    Fetch the arduino model and version
 */
child_process.execFile('/opt/utilities/bin/fetch_arduino_version',function(code,stdout,stderr) {
    if (stdout && stdout.indexOf('_')>-1) {
        var parts = stdout.split('_');
        config.arduinoModel = parts[0];
        config.arduinoVersion = parseFloat(parts[1]);
    } else if (stdout && stdout.length>0) {
        config.arduinoModel = 'V11';
        config.arduinoVersion = 0.36
    }
});
// We give 3 seconds to try and grab the arduino version
setTimeout(function() {
    // Setup the TTY serial port
    tty = new SerialPort(config.devtty, {
        parser: serialport.parsers.readline("\n")
    });
    utils.configure(config,tty);
    var up = upnode(clientHandlers).connect(connectionParams);
    up.on('up',function (remote) {
        utils.initRemote(remote);
        utils.changeLEDColor('green');
        console.log(utils.timestamp()+' All Systems Go');
    });
    up.on('reconnect',function() {
        utils.changeLEDColor('cyan');
        console.log(utils.timestamp()+' Reconnecting');
    });
},5000);

// Development overwrites
if (process.argv[2] == 'local') {
    config.cloudHost = process.argv[3];
    config.cloudPort = 3001;
    config.cloudStream = process.argv[3];
    config.cloudStreamPort = 3003;
    config.secure = false;
};

// Connect
// up down reconnect reconnect reconnect up
var upnode = require('upnode');
var proto = (config.secure) ? require('tls') : require('net');
var connectionParams = {
    ping:10000,
    timeout:5000,
    reconnect:2000,
    createStream:function () {
        return proto.connect(config.cloudPort, config.cloudHost);
    },
    block: function (remote, conn) {
        var params = utils.fetchBlockParams();
        var token = utils.fetchBlockToken();
        console.log(params);
        if (token) {
            remote.handshake(params, token, function (err, res) {
                if (err) console.error(utils.timestamp()+" "+err);
                else {
                    conn.emit('up',res);
                }
            });
            console.log(utils.timestamp()+' Connecting');
        } else {
            remote.activate(params,function(err,auth) {
                if (err||!auth) {
                    console.log(utils.timestamp()+" Error, Restarting");
                    process.exit(1);
                    return;
                }
                console.log(utils.timestamp()+" Received Authorisation, Confirming");
                fs.writeFile(config.tokenFile, auth.token, 'utf8',function(err) {
                    exec('sync');   // TODO: fsyncSync
                    if (err) throw err;
                    params.token=auth.token;
                    remote.confirmActivation(params,function(err) {
                        if (err) {
                            console.log(utils.timestamp()+" Error pairing block.")
                            console.log(utils.timestamp()+" "+err.error);
                            if (err.id===409) utils.changeLEDColor('blue');
                            fs.unlinkSync(config.tokenFile);
                        } else {
                            console.log(utils.timestamp()+" Confirmed Authorisation, Restarting");
                        }
                        process.exit(1);
                    });
                });
            });
            utils.changeLEDColor('purple');
            console.log(utils.timestamp()+' Awaiting Activation');
        }
    }
};
var clientHandlers = {
    revokeCredentials: function() {
        console.log(utils.timestamp()+" Invalid Token, Restarting");
        // Delete token
        fs.unlinkSync(config.tokenFile);
        // Restart
        process.exit(1);
    },
    execute: function(command,fn) {
        if (utils.executeCommand(command)) {
            fn(null);   // Executed successfully
        } else {
            fn(true);   // Error executing
        }
    },
    update: function(toUpdate) {
        console.log(utils.timestamp()+" Updating");
        if (typeof toUpdate !== "object"
            || !(toUpdate instanceof Array)) return false;
        else utils.updateCode(toUpdate);
    }
};

 /*
// Process event handlers
process.on('exit',function() {
    utils.changeLEDColor('yellow');
});
process.on('SIGINT',function() {
    // Ctrl + C
});
process.on('uncaughtException',function(err) {
    // Unknown error
    console.log(err);
    process.exit(1);
});
*/
<|endoftext|>


<commit_before>var fs = require('fs'),
    http = require('http'),
    path = require('path'),
    util = require('util'),
    exec = require('child_process').exec,
    child_process = require('child_process'),
    utils = require(__dirname+'/lib/client-utils.js'),
    serialport = require('serialport'),
    SerialPort = serialport.SerialPort,
    sendIv = 0,
    rebootIv,
    tty,
    config =  {
        client:'beagle',
        nodeVersion:0.8,
        cloudHost: 'zendo.ninja.is',
        cloudStream: 'stream.ninja.is',
        cloudStreamPort: 443,
        cloudPort: 443,
        devtty: "/dev/ttyO1",
        locksDir: "/etc/opt/ninja",
        serialFile: "/etc/opt/ninja/serial.conf",
        tokenFile: "/etc/opt/ninja/token.conf",
        updateLock: '/etc/opt/ninja/.has_updated',
        heartbeat_interval: 5000,
        secure:true
    };
    config.id=fs.readFileSync(config.serialFile).toString().replace(/\n/g,'');
    config.utilitiesVersion=(path.existsSync('/opt/utilities/version'))
        ? parseFloat(fs.readFileSync('/opt/utilities/version'))
        : 0.4;
    config.systemVersion=(path.existsSync('/opt/utilities/sys_version'))
        ? parseFloat(fs.readFileSync('/opt/utilities/sys_version'))
        : 0.4;

console.log(utils.timestamp()+' Ninja Block Starting Up');

/*
    Fetch the arduino model and version
 */
child_process.execFile('/opt/utilities/bin/fetch_arduino_version',function(code,stdout,stderr) {
    if (stdout && stdout.indexOf('_')>-1) {
        var parts = stdout.split('_');
        config.arduinoModel = parts[0];
        config.arduinoVersion = parseFloat(parts[1]);
    } else if (stdout && stdout.length>0) {
        config.arduinoModel = 'V11';
        config.arduinoVersion = 0.36
    }
});
// We give 3 seconds to try and grab the arduino version
setTimeout(function() {
    // Setup the TTY serial port
    tty = new SerialPort(config.devtty, {
        parser: serialport.parsers.readline("\n")
    });
    utils.configure(config,tty);
    utils.changeLEDColor('cyan');
    var up = upnode(clientHandlers).connect(connectionParams);
    up.on('up',function (remote) {
        utils.initRemote(remote);
        utils.changeLEDColor('green');
        console.log(utils.timestamp()+' All Systems Go');
    });
    up.on('reconnect',function() {
        utils.changeLEDColor('cyan');
        console.log(utils.timestamp()+' Reconnecting');
    });
},5000);

// Development overwrites
if (process.argv[2] == 'local') {
    config.cloudHost = process.argv[3];
    config.cloudPort = 3001;
    config.cloudStream = process.argv[3];
    config.cloudStreamPort = 3003;
    config.secure = false;
};

// Connect
// up down reconnect reconnect reconnect up
var upnode = require('upnode');
var proto = (config.secure) ? require('tls') : require('net');
var connectionParams = {
    ping:10000,
    timeout:5000,
    reconnect:2000,
    createStream:function () {
        return proto.connect(config.cloudPort, config.cloudHost);
    },
    block: function (remote, conn) {
        var params = utils.fetchBlockParams();
        var token = utils.fetchBlockToken();
        console.log(params);
        if (token) {
            remote.handshake(params, token, function (err, res) {
                if (err) console.error(utils.timestamp()+" "+err);
                else {
                    conn.emit('up',res);
                }
            });
            utils.changeLEDColor('cyan');
            console.log(utils.timestamp()+' Connecting');
        } else {
            remote.activate(params,function(err,auth) {
                if (err||!auth) {
                    console.log(utils.timestamp()+" Error, Restarting");
                    process.exit(1);
                    return;
                }
                console.log(utils.timestamp()+" Received Authorisation, Confirming");
                fs.writeFile(config.tokenFile, auth.token, 'utf8',function(err) {
                    exec('sync');   // TODO: fsyncSync
                    if (err) throw err;
                    params.token=auth.token;
                    remote.confirmActivation(params,function(err) {
                        if (err) {
                            console.log(utils.timestamp()+" Error pairing block.")
                            console.log(utils.timestamp()+" "+err.error);
                            if (err.id===409) utils.changeLEDColor('blue');
                            fs.unlinkSync(config.tokenFile);
                        } else {
                            console.log(utils.timestamp()+" Confirmed Authorisation, Restarting");
                        }
                        process.exit(1);
                    });
                });
            });
            utils.changeLEDColor('purple');
            console.log(utils.timestamp()+' Awaiting Activation');
        }
    }
};
var clientHandlers = {
    revokeCredentials: function() {
        console.log(utils.timestamp()+" Invalid Token, Restarting");
        // Delete token
        fs.unlinkSync(config.tokenFile);
        // Restart
        process.exit(1);
    },
    execute: function(command,fn) {
        if (utils.executeCommand(command)) {
            fn(null);   // Executed successfully
        } else {
            fn(true);   // Error executing
        }
    },
    update: function(toUpdate) {
        console.log(utils.timestamp()+" Updating");
        if (typeof toUpdate !== "object"
            || !(toUpdate instanceof Array)) return false;
        else utils.updateCode(toUpdate);
    }
};

 /*
// Process event handlers
process.on('exit',function() {
    utils.changeLEDColor('yellow');
});
process.on('SIGINT',function() {
    // Ctrl + C
});
process.on('uncaughtException',function(err) {
    // Unknown error
    console.log(err);
    process.exit(1);
});
*/
<commit_msg>Remove LED change to cyan causing race condition<commit_after>var fs = require('fs'),
    http = require('http'),
    path = require('path'),
    util = require('util'),
    exec = require('child_process').exec,
    child_process = require('child_process'),
    utils = require(__dirname+'/lib/client-utils.js'),
    serialport = require('serialport'),
    SerialPort = serialport.SerialPort,
    sendIv = 0,
    rebootIv,
    tty,
    config =  {
        client:'beagle',
        nodeVersion:0.8,
        cloudHost: 'zendo.ninja.is',
        cloudStream: 'stream.ninja.is',
        cloudStreamPort: 443,
        cloudPort: 443,
        devtty: "/dev/ttyO1",
        locksDir: "/etc/opt/ninja",
        serialFile: "/etc/opt/ninja/serial.conf",
        tokenFile: "/etc/opt/ninja/token.conf",
        updateLock: '/etc/opt/ninja/.has_updated',
        heartbeat_interval: 5000,
        secure:true
    };
    config.id=fs.readFileSync(config.serialFile).toString().replace(/\n/g,'');
    config.utilitiesVersion=(path.existsSync('/opt/utilities/version'))
        ? parseFloat(fs.readFileSync('/opt/utilities/version'))
        : 0.4;
    config.systemVersion=(path.existsSync('/opt/utilities/sys_version'))
        ? parseFloat(fs.readFileSync('/opt/utilities/sys_version'))
        : 0.4;

console.log(utils.timestamp()+' Ninja Block Starting Up');

/*
    Fetch the arduino model and version
 */
child_process.execFile('/opt/utilities/bin/fetch_arduino_version',function(code,stdout,stderr) {
    if (stdout && stdout.indexOf('_')>-1) {
        var parts = stdout.split('_');
        config.arduinoModel = parts[0];
        config.arduinoVersion = parseFloat(parts[1]);
    } else if (stdout && stdout.length>0) {
        config.arduinoModel = 'V11';
        config.arduinoVersion = 0.36
    }
});
// We give 3 seconds to try and grab the arduino version
setTimeout(function() {
    // Setup the TTY serial port
    tty = new SerialPort(config.devtty, {
        parser: serialport.parsers.readline("\n")
    });
    utils.configure(config,tty);
    var up = upnode(clientHandlers).connect(connectionParams);
    up.on('up',function (remote) {
        utils.initRemote(remote);
        utils.changeLEDColor('green');
        console.log(utils.timestamp()+' All Systems Go');
    });
    up.on('reconnect',function() {
        utils.changeLEDColor('cyan');
        console.log(utils.timestamp()+' Reconnecting');
    });
},5000);

// Development overwrites
if (process.argv[2] == 'local') {
    config.cloudHost = process.argv[3];
    config.cloudPort = 3001;
    config.cloudStream = process.argv[3];
    config.cloudStreamPort = 3003;
    config.secure = false;
};

// Connect
// up down reconnect reconnect reconnect up
var upnode = require('upnode');
var proto = (config.secure) ? require('tls') : require('net');
var connectionParams = {
    ping:10000,
    timeout:5000,
    reconnect:2000,
    createStream:function () {
        return proto.connect(config.cloudPort, config.cloudHost);
    },
    block: function (remote, conn) {
        var params = utils.fetchBlockParams();
        var token = utils.fetchBlockToken();
        console.log(params);
        if (token) {
            remote.handshake(params, token, function (err, res) {
                if (err) console.error(utils.timestamp()+" "+err);
                else {
                    conn.emit('up',res);
                }
            });
            utils.changeLEDColor('cyan');
            console.log(utils.timestamp()+' Connecting');
        } else {
            remote.activate(params,function(err,auth) {
                if (err||!auth) {
                    console.log(utils.timestamp()+" Error, Restarting");
                    process.exit(1);
                    return;
                }
                console.log(utils.timestamp()+" Received Authorisation, Confirming");
                fs.writeFile(config.tokenFile, auth.token, 'utf8',function(err) {
                    exec('sync');   // TODO: fsyncSync
                    if (err) throw err;
                    params.token=auth.token;
                    remote.confirmActivation(params,function(err) {
                        if (err) {
                            console.log(utils.timestamp()+" Error pairing block.")
                            console.log(utils.timestamp()+" "+err.error);
                            if (err.id===409) utils.changeLEDColor('blue');
                            fs.unlinkSync(config.tokenFile);
                        } else {
                            console.log(utils.timestamp()+" Confirmed Authorisation, Restarting");
                        }
                        process.exit(1);
                    });
                });
            });
            utils.changeLEDColor('purple');
            console.log(utils.timestamp()+' Awaiting Activation');
        }
    }
};
var clientHandlers = {
    revokeCredentials: function() {
        console.log(utils.timestamp()+" Invalid Token, Restarting");
        // Delete token
        fs.unlinkSync(config.tokenFile);
        // Restart
        process.exit(1);
    },
    execute: function(command,fn) {
        if (utils.executeCommand(command)) {
            fn(null);   // Executed successfully
        } else {
            fn(true);   // Error executing
        }
    },
    update: function(toUpdate) {
        console.log(utils.timestamp()+" Updating");
        if (typeof toUpdate !== "object"
            || !(toUpdate instanceof Array)) return false;
        else utils.updateCode(toUpdate);
    }
};

 /*
// Process event handlers
process.on('exit',function() {
    utils.changeLEDColor('yellow');
});
process.on('SIGINT',function() {
    // Ctrl + C
});
process.on('uncaughtException',function(err) {
    // Unknown error
    console.log(err);
    process.exit(1);
});
*/
<|endoftext|>