<commit_before>var async  = require('async');
var fs     = require('fs');
var path   = require('path');
var utils  = require('../utils');
var Issue  = utils.issues.Issue;

var TSV    = require('./tsv');
var json   = require('./json');
var NIFTI  = require('./nii');
var bval   = require('./bval');
var bvec   = require('./bvec');
var session = require('./session');
var headerFields = require('./headerFields');

var BIDS;
BIDS = {

    options: {},
    issues: [],

    /**
     * Start
     *
     * Takes either a filelist array or
     * a path to a BIDS directory and an
     * options object and starts
     * the validation process and
     * returns the errors and warnings as
     * arguments to the callback.
     */
    start: function (dir, options, callback) {
        var self = BIDS;
        utils.options.parse(options, function (issues, options) {
            if (issues && issues.length > 0) {
                // option parsing issues
                callback({config: issues});
            } else {
                self.options = options;
                BIDS.reset();
                utils.files.readDir(dir, function (files) {
                    self.quickTest(files, function (couldBeBIDS) {
                        if (couldBeBIDS) {
                            self.fullTest(files, callback);
                        } else {
                            // Return an error immediately if quickTest fails
                            var issue = self.quickTestError(dir);
                            var summary = {
                                sessions: [],
                                subjects: [],
                                tasks: [],
                                modalities: [],
                                totalFiles: Object.keys(files).length,
                                size: 0
                            };
                            callback(utils.issues.format([issue], summary, options));
                        }
                    });
                });
            }
        });
    },

    /*
     * Generates an error for quickTest failures
     */
    quickTestError: function (dir) {
        var filename;
        if (typeof window === 'undefined') {
            // For Node, grab the path from the dir string
            filename = path.basename(dir);
        } else {
            // Browser side we need to look it up more carefully
            if (dir.length && 'webkitRelativePath' in dir[0]) {
                var wrp = dir[0].webkitRelativePath;
                while (wrp.indexOf(path.sep) !== -1) {
                    wrp = path.dirname(wrp);
                }
                filename = wrp;
            } else {
                // Fallback for non-standard webkitRelativePath
                filename = 'uploaded-directory';
            }
        }
        var issue = new Issue({
            code: 61,
            file: {
                name: filename,
                path: path.join('.', filename),
                relativePath: path.join('', filename)
            }
        });
        return issue;
    },

    /**
     * Quick Test
     *
     * A quick test to see if it could be a BIDS
     * dataset based on structure/naming. If it
     * could be it will trigger the full validation
     * otherwise it will throw a callback with a
     * generic error.
     */
    quickTest: function (fileList, callback) {
        var couldBeBIDS = false;
        for (var key in fileList) {
            if (fileList.hasOwnProperty(key)) {
                var file = fileList[key];
                var path = utils.files.relativePath(file);
                if (path) {
                    path = path.split('/');
                    path = path.reverse();
                    if (
                        path[0].includes('.nii') &&
                        (
                            path[1] == 'anat' ||
                            path[1] == 'func' ||
                            path[1] == 'dwi'
                        ) &&
                        (
                            (path[2] && path[2].indexOf('ses-') == 0) ||
                            (path[2] && path[2].indexOf('sub-') == 0)
                        )
                    ) {
                        couldBeBIDS = true;
                        break;
                    }
                }
            }
        }
        callback(couldBeBIDS);
    },

    /**
     * Full Test
     *
     * Takes on an array of files and starts
     * the validation process for a BIDS
     * package.
     */
    fullTest: function (fileList, callback) {
        var self = this;

        var jsonContentsDict = {},
            bContentsDict = {},
            events = [],
            niftis = [],
            headers = [],
            participants = null,
            phenotypeParticipants = [],
            hasSubjectDir = false;

        var summary = {
            sessions: [],
            subjects: [],
            tasks: [],
            modalities: [],
            totalFiles: Object.keys(fileList).length,
            size: 0
        };

        // check for illegal character in task name and acq name

        var task_re = /sub-(.*?)_task-[a-zA-Z0-9]*[_-][a-zA-Z0-9]*(?:_acq-[a-zA-Z0-9-]*)?(?:_run-\d+)?_/g;
        var acq_re = /sub-(.*?)_task-\w+.\w+(_acq-[a-zA-Z0-9]*[_-][a-zA-Z0-9]*)(?:_run-\d+)?_/g;

        var sub_re = /sub-[a-zA-Z0-9]*[_-][a-zA-Z0-9]*_/g;   // illegal character in sub
        var ses_re = /ses-[a-zA-Z0-9]*[_-][a-zA-Z0-9]*?_(.*?)/g; //illegal character in ses

        var illegalchar_regex_list = [
            [task_re, 58, "task name contains illegal character:"],
            [acq_re, 59, "acq name contains illegal character:"],
            [sub_re, 62, "sub name contains illegal character:" ],
            [ses_re, 63, "ses name contains illegal character:" ]
        ];

        async.eachOfLimit(fileList, 200, function (file) {
            var completename = utils.files.relativePath(file);
            if(!(completename.startsWith('/derivatives') || completename.startsWith('/code') || completename.startsWith('/sourcedata'))) {
                for (var re_index = 0; re_index < illegalchar_regex_list.length; re_index++) {
                    var err_regex = illegalchar_regex_list[re_index][0];
                    var err_code = illegalchar_regex_list[re_index][1];
                    var err_evidence = illegalchar_regex_list[re_index][2];

                    if (err_regex.exec(completename)) {
                        self.issues.push(new Issue({
                            file: fileList[f],
                            code: err_code,
                            evidence: err_evidence + completename
                        }));
                    }
                }
            }
        })

        // validate individual files
        async.eachOfLimit(fileList, 200, function (file, key, cb) {
            var path = utils.files.relativePath(file);
            file.relativePath = path;

            // check for subject directory presence
            if (path.startsWith('/sub-')) {
                hasSubjectDir = true;
            }

            // ignore associated data
            if (utils.type.isAssociatedData(file.relativePath)) {
                process.nextTick(cb);
            }

            // validate path naming
            else if (!utils.type.isBIDS(file.relativePath)) {
                self.issues.push(new Issue({
                    file: file,
                    evidence: file.name,
                    code: 1
                }));
                process.nextTick(cb);
            }

            // capture niftis for later validation
            else if (file.name.endsWith('.nii') || file.name.endsWith('.nii.gz')) {
                niftis.push(file);

                // collect modality summary
                var pathParts = path.split('_');
                var suffix = pathParts[pathParts.length - 1];
                suffix = suffix.slice(0, suffix.indexOf('.'));
                if (summary.modalities.indexOf(suffix) === -1) {
                    summary.modalities.push(suffix);
                }

                process.nextTick(cb);
            }

            // validate tsv
            else if (file.name && file.name.endsWith('.tsv')) {
                utils.files.readFile(file, function (issue, contents) {
                    if (issue) {
                        self.issues.push(issue);
                        process.nextTick(cb);
                        return;
                    }
                    if (file.name.endsWith('_events.tsv')) {
                        events.push(file.relativePath);
                    }
                    TSV(file, contents, fileList, function (issues, participantList) {
                        if (participantList) {
                            if (file.name.endsWith('participants.tsv')) {
                                participants = {
                                    list: participantList,
                                    file: file
                                };
                            } else if (file.relativePath.includes('phenotype/')) {
                                phenotypeParticipants.push({
                                    list: participantList,
                                    file: file
                                });
                            }
                        }
                        self.issues = self.issues.concat(issues);
                        process.nextTick(cb);
                    });
                });
            }

            // validate bvec
            else if (file.name && file.name.endsWith('.bvec')) {
                utils.files.readFile(file, function (issue, contents) {
                    if (issue) {
                        self.issues.push(issue);
                        process.nextTick(cb);
                        return;
                    }
                    bContentsDict[file.relativePath] = contents;
                    bvec(file, contents, function (issues) {
                        self.issues = self.issues.concat(issues);
                        process.nextTick(cb);
                    });
                });
            }

            // validate bval
            else if (file.name && file.name.endsWith('.bval')) {
                utils.files.readFile(file, function (issue, contents) {
                    if (issue) {
                        self.issues.push(issue);
                        process.nextTick(cb);
                        return;
                    }
                    bContentsDict[file.relativePath] = contents;
                    bval(file, contents, function (issues) {
                        self.issues = self.issues.concat(issues);
                        process.nextTick(cb);
                    });
                });
            }

            // validate json
            else if (file.name && file.name.endsWith('.json')) {
                utils.files.readFile(file, function (issue, contents) {
                    if (issue) {
                        self.issues.push(issue);
                        process.nextTick(cb);
                        return;
                    }
                    json(file, contents, function (issues, jsObj) {
                        self.issues = self.issues.concat(issues);

                        // abort further tests if schema test does not pass
                        for (var i = 0; i < issues.length; i++) {
                            if (issues[i].severity === 'error') {
                                process.nextTick(cb);
                                return;
                            }
                        }

                        jsonContentsDict[file.relativePath] = jsObj;

                        // collect task summary
                        if (file.name.indexOf('task') > -1) {
                            var task = jsObj ? jsObj.TaskName : null;
                            if (task && summary.tasks.indexOf(task) === -1) {
                                summary.tasks.push(task);
                            }
                        }
                        process.nextTick(cb);
                    });
                });
            } else {
                process.nextTick(cb);
            }

            // collect file stats
            if (typeof window !== 'undefined') {
                if (file.size) {
                    summary.size += file.size;
                }
            } else {
                if (!file.stats) {
                    try {
                        file.stats = fs.statSync(file.path);
                    } catch (err) {
                        file.stats = {size: 0};
                    }
                }
                summary.size += file.stats.size;
            }

            // collect sessions & subjects
            if (!utils.type.isAssociatedData(file.relativePath) && utils.type.isBIDS(file.relativePath)) {
                var pathValues = utils.type.getPathValues(file.relativePath);

                if (pathValues.sub && summary.subjects.indexOf(pathValues.sub) === -1) {
                    summary.subjects.push(pathValues.sub);
                }
                if (pathValues.ses && summary.sessions.indexOf(pathValues.ses) === -1) {
                    summary.sessions.push(pathValues.ses);
                }
            }

        }, function () {
            async.eachOfLimit(niftis, 200, function (file, key, cb) {
                if (self.options.ignoreNiftiHeaders) {
                    NIFTI(null, file, jsonContentsDict, bContentsDict, fileList, events, function (issues) {
                        self.issues = self.issues.concat(issues);
                        process.nextTick(cb);
                    });
                } else {
                    utils.files.readNiftiHeader(file, function (header) {
                        // check if header could be read
                        if (header && header.hasOwnProperty('error')) {
                            self.issues.push(header.error);
                            process.nextTick(cb);
                        } else {
                            headers.push([file, header]);
                            NIFTI(header, file, jsonContentsDict, bContentsDict, fileList, events, function (issues) {
                                self.issues = self.issues.concat(issues);
                                process.nextTick(cb);
                            });
                        }
                    });
                }

            }, function () {
                if (!hasSubjectDir) {
                    self.issues.push(new Issue({code: 45}));
                }
                // check if participants file match found subjects

                if (participants) {
                    var participantsFromFile = participants.list.sort();
                    var participantsFromFolders = summary.subjects.sort();
                    if (!utils.array.equals(participantsFromFolders, participantsFromFile, true)) {
                        self.issues.push(new Issue({
                            code: 49,
                            evidence: "participants.tsv: " + participantsFromFile.join(', ') + " folder structure: " + participantsFromFolders.join(', '),
                            file: participants.file
                        }));
                    }
                }

                // check if dataset contains T1w
                if (summary.modalities.indexOf('T1w') < 0) {
                    self.issues.push(new Issue({
                        code: 53
                    }));
                }

                if (phenotypeParticipants && phenotypeParticipants.length > 0) {
                    for (var j = 0; j < phenotypeParticipants.length; j++) {
                        var fileParticpants = phenotypeParticipants[j];
                        var diff = utils.array.diff(fileParticpants.list, summary.subjects)[0];
                        if (diff && diff.length > 0) {
                            self.issues.push(new Issue({
                                code: 51,
                                evidence: 'sub-' + diff.join(', sub-'),
                                file: fileParticpants.file
                            }));
                        }
                    }
                }
                self.issues = self.issues.concat(headerFields(headers));
                self.issues = self.issues.concat(session(fileList));
                summary.modalities = utils.modalities.group(summary.modalities);
                var issues = utils.issues.format(self.issues, summary, self.options);
                callback(issues, summary);
            });
        });
    },

    /**
     * Reset
     *
     * Resets the in object data back to original values.
     */
    reset: function () {
        this.issues = [];
    }

};

