<commit_before>var templates = [
    "root/externallib/text!root/plugins/grades/activities.html"
];

define(templates,function (activities) {
    var plugin = {
        settings: {
            name: "grades",
            type: "course",
            menuURL: "#course/grades/",
            lang: {
                component: "core"
            }
        },

        routes: [
            ["course/grades/:courseid", "course_grades_activities", "viewActivities"]
        ],

        isPluginVisible: function() {
            // We store the WSName for later.
            // Since there is a but in the core_grades_get_grades we only allow to use the local_mobile plugin one.
            if (MM.util.wsAvailable('local_mobile_core_grades_get_grades')) {
                MM.plugins.grades.wsName = 'local_mobile_core_grades_get_grades';
                return true;
            }
            return false;
        },

        viewActivities: function(courseId) {

            // Adding loading icon.
            var menuEl = 'a[href="#course/grades/' + courseId + '"]';
            $(menuEl, '#panel-left').addClass('loading-row');

            MM.panels.showLoading('center');

            var data = {
                "options[0][name]" : "",
                "options[0][value]" : ""
            };
            data.courseid = courseId;

            MM.moodleWSCall('core_course_get_contents', data, function(contents) {
                var course = MM.db.get("courses", MM.config.current_site.id + "-" + courseId);

                var tpl = {
                    sections: contents,
                    course: course.toJSON() // Convert a model to a plain javascript object.
                };
                var html = MM.tpl.render(MM.plugins.grades.templates.activities.html, tpl);

                $(menuEl, '#panel-left').removeClass('loading-row');
                MM.panels.show("center", html, {title: MM.lang.s("grades"), hideRight: true});

                var id;
                // Now, load grades for all the elements
                $(".grade").each(function() {
                    id = $(this).attr("id");
                    if (id) {
                        MM.plugins.grades._getGradeForActivity(id);
                    }
                });
            });
        },

        _getGradeForActivity: function(id) {

            var info = id.split("-");

            var data = {
                "courseid" : parseInt(info[3]),
                "component" : "mod_" + info[4],
                "activityid" : parseInt(info[5]),
                "userids[0]" : MM.config.current_site.userid
            };

            var grade = "--";
            var range = "--";
            var percentage = "--";
            var feedback = "--";

            MM.moodleWSCall(MM.plugins.grades.wsName, data,
                // Succes callback.
                function(contents) {

                    if (contents.items && contents.items[0]) {
                        var min = contents.items[0]["grademin"];
                        var max = contents.items[0]["grademax"];
                        range = min + " - " + max;

                        if (contents.items[0]["grades"] && contents.items[0]["grades"][0]) {
                            gradeInfo = contents.items[0]["grades"][0];

                            grade = gradeInfo["str_long_grade"];
                            feedback = MM.util.formatText(gradeInfo["str_feedback"], true);
                            numGrade = gradeInfo["grade"];

                            var div = max - min;
                            if (numGrade && div) {
                                percentage = ((numGrade - min) * 100) / (div);
                                percentage += " %";
                            }
                        }
                    }

                    if (contents.outcomes && contents.outcomes[0] &&
                        contents.outcomes[0]["grades"] && contents.outcomes[0]["grades"][0]) {
                        var strGrade = contents.outcomes[0]["grades"][0]["str_grade"];
                        grade += " (" + strGrade + ")";
                    }

                    $("#" + id).html(grade);
                    $("#" + id.replace("-grade-", "-range-")).html(range);
                    $("#" + id.replace("-grade-", "-percentage-")).html(percentage);
                    $("#" + id.replace("-grade-", "-feedback-")).html(feedback);

                },
                {},
                // Error callback.
                function() {
                    var error = MM.lang.s("errorretrievinggradeinformation");
                    $("#" + id).html(error);
                    $("#" + id.replace("-grade-", "-range-")).html(error);
                    $("#" + id.replace("-grade-", "-percentage-")).html(error);
                    $("#" + id.replace("-grade-", "-feedback-")).html(error);
                }
            );

        },

        templates: {
            "activities": {
                html: activities
            }
        }
    };

    MM.registerPlugin(plugin);
});<commit_msg>MOBILE-634 Improve grades plugin errors checking<commit_after>var templates = [
    "root/externallib/text!root/plugins/grades/activities.html"
];

define(templates,function (activities) {
    var plugin = {
        settings: {
            name: "grades",
            type: "course",
            menuURL: "#course/grades/",
            lang: {
                component: "core"
            }
        },

        routes: [
            ["course/grades/:courseid", "course_grades_activities", "viewActivities"]
        ],

        isPluginVisible: function() {
            // We store the WSName for later.
            // Since there is a but in the core_grades_get_grades we only allow to use the local_mobile plugin one.
            if (MM.util.wsAvailable('local_mobile_core_grades_get_grades')) {
                MM.plugins.grades.wsName = 'local_mobile_core_grades_get_grades';
                return true;
            }
            return false;
        },

        viewActivities: function(courseId) {

            // Adding loading icon.
            var menuEl = 'a[href="#course/grades/' + courseId + '"]';
            $(menuEl, '#panel-left').addClass('loading-row');

            MM.panels.showLoading('center');

            var data = {
                "options[0][name]" : "",
                "options[0][value]" : ""
            };
            data.courseid = courseId;

            MM.moodleWSCall('core_course_get_contents', data, function(contents) {
                var course = MM.db.get("courses", MM.config.current_site.id + "-" + courseId);

                var tpl = {
                    sections: contents,
                    course: course.toJSON() // Convert a model to a plain javascript object.
                };
                var html = MM.tpl.render(MM.plugins.grades.templates.activities.html, tpl);

                $(menuEl, '#panel-left').removeClass('loading-row');
                MM.panels.show("center", html, {title: MM.lang.s("grades"), hideRight: true});

                var id;
                // Now, load grades for all the elements
                $(".grade").each(function() {
                    id = $(this).attr("id");
                    if (id) {
                        MM.plugins.grades._getGradeForActivity(id);
                    }
                });
            });
        },

        _getGradeForActivity: function(id) {

            var info = id.split("-");

            var data = {
                "courseid" : parseInt(info[3]),
                "component" : "mod_" + info[4],
                "activityid" : parseInt(info[5]),
                "userids[0]" : MM.config.current_site.userid
            };

            var grade = "--";
            var range = "--";
            var percentage = "--";
            var feedback = "--";

            MM.moodleWSCall(MM.plugins.grades.wsName, data,
                // Succes callback.
                function(contents) {

                    if (contents.items && contents.items[0]) {
                        var min = contents.items[0]["grademin"];
                        var max = contents.items[0]["grademax"];
                        range = min + " - " + max;

                        if (contents.items[0]["grades"] && contents.items[0]["grades"][0]) {
                            gradeInfo = contents.items[0]["grades"][0];

                            grade = gradeInfo["str_long_grade"];
                            feedback = MM.util.formatText(gradeInfo["str_feedback"], true);
                            numGrade = gradeInfo["grade"];

                            var div = max - min;
                            if (numGrade && div) {
                                percentage = ((numGrade - min) * 100) / (div);
                                percentage += " %";
                            }
                        }
                    }

                    if (contents.outcomes && contents.outcomes[0] &&
                        contents.outcomes[0]["grades"] && contents.outcomes[0]["grades"][0]) {
                        var strGrade = contents.outcomes[0]["grades"][0]["str_grade"];
                        grade += " (" + strGrade + ")";
                    }

                    $("#" + id).html(grade);
                    $("#" + id.replace("-grade-", "-range-")).html(range);
                    $("#" + id.replace("-grade-", "-percentage-")).html(percentage);
                    $("#" + id.replace("-grade-", "-feedback-")).html(feedback);

                },
                {},
                // Error callback.
                function(e) {
                    var error;

                    if (typeof e == "string") {
                        error = e;
                    } else {
                        error = MM.lang.s("errorretrievinggradeinformation");
                    }
                    $("#" + id).html(error);
                    $("#" + id.replace("-grade-", "-range-")).html(error);
                    $("#" + id.replace("-grade-", "-percentage-")).html(error);
                    $("#" + id.replace("-grade-", "-feedback-")).html(error);
                }
            );

        },

        templates: {
            "activities": {
                html: activities
            }
        }
    };

    MM.registerPlugin(plugin);
});<|endoftext|>


<commit_before>//    xiNET interaction viewer
//    Copyright 2013 Rappsilber Laboratory
//
//    This product includes software developed at
//    the Rappsilber Laboratory (http://www.rappsilberlab.org/).

//rename to sequence link?
ResidueLink.prototype = new xinet.Link();
function ResidueLink(id, proteinLink, fromBinding, toBinding, xlvController, interaction) {
    this.id = id;
    //this.matches = new Array(0); //rename to evidence, temp commented out as filtering disabled
    this.xlv = xlvController;
    this.proteinLink = proteinLink;
    if (fromBinding === '?-?') {
        this.fromStartRes = -1;
        this.fromEndRes = -1;
    } else {
        var match = /(\d+)-(\d+)/.exec(fromBinding);
        this.fromStartRes = match[1] * 1;
        this.fromEndRes = match[2] * 1;
    }
    if (toBinding === '?-?') {
        this.toStartRes = -1;
        this.toEndRes = -1;
    } else {
        match = /(\d+)-(\d+)/.exec(toBinding);
        this.toStartRes = match[1] * 1;
        this.toEndRes = match[2] * 1;
    }
    this.evidence = interaction;

    this.intra = false;
    if (typeof this.proteinLink !== 'undefined') {
        if (this.proteinLink.fromProtein === this.proteinLink.toProtein) {
            this.intra = true;
        }
    }
    this.ambig = false;
    this.tooltip = this.id;
    this.curveMidX = null;
    //used to avoid some unnecessary manipulation of DOM
    this.shown = false;
    this.dashed = false;

}

ResidueLink.prototype.initSVG = function() {
    if (typeof this.glyph === 'undefined') {
        this.glyph = document.createElementNS(xinet.svgns, "path");
        this.highlightGlyph = document.createElementNS(xinet.svgns, "path");
        this.glyph.setAttribute("stroke-linecap", "round");
        this.highlightGlyph.setAttribute("stroke-linecap", "round");

        this.glyph.setAttribute("class", "link");
        this.glyph.setAttribute("fill", "#E08214");
        this.glyph.setAttribute("stroke", "#A08214");
        this.highlightGlyph.setAttribute("class", "link");
        this.highlightGlyph.setAttribute("fill", "none");
        this.highlightGlyph.setAttribute("stroke", xinet.highlightColour.toRGB());
        this.highlightGlyph.setAttribute("stroke-width", "10");
        this.highlightGlyph.setAttribute("stroke-opacity", "0");

        if (typeof this.colour !== 'undefined') {
            this.glyph.setAttribute("fill", this.colour.toString());
        }

        //set the events for it
        var self = this;
        this.glyph.onmousedown = function(evt) {
            self.mouseDown(evt);
        };
        this.glyph.onmouseover = function(evt) {
            self.mouseOver(evt);
        };
        this.glyph.onmouseout = function(evt) {
            self.mouseOut(evt);
        };
        this.highlightGlyph.onmousedown = function(evt) {
            self.mouseDown(evt);
        };
        this.highlightGlyph.onmouseover = function(evt) {
            self.mouseOver(evt);
        };
        this.highlightGlyph.onmouseout = function(evt) {
            self.mouseOut(evt);
        };
    }
};

ResidueLink.prototype.getFromProtein = function() {
    return this.proteinLink.fromProtein;
};

ResidueLink.prototype.getToProtein = function() {
    return this.proteinLink.toProtein;
};

//andAlternatives means highlight alternative links in case of site ambiguity
ResidueLink.prototype.showHighlight = function(show, andAlternatives) {
    if (typeof andAlternatives === 'undefined') {
        andAlternatives = false;//TODO: tEMP HACK
    }
    if (this.shown) {
        if (show) {
            this.highlightGlyph.setAttribute("stroke-opacity", "1");
        } else {
            this.highlightGlyph.setAttribute("stroke-opacity", "0");
        }
    }
    if (andAlternatives && this.ambig) {
        //TODO: we want to highlight smallest possible set of alternatives?
        var mc = this.matches.length;
        for (var m = 0; m < mc; m++) {
            var match = this.matches[m];
            if (match.isAmbig()) {
                var rc = match.residueLinks.length;
                for (var rl = 0; rl < rc; rl++) {
                    var resLink = match.residueLinks[rl];
                    resLink.showHighlight(show, false);
                    resLink.proteinLink.showHighlight(show, false);
                }
            }
        }
    }
};