module.exports = BIDS;
<commit_msg>fix f reference<commit_after>var async  = require('async');
var fs     = require('fs');
var path   = require('path');
var utils  = require('../utils');
var Issue  = utils.issues.Issue;

var TSV    = require('./tsv');
var json   = require('./json');
var NIFTI  = require('./nii');
var bval   = require('./bval');
var bvec   = require('./bvec');
var session = require('./session');
var headerFields = require('./headerFields');

var BIDS;
BIDS = {

    options: {},
    issues: [],

    /**
     * Start
     *
     * Takes either a filelist array or
     * a path to a BIDS directory and an
     * options object and starts
     * the validation process and
     * returns the errors and warnings as
     * arguments to the callback.
     */
    start: function (dir, options, callback) {
        var self = BIDS;
        utils.options.parse(options, function (issues, options) {
            if (issues && issues.length > 0) {
                // option parsing issues
                callback({config: issues});
            } else {
                self.options = options;
                BIDS.reset();
                utils.files.readDir(dir, function (files) {
                    self.quickTest(files, function (couldBeBIDS) {
                        if (couldBeBIDS) {
                            self.fullTest(files, callback);
                        } else {
                            // Return an error immediately if quickTest fails
                            var issue = self.quickTestError(dir);
                            var summary = {
                                sessions: [],
                                subjects: [],
                                tasks: [],
                                modalities: [],
                                totalFiles: Object.keys(files).length,
                                size: 0
                            };
                            callback(utils.issues.format([issue], summary, options));
                        }
                    });
                });
            }
        });
    },

    /*
     * Generates an error for quickTest failures
     */
    quickTestError: function (dir) {
        var filename;
        if (typeof window === 'undefined') {
            // For Node, grab the path from the dir string
            filename = path.basename(dir);
        } else {
            // Browser side we need to look it up more carefully
            if (dir.length && 'webkitRelativePath' in dir[0]) {
                var wrp = dir[0].webkitRelativePath;
                while (wrp.indexOf(path.sep) !== -1) {
                    wrp = path.dirname(wrp);
                }
                filename = wrp;
            } else {
                // Fallback for non-standard webkitRelativePath
                filename = 'uploaded-directory';
            }
        }
        var issue = new Issue({
            code: 61,
            file: {
                name: filename,
                path: path.join('.', filename),
                relativePath: path.join('', filename)
            }
        });
        return issue;
    },

    /**
     * Quick Test
     *
     * A quick test to see if it could be a BIDS
     * dataset based on structure/naming. If it
     * could be it will trigger the full validation
     * otherwise it will throw a callback with a
     * generic error.
     */
    quickTest: function (fileList, callback) {
        var couldBeBIDS = false;
        for (var key in fileList) {
            if (fileList.hasOwnProperty(key)) {
                var file = fileList[key];
                var path = utils.files.relativePath(file);
                if (path) {
                    path = path.split('/');
                    path = path.reverse();
                    if (
                        path[0].includes('.nii') &&
                        (
                            path[1] == 'anat' ||
                            path[1] == 'func' ||
                            path[1] == 'dwi'
                        ) &&
                        (
                            (path[2] && path[2].indexOf('ses-') == 0) ||
                            (path[2] && path[2].indexOf('sub-') == 0)
                        )
                    ) {
                        couldBeBIDS = true;
                        break;
                    }
                }
            }
        }
        callback(couldBeBIDS);
    },

    /**
     * Full Test
     *
     * Takes on an array of files and starts
     * the validation process for a BIDS
     * package.
     */
    fullTest: function (fileList, callback) {
        var self = this;

        var jsonContentsDict = {},
            bContentsDict = {},
            events = [],
            niftis = [],
            headers = [],
            participants = null,
            phenotypeParticipants = [],
            hasSubjectDir = false;

        var summary = {
            sessions: [],
            subjects: [],
            tasks: [],
            modalities: [],
            totalFiles: Object.keys(fileList).length,
            size: 0
        };

        // check for illegal character in task name and acq name

        var task_re = /sub-(.*?)_task-[a-zA-Z0-9]*[_-][a-zA-Z0-9]*(?:_acq-[a-zA-Z0-9-]*)?(?:_run-\d+)?_/g;
        var acq_re = /sub-(.*?)_task-\w+.\w+(_acq-[a-zA-Z0-9]*[_-][a-zA-Z0-9]*)(?:_run-\d+)?_/g;

        var sub_re = /sub-[a-zA-Z0-9]*[_-][a-zA-Z0-9]*_/g;   // illegal character in sub
        var ses_re = /ses-[a-zA-Z0-9]*[_-][a-zA-Z0-9]*?_(.*?)/g; //illegal character in ses

        var illegalchar_regex_list = [
            [task_re, 58, "task name contains illegal character:"],
            [acq_re, 59, "acq name contains illegal character:"],
            [sub_re, 62, "sub name contains illegal character:" ],
            [ses_re, 63, "ses name contains illegal character:" ]
        ];

        async.eachOfLimit(fileList, 200, function (file) {
            var completename = utils.files.relativePath(file);
            if(!(completename.startsWith('/derivatives') || completename.startsWith('/code') || completename.startsWith('/sourcedata'))) {
                for (var re_index = 0; re_index < illegalchar_regex_list.length; re_index++) {
                    var err_regex = illegalchar_regex_list[re_index][0];
                    var err_code = illegalchar_regex_list[re_index][1];
                    var err_evidence = illegalchar_regex_list[re_index][2];

                    if (err_regex.exec(completename)) {
                        self.issues.push(new Issue({
                            file: file,
                            code: err_code,
                            evidence: err_evidence + completename
                        }));
                    }
                }
            }
        })

        // validate individual files
        async.eachOfLimit(fileList, 200, function (file, key, cb) {
            var path = utils.files.relativePath(file);
            file.relativePath = path;

            // check for subject directory presence
            if (path.startsWith('/sub-')) {
                hasSubjectDir = true;
            }

            // ignore associated data
            if (utils.type.isAssociatedData(file.relativePath)) {
                process.nextTick(cb);
            }

            // validate path naming
            else if (!utils.type.isBIDS(file.relativePath)) {
                self.issues.push(new Issue({
                    file: file,
                    evidence: file.name,
                    code: 1
                }));
                process.nextTick(cb);
            }

            // capture niftis for later validation
            else if (file.name.endsWith('.nii') || file.name.endsWith('.nii.gz')) {
                niftis.push(file);

                // collect modality summary
                var pathParts = path.split('_');
                var suffix = pathParts[pathParts.length - 1];
                suffix = suffix.slice(0, suffix.indexOf('.'));
                if (summary.modalities.indexOf(suffix) === -1) {
                    summary.modalities.push(suffix);
                }

                process.nextTick(cb);
            }

            // validate tsv
            else if (file.name && file.name.endsWith('.tsv')) {
                utils.files.readFile(file, function (issue, contents) {
                    if (issue) {
                        self.issues.push(issue);
                        process.nextTick(cb);
                        return;
                    }
                    if (file.name.endsWith('_events.tsv')) {
                        events.push(file.relativePath);
                    }
                    TSV(file, contents, fileList, function (issues, participantList) {
                        if (participantList) {
                            if (file.name.endsWith('participants.tsv')) {
                                participants = {
                                    list: participantList,
                                    file: file
                                };
                            } else if (file.relativePath.includes('phenotype/')) {
                                phenotypeParticipants.push({
                                    list: participantList,
                                    file: file
                                });
                            }
                        }
                        self.issues = self.issues.concat(issues);
                        process.nextTick(cb);
                    });
                });
            }

            // validate bvec
            else if (file.name && file.name.endsWith('.bvec')) {
                utils.files.readFile(file, function (issue, contents) {
                    if (issue) {
                        self.issues.push(issue);
                        process.nextTick(cb);
                        return;
                    }
                    bContentsDict[file.relativePath] = contents;
                    bvec(file, contents, function (issues) {
                        self.issues = self.issues.concat(issues);
                        process.nextTick(cb);
                    });
                });
            }

            // validate bval
            else if (file.name && file.name.endsWith('.bval')) {
                utils.files.readFile(file, function (issue, contents) {
                    if (issue) {
                        self.issues.push(issue);
                        process.nextTick(cb);
                        return;
                    }
                    bContentsDict[file.relativePath] = contents;
                    bval(file, contents, function (issues) {
                        self.issues = self.issues.concat(issues);
                        process.nextTick(cb);
                    });
                });
            }

            // validate json
            else if (file.name && file.name.endsWith('.json')) {
                utils.files.readFile(file, function (issue, contents) {
                    if (issue) {
                        self.issues.push(issue);
                        process.nextTick(cb);
                        return;
                    }
                    json(file, contents, function (issues, jsObj) {
                        self.issues = self.issues.concat(issues);

                        // abort further tests if schema test does not pass
                        for (var i = 0; i < issues.length; i++) {
                            if (issues[i].severity === 'error') {
                                process.nextTick(cb);
                                return;
                            }
                        }

                        jsonContentsDict[file.relativePath] = jsObj;

                        // collect task summary
                        if (file.name.indexOf('task') > -1) {
                            var task = jsObj ? jsObj.TaskName : null;
                            if (task && summary.tasks.indexOf(task) === -1) {
                                summary.tasks.push(task);
                            }
                        }
                        process.nextTick(cb);
                    });
                });
            } else {
                process.nextTick(cb);
            }

            // collect file stats
            if (typeof window !== 'undefined') {
                if (file.size) {
                    summary.size += file.size;
                }
            } else {
                if (!file.stats) {
                    try {
                        file.stats = fs.statSync(file.path);
                    } catch (err) {
                        file.stats = {size: 0};
                    }
                }
                summary.size += file.stats.size;
            }

            // collect sessions & subjects
            if (!utils.type.isAssociatedData(file.relativePath) && utils.type.isBIDS(file.relativePath)) {
                var pathValues = utils.type.getPathValues(file.relativePath);

                if (pathValues.sub && summary.subjects.indexOf(pathValues.sub) === -1) {
                    summary.subjects.push(pathValues.sub);
                }
                if (pathValues.ses && summary.sessions.indexOf(pathValues.ses) === -1) {
                    summary.sessions.push(pathValues.ses);
                }
            }

        }, function () {
            async.eachOfLimit(niftis, 200, function (file, key, cb) {
                if (self.options.ignoreNiftiHeaders) {
                    NIFTI(null, file, jsonContentsDict, bContentsDict, fileList, events, function (issues) {
                        self.issues = self.issues.concat(issues);
                        process.nextTick(cb);
                    });
                } else {
                    utils.files.readNiftiHeader(file, function (header) {
                        // check if header could be read
                        if (header && header.hasOwnProperty('error')) {
                            self.issues.push(header.error);
                            process.nextTick(cb);
                        } else {
                            headers.push([file, header]);
                            NIFTI(header, file, jsonContentsDict, bContentsDict, fileList, events, function (issues) {
                                self.issues = self.issues.concat(issues);
                                process.nextTick(cb);
                            });
                        }
                    });
                }

            }, function () {
                if (!hasSubjectDir) {
                    self.issues.push(new Issue({code: 45}));
                }
                // check if participants file match found subjects

                if (participants) {
                    var participantsFromFile = participants.list.sort();
                    var participantsFromFolders = summary.subjects.sort();
                    if (!utils.array.equals(participantsFromFolders, participantsFromFile, true)) {
                        self.issues.push(new Issue({
                            code: 49,
                            evidence: "participants.tsv: " + participantsFromFile.join(', ') + " folder structure: " + participantsFromFolders.join(', '),
                            file: participants.file
                        }));
                    }
                }

                // check if dataset contains T1w
                if (summary.modalities.indexOf('T1w') < 0) {
                    self.issues.push(new Issue({
                        code: 53
                    }));
                }

                if (phenotypeParticipants && phenotypeParticipants.length > 0) {
                    for (var j = 0; j < phenotypeParticipants.length; j++) {
                        var fileParticpants = phenotypeParticipants[j];
                        var diff = utils.array.diff(fileParticpants.list, summary.subjects)[0];
                        if (diff && diff.length > 0) {
                            self.issues.push(new Issue({
                                code: 51,
                                evidence: 'sub-' + diff.join(', sub-'),
                                file: fileParticpants.file
                            }));
                        }
                    }
                }
                self.issues = self.issues.concat(headerFields(headers));
                self.issues = self.issues.concat(session(fileList));
                summary.modalities = utils.modalities.group(summary.modalities);
                var issues = utils.issues.format(self.issues, summary, self.options);
                callback(issues, summary);
            });
        });
    },

    /**
     * Reset
     *
     * Resets the in object data back to original values.
     */
    reset: function () {
        this.issues = [];
    }

};

module.exports = BIDS;
<|endoftext|>


<commit_before>const Discord = require('discord.js');
exports.run = (client, message) => {
  const modlog = client.channels.find('name', 'pie-log');
  const anno2 = client.channels.find('name', 'inactivity-notice');
const mainuser = message.guild.member(message.author);
    const guildmember = message.content.split(" ").join(" ").slice(12)
if (guildmember.length < 1) return message.reply('Please specify on your reason for being inactive.').catch(console.error);
message.delete()

client.channels.get(anno2.id).send({embed: {
  color: 0xffff00,
  title: `Inactivity Notice`,
  description: ``,
  fields: [{
    name: "Inactive user:",
    value: `${message.author}`
  },{
  name: "Reason:",
  value: `${guildmember}`
  }]
    }
    })

mainuser.sendMessage({embed: {
  color: 0xff0000,
  title: "Inactivity Notice Filed",
  description: (`\n**You have filed an inactivity notice!**\n`),
fields: [{
name: "Your discord tag:",
value: `${message.author}`
},{
name: "Reason:",
value: `${say}`
},{
name: "If you believe that this was an error, please use the command ;active.",
value: "Otherwise, please use the command ;active after you are not inactive anymore to remove your inactive tag."
}]
    }})
  };

exports.conf = {
enabled: true,
guildOnly: false,
aliases: [],
permLevel: 0
};