//used when link clicked
ResidueLink.prototype.showID = function() {
    var fromProt = this.proteinLink.fromProtein;
    var toProt = this.proteinLink.toProtein;
    var linkInfo = "
" + this.fromStartRes + "-" + this.fromEndRes
            + ", " + fromProt.name + " (" + fromProt.accession
            + ") to " + this.toStartRes + "-" + this.toEndRes
            + ", " + toProt.name + " (" + toProt.accession
            + ")
";
    linkInfo += "
" + JSON.stringify(this.evidence, null, '\t') + "
";
    this.xlv.message(linkInfo);
};

//used when filter changed
ResidueLink.prototype.check = function(filter) {
    if (this.xlv.intraHidden && this.intra) {
        this.hide();
        return false;
    }
    if (this.proteinLink.hidden) {
        this.hide();
        return false;
    }
    //        if (this.proteinLink.sc >= this.xlv.cutOff) {
    this.show();
    return true;
//        } else {
//            this.hide();
//            return false;
//        }
//   }
};

ResidueLink.prototype.show = function() {
    if (this.xlv.initComplete) {
        if (!this.shown) {
            this.shown = true;
            if (typeof this.line === 'undefined') {
                this.initSVG();
            }
            if (this.intra) {
                this.line.setAttribute("stroke-width", xinet.linkWidth); //this.xlv.z*
                //                this.highlightLine.setAttribute("stroke-width", 10);
                this.proteinLink.fromProtein.intraLinksHighlights.appendChild(this.highlightLine);
                this.proteinLink.fromProtein.intraLinks.appendChild(this.line);
            }
            else {

                this.glyph.setAttribute("stroke-width", this.xlv.z * xinet.linkWidth);
                this.highlightGlyph.setAttribute("stroke-width", this.xlv.z * 10);
                this.setLinkCoordinates();
                this.xlv.highlights.appendChild(this.highlightGlyph);
                this.xlv.res_resLinks.appendChild(this.glyph);
            }
        }
    }
};

ResidueLink.prototype.hide = function() {
    if (this.xlv.initComplete) {
        if (this.shown) {
            this.shown = false;
            if (this.intra) {
//                this.proteinLink.fromProtein.intraLinksHighlights.removeChild(this.highlightLine);
                this.proteinLink.fromProtein.intraLinks.removeChild(this.line);
            }
            else {
//                this.xlv.res_resLinks.removeChild(this.line);
                this.xlv.highlights.removeChild(this.highlightLine);
            }
        }
    }
};

// update the links(lines) to fit to the protein
ResidueLink.prototype.setLinkCoordinates = function(interactor) {
    if (this.shown) { //don't waste time changing DOM if link is not visible

        //TODO: - tidy this up, store stick rotation in rad's not deg's
        //flip or not? - first get mid points
        var fromMidRes = (this.fromStartRes + this.fromEndRes) / 2;
        var fMid = this.proteinLink.fromProtein.getResidueCoordinates(fromMidRes);

        var toMidRes = (this.toStartRes + this.toEndRes) / 2;
        var tMid = this.proteinLink.toProtein.getResidueCoordinates(toMidRes);

        //calculate angle from fromProtMidPOint to toProtMidPoint
        var deltaX = fMid[0] - tMid[0];
        var deltaY = fMid[1] - tMid[1];

        var angleBetweenMidPoints = Math.atan2(deltaY, deltaX);
        var abmpDeg = angleBetweenMidPoints / (2 * Math.PI) * 360;
//        abmpDeg = abmpDeg % 360;
        if (abmpDeg < 0)
            abmpDeg += 360;
        var out = (abmpDeg - this.proteinLink.fromProtein.rotation);

        if (out < 0)
            out += 360;
//        xlv.message("FROM END- abmp:" + abmpDeg + "\tfpr:" + this.proteinLink.fromProtein.rotation
//                + "\td: " + out);

        var fRotRad = (this.proteinLink.fromProtein.rotation / 360) * Math.PI * 2;
        var fs = this.proteinLink.fromProtein.getResidueCoordinates(this.fromStartRes);
        var fe = this.proteinLink.fromProtein.getResidueCoordinates(this.fromEndRes);

        if (out > 180) {
            fRotRad = fRotRad - Math.PI;
        }

        var ftMid = [fMid[0] + (30 * Math.sin(fRotRad)),
            fMid[1] - (30 * Math.cos(fRotRad))];

        out = (abmpDeg - this.proteinLink.toProtein.rotation);
        if (out < 0)
            out += 360;
        xlv.message("TO END- abmp:" + abmpDeg + "\ttpr:" + this.proteinLink.toProtein.rotation
                + "\td: " + out);

        var tRotRad = (this.proteinLink.toProtein.rotation / 360) * Math.PI * 2;

        var ts = this.proteinLink.toProtein.getResidueCoordinates(this.toStartRes);
        var te = this.proteinLink.toProtein.getResidueCoordinates(this.toEndRes);
        if (out < 180) {
            tRotRad = tRotRad - Math.PI;
//            ts[1] += Protein.STICKHEIGHT / 2;
//            te[1] += Protein.STICKHEIGHT / 2;

        }
        else {
//            ts[1] -= Protein.STICKHEIGHT / 2;
//            te[1] -= Protein.STICKHEIGHT / 2;

        }

        var ttMid = [tMid[0] + (30 * Math.sin(tRotRad)),
            tMid[1] - (30 * Math.cos(tRotRad))];

        var triPointMid = [(ftMid[0] + ttMid[0]) / 2, (ftMid[1] + ttMid[1]) / 2];

        var path = 'M' + fs[0] + ',' + fs[1] +
                ' Q' + ftMid[0] + ',' + ftMid[1] + ' ' + triPointMid[0] + ',' + triPointMid[1] +
                ' Q' + ttMid[0] + ',' + ttMid[1] + ' ' + ts[0] + ',' + ts[1] +
                ' L' + te[0] + ',' + te[1] +
                ' Q ' + ttMid[0] + ',' + ttMid[1] + ' ' + triPointMid[0] + ',' + triPointMid[1]
                + ' Q' + ftMid[0] + ',' + ftMid[1] + ' ' + fe[0] + ',' + fe[1]
                + ' Z';

        this.glyph.setAttribute("d", path);
        this.highlightGlyph.setAttribute("d", path);

        function midPoint(a, b)
        {
            return [(a[0] + b[0]) / 2, (a[1] + b[1]) / 2];
        };
        function reflect(point, around, scale)
        {
            return [around[0] + scale * (around[0] - point[0]), around[1] + scale * (around[1] - point[1])];
        };

//        this.glyph.setAttribute("d", 'M' + fs[0] + ' ' + fs[1] +
//                ' L' + fMid[0] + ' ' + fMid[1]
//                + ' L' + fe[0] + ' ' + fe[1] + ' Z');

//        var triPointThird = [fMid[0] + ((tMid[0] - fMid[0]) / 3), fMid[1] + ((tMid[1] - fMid[1]) / 3)];
//        var triPointTwoThird = [fMid[0] + ((tMid[0] - fMid[0]) * 2 / 3), fMid[1] + ((tMid[1] - fMid[1]) * 2 / 3)];
//        var reflectFtMid = reflect(ftMid, triPointThird, 0.7);
//        var reflectTtMid = reflect(ttMid, triPointTwoThird, 0.7);
//        var path = 'M' + fs[0] + ',' + fs[1] +
//                ' Q' + ftMid[0] + ',' + ftMid[1] + ' ' + triPointThird[0] + ',' + triPointThird[1] +
//                ' C' + reflectFtMid[0] + ',' + reflectFtMid[1] + ' ' + reflectTtMid[0] + ',' + reflectTtMid[1] + ' ' + triPointTwoThird[0] + ',' + triPointTwoThird[1] +
//                ' Q' + ttMid[0] + ',' + ttMid[1] + ' ' + ts[0] + ',' + ts[1] +
//                ' L' + te[0] + ',' + te[1] +
//                ' Q' + ttMid[0] + ',' + ttMid[1] + ' ' + triPointTwoThird[0] + ',' + triPointTwoThird[1] +
//                ' C' + reflectTtMid[0] + ',' + reflectTtMid[1] + ' ' + reflectFtMid[0] + ',' + reflectFtMid[1] + ' ' + triPointThird[0] + ',' + triPointThird[1] +
//                ' Q' + ftMid[0] + ',' + ftMid[1] + ' ' + fe[0] + ',' + fe[1] +
//                ' Z';
    }
};<commit_msg>fixed bug in previous commit<commit_after>//    xiNET interaction viewer
//    Copyright 2013 Rappsilber Laboratory
//
//    This product includes software developed at
//    the Rappsilber Laboratory (http://www.rappsilberlab.org/).

//rename to sequence link?
ResidueLink.prototype = new xinet.Link();
function ResidueLink(id, proteinLink, fromBinding, toBinding, xlvController, interaction) {
    this.id = id;
    //this.matches = new Array(0); //rename to evidence, temp commented out as filtering disabled
    this.xlv = xlvController;
    this.proteinLink = proteinLink;
    if (fromBinding === '?-?') {
        this.fromStartRes = -1;
        this.fromEndRes = -1;
    } else {
        var match = /(\d+)-(\d+)/.exec(fromBinding);
        this.fromStartRes = match[1] * 1;
        this.fromEndRes = match[2] * 1;
    }
    if (toBinding === '?-?') {
        this.toStartRes = -1;
        this.toEndRes = -1;
    } else {
        match = /(\d+)-(\d+)/.exec(toBinding);
        this.toStartRes = match[1] * 1;
        this.toEndRes = match[2] * 1;
    }
    this.evidence = interaction;

    this.intra = false;
    if (typeof this.proteinLink !== 'undefined') {
        if (this.proteinLink.fromProtein === this.proteinLink.toProtein) {
            this.intra = true;
        }
    }
    this.ambig = false;
    this.tooltip = this.id;
    this.curveMidX = null;
    //used to avoid some unnecessary manipulation of DOM
    this.shown = false;
    this.dashed = false;

}

ResidueLink.prototype.initSVG = function() {
    if (typeof this.glyph === 'undefined') {
        this.glyph = document.createElementNS(xinet.svgns, "path");
        this.highlightGlyph = document.createElementNS(xinet.svgns, "path");
        this.glyph.setAttribute("stroke-linecap", "round");
        this.highlightGlyph.setAttribute("stroke-linecap", "round");

        this.glyph.setAttribute("class", "link");
        this.glyph.setAttribute("fill", "#E08214");
        this.glyph.setAttribute("stroke", "#A08214");
        this.highlightGlyph.setAttribute("class", "link");
        this.highlightGlyph.setAttribute("fill", "none");
        this.highlightGlyph.setAttribute("stroke", xinet.highlightColour.toRGB());
        this.highlightGlyph.setAttribute("stroke-width", "10");
        this.highlightGlyph.setAttribute("stroke-opacity", "0");

        if (typeof this.colour !== 'undefined') {
            this.glyph.setAttribute("fill", this.colour.toString());
        }

        //set the events for it
        var self = this;
        this.glyph.onmousedown = function(evt) {
            self.mouseDown(evt);
        };
        this.glyph.onmouseover = function(evt) {
            self.mouseOver(evt);
        };
        this.glyph.onmouseout = function(evt) {
            self.mouseOut(evt);
        };
        this.highlightGlyph.onmousedown = function(evt) {
            self.mouseDown(evt);
        };
        this.highlightGlyph.onmouseover = function(evt) {
            self.mouseOver(evt);
        };
        this.highlightGlyph.onmouseout = function(evt) {
            self.mouseOut(evt);
        };
    }
};

ResidueLink.prototype.getFromProtein = function() {
    return this.proteinLink.fromProtein;
};

ResidueLink.prototype.getToProtein = function() {
    return this.proteinLink.toProtein;
};

//andAlternatives means highlight alternative links in case of site ambiguity
ResidueLink.prototype.showHighlight = function(show, andAlternatives) {
    if (typeof andAlternatives === 'undefined') {
        andAlternatives = false;//TODO: tEMP HACK
    }
    if (this.shown) {
        if (show) {
            this.highlightGlyph.setAttribute("stroke-opacity", "1");
        } else {
            this.highlightGlyph.setAttribute("stroke-opacity", "0");
        }
    }
    if (andAlternatives && this.ambig) {
        //TODO: we want to highlight smallest possible set of alternatives?
        var mc = this.matches.length;
        for (var m = 0; m < mc; m++) {
            var match = this.matches[m];
            if (match.isAmbig()) {
                var rc = match.residueLinks.length;
                for (var rl = 0; rl < rc; rl++) {
                    var resLink = match.residueLinks[rl];
                    resLink.showHighlight(show, false);
                    resLink.proteinLink.showHighlight(show, false);
                }
            }
        }
    }
};

//used when link clicked
ResidueLink.prototype.showID = function() {
    var fromProt = this.proteinLink.fromProtein;
    var toProt = this.proteinLink.toProtein;
    var linkInfo = "
" + this.fromStartRes + "-" + this.fromEndRes
            + ", " + fromProt.name + " (" + fromProt.accession
            + ") to " + this.toStartRes + "-" + this.toEndRes
            + ", " + toProt.name + " (" + toProt.accession
            + ")
";
    linkInfo += "
" + JSON.stringify(this.evidence, null, '\t') + "
";
    this.xlv.message(linkInfo);
};

//used when filter changed
ResidueLink.prototype.check = function(filter) {
    if (this.xlv.intraHidden && this.intra) {
        this.hide();
        return false;
    }
    if (this.proteinLink.hidden) {
        this.hide();
        return false;
    }
    //        if (this.proteinLink.sc >= this.xlv.cutOff) {
    this.show();
    return true;
//        } else {
//            this.hide();
//            return false;
//        }
//   }
};

ResidueLink.prototype.show = function() {
    if (this.xlv.initComplete) {
        if (!this.shown) {
            this.shown = true;
            if (typeof this.line === 'undefined') {
                this.initSVG();
            }
            if (this.intra) {
                this.line.setAttribute("stroke-width", xinet.linkWidth); //this.xlv.z*
                //                this.highlightLine.setAttribute("stroke-width", 10);
                this.proteinLink.fromProtein.intraLinksHighlights.appendChild(this.highlightLine);
                this.proteinLink.fromProtein.intraLinks.appendChild(this.line);
            }
            else {

                this.glyph.setAttribute("stroke-width", this.xlv.z * xinet.linkWidth);
                this.highlightGlyph.setAttribute("stroke-width", this.xlv.z * 10);
                this.setLinkCoordinates();
                this.xlv.highlights.appendChild(this.highlightGlyph);
                this.xlv.res_resLinks.appendChild(this.glyph);
            }
        }
    }
};

ResidueLink.prototype.hide = function() {
    if (this.xlv.initComplete) {
        if (this.shown) {
            this.shown = false;
            if (this.intra) {
//                this.proteinLink.fromProtein.intraLinksHighlights.removeChild(this.highlightLine);
//                this.proteinLink.fromProtein.intraLinks.removeChild(this.line);
            }
            else {
                this.xlv.res_resLinks.removeChild(this.glyph);
                this.xlv.highlights.removeChild(this.highlightGlyph);
            }
        }
    }
};

// update the links(lines) to fit to the protein
ResidueLink.prototype.setLinkCoordinates = function(interactor) {
    if (this.shown) { //don't waste time changing DOM if link is not visible

        //TODO: - tidy this up, store stick rotation in rad's not deg's
        //flip or not? - first get mid points
        var fromMidRes = (this.fromStartRes + this.fromEndRes) / 2;
        var fMid = this.proteinLink.fromProtein.getResidueCoordinates(fromMidRes);

        var toMidRes = (this.toStartRes + this.toEndRes) / 2;
        var tMid = this.proteinLink.toProtein.getResidueCoordinates(toMidRes);

        //calculate angle from fromProtMidPOint to toProtMidPoint
        var deltaX = fMid[0] - tMid[0];
        var deltaY = fMid[1] - tMid[1];

        var angleBetweenMidPoints = Math.atan2(deltaY, deltaX);
        var abmpDeg = angleBetweenMidPoints / (2 * Math.PI) * 360;
//        abmpDeg = abmpDeg % 360;
        if (abmpDeg < 0)
            abmpDeg += 360;
        var out = (abmpDeg - this.proteinLink.fromProtein.rotation);

        if (out < 0)
            out += 360;
//        xlv.message("FROM END- abmp:" + abmpDeg + "\tfpr:" + this.proteinLink.fromProtein.rotation
//                + "\td: " + out);

        var fRotRad = (this.proteinLink.fromProtein.rotation / 360) * Math.PI * 2;
        var fs = this.proteinLink.fromProtein.getResidueCoordinates(this.fromStartRes);
        var fe = this.proteinLink.fromProtein.getResidueCoordinates(this.fromEndRes);

        if (out > 180) {
            fRotRad = fRotRad - Math.PI;
        }

        var ftMid = [fMid[0] + (30 * Math.sin(fRotRad)),
            fMid[1] - (30 * Math.cos(fRotRad))];

        out = (abmpDeg - this.proteinLink.toProtein.rotation);
        if (out < 0)
            out += 360;
        xlv.message("TO END- abmp:" + abmpDeg + "\ttpr:" + this.proteinLink.toProtein.rotation
                + "\td: " + out);

        var tRotRad = (this.proteinLink.toProtein.rotation / 360) * Math.PI * 2;

        var ts = this.proteinLink.toProtein.getResidueCoordinates(this.toStartRes);
        var te = this.proteinLink.toProtein.getResidueCoordinates(this.toEndRes);
        if (out < 180) {
            tRotRad = tRotRad - Math.PI;
//            ts[1] += Protein.STICKHEIGHT / 2;
//            te[1] += Protein.STICKHEIGHT / 2;

        }
        else {
//            ts[1] -= Protein.STICKHEIGHT / 2;
//            te[1] -= Protein.STICKHEIGHT / 2;

        }

        var ttMid = [tMid[0] + (30 * Math.sin(tRotRad)),
            tMid[1] - (30 * Math.cos(tRotRad))];

        var triPointMid = [(ftMid[0] + ttMid[0]) / 2, (ftMid[1] + ttMid[1]) / 2];

        var path = 'M' + fs[0] + ',' + fs[1] +
                ' Q' + ftMid[0] + ',' + ftMid[1] + ' ' + triPointMid[0] + ',' + triPointMid[1] +
                ' Q' + ttMid[0] + ',' + ttMid[1] + ' ' + ts[0] + ',' + ts[1] +
                ' L' + te[0] + ',' + te[1] +
                ' Q ' + ttMid[0] + ',' + ttMid[1] + ' ' + triPointMid[0] + ',' + triPointMid[1]
                + ' Q' + ftMid[0] + ',' + ftMid[1] + ' ' + fe[0] + ',' + fe[1]
                + ' Z';

        this.glyph.setAttribute("d", path);
        this.highlightGlyph.setAttribute("d", path);

        function midPoint(a, b)
        {
            return [(a[0] + b[0]) / 2, (a[1] + b[1]) / 2];
        };
        function reflect(point, around, scale)
        {
            return [around[0] + scale * (around[0] - point[0]), around[1] + scale * (around[1] - point[1])];
        };

//        this.glyph.setAttribute("d", 'M' + fs[0] + ' ' + fs[1] +
//                ' L' + fMid[0] + ' ' + fMid[1]
//                + ' L' + fe[0] + ' ' + fe[1] + ' Z');

//        var triPointThird = [fMid[0] + ((tMid[0] - fMid[0]) / 3), fMid[1] + ((tMid[1] - fMid[1]) / 3)];
//        var triPointTwoThird = [fMid[0] + ((tMid[0] - fMid[0]) * 2 / 3), fMid[1] + ((tMid[1] - fMid[1]) * 2 / 3)];
//        var reflectFtMid = reflect(ftMid, triPointThird, 0.7);
//        var reflectTtMid = reflect(ttMid, triPointTwoThird, 0.7);
//        var path = 'M' + fs[0] + ',' + fs[1] +
//                ' Q' + ftMid[0] + ',' + ftMid[1] + ' ' + triPointThird[0] + ',' + triPointThird[1] +
//                ' C' + reflectFtMid[0] + ',' + reflectFtMid[1] + ' ' + reflectTtMid[0] + ',' + reflectTtMid[1] + ' ' + triPointTwoThird[0] + ',' + triPointTwoThird[1] +
//                ' Q' + ttMid[0] + ',' + ttMid[1] + ' ' + ts[0] + ',' + ts[1] +
//                ' L' + te[0] + ',' + te[1] +
//                ' Q' + ttMid[0] + ',' + ttMid[1] + ' ' + triPointTwoThird[0] + ',' + triPointTwoThird[1] +
//                ' C' + reflectTtMid[0] + ',' + reflectTtMid[1] + ' ' + reflectFtMid[0] + ',' + reflectFtMid[1] + ' ' + triPointThird[0] + ',' + triPointThird[1] +
//                ' Q' + ftMid[0] + ',' + ftMid[1] + ' ' + fe[0] + ',' + fe[1] +
//                ' Z';
    }
};<|endoftext|>


<commit_before>import isArray from 'lodash/isArray'

export default {
  'filled?': {
    check: (validatable) => !!validatable,
  },
  'int?': {
    check: (validatable) => /^\d+$/.test(validatable),
  },
  'date?': {
    check: (validatable) => /^\d{4}-\d{2}-\d{2}$/.test(validatable),
  },
  'max_size?': {
    check: (validatable, option) => validatable.length < option,
    translationOptions: option => ({ num: option }),
  },
  'gt?': {
    check: (validatable, option) => validatable > option,
    translationOptions: option => ({ num: option }),
  },
  'matches?': {
    check: (validatable, option) => validatable == option,
    translationOptions: option => ({ num: option }),
  },
  'format?': {
    check: (validatable, regex) => regex.test(validatable),
  },
  'size?': {
    check: (validatable, range) =>
      validatable.length > range[0] && validatable.length < range[1],
    argType: (arg) => isArray(arg) ? 'range' : 'default',
    translationOptions: (option) => ({
      size_left: option[0], size_right: option[1]
    }),
  },
}
<commit_msg>filled validation fails on empty array<commit_after>import isArray from 'lodash/isArray'

export default {
  'filled?': {
    check: (validatable) =>
      isArray(validatable) ? validatable.length : !!validatable,
  },
  'int?': {
    check: (validatable) => /^\d+$/.test(validatable),
  },
  'date?': {
    check: (validatable) => /^\d{4}-\d{2}-\d{2}$/.test(validatable),
  },
  'max_size?': {
    check: (validatable, option) => validatable.length < option,
    translationOptions: option => ({ num: option }),
  },
  'gt?': {
    check: (validatable, option) => validatable > option,
    translationOptions: option => ({ num: option }),
  },
  'matches?': {
    check: (validatable, option) => validatable == option,
    translationOptions: option => ({ num: option }),
  },
  'format?': {
    check: (validatable, regex) => regex.test(validatable),
  },
  'size?': {
    check: (validatable, range) =>
      validatable.length > range[0] && validatable.length < range[1],
    argType: (arg) => isArray(arg) ? 'range' : 'default',
    translationOptions: (option) => ({
      size_left: option[0], size_right: option[1]
    }),
  },
}
<|endoftext|>


<commit_before>var mongoose     = require('mongoose');
var Schema       = mongoose.Schema;

var ObjectId = mongoose.Schema.Types.ObjectId;
var Location = require("./location_model");
var License = require("./license_model");
var SpaceType = require("./spacetype_model");
var Product = require("./product_model");
var Claylock = require("./claylock_model");

var SpaceSchema   = new Schema({
	name: String,
	location_id: { type: ObjectId, ref: 'Location' },
	meters_squared: Number,
	spacetype_id: { type: ObjectId, ref: 'SpaceType' },
	product_id: { type: ObjectId, ref: 'Product' },
	claylock_id: [{ type: ObjectId, ref: 'Claylock' }],
	date_created: { type: Date, default: Date.now },
	seats: Number,
	hot_oversell: Number,
	occasional_oversell: Number,
	shared: { type: Boolean, default: true },
	budget_price: Number, // Use this to calculate individual line item amounts
	aesthetic: String,
	aesthetic_impact: Number,
	business_case: Number,
	business_case_date: Date,
	business_case_factor: Number,
	market_rate: Number,
	market_date: Date,
	twelve_month_discount: Number,
	_owner_id: ObjectId,
	_deleted: { type: Boolean, default: false, index: true },
}, {
	toObject: {
		virtuals: true
	},
	toJSON: {
		virtuals: true
	},
	timestamps: true
});

SpaceSchema.set("_perms", {
	admin: "crud",
	owner: "r",
	user: "r",
	all: "r"
});

SpaceSchema.index( { "$**": "text" } );

SpaceSchema.virtual("budget_value_per_m2").get(function() {
	return this.budget_price/this.meters_squared;
});

SpaceSchema.virtual("actual_value_per_m2").get(function() {
	return this.actual_price/this.meters_squared;
});