exports.help = {
name: 'inactivity',
description: 'Files an inactivity notice under the channel #inactivity-notice',
usage: 'inactivity message'
};
<commit_msg>Update inactivity.js<commit_after>const Discord = require('discord.js');
exports.run = (client, message) => {
  const modlog = client.channels.find('name', 'pie-log');
  const anno2 = client.channels.find('name', 'inactivity-notice');
const mainuser = message.guild.member(message.author);
    const guildmember = message.content.split(" ").join(" ").slice(12)
if (guildmember.length < 1) return message.reply('Please specify on your reason for being inactive.').catch(console.error);
message.delete()

client.channels.get(anno2.id).send({embed: {
  color: 0xffff00,
  title: `Inactivity Notice`,
  description: ``,
  fields: [{
    name: "Inactive user:",
    value: `${message.author}`
  },{
  name: "Reason:",
  value: `${guildmember}`
  }]
    }
    })

mainuser.sendMessage({embed: {
  color: 0xff0000,
  title: "Inactivity Notice Filed",
  description: (`\n**You have filed an inactivity notice!**\n`),
fields: [{
name: "Your discord tag:",
value: `${message.author}`
},{
name: "Reason:",
value: `${guildmember}`
},{
name: "If you believe that this was an error, please use the command ;active.",
value: "Otherwise, please use the command ;active after you are not inactive anymore to remove your inactive tag."
}]
    }})
  };

exports.conf = {
enabled: true,
guildOnly: false,
aliases: [],
permLevel: 0
};

exports.help = {
name: 'inactivity',
description: 'Files an inactivity notice under the channel #inactivity-notice',
usage: 'inactivity message'
};
<|endoftext|>


<commit_before>// @flow

// wait other players and prepare map\settings for Game view

// list of settings, can NOT be changed, this is start game state
// - defaultMoney
// - map
// - unitLimit
// - userList

import React, {Component} from 'react';
import type {Node} from 'react';
import {connect} from 'react-redux';
import MainModel from './../../lib/main-model/index';

import {Link} from 'react-router-dom';

import {user} from './../../module/user';
import {socket, type SocketMessageType} from './../../module/socket';
import Game from './../../components/game';

// import uiStyle from './../../components/ui/ui.scss';
// import serviceStyle from './../../../css/service.scss';
// import type {AuthType} from '../../components/auth/reducer';
import * as serverApi from './../../module/server-api';
import type {PushedStatePayloadType} from './../../module/server-api';
import type {AllRoomSettingsType, ServerUserType} from './../../module/server-api';
import mapGuide from './../../maps/map-guide';

import routes, {type HistoryType, type MatchType} from './../../app/routes';
import type {BuildingType, MapType, UnitType, MapUserType} from '../../maps/type';

type StateType = {|
    settings?: AllRoomSettingsType,
    userList: Array<ServerUserType>,
    model: MainModel,
    isGameStart: boolean
|};

type PropsType = {|
    match: MatchType
|};

class Room extends Component<PropsType, StateType> {
    props: PropsType;
    state: StateType;

    constructor() {
        super();

        const view = this;

        const state: StateType = {
            userList: [],
            model: new MainModel(),
            isGameStart: false
        };

        view.state = state;
    }

    async componentDidMount(): Promise<string> {
        const view = this;
        const {props, state} = view;

        const roomId = props.match.params.roomId || '';

        if (roomId === '') {
            return '';
        }

        // try to join into room, in case disconnected
        const socketInitialPromiseResult = await socket.attr.initialPromise;
        const joinResult = await serverApi.joinRoom(roomId, user.getId(), socket.getId());

        const settings = await serverApi.getAllRoomSettings(roomId);
        const users = await serverApi.getAllRoomUsers(roomId);

        view.setState({
            settings: settings.settings,
            userList: users.users,
            model: new MainModel()
        });

        view.bindEventListeners();

        return roomId;
    }

    async componentWillUnmount(): Promise<void> {
        const view = this;
        const {props, state} = view;
        const {model} = state;
        const roomId = props.match.params.roomId || '';

        model.destroy();

        const leaveRoomResult = await serverApi.leaveRoom(roomId, user.getId());
    }

    bindEventListeners() {
        const view = this;
        const {props, state} = view;
        const {model} = state;

        model.listenTo(socket.attr.model, 'message', async (message: SocketMessageType): Promise<void> => {
            await view.onMessage(message);
        });
    }

    async onMessage(message: SocketMessageType): Promise<void> { // eslint-disable-line complexity
        const view = this;
        const {props, state} = view;
        const {model} = state;
        const roomId = props.match.params.roomId || '';
        let roomDataUsers = null;
        let roomDataSettings = null;

        if (!state.settings) {
            console.error('state.settings is not defined');
            return Promise.resolve();
        }

        const defaultMoney = state.settings.map.defaultMoney;

        switch (message.type) {
            case 'room__take-turn':
                break;

            case 'room__join-into-room':
            case 'room__leave-from-room':
            case 'room__user-disconnected':
                roomDataUsers = await serverApi.getAllRoomUsers(roomId);
                view.setState({
                    userList: roomDataUsers.users
                });

                roomDataSettings = await serverApi.getAllRoomSettings(roomId);

                roomDataSettings.settings.map.userList = roomDataUsers
                    .users.map((serverUser: ServerUserType, userIndex: number): MapUserType => {
                        return {
                            userId: serverUser.userId,
                            money: defaultMoney,
                            teamId: mapGuide.teamIdList[userIndex]
                        };
                    });

                roomDataSettings.settings.map.activeUserId = roomDataSettings.settings.map.userList[0].userId;

                await serverApi.setRoomSetting(roomId, {map: roomDataSettings.settings.map});

                roomDataSettings = await serverApi.getAllRoomSettings(roomId);

                view.setState({
                    settings: roomDataSettings.settings
                });

                break;

            case 'room__push-state':

                if (message.states.last.state.isGameStart === true && state.isGameStart !== true) {
                    console.warn('---> The game has begun!!!');

                    roomDataSettings = await serverApi.getAllRoomSettings(roomId);

                    view.setState({
                        settings: roomDataSettings.settings
                    });

                    view.setState({isGameStart: true});

                    return Promise.resolve();
                }

                break;

            default:
                console.error('---> view - room - unsupported message type: ' + message.type);
        }

        return Promise.resolve();
    }

    async startGame(): Promise<void> {
        const view = this;
        const {props, state} = view;
        const roomId = props.match.params.roomId || '';

        const userList: Array<ServerUserType> = state.userList
            .map((userItem: ServerUserType, userIndex: number): ServerUserType => {
                return {
                    socketId: userItem.socketId,
                    userId: userItem.userId,
                    teamId: mapGuide.teamIdList[userIndex]
                };
            });

        const takeTurnResult = await serverApi.takeTurn(roomId, user.getId());

        const map: MapType | void = state.settings && state.settings.map;

        if (!map) {
            console.error('---> map is not exists!!!!');
            return;
        }

        [].concat(map.buildings, map.units).forEach((userItem: BuildingType | UnitType) => { // eslint-disable-line complexity
            const userItemUserId = typeof userItem.userId === 'string' ? userItem.userId : '';

            if (typeof userItem.id !== 'string' || userItem.id === '') {
                userItem.id = [userItem.x, userItem.y, Math.random()].join('_'); // eslint-disable-line no-param-reassign
            }

            if (userItemUserId === '') {
                return;
            }

            const userIndex = parseInt(userItemUserId.replace(/\D/g, ''), 10) || 0;

            const userData: ServerUserType | void = userList[userIndex];

            if (!userData) {
                Reflect.deleteProperty(userItem, 'userId');
                return;
            }

            userItem.userId = userData.userId; // eslint-disable-line no-param-reassign
        });

        const setSettingResult = await serverApi.setRoomSetting(roomId, {
            userList
        });

        const setSettingResult1 = await serverApi.setRoomSetting(roomId, {
            map
        });

        const newState: PushedStatePayloadType = {
            isGameStart: true,
            activeUserId: user.getId(),
            map
        };

        const pushStateResult = await serverApi.pushState(roomId, user.getId(), {
            type: 'room__push-state',
            state: newState
        });
    }