module.exports = mongoose.model('Space', SpaceSchema);
<commit_msg>Make spacetype required<commit_after>var mongoose     = require('mongoose');
var Schema       = mongoose.Schema;

var ObjectId = mongoose.Schema.Types.ObjectId;
var Location = require("./location_model");
var License = require("./license_model");
var SpaceType = require("./spacetype_model");
var Product = require("./product_model");
var Claylock = require("./claylock_model");

var SpaceSchema   = new Schema({
	name: String,
	location_id: { type: ObjectId, ref: 'Location' },
	meters_squared: Number,
	spacetype_id: { type: ObjectId, ref: 'SpaceType', required: true },
	product_id: { type: ObjectId, ref: 'Product' },
	claylock_id: [{ type: ObjectId, ref: 'Claylock' }],
	date_created: { type: Date, default: Date.now },
	seats: Number,
	hot_oversell: Number,
	occasional_oversell: Number,
	shared: { type: Boolean, default: true },
	budget_price: Number, // Use this to calculate individual line item amounts
	aesthetic: String,
	aesthetic_impact: Number,
	business_case: Number,
	business_case_date: Date,
	business_case_factor: Number,
	market_rate: Number,
	market_date: Date,
	twelve_month_discount: Number,
	_owner_id: ObjectId,
	_deleted: { type: Boolean, default: false, index: true },
}, {
	toObject: {
		virtuals: true
	},
	toJSON: {
		virtuals: true
	},
	timestamps: true
});

SpaceSchema.set("_perms", {
	admin: "crud",
	owner: "r",
	user: "r",
	all: "r"
});

SpaceSchema.index( { "$**": "text" } );

SpaceSchema.virtual("budget_value_per_m2").get(function() {
	return this.budget_price/this.meters_squared;
});

SpaceSchema.virtual("actual_value_per_m2").get(function() {
	return this.actual_price/this.meters_squared;
});

module.exports = mongoose.model('Space', SpaceSchema);
<|endoftext|>


<commit_before>goog.provide('ol.source.CartoDB');

goog.require('ol');
goog.require('ol.obj');
goog.require('ol.source.State');
goog.require('ol.source.XYZ');

/**
 * @classdesc
 * Layer source for the CartoDB tiles.
 *
 * @constructor
 * @extends {ol.source.XYZ}
 * @param {olx.source.CartoDBOptions} options CartoDB options.
 * @api
 */
ol.source.CartoDB = function(options) {

  /**
   * @type {string}
   * @private
   */
  this.account_ = options.account;

  /**
   * @type {string}
   * @private
   */
  this.mapId_ = options.map || '';

  /**
   * @type {!Object}
   * @private
   */
  this.config_ = options.config || {};

  /**
   * @type {!Object.<string, CartoDBLayerInfo>}
   * @private
   */
  this.templateCache_ = {};

  ol.source.XYZ.call(this, {
    attributions: options.attributions,
    cacheSize: options.cacheSize,
    crossOrigin: options.crossOrigin,
    logo: options.logo,
    maxZoom: options.maxZoom !== undefined ? options.maxZoom : 18,
    minZoom: options.minZoom,
    projection: options.projection,
    state: ol.source.State.LOADING,
    wrapX: options.wrapX
  });
  this.initializeMap_();
};
ol.inherits(ol.source.CartoDB, ol.source.XYZ);

/**
 * Returns the current config.
 * @return {!Object} The current configuration.
 * @api
 */
ol.source.CartoDB.prototype.getConfig = function() {
  return this.config_;
};

/**
 * Updates the carto db config.
 * @param {Object} config a key-value lookup. Values will replace current values
 *     in the config.
 * @api
 */
ol.source.CartoDB.prototype.updateConfig = function(config) {
  ol.obj.assign(this.config_, config);
  this.initializeMap_();
};

/**
 * Sets the CartoDB config
 * @param {Object} config In the case of anonymous maps, a CartoDB configuration
 *     object.
 * If using named maps, a key-value lookup with the template parameters.
 * @api
 */
ol.source.CartoDB.prototype.setConfig = function(config) {
  this.config_ = config || {};
  this.initializeMap_();
};

/**
 * Issue a request to initialize the CartoDB map.
 * @private
 */
ol.source.CartoDB.prototype.initializeMap_ = function() {
  var paramHash = JSON.stringify(this.config_);
  if (this.templateCache_[paramHash]) {
    this.applyTemplate_(this.templateCache_[paramHash]);
    return;
  }
  var mapUrl = 'https://' + this.account_ + '.cartodb.com/api/v1/map';

  if (this.mapId_) {
    mapUrl += '/named/' + this.mapId_;
  }

  var client = new XMLHttpRequest();
  client.addEventListener('load', this.handleInitResponse_.bind(this, paramHash));
  client.addEventListener('error', this.handleInitError_.bind(this));
  client.open('POST', mapUrl);
  client.setRequestHeader('Content-type', 'application/json');
  client.send(JSON.stringify(this.config_));
};

/**
 * Handle map initialization response.
 * @param {string} paramHash a hash representing the parameter set that was used
 *     for the request
 * @param {Event} event Event.
 * @private
 */
ol.source.CartoDB.prototype.handleInitResponse_ = function(paramHash, event) {
  var client = /** @type {XMLHttpRequest} */ (event.target);
  // status will be 0 for file:// urls
  if (!client.status || client.status >= 200 && client.status < 300) {
    var response;
    try {
      response = /** @type {CartoDBLayerInfo} */(JSON.parse(client.responseText));
    } catch (err) {
      this.setState(ol.source.State.ERROR);
      return;
    }
    this.applyTemplate_(response);
    this.templateCache_[paramHash] = response;
    this.setState(ol.source.State.READY);
  } else {
    this.setState(ol.source.State.ERROR);
  }
};

/**
 * @private
 * @param {Event} event Event.
 */
ol.source.CartoDB.prototype.handleInitError_ = function(event) {
  this.setState(ol.source.State.ERROR);
};

/**
 * Apply the new tile urls returned by carto db
 * @param {CartoDBLayerInfo} data Result of carto db call.
 * @private
 */
ol.source.CartoDB.prototype.applyTemplate_ = function(data) {
  var tilesUrl = 'https://' + data.cdn_url.https + '/' + this.account_ +
      '/api/v1/map/' + data.layergroupid + '/{z}/{x}/{y}.png';
  this.setUrl(tilesUrl);
};
<commit_msg>Change cartodb domain from cartodb.com to carto.com<commit_after>goog.provide('ol.source.CartoDB');

goog.require('ol');
goog.require('ol.obj');
goog.require('ol.source.State');
goog.require('ol.source.XYZ');

/**
 * @classdesc
 * Layer source for the CartoDB Maps API.
 *
 * @constructor
 * @extends {ol.source.XYZ}
 * @param {olx.source.CartoDBOptions} options CartoDB options.
 * @api
 */
ol.source.CartoDB = function(options) {

  /**
   * @type {string}
   * @private
   */
  this.account_ = options.account;

  /**
   * @type {string}
   * @private
   */
  this.mapId_ = options.map || '';

  /**
   * @type {!Object}
   * @private
   */
  this.config_ = options.config || {};

  /**
   * @type {!Object.<string, CartoDBLayerInfo>}
   * @private
   */
  this.templateCache_ = {};

  ol.source.XYZ.call(this, {
    attributions: options.attributions,
    cacheSize: options.cacheSize,
    crossOrigin: options.crossOrigin,
    logo: options.logo,
    maxZoom: options.maxZoom !== undefined ? options.maxZoom : 18,
    minZoom: options.minZoom,
    projection: options.projection,
    state: ol.source.State.LOADING,
    wrapX: options.wrapX
  });
  this.initializeMap_();
};
ol.inherits(ol.source.CartoDB, ol.source.XYZ);

/**
 * Returns the current config.
 * @return {!Object} The current configuration.
 * @api
 */
ol.source.CartoDB.prototype.getConfig = function() {
  return this.config_;
};

/**
 * Updates the carto db config.
 * @param {Object} config a key-value lookup. Values will replace current values
 *     in the config.
 * @api
 */
ol.source.CartoDB.prototype.updateConfig = function(config) {
  ol.obj.assign(this.config_, config);
  this.initializeMap_();
};

/**
 * Sets the CartoDB config
 * @param {Object} config In the case of anonymous maps, a CartoDB configuration
 *     object.
 * If using named maps, a key-value lookup with the template parameters.
 * @api
 */
ol.source.CartoDB.prototype.setConfig = function(config) {
  this.config_ = config || {};
  this.initializeMap_();
};

/**
 * Issue a request to initialize the CartoDB map.
 * @private
 */
ol.source.CartoDB.prototype.initializeMap_ = function() {
  var paramHash = JSON.stringify(this.config_);
  if (this.templateCache_[paramHash]) {
    this.applyTemplate_(this.templateCache_[paramHash]);
    return;
  }
  var mapUrl = 'https://' + this.account_ + '.carto.com/api/v1/map';

  if (this.mapId_) {
    mapUrl += '/named/' + this.mapId_;
  }

  var client = new XMLHttpRequest();
  client.addEventListener('load', this.handleInitResponse_.bind(this, paramHash));
  client.addEventListener('error', this.handleInitError_.bind(this));
  client.open('POST', mapUrl);
  client.setRequestHeader('Content-type', 'application/json');
  client.send(JSON.stringify(this.config_));
};

/**
 * Handle map initialization response.
 * @param {string} paramHash a hash representing the parameter set that was used
 *     for the request
 * @param {Event} event Event.
 * @private
 */
ol.source.CartoDB.prototype.handleInitResponse_ = function(paramHash, event) {
  var client = /** @type {XMLHttpRequest} */ (event.target);
  // status will be 0 for file:// urls
  if (!client.status || client.status >= 200 && client.status < 300) {
    var response;
    try {
      response = /** @type {CartoDBLayerInfo} */(JSON.parse(client.responseText));
    } catch (err) {
      this.setState(ol.source.State.ERROR);
      return;
    }
    this.applyTemplate_(response);
    this.templateCache_[paramHash] = response;
    this.setState(ol.source.State.READY);
  } else {
    this.setState(ol.source.State.ERROR);
  }
};

/**
 * @private
 * @param {Event} event Event.
 */
ol.source.CartoDB.prototype.handleInitError_ = function(event) {
  this.setState(ol.source.State.ERROR);
};

/**
 * Apply the new tile urls returned by carto db
 * @param {CartoDBLayerInfo} data Result of carto db call.
 * @private
 */
ol.source.CartoDB.prototype.applyTemplate_ = function(data) {
  var tilesUrl = 'https://' + data.cdn_url.https + '/' + this.account_ +
      '/api/v1/map/' + data.layergroupid + '/{z}/{x}/{y}.png';
  this.setUrl(tilesUrl);
};
<|endoftext|>


<commit_before>exports.BattleStatuses = {
	brn: {
		effectType: 'Status',
		onStart: function (target) {
			this.add('-status', target, 'brn');
		},
		onAfterMoveSelf: function (pokemon) {
			this.damage(pokemon.maxhp / 8);
		},
		onBasePower: function (basePower, attacker, defender, move) {
			if (move && move.category === 'Physical' && attacker && attacker.ability !== 'guts') {
				return basePower / 2;
			}
		},
		onSwitchIn: function (pokemon) {
			pokemon.addVolatile('brnattackdrop');
			if (pokemon.side.foe.active[0] && pokemon.speed <= pokemon.side.foe.active[0].speed) {
				this.damage(pokemon.maxhp / 8);
			}
		}
	},
	par: {
		inherit: true,
		onBeforeMovePriority: 2,
		onBeforeMove: function (pokemon) {
			if (this.random(4) === 0) {
				this.add('cant', pokemon.id, 'par');
				return false;
			}
		}
	},
	slp: {
		effectType: 'Status',
		onStart: function (target) {
			this.add('-status', target.id, 'slp');
			// 1-5 turns
			this.effectData.time = this.random(2, 6);
		},
		onBeforeMovePriority: 2,
		onBeforeMove: function (pokemon, target, move) {
			pokemon.statusData.time--;
			if (pokemon.statusData.time <= 0) {
				pokemon.cureStatus();
				return;
			}
			this.add('cant', pokemon.id, 'slp');
			if (move.sleepUsable) {
				return;
			}
			return false;
		}
	},
	psn: {
		effectType: 'Status',
		onStart: function (target) {
			this.add('-status', target, 'psn');
		},
		onAfterMoveSelf: function (pokemon) {
			this.damage(pokemon.maxhp / 8);
		},
		onSwitchIn: function (pokemon) {
			if (pokemon.side.foe.active[0] && pokemon.speed <= pokemon.side.foe.active[0].speed) {
				this.damage(pokemon.maxhp / 8);
			}
		}
	},
	tox: {
		effectType: 'Status',
		onStart: function (target) {
			this.add('-status', target, 'tox');
			this.effectData.stage = 0;
		},
		onAfterMoveSelf: function (pokemon) {
			if (this.effectData.stage < 15) {
				this.effectData.stage++;
			}
			this.damage(this.clampIntRange(pokemon.maxhp / 16, 1) * this.effectData.stage);
		},
		onSwitchIn: function (pokemon) {
			this.effectData.stage = 0;
			pokemon.setStatus('psn');
			if (pokemon.side.foe.active[0] && pokemon.speed <= pokemon.side.foe.active[0].speed) {
				this.damage(pokemon.maxhp / 8);
			}
		}
	},
	partiallytrapped: {
		inherit: true,
		durationCallback: function (target, source) {
			return this.random(3, 6);
		}
	},
	sandstorm: {
		inherit: true,
		onWeather: function (target) {
			this.damage(target.maxhp / 8);
		}
	},
	stall: {
		duration: 2,
		counterMax: 255,
		onStart: function () {
			this.effectData.counter = 255;
		},
		onStallMove: function () {
			// Gen 2 starts counting at x=255, x/256 and then halves x on every turn
			var counter = this.effectData.counter || 255;
			this.debug("Success chance: " + Math.round(counter / 256) + "% (" + counter + "/256)");
			return (this.random(counter) === 0);
		},
		onRestart: function () {
			if (this.effectData.counter > this.effect.counterMax) {
				this.effectData.counter /= 2;
				if (this.effectData.counter < 0) this.effectData.counter = 0;
			}
			this.effectData.duration = 255;
		}
	}
};
<commit_msg>Gen 2: Burn is dealt with on damage calculation<commit_after>exports.BattleStatuses = {
	brn: {
		effectType: 'Status',
		onStart: function (target) {
			this.add('-status', target, 'brn');
		},
		onAfterMoveSelf: function (pokemon) {
			this.damage(pokemon.maxhp / 8);
		},
		onSwitchIn: function (pokemon) {
			if (pokemon.side.foe.active[0] && pokemon.speed <= pokemon.side.foe.active[0].speed) {
				this.damage(pokemon.maxhp / 8);
			}
		}
	},
	par: {
		inherit: true,
		onBeforeMovePriority: 2,
		onBeforeMove: function (pokemon) {
			if (this.random(4) === 0) {
				this.add('cant', pokemon.id, 'par');
				return false;
			}
		}
	},
	slp: {
		effectType: 'Status',
		onStart: function (target) {
			this.add('-status', target.id, 'slp');
			// 1-5 turns
			this.effectData.time = this.random(2, 6);
		},
		onBeforeMovePriority: 2,
		onBeforeMove: function (pokemon, target, move) {
			pokemon.statusData.time--;
			if (pokemon.statusData.time <= 0) {
				pokemon.cureStatus();
				return;
			}
			this.add('cant', pokemon.id, 'slp');
			if (move.sleepUsable) {
				return;
			}
			return false;
		}
	},
	psn: {
		effectType: 'Status',
		onStart: function (target) {
			this.add('-status', target, 'psn');
		},
		onAfterMoveSelf: function (pokemon) {
			this.damage(pokemon.maxhp / 8);
		},
		onSwitchIn: function (pokemon) {
			if (pokemon.side.foe.active[0] && pokemon.speed <= pokemon.side.foe.active[0].speed) {
				this.damage(pokemon.maxhp / 8);
			}
		}
	},
	tox: {
		effectType: 'Status',
		onStart: function (target) {
			this.add('-status', target, 'tox');
			this.effectData.stage = 0;
		},
		onAfterMoveSelf: function (pokemon) {
			if (this.effectData.stage < 15) {
				this.effectData.stage++;
			}
			this.damage(this.clampIntRange(pokemon.maxhp / 16, 1) * this.effectData.stage);
		},
		onSwitchIn: function (pokemon) {
			this.effectData.stage = 0;
			pokemon.setStatus('psn');
			if (pokemon.side.foe.active[0] && pokemon.speed <= pokemon.side.foe.active[0].speed) {
				this.damage(pokemon.maxhp / 8);
			}
		}
	},
	partiallytrapped: {
		inherit: true,
		durationCallback: function (target, source) {
			return this.random(3, 6);
		}
	},
	sandstorm: {
		inherit: true,
		onWeather: function (target) {
			this.damage(target.maxhp / 8);
		}
	},
	stall: {
		duration: 2,
		counterMax: 255,
		onStart: function () {
			this.effectData.counter = 255;
		},
		onStallMove: function () {
			// Gen 2 starts counting at x=255, x/256 and then halves x on every turn
			var counter = this.effectData.counter || 255;
			this.debug("Success chance: " + Math.round(counter / 256) + "% (" + counter + "/256)");
			return (this.random(counter) === 0);
		},
		onRestart: function () {
			if (this.effectData.counter > this.effect.counterMax) {
				this.effectData.counter /= 2;
				if (this.effectData.counter < 0) this.effectData.counter = 0;
			}
			this.effectData.duration = 255;
		}
	}
};
<|endoftext|>


<commit_before>exports.BattleFormats = {
	pokemon: {
		inherit: true,
		effectType: 'Banlist',
		onValidateSet: function (set) {
			var template = this.getTemplate(set.species);
			var item = this.getItem(set.item);
			if (item && item.id === 'griseousorb' && template.num !== 487) {
				return ['Griseous Orb can only be held by Giratina in Generation 4.'];
			}
		}
	}
};

<commit_msg>Gen 4: implement Arceus EV restrictions<commit_after>exports.BattleFormats = {
	pokemon: {
		inherit: true,
		effectType: 'Banlist',
		onValidateSet: function (set) {
			var template = this.getTemplate(set.species);
			var item = this.getItem(set.item);
			if (item && item.id === 'griseousorb' && template.num !== 487) {
				return ['Griseous Orb can only be held by Giratina in Generation 4.'];
			}
			if (template.num === 493 && set.evs) {
				for (var stat in set.evs) {
					if (set.evs[stat] > 100) return ["Arceus may not have more than 100 of any EVs in Generation 4."];
				}
			}
		}
	}
};

<|endoftext|>