    render(): Node { // eslint-disable-line complexity
        const view = this;
        const {props, state} = view;
        const roomId = props.match.params.roomId || '';

        if (state.isGameStart === true) {
            return <Game roomId={roomId}/>;
        }

        return

Room

wait for other players

                {state && state.userList && JSON.stringify(state.userList)}

                {state && state.settings && JSON.stringify(state.settings)}

            <button onClick={async (): Promise<void> => {
                await view.startGame();
            }}>
                start
            </button>

;
    }
}

export default connect(
    (state: {}): {} => ({
        // app: state.app
    }),
    {}
)(Room);
<commit_msg>users map state - begin<commit_after>// @flow

// wait other players and prepare map\settings for Game view

// list of settings, can NOT be changed, this is start game state
// - defaultMoney
// - map
// - unitLimit
// - userList

import React, {Component} from 'react';
import type {Node} from 'react';
import {connect} from 'react-redux';
import MainModel from './../../lib/main-model/index';

import {Link} from 'react-router-dom';

import {user} from './../../module/user';
import {socket, type SocketMessageType} from './../../module/socket';
import Game from './../../components/game';

// import uiStyle from './../../components/ui/ui.scss';
// import serviceStyle from './../../../css/service.scss';
// import type {AuthType} from '../../components/auth/reducer';
import * as serverApi from './../../module/server-api';
import type {PushedStatePayloadType} from './../../module/server-api';
import type {AllRoomSettingsType, ServerUserType} from './../../module/server-api';
import mapGuide from './../../maps/map-guide';

import routes, {type HistoryType, type MatchType} from './../../app/routes';
import type {BuildingType, MapType, UnitType, MapUserType} from '../../maps/type';

type StateType = {|
    settings?: AllRoomSettingsType,
    userList: Array<ServerUserType>,
    model: MainModel,
    isGameStart: boolean
|};

type PropsType = {|
    match: MatchType
|};

class Room extends Component<PropsType, StateType> {
    props: PropsType;
    state: StateType;

    constructor() {
        super();

        const view = this;

        const state: StateType = {
            userList: [],
            model: new MainModel(),
            isGameStart: false
        };

        view.state = state;
    }

    async componentDidMount(): Promise<string> {
        const view = this;
        const {props, state} = view;

        const roomId = props.match.params.roomId || '';

        if (roomId === '') {
            return '';
        }

        // try to join into room, in case disconnected
        const socketInitialPromiseResult = await socket.attr.initialPromise;
        const joinResult = await serverApi.joinRoom(roomId, user.getId(), socket.getId());

        const settings = await serverApi.getAllRoomSettings(roomId);
        const users = await serverApi.getAllRoomUsers(roomId);

        view.setState({
            settings: settings.settings,
            userList: users.users,
            model: new MainModel()
        });

        view.bindEventListeners();

        return roomId;
    }

    async componentWillUnmount(): Promise<void> {
        const view = this;
        const {props, state} = view;
        const {model} = state;
        const roomId = props.match.params.roomId || '';

        model.destroy();

        const leaveRoomResult = await serverApi.leaveRoom(roomId, user.getId());
    }

    bindEventListeners() {
        const view = this;
        const {props, state} = view;
        const {model} = state;

        model.listenTo(socket.attr.model, 'message', async (message: SocketMessageType): Promise<void> => {
            await view.onMessage(message);
        });
    }

    async onMessage(message: SocketMessageType): Promise<void> { // eslint-disable-line complexity
        const view = this;
        const {props, state} = view;
        const {model} = state;
        const roomId = props.match.params.roomId || '';
        let roomDataUsers = null;
        let roomDataSettings = null;

        if (!state.settings) {
            console.error('state.settings is not defined');
            return Promise.resolve();
        }

        const defaultMoney = state.settings.map.defaultMoney;

        switch (message.type) {
            case 'room__take-turn':
                break;

            case 'room__drop-turn':
                break;

            case 'room__join-into-room':
            case 'room__leave-from-room':
            case 'room__user-disconnected':
                roomDataUsers = await serverApi.getAllRoomUsers(roomId);
                view.setState({
                    userList: roomDataUsers.users
                });

                roomDataSettings = await serverApi.getAllRoomSettings(roomId);

                roomDataSettings.settings.map.userList = roomDataUsers
                    .users.map((serverUser: ServerUserType, userIndex: number): MapUserType => {
                        return {
                            userId: serverUser.userId,
                            money: defaultMoney,
                            teamId: mapGuide.teamIdList[userIndex]
                        };
                    });

                roomDataSettings.settings.map.activeUserId = roomDataSettings.settings.map.userList[0].userId;

                await serverApi.setRoomSetting(roomId, {map: roomDataSettings.settings.map});

                roomDataSettings = await serverApi.getAllRoomSettings(roomId);

                view.setState({
                    settings: roomDataSettings.settings
                });

                break;

            case 'room__push-state':

                if (message.states.last.state.isGameStart === true && state.isGameStart !== true) {
                    console.warn('---> The game has begun!!!');

                    roomDataSettings = await serverApi.getAllRoomSettings(roomId);

                    view.setState({
                        settings: roomDataSettings.settings
                    });

                    view.setState({isGameStart: true});

                    return Promise.resolve();
                }

                break;

            default:
                console.error('---> view - room - unsupported message type: ' + message.type);
        }

        return Promise.resolve();
    }

    async startGame(): Promise<void> {
        const view = this;
        const {props, state} = view;
        const roomId = props.match.params.roomId || '';

        const userList: Array<ServerUserType> = state.userList
            .map((userItem: ServerUserType, userIndex: number): ServerUserType => {
                return {
                    socketId: userItem.socketId,
                    userId: userItem.userId,
                    teamId: mapGuide.teamIdList[userIndex]
                };
            });

        const takeTurnResult = await serverApi.takeTurn(roomId, user.getId());

        const map: MapType | void = state.settings && state.settings.map;

        if (!map) {
            console.error('---> map is not exists!!!!');
            return;
        }

        [].concat(map.buildings, map.units).forEach((userItem: BuildingType | UnitType) => { // eslint-disable-line complexity
            const userItemUserId = typeof userItem.userId === 'string' ? userItem.userId : '';

            if (typeof userItem.id !== 'string' || userItem.id === '') {
                userItem.id = [userItem.x, userItem.y, Math.random()].join('_'); // eslint-disable-line no-param-reassign
            }

            if (userItemUserId === '') {
                return;
            }

            const userIndex = parseInt(userItemUserId.replace(/\D/g, ''), 10) || 0;

            const userData: ServerUserType | void = userList[userIndex];

            if (!userData) {
                Reflect.deleteProperty(userItem, 'userId');
                return;
            }

            userItem.userId = userData.userId; // eslint-disable-line no-param-reassign
        });

        const setSettingResult = await serverApi.setRoomSetting(roomId, {
            userList
        });

        const setSettingResult1 = await serverApi.setRoomSetting(roomId, {
            map
        });

        const newState: PushedStatePayloadType = {
            isGameStart: true,
            activeUserId: user.getId(),
            map
        };

        const pushStateResult = await serverApi.pushState(roomId, user.getId(), {
            type: 'room__push-state',
            state: newState
        });
    }

    render(): Node { // eslint-disable-line complexity
        const view = this;
        const {props, state} = view;
        const roomId = props.match.params.roomId || '';

        if (state.isGameStart === true) {
            return <Game roomId={roomId}/>;
        }

        return

Room

wait for other players

                {state && state.userList && JSON.stringify(state.userList)}

                {state && state.settings && JSON.stringify(state.settings)}

            <button onClick={async (): Promise<void> => {
                await view.startGame();
            }}>
                start
            </button>

;
    }
}

export default connect(
    (state: {}): {} => ({
        // app: state.app
    }),
    {}
)(Room);
<|endoftext|>