<commit_before>exports.BattleMovedex = {
	"chainbolt": {
		num: 701,
		accuracy: 100,
		basePower: 25,
		category: "Special",
		desc: "Deals damage to one adjacent target and hits two to five times. Has a 1/3 chance to hit two or three times, and a 1/6 chance to hit four or five times. If one of the hits breaks the target's Substitute, it will take damage for the remaining hits. If the user has the Ability Skill Link, this move will always hit five times.",
		shortDesc: "Hits 2-5 times in one turn.",
		id: "chainbolt",
		isViable: true,
		name: "Chain Bolt",
		pp: 30,
		priority: 0,
		multihit: [2,5],
		secondary: false,
		target: "normal",
		type: "Electric"
	},
	"deserttomb": {
		num: 702,
		accuracy: 95,
		basePower: 85,
		category: "Special",
		desc: "Deals damage to one adjacent target and prevents it from switching for four or five turns; seven turns if the user is holding Grip Claw. Causes damage to the target equal to 1/16 of its maximum HP (1/8 if the user is holding Binding Band), rounded down, at the end of each turn during effect. The target can still switch out if it is holding Shed Shell or uses Baton Pass, U-turn, or Volt Switch. The effect ends if either the user or the target leaves the field, or if the target uses Rapid Spin. This effect is not stackable or reset by using this or another partial-trapping move.",
		shortDesc: "Traps and damages the target for 4-5 turns.",
		id: "deserttomb",
		isViable: true,
		name: "Desert Tomb",
		pp: 10,
		priority: 0,
		volatileStatus: 'partiallytrapped',
		secondary: false,
		target: "normal",
		type: "Rock"
	},
	"distortion": {
		num: 703,
		accuracy: 100,
		basePower: 80,
		category: "Special",
		desc: "Blah Blah Blah",
		shortDesc: "Blah Blah Blah",
		id: "distortion",
		isViable: true,
		name: "Distortion",
		pp: 10,
		priority: -1,
		beforeTurnCallback: function(pokemon, target) {
			decision = this.willMove(target);
			if (decision.move.priority !== 0) {
				this.willMove(pokemon).move.priority = decision.move.priority + 1;
			}
		},
		secondary: false,
		target: "normal",
		type: "Psychic"
	},
	"divingtalon": {
		num: 704,
		accuracy: 100,
		basePower: 40,
		category: "Physical",
		desc: "Deals damage to one adjacent target. Makes contact. Priority +1.",
		shortDesc: "Usually goes first.",
		id: "divingtalon",
		isViable: true,
		name: "Diving Talon",
		pp: 20,
		priority: 1,
		isContact: true,
		secondary: false,
		target: "normal",
		type: "Flying"
	},
	"firerocket": {
		num: 705,
		accuracy: 100,
		basePower: 40,
		category: "Physical",
		desc: "Deals damage to one adjacent target. Makes contact. Priority +1.",
		shortDesc: "Usually goes first.",
		id: "firerocket",
		isViable: true,
		name: "Fire Rocket",
		pp: 20,
		priority: 1,
		isContact: true,
		secondary: false,
		target: "normal",
		type: "Fire"
	},
	"focusball": {
		num: 706,
		accuracy: 100,
		basePower: 80,
		category: "Special",
		desc: "Deals damage to one adjacent target with a 20% chance to lower its Special Defense by 1 stage.",
		shortDesc: "20% chance to lower the target's Sp. Def by 1.",
		id: "focusball",
		isViable: true,
		name: "Focus Ball",
		pp: 15,
		priority: 0,
		secondary: {
			chance: 20,
			boosts: {
				spd: -1
			}
		},
		target: "normal",
		type: "Fighting"
	},
	"gyre": {
		num: 707,
		accuracy: 85,
		basePower: 100,
		basePowerCallback: function() {
			if (this.weather) return 125;
			return 100;
		},
		category: "Special",
		desc: "Deals damage to one adjacent target. Power doubles during weather effects and this move's type changes to match; Ice-type during Hail, Water-type during Rain Dance, Rock-type during Sandstorm, and Fire-type during Sunny Day.",
		shortDesc: "Power doubles and type varies in each weather.",
		id: "gyre",
		isViable: true,
		name: "Gyre",
		pp: 10,
		priority: 0,
		onModifyMove: function(move) {
			switch (this.effectiveWeather()) {
			case 'sunnyday':
				move.type = 'Fire';
				break;
			case 'raindance':
				move.type = 'Water';
				break;
			case 'sandstorm':
				move.type = 'Rock';
				break;
			case 'hail':
				move.type = 'Ice';
				break;
			}
		},
		secondary: false,
		target: "flying",
		type: "Flying"
	},
	"magnify": {
		num: 708,
		accuracy: true,
		basePower: 0,
		category: "Status",
		desc: "Raises the user's Accuracy by 2 stages.",
		shortDesc: "Boosts the user's Accuracy by 2.",
		id: "magnify",
		isViable: true,
		name: "Magnify",
		pp: 30,
		priority: 0,
		isSnatchable: true,
		boosts: {
			accuracy: 2
		},
		secondary: false,
		target: "self",
		type: "Steel"
	},
	"mindforce": {
		num: 709,
		accuracy: 100,
		basePower: 80,
		category: "Special",
		desc: "Deals damage to one adjacent target. Fails if the target did not select a status move for use this turn, or if the target moves before the user. Priority +1.",
		shortDesc: "Usually goes first. Fails if target is not using a status move.",
		id: "mindforce",
		isViable: true,
		name: "Mind Force",
		pp: 5,
		priority: 1,
		onTryHit: function(target) {
			decision = this.willMove(target);
			if (!decision || decision.choice !== 'move' || (decision.move.category !== 'Status' && decision.move.id !== 'mefirst')) {
				return false;
			}
		},
		secondary: false,
		target: "normal",
		type: "Psychic"
	},
	"mysticroom": {
		num: 710,
		accuracy: true,
		basePower: 0,
		category: "Status",
		desc: "For 5 turns, all active Pokemon have their Attack and Special Attack stats swapped. Stat stage changes are unaffected. If this move is used during the effect, the effect ends. Priority -7.",
		shortDesc: "For 5 turns, all Attack and Sp. Atk stats switch.",
		id: "mysticroom",
		name: "Mystic Room",
		pp: 10,
		priority: -7,
		onHitField: function(target, source, effect) {
			if (this.pseudoWeather['mysticroom']) {
				this.removePseudoWeather('mysticroom', source, effect, '[of] '+source);
			} else {
				this.addPseudoWeather('mysticroom', source, effect, '[of] '+source);
			}
		},
		effect: {
			duration: 5,
			onStart: function(side, source) {
				this.add('-fieldstart', 'move: MysticRoom', '[of] '+source);
			},
			onModifyAtkPriority: 100,
			onModifyAtk: function(atk, pokemon) {
			return pokemon.stats.spa;
			},
			onModifySpAPriority: 100,
			onModifySpA: function(spa, pokemon) {
				return pokemon.stats.atk;
			},
			onResidualOrder: 24,
			onEnd: function() {
				this.add('-fieldend', 'move: Mystic Room');
			}
		},
		secondary: false,
		target: "all",
		type: "Psychic"
	},
	"rabidfrenzy": {
		num: 711,
		accuracy: 100,
		basePower: 120,
		category: "Physical",
		desc: "Deals damage to one adjacent target and lowers the user's Defense and Special Defense by 1 stage. Makes contact.",
		shortDesc: "Lowers the user's Defense and Sp. Def by 1.",
		id: "rabidfrenzy",
		isViable: true,
		name: "Rabid Frenzy",
		pp: 5,
		priority: 0,
		isContact: true,
		self: {
			boosts: {
				def: -1,
				spd: -1
			}
		},
		secondary: false,
		target: "normal",
		type: "Dark"
	},
	"rootbomb": {
		num: 712,
		accuracy: 100,
		basePower: 30,
		category: "Physical",
		desc: "Deals damage to all adjacent foes and changes type to Grass before hitting.",
		shortDesc: "Changes foes type to Grass before hitting.",
		id: "rootbomb",
		name: "Root Bomb",
		pp: 15,
		priority: 0,
		effect: {
			onBeforeMove: function(pokemon, target, move) {
				this.add('-start', target, 'typechange', 'Grass');
				target.types = ['Grass'];
			}
		},
		secondary: false,
		target: "normal",
		type: "Fire"
	},
	"shadowsword": {
		num: 713,
		accuracy: 100,
		basePower: 85,
		category: "Physical",
		desc: "Deals damage to one adjacent target with a 20% chance to lower its Defense by 1 stage. Makes contact.",
		shortDesc: "20% chance to lower the target's Defense by 1",
		id: "shadowsword",
		name: "Shadow Sword",
		pp: 20,
		priority: 0,
		isContact: true,
		secondary: {
			chance: 20,
			boosts: {
				def: -1
			}
		},
		target: "ghost",
		type: "Ghost"
	},
	"shortcircuit": {
		num: 714,
		accuracy: 100,
		basePower: 40,
		category: "Special",
		desc: "Deals damage to one adjacent target. Makes contact. Priority +1.",
		shortDesc: "Usually goes first.",
		id: "shortcircuit",
		isViable: true,
		name: "Short Circuit",
		pp: 20,
		priority: 1,
		isContact: true,
		secondary: false,
		target: "normal",
		type: "Electric"
	},
	"starstorm": {
		num: 715,
		accuracy: 85,
		basePower: 120,
		category: "Special",
		desc: "Deals damage to one adjacent target with a 20% chance to lower its Special Defense by 1 stage.",
		shortDesc: "20% chance to lower the target's Sp. Def by 1.",
		id: "starstorm",
		isViable: true,
		name: "Star Storm",
		pp: 5,
		priority: 0,
		secondary: {
			chance: 20,
			boosts: {
				spd: -1
			}
		},
		target: "normal",
		type: "Psychic"
	},
        "vendetta": {
                num: 716,
                accuracy: 85,
                basePower: 120,
                category: "Special",
                desc: "Deals damage to one adjacent target with a 20 chance to hit it with Spite.",
                shortDesc: "20% chance to spite the target.",
                id: "vendetta",
                isViable: true,
                name: "Vendetta",
                pp: 5,
                priority: 0,
                onHit: function(target) {
                        var spiteroll = this.random(100);
                        if (spiteroll > 80) {
                                if (target.deductPP(target.lastMove, 4)) {
                                        this.add("-activate", target, 'move: vendetta', target.lastMove, 4);
                                        return;
                                }
                        }
                        return false;
                },
                target: "normal",
                type: "Ghost"
        },
	"voidhorn": {
		num: 717,
		accuracy: 85,
		basePower: 120,
		category: "Physical",
		desc: "Deals damage to one adjacent target",
		shortDesc: "Strong Ghost-Type Move",
		id: "voidhorn",
		isViable: true,
		name: "Void Horn",
		pp: 10,
		priority: 0,
		isContact: true,
		secondary: false,
		target: "normal",
		type: "Ghost"
	},
};
<commit_msg>Update moves.js<commit_after>exports.BattleMovedex = {
	"chainbolt": {
		num: 701,
		accuracy: 100,
		basePower: 25,
		category: "Special",
		desc: "Deals damage to one adjacent target and hits two to five times. Has a 1/3 chance to hit two or three times, and a 1/6 chance to hit four or five times. If one of the hits breaks the target's Substitute, it will take damage for the remaining hits. If the user has the Ability Skill Link, this move will always hit five times.",
		shortDesc: "Hits 2-5 times in one turn.",
		id: "chainbolt",
		isViable: true,
		name: "Chain Bolt",
		pp: 30,
		priority: 0,
		multihit: [2,5],
		secondary: false,
		target: "normal",
		type: "Electric"
	},
	"deserttomb": {
		num: 702,
		accuracy: 95,
		basePower: 85,
		category: "Special",
		desc: "Deals damage to one adjacent target and prevents it from switching for four or five turns; seven turns if the user is holding Grip Claw. Causes damage to the target equal to 1/16 of its maximum HP (1/8 if the user is holding Binding Band), rounded down, at the end of each turn during effect. The target can still switch out if it is holding Shed Shell or uses Baton Pass, U-turn, or Volt Switch. The effect ends if either the user or the target leaves the field, or if the target uses Rapid Spin. This effect is not stackable or reset by using this or another partial-trapping move.",
		shortDesc: "Traps and damages the target for 4-5 turns.",
		id: "deserttomb",
		isViable: true,
		name: "Desert Tomb",
		pp: 10,
		priority: 0,
		volatileStatus: 'partiallytrapped',
		secondary: false,
		target: "normal",
		type: "Rock"
	},
	"distortion": {
		num: 703,
		accuracy: 100,
		basePower: 80,
		category: "Special",
		desc: "Blah Blah Blah",
		shortDesc: "Blah Blah Blah",
		id: "distortion",
		isViable: true,
		name: "Distortion",
		pp: 10,
		priority: -1,
		beforeTurnCallback: function(pokemon, target) {
			decision = this.willMove(target);
			if (decision.move.priority === 1) {
				this.willMove(pokemon).move.priority = 1;
			}
		},
		secondary: false,
		target: "normal",
		type: "Psychic"
	},
	"divingtalon": {
		num: 704,
		accuracy: 100,
		basePower: 40,
		category: "Physical",
		desc: "Deals damage to one adjacent target. Makes contact. Priority +1.",
		shortDesc: "Usually goes first.",
		id: "divingtalon",
		isViable: true,
		name: "Diving Talon",
		pp: 20,
		priority: 1,
		isContact: true,
		secondary: false,
		target: "normal",
		type: "Flying"
	},
	"firerocket": {
		num: 705,
		accuracy: 100,
		basePower: 40,
		category: "Physical",
		desc: "Deals damage to one adjacent target. Makes contact. Priority +1.",
		shortDesc: "Usually goes first.",
		id: "firerocket",
		isViable: true,
		name: "Fire Rocket",
		pp: 20,
		priority: 1,
		isContact: true,
		secondary: false,
		target: "normal",
		type: "Fire"
	},
	"focusball": {
		num: 706,
		accuracy: 100,
		basePower: 80,
		category: "Special",
		desc: "Deals damage to one adjacent target with a 20% chance to lower its Special Defense by 1 stage.",
		shortDesc: "20% chance to lower the target's Sp. Def by 1.",
		id: "focusball",
		isViable: true,
		name: "Focus Ball",
		pp: 15,
		priority: 0,
		secondary: {
			chance: 20,
			boosts: {
				spd: -1
			}
		},
		target: "normal",
		type: "Fighting"
	},
	"gyre": {
		num: 707,
		accuracy: 85,
		basePower: 100,
		basePowerCallback: function() {
			if (this.weather) return 125;
			return 100;
		},
		category: "Special",
		desc: "Deals damage to one adjacent target. Power doubles during weather effects and this move's type changes to match; Ice-type during Hail, Water-type during Rain Dance, Rock-type during Sandstorm, and Fire-type during Sunny Day.",
		shortDesc: "Power doubles and type varies in each weather.",
		id: "gyre",
		isViable: true,
		name: "Gyre",
		pp: 10,
		priority: 0,
		onModifyMove: function(move) {
			switch (this.effectiveWeather()) {
			case 'sunnyday':
				move.type = 'Fire';
				break;
			case 'raindance':
				move.type = 'Water';
				break;
			case 'sandstorm':
				move.type = 'Rock';
				break;
			case 'hail':
				move.type = 'Ice';
				break;
			}
		},
		secondary: false,
		target: "flying",
		type: "Flying"
	},
	"magnify": {
		num: 708,
		accuracy: true,
		basePower: 0,
		category: "Status",
		desc: "Raises the user's Accuracy by 2 stages.",
		shortDesc: "Boosts the user's Accuracy by 2.",
		id: "magnify",
		isViable: true,
		name: "Magnify",
		pp: 30,
		priority: 0,
		isSnatchable: true,
		boosts: {
			accuracy: 2
		},
		secondary: false,
		target: "self",
		type: "Steel"
	},
	"mindforce": {
		num: 709,
		accuracy: 100,
		basePower: 80,
		category: "Special",
		desc: "Deals damage to one adjacent target. Fails if the target did not select a status move for use this turn, or if the target moves before the user. Priority +1.",
		shortDesc: "Usually goes first. Fails if target is not using a status move.",
		id: "mindforce",
		isViable: true,
		name: "Mind Force",
		pp: 5,
		priority: 1,
		onTryHit: function(target) {
			decision = this.willMove(target);
			if (!decision || decision.choice !== 'move' || (decision.move.category !== 'Status' && decision.move.id !== 'mefirst')) {
				return false;
			}
		},
		secondary: false,
		target: "normal",
		type: "Psychic"
	},
	"mysticroom": {
		num: 710,
		accuracy: true,
		basePower: 0,
		category: "Status",
		desc: "For 5 turns, all active Pokemon have their Attack and Special Attack stats swapped. Stat stage changes are unaffected. If this move is used during the effect, the effect ends. Priority -7.",
		shortDesc: "For 5 turns, all Attack and Sp. Atk stats switch.",
		id: "mysticroom",
		name: "Mystic Room",
		pp: 10,
		priority: -7,
		onHitField: function(target, source, effect) {
			if (this.pseudoWeather['mysticroom']) {
				this.removePseudoWeather('mysticroom', source, effect, '[of] '+source);
			} else {
				this.addPseudoWeather('mysticroom', source, effect, '[of] '+source);
			}
		},
		effect: {
			duration: 5,
			onStart: function(side, source) {
				this.add('-fieldstart', 'move: MysticRoom', '[of] '+source);
			},
			onModifyAtkPriority: 100,
			onModifyAtk: function(atk, pokemon) {
			return pokemon.stats.spa;
			},
			onModifySpAPriority: 100,
			onModifySpA: function(spa, pokemon) {
				return pokemon.stats.atk;
			},
			onResidualOrder: 24,
			onEnd: function() {
				this.add('-fieldend', 'move: Mystic Room');
			}
		},
		secondary: false,
		target: "all",
		type: "Psychic"
	},
	"rabidfrenzy": {
		num: 711,
		accuracy: 100,
		basePower: 120,
		category: "Physical",
		desc: "Deals damage to one adjacent target and lowers the user's Defense and Special Defense by 1 stage. Makes contact.",
		shortDesc: "Lowers the user's Defense and Sp. Def by 1.",
		id: "rabidfrenzy",
		isViable: true,
		name: "Rabid Frenzy",
		pp: 5,
		priority: 0,
		isContact: true,
		self: {
			boosts: {
				def: -1,
				spd: -1
			}
		},
		secondary: false,
		target: "normal",
		type: "Dark"
	},
	"rootbomb": {
		num: 712,
		accuracy: 100,
		basePower: 30,
		category: "Physical",
		desc: "Deals damage to all adjacent foes and changes type to Grass before hitting.",
		shortDesc: "Changes foes type to Grass before hitting.",
		id: "rootbomb",
		name: "Root Bomb",
		pp: 15,
		priority: 0,
		effect: {
			onBeforeMove: function(pokemon, target, move) {
				this.add('-start', target, 'typechange', 'Grass');
				target.types = ['Grass'];
			}
		},
		secondary: false,
		target: "normal",
		type: "Fire"
	},
	"shadowsword": {
		num: 713,
		accuracy: 100,
		basePower: 85,
		category: "Physical",
		desc: "Deals damage to one adjacent target with a 20% chance to lower its Defense by 1 stage. Makes contact.",
		shortDesc: "20% chance to lower the target's Defense by 1",
		id: "shadowsword",
		name: "Shadow Sword",
		pp: 20,
		priority: 0,
		isContact: true,
		secondary: {
			chance: 20,
			boosts: {
				def: -1
			}
		},
		target: "ghost",
		type: "Ghost"
	},
	"shortcircuit": {
		num: 714,
		accuracy: 100,
		basePower: 40,
		category: "Special",
		desc: "Deals damage to one adjacent target. Makes contact. Priority +1.",
		shortDesc: "Usually goes first.",
		id: "shortcircuit",
		isViable: true,
		name: "Short Circuit",
		pp: 20,
		priority: 1,
		isContact: true,
		secondary: false,
		target: "normal",
		type: "Electric"
	},
	"starstorm": {
		num: 715,
		accuracy: 85,
		basePower: 120,
		category: "Special",
		desc: "Deals damage to one adjacent target with a 20% chance to lower its Special Defense by 1 stage.",
		shortDesc: "20% chance to lower the target's Sp. Def by 1.",
		id: "starstorm",
		isViable: true,
		name: "Star Storm",
		pp: 5,
		priority: 0,
		secondary: {
			chance: 20,
			boosts: {
				spd: -1
			}
		},
		target: "normal",
		type: "Psychic"
	},
        "vendetta": {
                num: 716,
                accuracy: 85,
                basePower: 120,
                category: "Special",
                desc: "Deals damage to one adjacent target with a 20 chance to hit it with Spite.",
                shortDesc: "20% chance to spite the target.",
                id: "vendetta",
                isViable: true,
                name: "Vendetta",
                pp: 5,
                priority: 0,
                onHit: function(target) {
                        var spiteroll = this.random(100);
                        if (spiteroll > 80) {
                                if (target.deductPP(target.lastMove, 4)) {
                                        this.add("-activate", target, 'move: vendetta', target.lastMove, 4);
                                        return;
                                }
                        }
                        return false;
                },
                target: "normal",
                type: "Ghost"
        },
	"voidhorn": {
		num: 717,
		accuracy: 85,
		basePower: 120,
		category: "Physical",
		desc: "Deals damage to one adjacent target",
		shortDesc: "Strong Ghost-Type Move",
		id: "voidhorn",
		isViable: true,
		name: "Void Horn",
		pp: 10,
		priority: 0,
		isContact: true,
		secondary: false,
		target: "normal",
		type: "Ghost"
	},
};
<|endoftext|>


<commit_before>import React from 'react';
import PropTypes from 'prop-types';

import DayPickerInput from 'react-day-picker/DayPickerInput';
import 'react-day-picker/lib/style.css';

function CustomOverlay({ classNames, selectedDay, children }) {
  return (

Hello day picker!

          {selectedDay
            ? `You picked: ${selectedDay.toLocaleDateString()}`
            : 'Please pick a day'}

        {children}

  );
}

CustomOverlay.propTypes = {
  classNames: PropTypes.object.isRequired,
  selectedDay: PropTypes.oneOfType([Date]),
  children: PropTypes.number.isRequired,
};

export default function Example() {
  return (
    <DayPickerInput
      overlayComponent={CustomOverlay}
      dayPickerProps={{
        todayButton: 'Today',
      }}
    />
  );
}
<commit_msg>Make sure we pass the correct props to the overlay<commit_after>import React from 'react';
import PropTypes from 'prop-types';

import DayPickerInput from 'react-day-picker/DayPickerInput';
import 'react-day-picker/lib/style.css';

function CustomOverlay({ classNames, selectedDay, children, ...props }) {
  return (

Hello day picker!

        <button onClick={() => console.log('clicked!')}>button</button>

          {selectedDay
            ? `You picked: ${selectedDay.toLocaleDateString()}`
            : 'Please pick a day'}

        {children}

  );
}

CustomOverlay.propTypes = {
  classNames: PropTypes.object.isRequired,
  selectedDay: PropTypes.instanceOf(Date),
  children: PropTypes.node.isRequired,
};

export default function Example() {
  return (
    <DayPickerInput
      overlayComponent={CustomOverlay}
      dayPickerProps={{
        todayButton: 'Today',
      }}
    />
  );
}
<|endoftext|>


<commit_before>'use strict';

var async = require('async');
var connection = require('./connection');
var config = require('../config');

/**
 * @description Storage driver that persists:
 *
 * * Metadata envelopes in a private Cloud Files container.
 * * Assets in a CDN-enabled Cloud Files container.
 * * API keys in MongoDB.
 *
 * This is used in deployed clusters.
 */
function RemoteStorage () {}

/**
 * @description Initialize connections to external systems.
 */
RemoteStorage.prototype.setup = function (callback) {
  connection.setup((err) => {
    if (err) return callback(err);

    // Attempt to create the latch index. If we can, we're responsible for setting up the initial
    // index and alias. Otherwise, another content service is on it.
    connection.elastic.indices.create({ index: 'latch', ignore: 400 }, (err, response, status) => {
      if (err) return callback(err);

      if (status === 400) {
        // The latch index already existed, so another service is creating the search indices.
        // Note that there's a race condition that occurs when a service that *isn't* creating
        // the search indices attempts to store content between the creation of the latch index
        // and the makeIndexActive() call below in the service that is.
        return callback(null);
      }

      let indexName = `envelopes_${Date.now()}`;
      this.createNewIndex(indexName, (err) => {
        if (err) return callback(err);

        this.makeIndexActive(indexName, callback);
      });
    });
  });
};

/**
 * @description Do nothing, because writing code to wipe production clean feels like a *bad* idea.
 */
RemoteStorage.prototype.clear = function (callback) {
  callback(null);
};

/**
 * @description Return prefix of the URL that assets are served under.
 */
RemoteStorage.prototype.assetURLPrefix = function () {
  return connection.assetContainer.cdnSslUri + '/';
};

/**
 * @description Upload an asset to the Cloud Files asset container.
 */
RemoteStorage.prototype.storeAsset = function (asset, callback) {
  var up = connection.client.upload({
    container: config.assetContainer(),
    remote: asset.filename,
    contentType: asset.type,
    headers: {
      'Access-Control-Allow-Origin': '*'
    }
  });

  up.on('error', callback);

  up.on('success', function () {
    asset.publicURL = this.assetURLPrefix() + encodeURIComponent(asset.filename);
    callback(null, asset);
  }.bind(this));

  asset.chunks.forEach(function (chunk) {
    up.write(chunk);
  });

  up.end();
};

/**
 * @description Store this asset in the MongoDB named asset collection, overwriting one with the
 * same name if present.
 */
RemoteStorage.prototype.nameAsset = function (asset, callback) {
  connection.db.collection('layoutAssets').updateOne({
    key: asset.key
  }, {
    $set: {
      key: asset.key,
      publicURL: asset.publicURL
    }
  }, {
    upsert: true
  },
    function (err) {
      callback(err, asset);
    }
  );
};

/**
 * @description List all assets that have been persisted in Mongo with nameAsset.
 */
RemoteStorage.prototype.findNamedAssets = function (callback) {
  connection.db.collection('layoutAssets').find().toArray(callback);
};

/**
 * @description Retrieve an asset directly through the content service API. This is useless for
 *  remote storage (because you can and should use the CDN url instead) but implemented for
 *  parity with memory storage.
 */
RemoteStorage.prototype.getAsset = function (filename, callback) {
  var source = connection.client.download({
    container: config.assetContainer(),
    remote: filename
  });
  var chunks = [];

  source.on('error', function (err) {
    callback(err);
  });

  source.on('data', function (chunk) {
    chunks.push(chunk);
  });

  source.on('complete', function (resp) {
    var complete = Buffer.concat(chunks);

    if (resp.statusCode > 400) {
      var err = new Error('Cloud Files error');

      err.statusCode = resp.statusCode;
      err.responseBody = complete;

      return callback(err);
    }

    callback(null, { contentType: resp.contentType, body: complete });
  });
};

/**
 * @description Store a newly generated API key in the keys collection.
 */
RemoteStorage.prototype.storeKey = function (key, callback) {
  connection.db.collection('apiKeys').insertOne(key, callback);
};

/**
 * @description Forget a previously stored API key by key value.
 */
RemoteStorage.prototype.deleteKey = function (apikey, callback) {
  connection.db.collection('apiKeys').deleteOne({
    apikey: apikey
  }, callback);
};

/**
 * @description Return an Array of keys that match the provided API key. Will most frequently
 *   return either zero or one results, but you never know.
 */
RemoteStorage.prototype.findKeys = function (apikey, callback) {
  connection.db.collection('apiKeys').find({
    apikey: apikey
  }).toArray(callback);
};

RemoteStorage.prototype._storeContent = function (contentID, content, callback) {
  var dest = connection.client.upload({
    container: config.contentContainer(),
    remote: encodeURIComponent(contentID)
  });

  dest.on('err', callback);

  dest.on('success', function () {
    callback();
  });

  dest.end(content);
};

RemoteStorage.prototype._getContent = function (contentID, callback) {
  var source = connection.client.download({
    container: config.contentContainer(),
    remote: encodeURIComponent(contentID)
  });
  var chunks = [];

  source.on('error', function (err) {
    callback(err);
  });

  source.on('data', function (chunk) {
    chunks.push(chunk);
  });

  source.on('complete', function (resp) {
    var complete = Buffer.concat(chunks);

    if (resp.statusCode > 400) {
      var err = new Error('Cloud Files error');

      err.statusCode = resp.statusCode;
      err.responseBody = complete;

      return callback(err);
    }

    callback(null, complete);
  });
};

RemoteStorage.prototype.deleteContent = function (contentID, callback) {
  connection.client.removeFile(config.contentContainer(), encodeURIComponent(contentID), function (err) {
    if (err && err.statusCode === 404) {
      // It's already deleted, so this is fine. Everything is fine.
      return callback(null);
    }

    callback(err);
  });
};

RemoteStorage.prototype.listContent = function (callback) {
  var perPage = 10000;

  var nextPage = function (marker) {
    var options = { limit: perPage };
    if (marker !== null) {
      options.marker = marker;
    }

    connection.client.getFiles(config.contentContainer(), options, function (err, files) {
      if (err) return callback(err);

      var fileNames = files.map(function (e) { return decodeURIComponent(e.name); });

      var next = function () {
        // The last page was empty. We're done and we've already sent our done sentinel.
        if (fileNames.length === 0) return;

        if (fileNames.length < perPage) {
          // Enumeration is complete. Invoke the callback a final time with an empty result set
          // to signal completion.
          callback(null, [], function () {});
          return;
        }

        // We (may) still have files to go. Onward to the next page.
        nextPage(fileNames[fileNames.length - 1]);
      };

      callback(null, fileNames, next);
    });
  };

  nextPage(null);
};

RemoteStorage.prototype.createNewIndex = function (indexName, callback) {
  let envelopeMapping = {
    properties: {
      title: { type: 'string', index: 'analyzed' },
      body: { type: 'string', index: 'analyzed' },
      keywords: { type: 'string', index: 'analyzed' },
      categories: { type: 'string', index: 'not_analyzed' }
    }
  };

  connection.elastic.indices.create({ index: indexName }, (err) => {
    if (err) return callback(err);

    connection.elastic.indices.putMapping({
      index: indexName,
      type: 'envelope',
      body: {
        envelope: envelopeMapping
      }
    }, callback);
  });
};

RemoteStorage.prototype._indexContent = function (contentID, envelope, indexName, callback) {
  connection.elastic.index({
    index: indexName,
    type: 'envelope',
    id: contentID,
    body: envelope
  }, callback);
};