<commit_before>angular.module('services')

.factory('store', function ($rootScope) {

    var storeState = {
        _prefixes: {},
        store: rdfstore.create(function(err, store) {}),
        prefixes: function() {
            return this._prefixes;
        },
        prefixesAsText: function() {
            var pfxText = "";
            var prefixes = this.prefixes();

            for (var pfx in prefixes) {
                if (prefixes[pfx]) {
                    pfxText += "PREFIX " + pfx + ": <" + prefixes[pfx] + "> \n"; // "
";
                }
            }

            return pfxText;
        }
    };

    var update = function (rdfData) {

        function broadcast(err, results) {
            $rootScope.$broadcast('store.update', storeState);
        }

        function loadData() {
            storeState.store.load("text/turtle", rdfData, broadcast);
        }

        // FIXME rdfstore-js does not support PREFIX parsing, workaround here

        function loadPrefixes(err, triple, prefixes) {
            storeState._prefixes = prefixes;

            if (!triple) {
                for (var prx in prefixes) {
                    if (Object.keys(storeState.store.rdf.prefixes).indexOf(prx) < 0) {
                        storeState.store.setPrefix(prx, prefixes[prx]);
                    }
                }

                loadData();
            }
        }

        var parser = N3.Parser();
        parser.parse(rdfData, loadPrefixes);
    };

    return {
        update: update,
        store: storeState
    };
});<commit_msg>refactoring prefix parsing when data loading<commit_after>angular.module('services')

.factory('store', function ($rootScope) {

    var storeState = {
        _prefixes: {},
        store: rdfstore.create(function(err, store) {}),
        prefixes: function() {
            return this._prefixes;
        },
        prefixesAsText: function() {
            var pfxText = "";
            var prefixes = this.prefixes();

            for (var pfx in prefixes) {
                if (prefixes[pfx]) {
                    pfxText += "PREFIX " + pfx + ": <" + prefixes[pfx] + "> \n"; // "
";
                }
            }

            return pfxText;
        }
    };

    var update = function (rdfData) {

        function broadcast(err, results) {
            storeState._prefixes['rdf'] = storeState.store.rdf.prefixes['rdf'];
            storeState._prefixes['rdfs'] = storeState.store.rdf.prefixes['rdfs'];

            $rootScope.$broadcast('store.update', storeState);
        }

        function parsePrefix(namespace, URI) {
            if (Object.keys(storeState.store.rdf.prefixes).indexOf(namespace) < 0) {
                storeState.store.setPrefix(namespace, URI);
                storeState._prefixes[namespace] = URI;
            }
        }

        var parser = N3.Parser();
        parser.parse(rdfData, null, parsePrefix);

        storeState.store.load("text/turtle", rdfData, broadcast);
    };

    return {
        update: update,
        store: storeState
    };
});<|endoftext|>


<commit_before>define(function() {
  /* Written for use with require.js */

  // Return object
  var sg = {};

  sg.GameGrid = function (options) {
    /*
     * Return a Block class for a grid with specified options.
     *
     * Options:
     *  - ctx:        Canvas context to draw on
     *  - squareSize: Size of each square in the grid
     *  - offset:     Amount to offset the drawing by on the canvas. Should
     *                have `x` and `y` attributes.  Defaults to 0.
     */

    // Allow these to be accessed by the Block class
    var ctx = options.ctx;
    var squareSize = options.squareSize;
    var offset = options.offset || { x: 0, y: 0 };

    function drawSquare(coords, size, fill) {
      ctx.fillStyle = fill;
      ctx.fillRect(coords.x, coords.y, size, size);
    }

    function Block(coords, options) {
      /*
       * Options:
       *  - fill
       *  - hasBorder
       *  - borderSize
       *  - borderFill
       */

      this.coords = {
        x: coords.x * squareSize + offset.x,
        y: coords.y * squareSize + offset.y
      };

      // Set all options (but don't overwrite existing properties)
      for (var key in options) {
        if (!(this[key] || this.prototype[key])) {
          this[key] = options[key];
        }
      }

    }

    Block.prototype.draw = function() {
      /* Draw the block. */

      // No border - simple drawing
      if (!this.hasBorder) {
        drawSquare(this.coords, squareSize, this.fill);
        return;
      }

      // Draw border (middle will be filled with fill)
      drawSquare(this.coords, squareSize, this.borderFill);

      // Coords of inside are shifted by border size
      var insideCoords = {
        x: this.coords.x - this.borderSize,
        y: this.coords.y - this.borderSize
      };
      // Size of inside is shrunk by twice border size
      var insideSize = squareSize - 2 * this.borderSize;

      // Draw fill
      drawSquare(insideCoords, insideSize, this.fill);
    };

    Block.prototype.undraw = function() {
      /* Undraw the block. */
      ctx.clearRect(this.coords.x, this.coords.y, squareSize, squareSize);
    };

    // Return the class that can be used for the grid
    return Block;
  }

  sg.registerControls = function(game, controls, sel) {
    /*
     * Register controls for a game (as handlers).
     *
     * Arguments:
     *  - game: Object (game) to save control objects to.  Controls are called
     *  with game as `this`.
     *  - controls: Array of controls to register.  Each control should have
     *  - sel: Prepended to id when searching for the element for a control.
     *  Elements are searched for by id attribute.
     *  elements:
     *    + id: game[id + 'Control'] is set to the event listener
     *    + event: event to listen to
     *    + handler: handler to be called with the event when it is triggered
     *
     * Note: Events are propagated up.
     */

    for(var i = 0; i < controls.length; ++i) {
      var control = controls[i];

      // Control element
      var el = document.getElementById(sel + control.id);
      game[control.id + 'Control'] = el;
      el.addEventListener(control.event, control.handler.bind(game));
    };

  };

  sg.copyPoint = function(pt) {
    return { x: pt.x, y: pt.y };
  }

  sg.addPoints = function(pt1, pt2) {
    return {
      x: pt1.x + pt2.x,
      y: pt1.y + pt2.y
    };
  }

  return sg;
});
<commit_msg>Semicolons and pointsEqual function<commit_after>define(function() {
  /* Written for use with require.js */

  // Return object
  var sg = {};

  sg.GameGrid = function (options) {
    /*
     * Return a Block class for a grid with specified options.
     *
     * Options:
     *  - ctx:        Canvas context to draw on
     *  - squareSize: Size of each square in the grid
     *  - offset:     Amount to offset the drawing by on the canvas. Should
     *                have `x` and `y` attributes.  Defaults to 0.
     */

    // Allow these to be accessed by the Block class
    var ctx = options.ctx;
    var squareSize = options.squareSize;
    var offset = options.offset || { x: 0, y: 0 };

    function drawSquare(coords, size, fill) {
      ctx.fillStyle = fill;
      ctx.fillRect(coords.x, coords.y, size, size);
    }

    function Block(coords, options) {
      /*
       * Options:
       *  - fill
       *  - hasBorder
       *  - borderSize
       *  - borderFill
       */

      this.coords = {
        x: coords.x * squareSize + offset.x,
        y: coords.y * squareSize + offset.y
      };

      // Set all options (but don't overwrite existing properties)
      for (var key in options) {
        if (!(this[key] || this.prototype[key])) {
          this[key] = options[key];
        }
      }

    }

    Block.prototype.draw = function() {
      /* Draw the block. */

      // No border - simple drawing
      if (!this.hasBorder) {
        drawSquare(this.coords, squareSize, this.fill);
        return;
      }

      // Draw border (middle will be filled with fill)
      drawSquare(this.coords, squareSize, this.borderFill);

      // Coords of inside are shifted by border size
      var insideCoords = {
        x: this.coords.x - this.borderSize,
        y: this.coords.y - this.borderSize
      };
      // Size of inside is shrunk by twice border size
      var insideSize = squareSize - 2 * this.borderSize;

      // Draw fill
      drawSquare(insideCoords, insideSize, this.fill);
    };

    Block.prototype.undraw = function() {
      /* Undraw the block. */
      ctx.clearRect(this.coords.x, this.coords.y, squareSize, squareSize);
    };

    // Return the class that can be used for the grid
    return Block;
  }

  sg.registerControls = function(game, controls, sel) {
    /*
     * Register controls for a game (as handlers).
     *
     * Arguments:
     *  - game: Object (game) to save control objects to.  Controls are called
     *  with game as `this`.
     *  - controls: Array of controls to register.  Each control should have
     *  - sel: Prepended to id when searching for the element for a control.
     *  Elements are searched for by id attribute.
     *  elements:
     *    + id: game[id + 'Control'] is set to the event listener
     *    + event: event to listen to
     *    + handler: handler to be called with the event when it is triggered
     *
     * Note: Events are propagated up.
     */

    for(var i = 0; i < controls.length; ++i) {
      var control = controls[i];

      // Control element
      var el = document.getElementById(sel + control.id);
      game[control.id + 'Control'] = el;
      el.addEventListener(control.event, control.handler.bind(game));
    };

  };

  sg.copyPoint = function(pt) {
    return { x: pt.x, y: pt.y };
  };

  sg.addPoints = function(pt1, pt2) {
    return {
      x: pt1.x + pt2.x,
      y: pt1.y + pt2.y
    };
  };

  sg.pointsEqual = function(pt1, pt2) {
    return (pt1.x === pt2.x) && (pt1.y === pt2.y);
  };

  return sg;
});
<|endoftext|>


<commit_before>/**
 * Copyright (c) 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule GraphQLQueryRunner
 * @typechecks
 * @flow
 */

'use strict';

const RelayFetchMode = require('RelayFetchMode');
import type {FetchMode} from 'RelayFetchMode';
import type {RelayQuerySet} from 'RelayInternalTypes';
import type {PendingFetch} from 'RelayPendingQueryTracker';
const RelayNetworkLayer = require('RelayNetworkLayer');
const RelayProfiler = require('RelayProfiler');
import type RelayQuery from 'RelayQuery';
const RelayReadyState = require('RelayReadyState');
import type RelayStoreData from 'RelayStoreData';
const RelayTaskScheduler = require('RelayTaskScheduler');

const checkRelayQueryData = require('checkRelayQueryData');
const diffRelayQuery = require('diffRelayQuery');
const everyObject = require('everyObject');
const flattenSplitRelayQueries = require('flattenSplitRelayQueries');
const forEachObject = require('forEachObject');
const generateForceIndex = require('generateForceIndex');
const mapObject = require('mapObject');
const resolveImmediate = require('resolveImmediate');
const someObject = require('someObject');
const splitDeferredRelayQueries = require('splitDeferredRelayQueries');
const warning = require('warning');

import type {
  Abortable,
  ReadyStateChangeCallback,
} from 'RelayTypes';

type RelayProfileHandler = {stop: () => void};

/**
 * This is the high-level entry point for sending queries to the GraphQL
 * endpoint. It provides methods for scheduling queries (`run`), force-fetching
 * queries (ie. ignoring the cache; `forceFetch`).
 *
 * In order to send minimal queries and avoid re-retrieving data,
 * `GraphQLQueryRunner` maintains a registry of pending (in-flight) queries, and
 * "subtracts" those from any new queries that callers enqueue.
 *
 * @internal
 */
class GraphQLQueryRunner {
  _storeData: RelayStoreData;

  constructor(storeData: RelayStoreData) {
    this._storeData = storeData;
  }

  /**
   * Fetches data required to resolve a set of queries. See the `RelayStore`
   * module for documentation on the callback.
   */
  run(
    querySet: RelayQuerySet,
    callback: ReadyStateChangeCallback,
    fetchMode?: FetchMode
  ): Abortable {
    fetchMode = fetchMode || RelayFetchMode.CLIENT;
    var profiler = fetchMode === RelayFetchMode.REFETCH ?
      RelayProfiler.profile('GraphQLQueryRunner.forceFetch') :
      RelayProfiler.profile('GraphQLQueryRunner.primeCache');

    var diffQueries = [];
    if (fetchMode === RelayFetchMode.CLIENT) {
      forEachObject(querySet, query => {
        if (query) {
          diffQueries.push(...diffRelayQuery(
            query,
            this._storeData.getRecordStore(),
            this._storeData.getQueryTracker()
          ));
        }
      });
    } else {
      forEachObject(querySet, query => {
        if (query) {
          diffQueries.push(query);
        }
      });
    }

    return runQueries(
      this._storeData,
      diffQueries,
      callback,
      fetchMode,
      profiler
    );
  }

  /**
   * Ignores the cache and fetches data required to resolve a set of queries.
   * Uses the data we get back from the server to overwrite data in the cache.
   *
   * Even though we're ignoring the cache, we will still invoke the callback
   * immediately with `ready: true` if `querySet` can be resolved by the cache.
   */
  forceFetch(
    querySet: RelayQuerySet,
    callback: ReadyStateChangeCallback
  ): Abortable {
    var fetchMode = RelayFetchMode.REFETCH;
    var profiler = RelayProfiler.profile('GraphQLQueryRunner.forceFetch');
    var queries = [];
    forEachObject(querySet, query => {
      query && queries.push(query);
    });

    return runQueries(this._storeData, queries, callback, fetchMode, profiler);
  }
}

function hasItems(map: Object): boolean {
  return !!Object.keys(map).length;
}

function splitAndFlattenQueries(
  queries: Array<RelayQuery.Root>
): Array<RelayQuery.Root> {
  if (!RelayNetworkLayer.supports('defer')) {
    if (__DEV__) {
      queries.forEach(query => {
        warning(
          !query.hasDeferredDescendant(),
          'Relay: Query `%s` contains a deferred fragment (e.g. ' +
          '`getFragment(\'foo\').defer()`) which is not supported by the ' +
          'default network layer. This query will be sent without deferral.',
          query.getName()
        );
      });
    }
    return queries;
  }

  var flattenedQueries = [];
  queries.forEach(query => {
    return flattenedQueries.push(
      ...flattenSplitRelayQueries(
        splitDeferredRelayQueries(query)
      )
    );
  });
  return flattenedQueries;
}

function runQueries(
  storeData: RelayStoreData,
  queries: Array<RelayQuery.Root>,
  callback: ReadyStateChangeCallback,
  fetchMode: FetchMode,
  profiler: RelayProfileHandler
): Abortable {
  const readyState = new RelayReadyState(callback);

  var remainingFetchMap: {[queryID: string]: PendingFetch} = {};
  var remainingRequiredFetchMap: {[queryID: string]: PendingFetch} = {};

  function onResolved(pendingFetch: PendingFetch) {
    var pendingQuery = pendingFetch.getQuery();
    var pendingQueryID = pendingQuery.getID();
    delete remainingFetchMap[pendingQueryID];
    if (!pendingQuery.isDeferred()) {
      delete remainingRequiredFetchMap[pendingQueryID];
    }

    if (hasItems(remainingRequiredFetchMap)) {
      return;
    }

    if (someObject(remainingFetchMap, query => query.isResolvable())) {
      // The other resolvable query will resolve imminently and call
      // `readyState.update` instead.
      return;
    }

    if (hasItems(remainingFetchMap)) {
      readyState.update({done: false, ready: true, stale: false});
    } else {
      readyState.update({done: true, ready: true, stale: false});
    }
  }

  function onRejected(pendingFetch: PendingFetch, error: Error) {
    readyState.update({error});

    var pendingQuery = pendingFetch.getQuery();
    var pendingQueryID = pendingQuery.getID();
    delete remainingFetchMap[pendingQueryID];
    if (!pendingQuery.isDeferred()) {
      delete remainingRequiredFetchMap[pendingQueryID];
    }
  }

  function canResolve(fetch: PendingFetch): boolean {
    return checkRelayQueryData(
      storeData.getQueuedStore(),
      fetch.getQuery()
    );
  }

  RelayTaskScheduler.enqueue(() => {
    var forceIndex = fetchMode === RelayFetchMode.REFETCH ?
      generateForceIndex() : null;

    splitAndFlattenQueries(queries).forEach(query => {
      var pendingFetch = storeData.getPendingQueryTracker().add(
        {query, fetchMode, forceIndex, storeData}
      );
      var queryID = query.getID();
      remainingFetchMap[queryID] = pendingFetch;
      if (!query.isDeferred()) {
        remainingRequiredFetchMap[queryID] = pendingFetch;
      }
      pendingFetch.getResolvedPromise().then(
        onResolved.bind(null, pendingFetch),
        onRejected.bind(null, pendingFetch)
      );
    });

    if (!hasItems(remainingFetchMap)) {
      readyState.update({done: true, ready: true});
    } else {
      if (!hasItems(remainingRequiredFetchMap)) {
        readyState.update({ready: true});
      } else {
        readyState.update({ready: false});
        resolveImmediate(() => {
          if (storeData.hasCacheManager()) {
            var requiredQueryMap = mapObject(
              remainingRequiredFetchMap,
              value => value.getQuery()
            );
            storeData.readFromDiskCache(requiredQueryMap, {
              onSuccess: () => {
                if (hasItems(remainingRequiredFetchMap)) {
                  readyState.update({ready: true, stale: true});
                }
              },
            });
          } else {
            if (everyObject(remainingRequiredFetchMap, canResolve)) {
              if (hasItems(remainingRequiredFetchMap)) {
                readyState.update({ready: true, stale: true});
              }
            }
          }
        });
      }
    }
    // Stop profiling when queries have been sent to the network layer.
    profiler.stop();
  }).done();

  return {
    abort(): void {
      readyState.update({aborted: true});
    },
  };
}