RemoteStorage.prototype.makeIndexActive = function (indexName, callback) {
  connection.elastic.indices.updateAliases({
    body: {
      actions: [
        { remove: { index: '*', alias: 'envelopes_current' } },
        { add: { index: indexName, alias: 'envelopes_current' } }
      ]
    }
  }, (err) => {
    if (err) return callback(err);

    connection.elastic.indices.get({
      index: 'envelopes*',
      ignoreUnavailable: true,
      feature: '_settings'
    }, (err, response, status) => {
      if (err) return callback(err);

      console.log(require('util').inspect(response, { depth: null }));

      let indexNames = Object.keys(response).filter((n) => n !== indexName);

      async.each(indexNames, (name, cb) => {
        connection.elastic.indices.delete({ index: name }, cb);
      }, callback);
    });
  });
};

RemoteStorage.prototype.queryContent = function (query, categories, pageNumber, perPage, callback) {
  var q = {};

  if (!categories) {
    q.match = { _all: query };
  } else {
    q.filtered = {
      query: { match: { _all: query } },
      filter: { terms: { categories: categories } }
    };
  }

  connection.elastic.search({
    index: 'envelopes_current',
    type: 'envelope',
    from: (pageNumber - 1) * perPage,
    size: perPage,
    ignoreUnavailable: true,
    body: {
      query: q,
      highlight: {
        fields: {
          body: {}
        }
      }
    }
  }, callback);
};

RemoteStorage.prototype.unindexContent = function (contentID, callback) {
  connection.elastic.delete({
    index: 'envelopes_current',
    type: 'envelope',
    id: contentID
  }, function (err) {
    if (err && err.status === '404') {
      // It's already gone. Disregard.
      return callback(null);
    }

    callback(err);
  });
};

RemoteStorage.prototype.storeSHA = function (sha, callback) {
  connection.db.collection('sha').updateOne({
    key: 'controlRepository'
  }, {
    $set: {
      key: 'controlRepository',
      sha: sha
    }
  }, {
    upsert: true
  }, callback);
};

RemoteStorage.prototype.getSHA = function (callback) {
  connection.db.collection('sha').findOne({key: 'controlRepository'}, function (err, doc) {
    if (err) {
      return callback(err);
    }

    if (doc === null) {
      return callback(null, null);
    }

    callback(null, doc.sha);
  });
};

module.exports = {
  RemoteStorage: RemoteStorage
};
<commit_msg>My console.log was showing<commit_after>'use strict';

var async = require('async');
var connection = require('./connection');
var config = require('../config');

/**
 * @description Storage driver that persists:
 *
 * * Metadata envelopes in a private Cloud Files container.
 * * Assets in a CDN-enabled Cloud Files container.
 * * API keys in MongoDB.
 *
 * This is used in deployed clusters.
 */
function RemoteStorage () {}

/**
 * @description Initialize connections to external systems.
 */
RemoteStorage.prototype.setup = function (callback) {
  connection.setup((err) => {
    if (err) return callback(err);

    // Attempt to create the latch index. If we can, we're responsible for setting up the initial
    // index and alias. Otherwise, another content service is on it.
    connection.elastic.indices.create({ index: 'latch', ignore: 400 }, (err, response, status) => {
      if (err) return callback(err);

      if (status === 400) {
        // The latch index already existed, so another service is creating the search indices.
        // Note that there's a race condition that occurs when a service that *isn't* creating
        // the search indices attempts to store content between the creation of the latch index
        // and the makeIndexActive() call below in the service that is.
        return callback(null);
      }

      let indexName = `envelopes_${Date.now()}`;
      this.createNewIndex(indexName, (err) => {
        if (err) return callback(err);

        this.makeIndexActive(indexName, callback);
      });
    });
  });
};

/**
 * @description Do nothing, because writing code to wipe production clean feels like a *bad* idea.
 */
RemoteStorage.prototype.clear = function (callback) {
  callback(null);
};

/**
 * @description Return prefix of the URL that assets are served under.
 */
RemoteStorage.prototype.assetURLPrefix = function () {
  return connection.assetContainer.cdnSslUri + '/';
};

/**
 * @description Upload an asset to the Cloud Files asset container.
 */
RemoteStorage.prototype.storeAsset = function (asset, callback) {
  var up = connection.client.upload({
    container: config.assetContainer(),
    remote: asset.filename,
    contentType: asset.type,
    headers: {
      'Access-Control-Allow-Origin': '*'
    }
  });

  up.on('error', callback);

  up.on('success', function () {
    asset.publicURL = this.assetURLPrefix() + encodeURIComponent(asset.filename);
    callback(null, asset);
  }.bind(this));

  asset.chunks.forEach(function (chunk) {
    up.write(chunk);
  });

  up.end();
};

/**
 * @description Store this asset in the MongoDB named asset collection, overwriting one with the
 * same name if present.
 */
RemoteStorage.prototype.nameAsset = function (asset, callback) {
  connection.db.collection('layoutAssets').updateOne({
    key: asset.key
  }, {
    $set: {
      key: asset.key,
      publicURL: asset.publicURL
    }
  }, {
    upsert: true
  },
    function (err) {
      callback(err, asset);
    }
  );
};

/**
 * @description List all assets that have been persisted in Mongo with nameAsset.
 */
RemoteStorage.prototype.findNamedAssets = function (callback) {
  connection.db.collection('layoutAssets').find().toArray(callback);
};

/**
 * @description Retrieve an asset directly through the content service API. This is useless for
 *  remote storage (because you can and should use the CDN url instead) but implemented for
 *  parity with memory storage.
 */
RemoteStorage.prototype.getAsset = function (filename, callback) {
  var source = connection.client.download({
    container: config.assetContainer(),
    remote: filename
  });
  var chunks = [];

  source.on('error', function (err) {
    callback(err);
  });

  source.on('data', function (chunk) {
    chunks.push(chunk);
  });

  source.on('complete', function (resp) {
    var complete = Buffer.concat(chunks);

    if (resp.statusCode > 400) {
      var err = new Error('Cloud Files error');

      err.statusCode = resp.statusCode;
      err.responseBody = complete;

      return callback(err);
    }

    callback(null, { contentType: resp.contentType, body: complete });
  });
};

/**
 * @description Store a newly generated API key in the keys collection.
 */
RemoteStorage.prototype.storeKey = function (key, callback) {
  connection.db.collection('apiKeys').insertOne(key, callback);
};

/**
 * @description Forget a previously stored API key by key value.
 */
RemoteStorage.prototype.deleteKey = function (apikey, callback) {
  connection.db.collection('apiKeys').deleteOne({
    apikey: apikey
  }, callback);
};

/**
 * @description Return an Array of keys that match the provided API key. Will most frequently
 *   return either zero or one results, but you never know.
 */
RemoteStorage.prototype.findKeys = function (apikey, callback) {
  connection.db.collection('apiKeys').find({
    apikey: apikey
  }).toArray(callback);
};

RemoteStorage.prototype._storeContent = function (contentID, content, callback) {
  var dest = connection.client.upload({
    container: config.contentContainer(),
    remote: encodeURIComponent(contentID)
  });

  dest.on('err', callback);

  dest.on('success', function () {
    callback();
  });

  dest.end(content);
};

RemoteStorage.prototype._getContent = function (contentID, callback) {
  var source = connection.client.download({
    container: config.contentContainer(),
    remote: encodeURIComponent(contentID)
  });
  var chunks = [];

  source.on('error', function (err) {
    callback(err);
  });

  source.on('data', function (chunk) {
    chunks.push(chunk);
  });

  source.on('complete', function (resp) {
    var complete = Buffer.concat(chunks);

    if (resp.statusCode > 400) {
      var err = new Error('Cloud Files error');

      err.statusCode = resp.statusCode;
      err.responseBody = complete;

      return callback(err);
    }

    callback(null, complete);
  });
};

RemoteStorage.prototype.deleteContent = function (contentID, callback) {
  connection.client.removeFile(config.contentContainer(), encodeURIComponent(contentID), function (err) {
    if (err && err.statusCode === 404) {
      // It's already deleted, so this is fine. Everything is fine.
      return callback(null);
    }

    callback(err);
  });
};

RemoteStorage.prototype.listContent = function (callback) {
  var perPage = 10000;

  var nextPage = function (marker) {
    var options = { limit: perPage };
    if (marker !== null) {
      options.marker = marker;
    }

    connection.client.getFiles(config.contentContainer(), options, function (err, files) {
      if (err) return callback(err);

      var fileNames = files.map(function (e) { return decodeURIComponent(e.name); });

      var next = function () {
        // The last page was empty. We're done and we've already sent our done sentinel.
        if (fileNames.length === 0) return;

        if (fileNames.length < perPage) {
          // Enumeration is complete. Invoke the callback a final time with an empty result set
          // to signal completion.
          callback(null, [], function () {});
          return;
        }

        // We (may) still have files to go. Onward to the next page.
        nextPage(fileNames[fileNames.length - 1]);
      };

      callback(null, fileNames, next);
    });
  };

  nextPage(null);
};

RemoteStorage.prototype.createNewIndex = function (indexName, callback) {
  let envelopeMapping = {
    properties: {
      title: { type: 'string', index: 'analyzed' },
      body: { type: 'string', index: 'analyzed' },
      keywords: { type: 'string', index: 'analyzed' },
      categories: { type: 'string', index: 'not_analyzed' }
    }
  };

  connection.elastic.indices.create({ index: indexName }, (err) => {
    if (err) return callback(err);

    connection.elastic.indices.putMapping({
      index: indexName,
      type: 'envelope',
      body: {
        envelope: envelopeMapping
      }
    }, callback);
  });
};

RemoteStorage.prototype._indexContent = function (contentID, envelope, indexName, callback) {
  connection.elastic.index({
    index: indexName,
    type: 'envelope',
    id: contentID,
    body: envelope
  }, callback);
};

RemoteStorage.prototype.makeIndexActive = function (indexName, callback) {
  connection.elastic.indices.updateAliases({
    body: {
      actions: [
        { remove: { index: '*', alias: 'envelopes_current' } },
        { add: { index: indexName, alias: 'envelopes_current' } }
      ]
    }
  }, (err) => {
    if (err) return callback(err);

    connection.elastic.indices.get({
      index: 'envelopes*',
      ignoreUnavailable: true,
      feature: '_settings'
    }, (err, response, status) => {
      if (err) return callback(err);

      let indexNames = Object.keys(response).filter((n) => n !== indexName);

      async.each(indexNames, (name, cb) => {
        connection.elastic.indices.delete({ index: name }, cb);
      }, callback);
    });
  });
};

RemoteStorage.prototype.queryContent = function (query, categories, pageNumber, perPage, callback) {
  var q = {};

  if (!categories) {
    q.match = { _all: query };
  } else {
    q.filtered = {
      query: { match: { _all: query } },
      filter: { terms: { categories: categories } }
    };
  }

  connection.elastic.search({
    index: 'envelopes_current',
    type: 'envelope',
    from: (pageNumber - 1) * perPage,
    size: perPage,
    ignoreUnavailable: true,
    body: {
      query: q,
      highlight: {
        fields: {
          body: {}
        }
      }
    }
  }, callback);
};

RemoteStorage.prototype.unindexContent = function (contentID, callback) {
  connection.elastic.delete({
    index: 'envelopes_current',
    type: 'envelope',
    id: contentID
  }, function (err) {
    if (err && err.status === '404') {
      // It's already gone. Disregard.
      return callback(null);
    }

    callback(err);
  });
};

RemoteStorage.prototype.storeSHA = function (sha, callback) {
  connection.db.collection('sha').updateOne({
    key: 'controlRepository'
  }, {
    $set: {
      key: 'controlRepository',
      sha: sha
    }
  }, {
    upsert: true
  }, callback);
};

RemoteStorage.prototype.getSHA = function (callback) {
  connection.db.collection('sha').findOne({key: 'controlRepository'}, function (err, doc) {
    if (err) {
      return callback(err);
    }

    if (doc === null) {
      return callback(null, null);
    }

    callback(null, doc.sha);
  });
};

module.exports = {
  RemoteStorage: RemoteStorage
};
<|endoftext|>


<commit_before>var stormpath = require('./index');

module.exports.loginRequired = function(req, res, next) {
  if (!res.locals.user) {
    res.redirect(302, '/login');
  } else {
    next();
  }
};
<commit_msg>Fixing our authentication redirection middleware.<commit_after>var stormpath = require('./index');

module.exports.loginRequired = function(req, res, next) {
  if (!res.locals.user) {
    var url = req.app.get('stormpathLoginUrl') + '?next=' + encodeURIComponent(req.route.path);
    res.redirect(302, url);
  } else {
    next();
  }
};
<|endoftext|>