module.exports = GraphQLQueryRunner;
<commit_msg>Relay: Invoke `diffRelayQuery` Asynchronously<commit_after>/**
 * Copyright (c) 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule GraphQLQueryRunner
 * @typechecks
 * @flow
 */

'use strict';

const RelayFetchMode = require('RelayFetchMode');
import type {FetchMode} from 'RelayFetchMode';
import type {RelayQuerySet} from 'RelayInternalTypes';
import type {PendingFetch} from 'RelayPendingQueryTracker';
const RelayNetworkLayer = require('RelayNetworkLayer');
const RelayProfiler = require('RelayProfiler');
import type RelayQuery from 'RelayQuery';
const RelayReadyState = require('RelayReadyState');
import type RelayStoreData from 'RelayStoreData';
const RelayTaskScheduler = require('RelayTaskScheduler');

const checkRelayQueryData = require('checkRelayQueryData');
const diffRelayQuery = require('diffRelayQuery');
const everyObject = require('everyObject');
const flattenSplitRelayQueries = require('flattenSplitRelayQueries');
const forEachObject = require('forEachObject');
const generateForceIndex = require('generateForceIndex');
const mapObject = require('mapObject');
const resolveImmediate = require('resolveImmediate');
const someObject = require('someObject');
const splitDeferredRelayQueries = require('splitDeferredRelayQueries');
const warning = require('warning');

import type {
  Abortable,
  ReadyStateChangeCallback,
} from 'RelayTypes';

/**
 * This is the high-level entry point for sending queries to the GraphQL
 * endpoint. It provides methods for scheduling queries (`run`), force-fetching
 * queries (ie. ignoring the cache; `forceFetch`).
 *
 * In order to send minimal queries and avoid re-retrieving data,
 * `GraphQLQueryRunner` maintains a registry of pending (in-flight) queries, and
 * "subtracts" those from any new queries that callers enqueue.
 *
 * @internal
 */
class GraphQLQueryRunner {
  _storeData: RelayStoreData;

  constructor(storeData: RelayStoreData) {
    this._storeData = storeData;
  }

  /**
   * Fetches data required to resolve a set of queries. See the `RelayStore`
   * module for documentation on the callback.
   */
  run(
    querySet: RelayQuerySet,
    callback: ReadyStateChangeCallback,
    fetchMode?: FetchMode = RelayFetchMode.CLIENT
  ): Abortable {
    return runQueries(this._storeData, querySet, callback, fetchMode);
  }

  /**
   * Ignores the cache and fetches data required to resolve a set of queries.
   * Uses the data we get back from the server to overwrite data in the cache.
   *
   * Even though we're ignoring the cache, we will still invoke the callback
   * immediately with `ready: true` if `querySet` can be resolved by the cache.
   */
  forceFetch(
    querySet: RelayQuerySet,
    callback: ReadyStateChangeCallback
  ): Abortable {
    const fetchMode = RelayFetchMode.REFETCH;
    return runQueries(this._storeData, querySet, callback, fetchMode);
  }
}

function hasItems(map: Object): boolean {
  return !!Object.keys(map).length;
}

function splitAndFlattenQueries(
  queries: Array<RelayQuery.Root>
): Array<RelayQuery.Root> {
  if (!RelayNetworkLayer.supports('defer')) {
    if (__DEV__) {
      queries.forEach(query => {
        warning(
          !query.hasDeferredDescendant(),
          'Relay: Query `%s` contains a deferred fragment (e.g. ' +
          '`getFragment(\'foo\').defer()`) which is not supported by the ' +
          'default network layer. This query will be sent without deferral.',
          query.getName()
        );
      });
    }
    return queries;
  }

  var flattenedQueries = [];
  queries.forEach(query => {
    return flattenedQueries.push(
      ...flattenSplitRelayQueries(
        splitDeferredRelayQueries(query)
      )
    );
  });
  return flattenedQueries;
}

function runQueries(
  storeData: RelayStoreData,
  querySet: RelayQuerySet,
  callback: ReadyStateChangeCallback,
  fetchMode: FetchMode
): Abortable {
  const profiler = fetchMode === RelayFetchMode.REFETCH ?
    RelayProfiler.profile('GraphQLQueryRunner.forceFetch') :
    RelayProfiler.profile('GraphQLQueryRunner.primeCache');

  const readyState = new RelayReadyState(callback);

  var remainingFetchMap: {[queryID: string]: PendingFetch} = {};
  var remainingRequiredFetchMap: {[queryID: string]: PendingFetch} = {};

  function onResolved(pendingFetch: PendingFetch) {
    var pendingQuery = pendingFetch.getQuery();
    var pendingQueryID = pendingQuery.getID();
    delete remainingFetchMap[pendingQueryID];
    if (!pendingQuery.isDeferred()) {
      delete remainingRequiredFetchMap[pendingQueryID];
    }

    if (hasItems(remainingRequiredFetchMap)) {
      return;
    }

    if (someObject(remainingFetchMap, query => query.isResolvable())) {
      // The other resolvable query will resolve imminently and call
      // `readyState.update` instead.
      return;
    }

    if (hasItems(remainingFetchMap)) {
      readyState.update({done: false, ready: true, stale: false});
    } else {
      readyState.update({done: true, ready: true, stale: false});
    }
  }

  function onRejected(pendingFetch: PendingFetch, error: Error) {
    readyState.update({error});

    var pendingQuery = pendingFetch.getQuery();
    var pendingQueryID = pendingQuery.getID();
    delete remainingFetchMap[pendingQueryID];
    if (!pendingQuery.isDeferred()) {
      delete remainingRequiredFetchMap[pendingQueryID];
    }
  }

  function canResolve(fetch: PendingFetch): boolean {
    return checkRelayQueryData(
      storeData.getQueuedStore(),
      fetch.getQuery()
    );
  }

  RelayTaskScheduler.enqueue(() => {
    const forceIndex = fetchMode === RelayFetchMode.REFETCH ?
      generateForceIndex() :
      null;

    const queries = [];
    if (fetchMode === RelayFetchMode.CLIENT) {
      forEachObject(querySet, query => {
        if (query) {
          queries.push(...diffRelayQuery(
            query,
            storeData.getRecordStore(),
            storeData.getQueryTracker()
          ));
        }
      });
    } else {
      forEachObject(querySet, query => {
        if (query) {
          queries.push(query);
        }
      });
    }

    splitAndFlattenQueries(queries).forEach(query => {
      var pendingFetch = storeData.getPendingQueryTracker().add(
        {query, fetchMode, forceIndex, storeData}
      );
      var queryID = query.getID();
      remainingFetchMap[queryID] = pendingFetch;
      if (!query.isDeferred()) {
        remainingRequiredFetchMap[queryID] = pendingFetch;
      }
      pendingFetch.getResolvedPromise().then(
        onResolved.bind(null, pendingFetch),
        onRejected.bind(null, pendingFetch)
      );
    });

    if (!hasItems(remainingFetchMap)) {
      readyState.update({done: true, ready: true});
    } else {
      if (!hasItems(remainingRequiredFetchMap)) {
        readyState.update({ready: true});
      } else {
        readyState.update({ready: false});
        resolveImmediate(() => {
          if (storeData.hasCacheManager()) {
            var requiredQueryMap = mapObject(
              remainingRequiredFetchMap,
              value => value.getQuery()
            );
            storeData.readFromDiskCache(requiredQueryMap, {
              onSuccess: () => {
                if (hasItems(remainingRequiredFetchMap)) {
                  readyState.update({ready: true, stale: true});
                }
              },
            });
          } else {
            if (everyObject(remainingRequiredFetchMap, canResolve)) {
              if (hasItems(remainingRequiredFetchMap)) {
                readyState.update({ready: true, stale: true});
              }
            }
          }
        });
      }
    }
    // Stop profiling when queries have been sent to the network layer.
    profiler.stop();
  }).done();

  return {
    abort(): void {
      readyState.update({aborted: true});
    },
  };
}

module.exports = GraphQLQueryRunner;
<|endoftext|>