<commit_before>var path = require('path');
var fs = require('fs');
var util = require('util');
var assert = require('assert');
var Module = require('module');
var _ = require('underscore');
var Bones = require('bones');

var utils = Bones.utils;

// Load wrappers
var wrappers = {};
fs.readdirSync(__dirname).forEach(function(name) {
    var match = name.match(/^(.+)\.(prefix|suffix)\.js$/);
    if (match) {
        wrappers[match[1]] = wrappers[match[1]] || {};
        wrappers[match[1]][match[2]] =
            fs.readFileSync(path.join(__dirname, name), 'utf8')
                .split('\n').join('');
    }
});

require.extensions['.bones'] = function(module, filename) {
    var content = fs.readFileSync(filename, 'utf8');
    var kind = utils.singularize(path.basename(path.dirname(filename)));

    wrappers[kind] = wrappers[kind] || {};
    wrappers[kind].prefix = wrappers[kind].prefix || '';
    wrappers[kind].suffix = wrappers[kind].suffix || '';

    content = wrappers[kind].prefix + ';' + content + '\n;' + wrappers[kind].suffix;
    module._compile(content, filename);

    if (module.exports) {
        Bones.plugin.add(module.exports, filename);
    }
};

// Default template engine.
require.extensions['._'] = function(module, filename) {
    var content = fs.readFileSync(filename, 'utf8');
    var name = path.basename(filename).replace(/\..+$/, '');

    try {
        module.exports = _.template(content);
        Bones.plugin.add(module.exports, filename);
    } catch (err) {
        var lines = err.message.split('\n');
        lines.splice(1, 0, '    in template ' + filename);
        err.message = lines.join('\n');
        throw err;
    }

    module.exports.register = function(app) {
        if (app.assets) {
            app.assets.templates.push({
                filename: filename,
                content: 'template = ' + module.exports + ';'
            });
        }
    };
};

module.exports = Plugin;
function Plugin() {
    this.directories = [];
    this.order = [];
    this.config = {};
    this.controllers = {};
    this.models = {};
    this.templates = {};
    this.views = {};
    this.servers = {};
    this.commands = {};
};

function alphabetical(a, b) {
    return a.toLowerCase().localeCompare(b.toLowerCase());
}

Plugin.prototype.load = function(dir) {
    if (this.directories.indexOf(dir) < 0) {
        this.directories.push(dir);
        this.require(dir, 'controllers');
        this.require(dir, 'models');
        this.require(dir, 'templates');
        this.require(dir, 'views');
        this.require(dir, 'servers');
        this.require(dir, 'commands');
    }
    return this;
};

Plugin.prototype.require = function(dir, kind) {
    dir = path.join(dir, kind);
    try {
        fs.readdirSync(dir).sort(alphabetical).forEach(function(name) {
            var file = path.join(dir, name);
            if (path.extname(file) in require.extensions &&
                path.basename(file)[0] !== '.' &&
                fs.statSync(file).isFile()) {
                require(file);
            }
        });
    } catch(err) {
        if (err.code !== 'ENOENT') throw err;
    }

    return this;
};

Plugin.prototype.add = function(component, filename) {
    if (!component.files) component.files = [];
    component.files.push(filename);

    if (!component.title) {
        component.title = path.basename(filename).replace(/\..+$/, '');
    }

    var kind = path.basename(path.dirname(filename));
    Bones.plugin[kind][component.title] = component;
    Bones.plugin.order.push(filename);
};

Plugin.prototype.start = function(callback) {
    this.argv = require('optimist').argv;

    var command = this.argv._.length ? this.argv._[0] : 'start';
    if (this.argv.help || !(command in this.commands)) {
        this.help(callback);
    } else {
        var command = this.commands[command];
        if (this.loadConfig(command)) {
            return new command(this, callback);
        } else if (callback) {
            callback();
        }
    }
};

Plugin.prototype.loadConfig = function(command) {
    var config = this.config;
    command.options = command.options || {};

    if (this.argv.config) {
        try {
            _.extend(config, JSON.parse(fs.readFileSync(this.argv.config, 'utf8')));
        } catch(e) {
            console.error(utils.colorize('Invalid JSON config file: ' +
                this.argv.config, 'red'));
            process.exit(2);
        }
    }

    for (var key in command.options) {
        if (!(key in config)) {
            config[key] = command.options[key]['default'];
        }
        if (command.options[key]['shortcut'] in this.argv) {
            config[key] = this.argv[command.options[key]['shortcut']];
            delete this.argv[command.options[key]['shortcut']];
        }
        if (key in this.argv) {
            config[key] = this.argv[key];
            delete this.argv[key];
        }
    }

    _.defaults(config, this.argv);

    var showConfig = false;
    for (var key in config) {
        if (key === 'config' || key === '_' || key[0] === '$') {
            delete config[key];
        }
        else if (key === 'show-config') {
            showConfig = config[key];
            delete config[key];
        }
        else {
            if (!(key in command.options)) {
                if (key in this.argv) {
                    // It was specified on the command line.
                     console.warn(utils.colorize('Note: Unknown option "' + key + '".', 'yellow'));
                } else {
                    // It's from the config file.
                    console.warn(utils.colorize('Note: Unknown option "' + key + '" in config file.', 'yellow'));
                }
            } else if (command.options[key].required && typeof config[key] === 'undefined') {
                console.warn(utils.colorize('Error: "' + key + '" is required.', 'red'));
                process.exit(2);
            }
        }
    }

    for (var key in config) {
        if (typeof config[key] === 'function') {
            config[key] = config[key](this, config);
        }
    }

    if (showConfig) {
        console.warn(utils.colorize('Using configuration:', 'green'));
        console.warn(JSON.stringify(config, false, 4));
        return false;
    } else {
        return true;
    }
};

Plugin.prototype.help = function(callback) {
    var output = [];
    var command = this.argv._.length ? this.argv._[0] : false;
    if (command !== false && command in this.commands) {
        // Display information about this command.
        var command = this.commands[command];
        output.push(['Usage: %s', utils.colorize(this.argv['$0'] + ' ' +
            command.title +
            (command.usage ? ' ' + command.usage : '') +
            ' [options...]', 'green')]);

        output.push(['%s%s: %s',
            utils.colorize(command.title, 'yellow', 'bold'),
            utils.colorize(command.usage ? ' ' + command.usage : '', 'yellow'),
            command.description]);

        var options = [];
        for (var key in command.options) {
            var option = command.options[key];
            var required = '';
            if (option.required) {
                required = utils.colorize('(Required)', 'default', 'bold');
            } else {
                var value = option['default'];
                if (typeof value === 'function') value = value(this);
                required = '(Default: ' + JSON.stringify(value) +')';
            }

            options.push([
                option.shortcut ? '-' + option.shortcut : '',
                '--' + (option.title || key),
                (option.description ? option.description + ' ' : '') + required
            ]);
        }
        options.push([ '', '--config=[path]', 'Path to JSON configuration file.' ]);

        table(options).forEach(function(line) { output.push(line); });
    } else {
        // Display information about all available commands.
        output.push(['Usage: %s for a list of options.', utils.colorize(this.argv['$0'] + ' ' + (command || '[command]') + ' --help', 'green')]);
        output.push(['Available commands are:']);
        var commands = [];
        for (var key in this.commands) {
            commands.push([ this.commands[key].title + ':', this.commands[key].description || '']);
        }
        table(commands).forEach(function(line) { output.push(line); });
    }

    if (callback) {
        callback(output);
    } else {
        output.forEach(function(params) {
            console.warn.apply(console, params);
        });
        process.exit(1);
    }
};

function table(fields) {
    var output = [];
    if (!fields[0]) return output;
    var lengths = fields[0].map(function(val, i) {
        return Math.max.apply(Math, fields.map(function(field) {
            return field[i].length;
        }));
    });
    fields.forEach(function(field) {
        output.push([
            '  ' + field.map(function(val, i) {
                if (i >= lengths.length - 1) return val;
                return val + Array(lengths[i] - val.length + 1).join(' ');
            }).join('  ')
        ]);
    });
    return output;
};
<commit_msg>avoid printing the command usage twice, and support an array of usage patterns.<commit_after>var path = require('path');
var fs = require('fs');
var util = require('util');
var assert = require('assert');
var Module = require('module');
var _ = require('underscore');
var Bones = require('bones');

var utils = Bones.utils;

// Load wrappers
var wrappers = {};
fs.readdirSync(__dirname).forEach(function(name) {
    var match = name.match(/^(.+)\.(prefix|suffix)\.js$/);
    if (match) {
        wrappers[match[1]] = wrappers[match[1]] || {};
        wrappers[match[1]][match[2]] =
            fs.readFileSync(path.join(__dirname, name), 'utf8')
                .split('\n').join('');
    }
});

require.extensions['.bones'] = function(module, filename) {
    var content = fs.readFileSync(filename, 'utf8');
    var kind = utils.singularize(path.basename(path.dirname(filename)));

    wrappers[kind] = wrappers[kind] || {};
    wrappers[kind].prefix = wrappers[kind].prefix || '';
    wrappers[kind].suffix = wrappers[kind].suffix || '';

    content = wrappers[kind].prefix + ';' + content + '\n;' + wrappers[kind].suffix;
    module._compile(content, filename);

    if (module.exports) {
        Bones.plugin.add(module.exports, filename);
    }
};

// Default template engine.
require.extensions['._'] = function(module, filename) {
    var content = fs.readFileSync(filename, 'utf8');
    var name = path.basename(filename).replace(/\..+$/, '');

    try {
        module.exports = _.template(content);
        Bones.plugin.add(module.exports, filename);
    } catch (err) {
        var lines = err.message.split('\n');
        lines.splice(1, 0, '    in template ' + filename);
        err.message = lines.join('\n');
        throw err;
    }

    module.exports.register = function(app) {
        if (app.assets) {
            app.assets.templates.push({
                filename: filename,
                content: 'template = ' + module.exports + ';'
            });
        }
    };
};

module.exports = Plugin;
function Plugin() {
    this.directories = [];
    this.order = [];
    this.config = {};
    this.controllers = {};
    this.models = {};
    this.templates = {};
    this.views = {};
    this.servers = {};
    this.commands = {};
};

function alphabetical(a, b) {
    return a.toLowerCase().localeCompare(b.toLowerCase());
}

Plugin.prototype.load = function(dir) {
    if (this.directories.indexOf(dir) < 0) {
        this.directories.push(dir);
        this.require(dir, 'controllers');
        this.require(dir, 'models');
        this.require(dir, 'templates');
        this.require(dir, 'views');
        this.require(dir, 'servers');
        this.require(dir, 'commands');
    }
    return this;
};

Plugin.prototype.require = function(dir, kind) {
    dir = path.join(dir, kind);
    try {
        fs.readdirSync(dir).sort(alphabetical).forEach(function(name) {
            var file = path.join(dir, name);
            if (path.extname(file) in require.extensions &&
                path.basename(file)[0] !== '.' &&
                fs.statSync(file).isFile()) {
                require(file);
            }
        });
    } catch(err) {
        if (err.code !== 'ENOENT') throw err;
    }

    return this;
};

Plugin.prototype.add = function(component, filename) {
    if (!component.files) component.files = [];
    component.files.push(filename);

    if (!component.title) {
        component.title = path.basename(filename).replace(/\..+$/, '');
    }

    var kind = path.basename(path.dirname(filename));
    Bones.plugin[kind][component.title] = component;
    Bones.plugin.order.push(filename);
};

Plugin.prototype.start = function(callback) {
    this.argv = require('optimist').argv;

    var command = this.argv._.length ? this.argv._[0] : 'start';
    if (this.argv.help || !(command in this.commands)) {
        this.help(callback);
    } else {
        var command = this.commands[command];
        if (this.loadConfig(command)) {
            return new command(this, callback);
        } else if (callback) {
            callback();
        }
    }
};

Plugin.prototype.loadConfig = function(command) {
    var config = this.config;
    command.options = command.options || {};

    if (this.argv.config) {
        try {
            _.extend(config, JSON.parse(fs.readFileSync(this.argv.config, 'utf8')));
        } catch(e) {
            console.error(utils.colorize('Invalid JSON config file: ' +
                this.argv.config, 'red'));
            process.exit(2);
        }
    }

    for (var key in command.options) {
        if (!(key in config)) {
            config[key] = command.options[key]['default'];
        }
        if (command.options[key]['shortcut'] in this.argv) {
            config[key] = this.argv[command.options[key]['shortcut']];
            delete this.argv[command.options[key]['shortcut']];
        }
        if (key in this.argv) {
            config[key] = this.argv[key];
            delete this.argv[key];
        }
    }

    _.defaults(config, this.argv);

    var showConfig = false;
    for (var key in config) {
        if (key === 'config' || key === '_' || key[0] === '$') {
            delete config[key];
        }
        else if (key === 'show-config') {
            showConfig = config[key];
            delete config[key];
        }
        else {
            if (!(key in command.options)) {
                if (key in this.argv) {
                    // It was specified on the command line.
                     console.warn(utils.colorize('Note: Unknown option "' + key + '".', 'yellow'));
                } else {
                    // It's from the config file.
                    console.warn(utils.colorize('Note: Unknown option "' + key + '" in config file.', 'yellow'));
                }
            } else if (command.options[key].required && typeof config[key] === 'undefined') {
                console.warn(utils.colorize('Error: "' + key + '" is required.', 'red'));
                process.exit(2);
            }
        }
    }

    for (var key in config) {
        if (typeof config[key] === 'function') {
            config[key] = config[key](this, config);
        }
    }

    if (showConfig) {
        console.warn(utils.colorize('Using configuration:', 'green'));
        console.warn(JSON.stringify(config, false, 4));
        return false;
    } else {
        return true;
    }
};

Plugin.prototype.help = function(callback) {
    var output = [];
    var command = this.argv._.length ? this.argv._[0] : false;
    if (command !== false && command in this.commands) {
        // Display information about this command.
        var command = this.commands[command];

        output.push(['Usage: %s', utils.colorize(this.argv['$0'] + ' <command> [options...]', 'green')]);

        output.push(["Commands: " + command.description]);

        var usage = command.usage || [''];
        _(_.isArray(usage) ? usage : [usage]).each(function(item) {
            output.push([
                '  %s %s',
                utils.colorize(command.title, 'yellow', 'bold'),
                utils.colorize(item, 'yellow'),
            ]);
        });

        output.push(['\nOptions : ']);
        var options = [];
        for (var key in command.options) {
            var option = command.options[key];
            var required = '';
            if (option.required) {
                required = utils.colorize('(Required)', 'default', 'bold');
            } else {
                var value = option['default'];
                if (typeof value === 'function') value = value(this);
                required = '(Default: ' + JSON.stringify(value) +')';
            }

            options.push([
                option.shortcut ? '-' + option.shortcut : '',
                '--' + (option.title || key),
                (option.description ? option.description + ' ' : '') + required
            ]);
        }
        options.push([ '', '--config=[path]', 'Path to JSON configuration file.' ]);

        table(options).forEach(function(line) { output.push(line); });
    } else {
        // Display information about all available commands.
        output.push(['Usage: %s for a list of options.', utils.colorize(this.argv['$0'] + ' ' + (command || '[command]') + ' --help', 'green')]);
        output.push(['Available commands are:']);
        var commands = [];
        for (var key in this.commands) {
            commands.push([ this.commands[key].title + ':', this.commands[key].description || '']);
        }
        table(commands).forEach(function(line) { output.push(line); });
    }

    if (callback) {
        callback(output);
    } else {
        output.forEach(function(params) {
            console.warn.apply(console, params);
        });
        process.exit(1);
    }
};

function table(fields) {
    var output = [];
    if (!fields[0]) return output;
    var lengths = fields[0].map(function(val, i) {
        return Math.max.apply(Math, fields.map(function(field) {
            return field[i].length;
        }));
    });
    fields.forEach(function(field) {
        output.push([
            '  ' + field.map(function(val, i) {
                if (i >= lengths.length - 1) return val;
                return val + Array(lengths[i] - val.length + 1).join(' ');
            }).join('  ')
        ]);
    });
    return output;
};
<|endoftext|>


<commit_before>// Dependencies
var express = require('express');
var MongoClient = require('mongodb').MongoClient;
var crypto = require('crypto');
var nconf = require('nconf');
var path = require('path');
var icalendar = require('icalendar');
var request = require('request');
var async = require('async');

//
var app = express();

// Configuration
nconf
    .argv()
    .env()
    .file('custom', __dirname+'/config.json')
    .file(__dirname+'/default.config.json');

// Optionally serve the public front-end app
if (nconf.get('server:publicDir')) {
    app.use(express.static( path.resolve(__dirname, nconf.get('server:publicDir') )));
}

// API for Browser App
/**
Main
*/
MongoClient.connect('mongodb://'+nconf.get('database:hostname')+':'+nconf.get('database:port')+'/'+nconf.get('database:name'), function(err, db) {
    if(err) throw err;

    var collection = db.collection(nconf.get('database:collection'));

    // Setup Express server
    app.use(express.bodyParser());

    /**
    =========================================================================
    API
    */
    app.get('/api/uniapi', function(req, res) {
      res.json({
        "protocol": nconf.get('uniapi:protocol'),
        "hostname": nconf.get('uniapi:hostname'),
        "port": nconf.get('uniapi:port')
      });
    });

    var saveCalendar = function(calendarId, courses, collection, callback) {
        var data = {
            calendarId: calendarId,
            courses: courses
        };
        collection.update({ 'calendarId': calendarId }, data, {upsert: true}, function(err, docs) {
            //console.log(err, docs);
            callback(err, docs);
        });
    };

    // Create Calendar
    app.post('/api/calendar', function(req, res) {
        console.log(req.body);

        var courses = JSON.parse(req.body.courses);

        if (courses.length > 0) {

            // Create secure CalendarId
            var timestamp = (+(new Date()))+"";
            var temp = (timestamp + nconf.get('secret'));
            console.log(temp);
            var digest = "hex"
            var calendarId = crypto.createHash('md5').update(temp).digest(digest);
            console.log(calendarId);

            saveCalendar(calendarId, courses, collection, function(err, docs) {
                console.log('Saved Courses');

                var url = "/calendar/"+calendarId+"/calendar.ics";
                res.json({
                    'url': url,
                    'courses': courses
                }, 201);

            });

        } else {
            res.json({
                'error': "No courses were found."
            }, 204);
        }

    });

    // Stats
    app.get('/api/stats.json', function(req, res) {
        // Count
        collection.count(function(err, count) {
            res.json({ 'calendars': count }, 200);
        });

    });

    // var timeFromStr = function(str) {
    //     // Ex: "1245"
    //     //console.log('timeFromStr: ', str);
    //     var n = parseInt(str);
    //     var hours = parseInt(n/100);
    //     var minutes = n - hours*100;
    //     return {
    //         "hours": hours,
    //         "minutes": minutes
    //     };
    // };

    var jsonToEvent = function(json) {
        //console.log('jsonToEvent: ', json);
        var vevent = new icalendar.VEvent("event-"+json.crn);
        var summary = json.Subj_code + " " + json.Crse_numb + " - " + json.Crse_title;
        vevent.setSummary(summary);

        vevent.setDescription(json.Faculty);
        // Calculate date and event length
        var startDate = new Date(json.Start_date);
        var startTime = json.Begin_time;
        startDate.setHours(startTime.hours, startTime.minutes);

        var endTime = json.End_time;
        var endDate = new Date(json.Start_date);
        endDate.setHours(endTime.hours, endTime.minutes);
        // console.log(startDate, endDate);
        vevent.setDate(startDate, endDate);

        var days = [];
        if (json.Mon_day) {
            days.push("MO");
        }
        if (json.Tue_day) {
            days.push("TU");
        }
        if (json.Wed_day) {
            days.push("WE");
        }
        if (json.Thu_day) {
            days.push("TH");
        }
        if (json.Fri_day) {
            days.push("FR");
        }
        if (json.Sat_day) {
            days.push("SA");
        }
        if (json.Sun_day) {
            days.push("SU");
        }
        // console.log(days);
        vevent.addProperty('RRULE', { FREQ: 'WEEKLY', BYDAY: days.join(','), UNTIL: new Date(json.End_date) });
        var location = json.Bldg_code + " " + json.Room_code;
        vevent.addProperty('LOCATION', location);
        return vevent;
    };

    var coursesToCalendar = function(courses, callback) {
        //console.log(courses);
        // Generate iCalendar file
        var calendar = new icalendar.iCalendar();
        // Replace PRODID
        calendar.properties.PRODID = [];
        calendar.addProperty('PRODID',"-//Saint Mary's University//Calendar//EN");
        // Add events to calendar
        for (var i=0, len=courses.length; i<len; i++)
        {
            var course = courses[i];
            if (course.Begin_time && course.End_time)
            {
              var vevent = jsonToEvent(course);
              calendar.addComponent(vevent);
            }
        }
        return callback(calendar);
    };

    var reloadCourses = function(courses, callback) {

        async.map(courses, function(course, cb) {
            var baseurl = nconf.get('uniapi:protocol')+"://"+nconf.get('uniapi:hostname')+":"+nconf.get('uniapi:port');
            var url = baseurl + "/api/v1/courses/"+course._id;
            request(url, function(error, response, body) {
                console.log(error, body);
                try {
                    if (!error && !!body) {
                        var temp = JSON.parse(body);
                        course = temp;
                    }
                } catch (e) {
                    //
                }
                return cb(null, course);
            });
        }, function(err, results) {
            return callback && callback(results);
        });
    };

    // Calendar
    app.get('/calendar/:calendarId/calendar.ics', function(req, res) {
        console.log('Calendar', req.params);
        var calendarId = req.params.calendarId;

        // Locate all the entries using find
        collection.find({'calendarId': calendarId }).toArray(function(err, results) {
            console.dir(results);
            var result = results[0] || { 'courses': [] };
            var courses = result.courses;

            try {
                reloadCourses(courses, function(courses) {
                    coursesToCalendar(courses, function(calendar) {
                        res.header("Content-Type", "text/calendar; charset=utf-8");
                        res.header("Content-Disposition", "inline; filename=calendar.ics");
                        res.end(calendar.toString());
                    });
                })
            } catch (e) {
                res.json({"error": e.message });
            }

        });

    });

    // Start server
    app.listen(process.env.PORT || nconf.get('server:port'), function() {
        console.log('iCal-Sync Server is listening on '+nconf.get('server:port')+'.');
    });

});
<commit_msg>Clean up source code. Remove console.log statements and unused functions<commit_after>// Dependencies
var express = require('express');
var MongoClient = require('mongodb').MongoClient;
var crypto = require('crypto');
var nconf = require('nconf');
var path = require('path');
var icalendar = require('icalendar');
var request = require('request');
var async = require('async');

//
var app = express();

// Configuration
nconf
    .argv()
    .env()
    .file('custom', __dirname+'/config.json')
    .file(__dirname+'/default.config.json');

// Optionally serve the public front-end app
if (nconf.get('server:publicDir')) {
    app.use(express.static( path.resolve(__dirname, nconf.get('server:publicDir') )));
}

// API for Browser App
/**
Main
*/
MongoClient.connect('mongodb://'+nconf.get('database:hostname')+':'+nconf.get('database:port')+'/'+nconf.get('database:name'), function(err, db) {
    if(err) throw err;

    var collection = db.collection(nconf.get('database:collection'));

    // Setup Express server
    app.use(express.bodyParser());

    /**
    =========================================================================
    API
    */
    app.get('/api/uniapi', function(req, res) {
      res.json({
        "protocol": nconf.get('uniapi:protocol'),
        "hostname": nconf.get('uniapi:hostname'),
        "port": nconf.get('uniapi:port')
      });
    });

    var saveCalendar = function(calendarId, courses, collection, callback) {
        var data = {
            calendarId: calendarId,
            courses: courses
        };
        collection.update({ 'calendarId': calendarId }, data, {upsert: true}, function(err, docs) {
            //console.log(err, docs);
            callback(err, docs);
        });
    };

    // Create Calendar
    app.post('/api/calendar', function(req, res) {
        // console.log(req.body);

        var courses = JSON.parse(req.body.courses);

        if (courses.length > 0) {

            // Create secure CalendarId
            var timestamp = (+(new Date()))+"";
            var temp = (timestamp + nconf.get('secret'));
            // console.log(temp);
            var digest = "hex"
            var calendarId = crypto.createHash('md5').update(temp).digest(digest);
            // console.log(calendarId);

            saveCalendar(calendarId, courses, collection, function(err, docs) {
                // console.log('Saved Courses');

                var url = "/calendar/"+calendarId+"/calendar.ics";
                res.json({
                    'url': url,
                    'courses': courses
                }, 201);

            });

        } else {
            res.json({
                'error': "No courses were found."
            }, 204);
        }

    });

    // Stats
    app.get('/api/stats.json', function(req, res) {
        // Count
        collection.count(function(err, count) {
            res.json({ 'calendars': count }, 200);
        });

    });

    var jsonToEvent = function(json) {
        //console.log('jsonToEvent: ', json);
        var vevent = new icalendar.VEvent("event-"+json.crn);
        var summary = json.Subj_code + " " + json.Crse_numb + " - " + json.Crse_title;
        vevent.setSummary(summary);

        vevent.setDescription(json.Faculty);
        // Calculate date and event length
        var startDate = new Date(json.Start_date);
        var startTime = json.Begin_time;
        startDate.setHours(startTime.hours, startTime.minutes);

        var endTime = json.End_time;
        var endDate = new Date(json.Start_date);
        endDate.setHours(endTime.hours, endTime.minutes);
        // console.log(startDate, endDate);
        vevent.setDate(startDate, endDate);

        var days = [];
        if (json.Mon_day) {
            days.push("MO");
        }
        if (json.Tue_day) {
            days.push("TU");
        }
        if (json.Wed_day) {
            days.push("WE");
        }
        if (json.Thu_day) {
            days.push("TH");
        }
        if (json.Fri_day) {
            days.push("FR");
        }
        if (json.Sat_day) {
            days.push("SA");
        }
        if (json.Sun_day) {
            days.push("SU");
        }
        // console.log(days);
        vevent.addProperty('RRULE', { FREQ: 'WEEKLY', BYDAY: days.join(','), UNTIL: new Date(json.End_date) });
        var location = json.Bldg_code + " " + json.Room_code;
        vevent.addProperty('LOCATION', location);
        return vevent;
    };

    var coursesToCalendar = function(courses, callback) {
        //console.log(courses);
        // Generate iCalendar file
        var calendar = new icalendar.iCalendar();
        // Replace PRODID
        calendar.properties.PRODID = [];
        calendar.addProperty('PRODID',"-//Saint Mary's University//Calendar//EN");
        // Add events to calendar
        for (var i=0, len=courses.length; i<len; i++)
        {
            var course = courses[i];
            if (course.Begin_time && course.End_time)
            {
              var vevent = jsonToEvent(course);
              calendar.addComponent(vevent);
            }
        }
        return callback(calendar);
    };

    var reloadCourses = function(courses, callback) {

        async.map(courses, function(course, cb) {
            var baseurl = nconf.get('uniapi:protocol')+"://"+nconf.get('uniapi:hostname')+":"+nconf.get('uniapi:port');
            var url = baseurl + "/api/v1/courses/"+course._id;
            request(url, function(error, response, body) {
                // console.log(error, body);
                try {
                    if (!error && !!body) {
                        var temp = JSON.parse(body);
                        course = temp;
                    }
                } catch (e) {
                    //
                }
                return cb(null, course);
            });
        }, function(err, results) {
            return callback && callback(results);
        });
    };

    // Calendar
    app.get('/calendar/:calendarId/calendar.ics', function(req, res) {
        // console.log('Calendar', req.params);
        var calendarId = req.params.calendarId;

        // Locate all the entries using find
        collection.find({'calendarId': calendarId }).toArray(function(err, results) {
            // console.dir(results);
            var result = results[0] || { 'courses': [] };
            var courses = result.courses;

            try {
                reloadCourses(courses, function(courses) {
                    coursesToCalendar(courses, function(calendar) {
                        res.header("Content-Type", "text/calendar; charset=utf-8");
                        res.header("Content-Disposition", "inline; filename=calendar.ics");
                        res.end(calendar.toString());
                    });
                })
            } catch (e) {
                res.json({"error": e.message });
            }

        });

    });

    // Start server
    app.listen(process.env.PORT || nconf.get('server:port'), function() {
        console.log('iCal-Sync Server is listening on '+nconf.get('server:port')+'.');
    });

});
<|endoftext|>


<commit_before>// notice_start
/*
 * Copyright 2015 Dev Shop Limited
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
// notice_end

var webpack = require('webpack');
var path = require('path');

module.exports = {
    entry: './src/index.js',
    output: {
        libraryTarget: 'umd',
        sourcePrefix: '    ',
        library: 'esp',
        path: './dist/',
        filename: 'esp.js'
    },
    devtool: 'source-map',
    debug:false,
    module: {
        loaders: [
            {
                loader: "babel-loader",
                include: [
                    path.resolve(__dirname, "src"),
                ],
                test: /\.js$/
            }
        ]
    },
    eslint: {
        configFile: './.eslintrc'
    },
    plugins: [
        new webpack.optimize.OccurenceOrderPlugin(true)
    ]
};<commit_msg>Don't use min<commit_after>// notice_start
/*
 * Copyright 2015 Dev Shop Limited
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
// notice_end

var webpack = require('webpack');
var path = require('path');

module.exports = {
    entry: './src/index.js',
    output: {
        libraryTarget: 'umd',
        sourcePrefix: '    ',
        library: 'esp',
        path: './dist/',
        filename: 'esp.min.js'
    },
    devtool: 'source-map',
    debug:false,
    module: {
        loaders: [
            {
                loader: "babel-loader",
                include: [
                    path.resolve(__dirname, "src"),
                ],
                test: /\.js$/
            }
        ]
    },
    eslint: {
        configFile: './.eslintrc'
    },
    plugins: [
        new webpack.optimize.UglifyJsPlugin({minimize: true}),
        new webpack.optimize.OccurenceOrderPlugin(true)
    ]
};<|endoftext|>


<commit_before>/*
  CodeMirror
*/

var React = require('react');
var ReactCodeMirror = require('react-codemirror');
require('codemirror/mode/javascript/javascript');
require('../styles/codemirror.css');
require('codemirror/addon/edit/matchbrackets.js');

var CodeMirror = React.createClass({
  getInitialState: function() {
    return {
      code: 'Write your algorithm here.',
      mode: 'javascript'
    }
  },
  handleClick: function() {
    this.props.getCode(this.state.code);
  },

  updateCode: function(newCode) {
    this.setState({
      code: newCode
    });
  },

  render: function() {
    var options = {
      lineNumbers: true,
      mode: 'javascript',
      tabSize: 2,
      matchbrackets: true
    };

    var style = {
      container: {
        textAlign: 'center'
      },
      editor: {
        boxShadow: '0 0.1rem 0.6rem rgba(0, 0, 0, 0.12), 0 0.1rem 0.4rem rgba(0, 0, 0, 0.24)',
        transition: 'all 450ms cubic-bezier(0.23, 1, 0.32, 1) 0ms',
      },
      button: {
        backgroundColor: '#a1a0a0',
        border: '1rem',
        borderRadius: '0.2rem',
        boxShadow: 'rgba(0, 0, 0, 0.117647) 0 0.1rem 0.6rem, rgba(0, 0, 0, 0.239216) 0 0.1rem 0.4rem',
        color: '#fffdfd',
        cursor: 'pointer',
        fontFamily: 'Roboto',
        fontSize: '1.4rem',
        fontWeight: 500,
        lineHeight: '3.6rem',
        marginTop: '1.6rem',
        outline: 'none',
        padding: '0 1.6rem',
        textTransform: 'uppercase',
        transition: 'all 450ms cubic-bezier(0.23, 1, 0.32, 1) 0ms'
      }
    };

    return (

          <ReactCodeMirror ref="editor" value={this.state.code} onChange={this.updateCode} options={options} />

        <button style={style.button} onClick={this.handleClick}>plexIt</button>

    )
  }
});

module.exports = CodeMirror;
<commit_msg>[feature] codemirror bracket closing<commit_after>/*
  CodeMirror
*/

var React = require('react');
var ReactCodeMirror = require('react-codemirror');
require('codemirror/mode/javascript/javascript');
require('codemirror/addon/edit/matchbrackets.js');
require('codemirror/addon/edit/closebrackets.js');
require('../styles/codemirror.css');

var CodeMirror = React.createClass({
  getInitialState: function() {
    return {
      code: 'Write your algorithm here.',
      mode: 'javascript'
    }
  },
  handleClick: function() {
    this.props.getCode(this.state.code);
  },

  updateCode: function(newCode) {
    this.setState({
      code: newCode
    });
  },

  render: function() {
    var options = {
      lineNumbers: true,
      mode: 'javascript',
      tabSize: 2,
      matchBrackets: true,
      autoCloseBrackets: true
    };

    var style = {
      container: {
        textAlign: 'center'
      },
      editor: {
        boxShadow: '0 0.1rem 0.6rem rgba(0, 0, 0, 0.12), 0 0.1rem 0.4rem rgba(0, 0, 0, 0.24)',
        transition: 'all 450ms cubic-bezier(0.23, 1, 0.32, 1) 0ms',
      },
      button: {
        backgroundColor: '#a1a0a0',
        border: '1rem',
        borderRadius: '0.2rem',
        boxShadow: 'rgba(0, 0, 0, 0.117647) 0 0.1rem 0.6rem, rgba(0, 0, 0, 0.239216) 0 0.1rem 0.4rem',
        color: '#fffdfd',
        cursor: 'pointer',
        fontFamily: 'Roboto',
        fontSize: '1.4rem',
        fontWeight: 500,
        lineHeight: '3.6rem',
        marginTop: '1.6rem',
        outline: 'none',
        padding: '0 1.6rem',
        textTransform: 'uppercase',
        transition: 'all 450ms cubic-bezier(0.23, 1, 0.32, 1) 0ms'
      }
    };

    return (

          <ReactCodeMirror ref="editor" value={this.state.code} onChange={this.updateCode} options={options} />

        <button style={style.button} onClick={this.handleClick}>plexIt</button>

    )
  }
});

module.exports = CodeMirror;
<|endoftext|>


<commit_before>"use strict"
/*
    CLI-GUI2 - Command line interface library
    Copyright (C) 2016 Andrew S

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as published
    by the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
module.exports = class search {
    constructor(main, title, opt, c) {
        this.main = main;
        this.vis = main.visual
        this.title = title;
        this.call = c;
        this.options = this.genOptions(opt, c)
        this.results = this.options;
        this.chosen = 0;
        this.text = [];
        this.cursor = 0;

        this.flicker = false;

    }
    init() { // flicker
        this.interval = setInterval(function () {
            this.flicker = !this.flicker
            this.update()

        }.bind(this), 700)
        this.update()
    }
    onRemove() {
        clearInterval(this.interval)
    }
    genOptions(opt, c) { // generate options

        /*
        four methods

        1.

        options: ["a","b","c"]
        call: function(main,choice) {

        2.

        options: ["a","b","c"]
        call: [function(){},function(){},function(){}]

        3.

        options/call: [{name: "a",call: function() {}}]

        4.

        options/call: {a: function() {}}

        final output:

        [
        {
        vis: (something),
        call: (something)
        }

        ]
        */

        var final = [];
        if (!opt[0] || !opt[0].name) { // not object array
            if (!opt[0]) { // Is an object
                for (var i in opt) {
                    final.push({
                        name: i,
                        call: opt[i]
                    })
                }
            } else { // is an array
                for (var i = 0; i < opt.length; i++) {
                    final.push({
                        name: opt[i],
                        call: (typeof c != "function") ? c[i] : null
                    })
                }
            }

        }

        final.forEach((f) => {

            f.vis = this.vis.fill(f.name)
        });
        return final;
    }
    onKey(key) {
        switch (key) {
        case "UP":
            if (this.chosen < 0) return
            this.chosen--;
            break;
        case "DOWN":
            if (this.chosen >= this.options.length - 1) return
            this.chosen++;
            break;
        case "ENTER":
            if (this.chosen == -1) return this.enter();

            this.out(this.results[this.chosen]);
            return
            break;
        case "LEFT":
            if (this.chosen == -1) this.left();
            break;
        case "RIGHT":
            if (this.chosen == -1) this.right();
            break;
        case "BACK":
            if (this.chosen == -1) this.back();
            break;
        case "ESC":
            break;
        default:
            this.chosen = -1
            this.key(key)
            break;
        }
        this.update()
    }
    update() {
        if (!this.vis.getClearance(this)) return;

        var max = this.vis.height - 6;

        var a = 0;
        this.vis.reset();
        this.vis.setRow(0, this.vis.centerHor(this.title));

        a += 3;

        var buf = Math.floor(this.cursor / (this.vis.width - 1)) * (this.vis.width - 1);
        var visible = this.text.slice(buf, buf + this.vis.width - 1).join("");

        if (this.chosen == -1) {
            if (this.flicker) { // add cursor
                var pos = this.cursor - buf;

                var c = visible.slice(pos, pos + 1);
                visible = visible.slice(0, pos) + '\x1b[47m\x1b[30m' + (c ? c : " ") + "\x1b[37m\x1b[40m" + visible.slice(pos + 1);

            }
        } else if (!this.text.length) {
            visible = "\x1b[2mSearch";
        }
        if (this.text.length && this.results[0] && this.text.length < this.results[0].name.length) visible += "\x1b[2m" + this.results[0].name.substr((this.flicker) ? this.text.length + 1 : this.text.length) + "\x1b[0m"

        this.vis.setRow(1, this.vis.fill(visible), "\x1b[40m\x1b[37m");

        var chosen = Math.max(0, this.chosen)

        var counter = Math.floor(chosen / max);
        var len = this.results.length;
        if (counter) this.vis.setRow(a, this.vis.centerHor("▲ ▲ ▲ ▲ ▲"), "\x1b[40m\x1b[37m");
        else this.vis.setRow(a, this.vis.fill(""), "\x1b[40m\x1b[37m");

        var pointer = counter * max
        for (var i = 0; i < max && pointer < len; i++, pointer++) {
            a++;

            if (pointer == this.chosen) {

                this.vis.setRow(a, this.results[pointer].vis, '\x1b[47m\x1b[30m');
            } else
                this.vis.setRow(a, this.results[pointer].vis, "\x1b[40m\x1b[37m");
        }

        //  console.log(pointer, max)
        if (i < max) {
            for (var j = 0; j <= max - i; j++) {

                this.vis.setRow(++a, this.vis.fill(""), "\x1b[40m\x1b[37m");
            }
        }
        if (counter < Math.floor(len / max)) {
            this.vis.setRow(++a, this.vis.centerHor("▼ ▼ ▼ ▼ ▼"), "\x1b[40m\x1b[37m");
        } else {
            this.vis.setRow(++a, this.vis.fill(""), "\x1b[40m\x1b[37m");
        }

        this.vis.update()
    }

    enter() { // enter key
        this.text = this.results[0].name.split("")
        this.out(this.results[0]);
    }
    out(chosen) {
        if (typeof this.call == "function") {
            this.call(this.main, chosen.name)

            return;
        }
        if (chosen.call) {
            chosen.call(this.main)
            return;
        }
    }
    left() { // left key

        if (this.cursor <= 0) return;
        this.flicker = true;
        this.cursor--;
        this.update()
    }
    right() { // right key
        if (this.cursor >= this.text.length) return;
        this.flicker = true;
        this.cursor++;
        this.update()
    }
    back() { // back key
        if (this.cursor <= 0) return;
        this.flicker = true;
        this.text.splice(this.cursor - 1, 1)
        this.cursor--;
        this.results = [];
        var text = this.text.join("")
        if (!text.length) this.results = this.options;
        else
            this.options.forEach((obj) => {

                if (obj.name.substr(0, text.length) == text) this.results.push(obj);
            })
        this.update()
    }
    key(key) { // key
        this.text.splice(this.cursor, 0, key)
        this.flicker = true;
        this.cursor++;
        this.results = [];
        var text = this.text.join("")
        if (!text.length) this.results = this.options;
        else
            this.options.forEach((obj) => {

                if (obj.name.substr(0, text.length) == text) this.results.push(obj);
            })
        this.update()
    }
}<commit_msg>Update<commit_after>"use strict"
/*
    CLI-GUI2 - Command line interface library
    Copyright (C) 2016 Andrew S

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as published
    by the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
module.exports = class search {
    constructor(main, title, opt, c) {
        this.main = main;
        this.vis = main.visual
        this.title = title;
        this.call = c;
        this.options = this.genOptions(opt, c)
        this.results = this.options;
        this.chosen = 0;
        this.text = [];
        this.cursor = 0;

        this.flicker = false;

    }
    init() { // flicker
        this.interval = setInterval(function () {
            this.flicker = !this.flicker
            this.update()

        }.bind(this), 700)
        this.update()
    }
    onRemove() {
        clearInterval(this.interval)
    }
    genOptions(opt, c) { // generate options

        /*
            four methods

            1.

            options: ["a","b","c"]
            call: function(main,choice) {

            2.

            options: ["a","b","c"]
            call: [function(){},function(){},function(){}]

            3.

            options/call: [{name: "a",call: function() {}}]

            4.

            options/call: {a: function() {}}

            final output:

            [
            {
            vis: (something),
            call: (something)
            }

            ]
            */

        var final = [];
        if (!opt[0] || !opt[0].name) { // not object array
            if (!opt[0]) { // Is an object
                for (var i in opt) {
                    final.push({
                        name: i,
                        call: opt[i]
                    })
                }
            } else { // is an array
                for (var i = 0; i < opt.length; i++) {
                    final.push({
                        name: opt[i],
                        call: (typeof c != "function") ? c[i] : null
                    })
                }
            }

        }

        final.forEach((f) => {

            f.vis = this.vis.fill(f.name)
        });
        return final;
    }
    onKey(key) {
        switch (key) {
        case "UP":
            if (this.chosen < 0) return
            this.chosen--;
            break;
        case "DOWN":
            if (this.chosen >= this.options.length - 1) return
            this.chosen++;
            break;
        case "ENTER":
            if (this.chosen == -1) return this.enter();

            this.out(this.results[this.chosen]);
            return
            break;
        case "LEFT":
            if (this.chosen == -1) this.left();
            break;
        case "RIGHT":
            if (this.chosen == -1) this.right();
            break;
        case "BACK":
            if (this.chosen == -1) this.back();
            break;
        case "ESC":
            break;
        default:
            this.chosen = -1
            this.key(key)
            break;
        }
        this.update()
    }
    update() {
        if (!this.vis.getClearance(this)) return;

        var max = this.vis.height - 6;

        var a = 0;
        this.vis.reset();
        this.vis.setRow(0, this.vis.centerHor(this.title));

        a += 3;

        var buf = Math.floor(this.cursor / (this.vis.width - 1)) * (this.vis.width - 1);
        var visible = this.text.slice(buf, buf + this.vis.width - 1).join("");

        if (this.chosen == -1) {
            if (this.flicker) { // add cursor
                var pos = this.cursor - buf;

                var c = visible.slice(pos, pos + 1);
                visible = visible.slice(0, pos) + '\x1b[47m\x1b[30m' + (c ? c : " ") + "\x1b[37m\x1b[40m" + visible.slice(pos + 1);

            }
            if (this.text.length && this.results[0] && this.text.length < this.results[0].name.length) visible += "\x1b[2m" + this.results[0].name.substr((this.flicker) ? this.text.length + 1 : this.text.length) + "\x1b[0m";
        } else if (!this.text.length) {
            visible = "\x1b[2mSearch";
        } else if (this.results[0]) {
            visible = this.results[0].name;
        }

        this.vis.setRow(1, this.vis.fill(visible), "\x1b[40m\x1b[37m");

        this.vis.setRow(2, this.vis.centerHor((this.text.length ? ("Searching for: " + this.text.join("") + ", ") : "") + this.results.length + " Result" + ((this.results.length == 1) ? "" : "s")))

        var chosen = Math.max(0, this.chosen)

        var counter = Math.floor(chosen / max);
        var len = this.results.length;
        if (counter) this.vis.setRow(a, this.vis.centerHor("▲ ▲ ▲ ▲ ▲"), "\x1b[40m\x1b[37m");
        else this.vis.setRow(a, this.vis.fill(""), "\x1b[40m\x1b[37m");

        var pointer = counter * max
        for (var i = 0; i < max && pointer < len; i++, pointer++) {
            a++;

            if (pointer == this.chosen) {

                this.vis.setRow(a, this.results[pointer].vis, '\x1b[47m\x1b[30m');
            } else
                this.vis.setRow(a, this.results[pointer].vis, "\x1b[40m\x1b[37m");
        }

        //  console.log(pointer, max)
        if (i < max) {
            for (var j = 0; j <= max - i; j++) {

                this.vis.setRow(++a, this.vis.fill(""), "\x1b[40m\x1b[37m");
            }
        }
        if (counter < Math.floor(len / max)) {
            this.vis.setRow(++a, this.vis.centerHor("▼ ▼ ▼ ▼ ▼"), "\x1b[40m\x1b[37m");
        } else {
            this.vis.setRow(++a, this.vis.fill(""), "\x1b[40m\x1b[37m");
        }

        this.vis.update()
    }

    enter() { // enter key
        this.text = this.results[0].name.split("")
        this.out(this.results[0]);
    }
    out(chosen) {
        if (typeof this.call == "function") {
            this.call(this.main, chosen.name)

            return;
        }
        if (chosen.call) {
            chosen.call(this.main)
            return;
        }
    }
    left() { // left key

        if (this.cursor <= 0) return;
        this.flicker = true;
        this.cursor--;
        this.update()
    }
    right() { // right key
        if (this.cursor >= this.text.length) return;
        this.flicker = true;
        this.cursor++;
        this.update()
    }
    back() { // back key
        if (this.cursor <= 0) return;
        this.flicker = true;
        this.text.splice(this.cursor - 1, 1)
        this.cursor--;
        this.results = [];
        var text = this.text.join("")
        if (!text.length) this.results = this.options;
        else
            this.options.forEach((obj) => {

                if (obj.name.substr(0, text.length) == text) this.results.push(obj);
            })
        this.update()
    }
    key(key) { // key
        this.text.splice(this.cursor, 0, key)
        this.flicker = true;
        this.cursor++;
        this.results = [];
        var text = this.text.join("")
        if (!text.length) this.results = this.options;
        else
            this.options.forEach((obj) => {

                if (obj.name.substr(0, text.length) == text) this.results.push(obj);
            })
        this.update()
    }
}<|endoftext|>


<commit_before>import { join } from 'path'
import { existsSync } from 'fs'
import { createElement } from 'react'
import { renderToString, renderToStaticMarkup } from 'react-dom/server'
import send from 'send'
import generateETag from 'etag'
import fresh from 'fresh'
import requireModule from './require'
import getConfig from './config'
import resolvePath from './resolve'
import { Router } from '../lib/router'
import { loadGetInitialProps } from '../lib/utils'
import Head, { defaultHead } from '../lib/head'
import App from '../lib/app'
import ErrorDebug from '../lib/error-debug'
import { flushChunks } from '../lib/dynamic'
import xssFilters from 'xss-filters'

export async function render (req, res, pathname, query, opts) {
  const html = await renderToHTML(req, res, pathname, opts)
  sendHTML(req, res, html, req.method, opts)
}

export function renderToHTML (req, res, pathname, query, opts) {
  return doRender(req, res, pathname, query, opts)
}

export async function renderError (err, req, res, pathname, query, opts) {
  const html = await renderErrorToHTML(err, req, res, query, opts)
  sendHTML(req, res, html, req.method, opts)
}

export function renderErrorToHTML (err, req, res, pathname, query, opts = {}) {
  return doRender(req, res, pathname, query, { ...opts, err, page: '_error' })
}

async function doRender (req, res, pathname, query, {
  err,
  page,
  buildId,
  buildStats,
  hotReloader,
  assetPrefix,
  availableChunks,
  dir = process.cwd(),
  dev = false,
  staticMarkup = false,
  nextExport = false
} = {}) {
  page = page || pathname

  await ensurePage(page, { dir, hotReloader })

  const dist = getConfig(dir).distDir

  let [Component, Document] = await Promise.all([
    requireModule(join(dir, dist, 'dist', 'pages', page)),
    requireModule(join(dir, dist, 'dist', 'pages', '_document'))
  ])
  Component = Component.default || Component
  Document = Document.default || Document
  const asPath = req.url
  const ctx = { err, req, res, pathname, query, asPath }
  const props = await loadGetInitialProps(Component, ctx)

  // the response might be finshed on the getinitialprops call
  if (res.finished) return

  const renderPage = () => {
    const app = createElement(App, {
      Component,
      props,
      router: new Router(pathname, query)
    })

    const render = staticMarkup ? renderToStaticMarkup : renderToString

    let html
    let head
    let errorHtml = ''
    try {
      html = render(app)
    } finally {
      head = Head.rewind() || defaultHead()
    }
    const chunks = loadChunks({ dev, dir, dist, availableChunks })

    if (err && dev) {
      errorHtml = render(createElement(ErrorDebug, { error: err }))
    }

    return { html, head, errorHtml, chunks }
  }

  const docProps = await loadGetInitialProps(Document, { ...ctx, renderPage })
  // While developing, we should not cache any assets.
  // So, we use a different buildId for each page load.
  // With that we can ensure, we have unique URL for assets per every page load.
  // So, it'll prevent issues like this: https://git.io/vHLtb
  const devBuildId = Date.now()

  if (res.finished) return

  if (!Document.prototype || !Document.prototype.isReactComponent) throw new Error('_document.js is not exporting a React element')
  const doc = createElement(Document, {
    __NEXT_DATA__: {
      props,
      pathname,
      query,
      buildId: dev ? devBuildId : buildId,
      buildStats,
      assetPrefix,
      nextExport,
      err: (err) ? serializeError(dev, err) : null
    },
    dev,
    dir,
    staticMarkup,
    ...docProps
  })

  return '<!DOCTYPE html>' + renderToStaticMarkup(doc)
}

export async function renderScript (req, res, page, opts) {
  try {
    const dist = getConfig(opts.dir).distDir
    const path = join(opts.dir, dist, 'bundles', 'pages', page)
    const realPath = await resolvePath(path)
    await serveStatic(req, res, realPath)
  } catch (err) {
    if (err.code === 'ENOENT') {
      renderScriptError(req, res, page, err, {}, opts)
      return
    }

    throw err
  }
}

export async function renderScriptError (req, res, page, error, customFields, opts) {
  // Asks CDNs and others to not to cache the errored page
  res.setHeader('Cache-Control', 'no-store, must-revalidate')
  // prevent XSS attacks by filtering the page before printing it.
  page = xssFilters.uriInSingleQuotedAttr(page)

  if (error.code === 'ENOENT') {
    res.setHeader('Content-Type', 'text/javascript')
    res.end(`
      window.__NEXT_REGISTER_PAGE('${page}', function() {
        var error = new Error('Page does not exist: ${page}')
        error.statusCode = 404

        return { error: error }
      })
    `)
    return
  }

  res.setHeader('Content-Type', 'text/javascript')
  const errorJson = {
    ...errorToJSON(error),
    ...customFields
  }

  res.end(`
    window.__NEXT_REGISTER_PAGE('${page}', function() {
      var error = ${JSON.stringify(errorJson)}
      return { error: error }
    })
  `)
}

export function sendHTML (req, res, html, method, { dev }) {
  if (res.finished) return
  const etag = generateETag(html)

  if (fresh(req.headers, { etag })) {
    res.statusCode = 304
    res.end()
    return
  }

  if (dev) {
    // In dev, we should not cache pages for any reason.
    // That's why we do this.
    res.setHeader('Cache-Control', 'no-store, must-revalidate')
  }

  res.setHeader('ETag', etag)
  res.setHeader('Content-Type', 'text/html')
  res.setHeader('Content-Length', Buffer.byteLength(html))
  res.end(method === 'HEAD' ? null : html)
}

export function sendJSON (res, obj, method) {
  if (res.finished) return

  const json = JSON.stringify(obj)
  res.setHeader('Content-Type', 'application/json')
  res.setHeader('Content-Length', Buffer.byteLength(json))
  res.end(method === 'HEAD' ? null : json)
}

function errorToJSON (err) {
  const { name, message, stack } = err
  const json = { name, message, stack }

  if (err.module) {
    // rawRequest contains the filename of the module which has the error.
    const { rawRequest } = err.module
    json.module = { rawRequest }
  }

  return json
}

function serializeError (dev, err) {
  if (dev) {
    return errorToJSON(err)
  }

  return { message: '500 - Internal Server Error.' }
}

export function serveStatic (req, res, path) {
  return new Promise((resolve, reject) => {
    send(req, path)
    .on('directory', () => {
      // We don't allow directories to be read.
      const err = new Error('No directory access')
      err.code = 'ENOENT'
      reject(err)
    })
    .on('error', reject)
    .pipe(res)
    .on('finish', resolve)
  })
}

async function ensurePage (page, { dir, hotReloader }) {
  if (!hotReloader) return
  if (page === '_error' || page === '_document') return

  await hotReloader.ensurePage(page)
}

function loadChunks ({ dev, dir, dist, availableChunks }) {
  const flushedChunks = flushChunks()
  const validChunks = []

  for (var chunk of flushedChunks) {
    const filename = join(dir, dist, 'chunks', chunk)
    const exists = dev ? existsSync(filename) : availableChunks[chunk]
    if (exists) {
      validChunks.push(chunk)
    }
  }

  return validChunks
}
<commit_msg>Fix #2353 - server render parameters (#2356)<commit_after>import { join } from 'path'
import { existsSync } from 'fs'
import { createElement } from 'react'
import { renderToString, renderToStaticMarkup } from 'react-dom/server'
import send from 'send'
import generateETag from 'etag'
import fresh from 'fresh'
import requireModule from './require'
import getConfig from './config'
import resolvePath from './resolve'
import { Router } from '../lib/router'
import { loadGetInitialProps } from '../lib/utils'
import Head, { defaultHead } from '../lib/head'
import App from '../lib/app'
import ErrorDebug from '../lib/error-debug'
import { flushChunks } from '../lib/dynamic'
import xssFilters from 'xss-filters'

export async function render (req, res, pathname, query, opts) {
  const html = await renderToHTML(req, res, pathname, query, opts)
  sendHTML(req, res, html, req.method, opts)
}

export function renderToHTML (req, res, pathname, query, opts) {
  return doRender(req, res, pathname, query, opts)
}

export async function renderError (err, req, res, pathname, query, opts) {
  const html = await renderErrorToHTML(err, req, res, query, opts)
  sendHTML(req, res, html, req.method, opts)
}

export function renderErrorToHTML (err, req, res, pathname, query, opts = {}) {
  return doRender(req, res, pathname, query, { ...opts, err, page: '_error' })
}

async function doRender (req, res, pathname, query, {
  err,
  page,
  buildId,
  buildStats,
  hotReloader,
  assetPrefix,
  availableChunks,
  dir = process.cwd(),
  dev = false,
  staticMarkup = false,
  nextExport = false
} = {}) {
  page = page || pathname

  await ensurePage(page, { dir, hotReloader })

  const dist = getConfig(dir).distDir

  let [Component, Document] = await Promise.all([
    requireModule(join(dir, dist, 'dist', 'pages', page)),
    requireModule(join(dir, dist, 'dist', 'pages', '_document'))
  ])
  Component = Component.default || Component
  Document = Document.default || Document
  const asPath = req.url
  const ctx = { err, req, res, pathname, query, asPath }
  const props = await loadGetInitialProps(Component, ctx)

  // the response might be finshed on the getinitialprops call
  if (res.finished) return

  const renderPage = () => {
    const app = createElement(App, {
      Component,
      props,
      router: new Router(pathname, query)
    })

    const render = staticMarkup ? renderToStaticMarkup : renderToString

    let html
    let head
    let errorHtml = ''
    try {
      html = render(app)
    } finally {
      head = Head.rewind() || defaultHead()
    }
    const chunks = loadChunks({ dev, dir, dist, availableChunks })

    if (err && dev) {
      errorHtml = render(createElement(ErrorDebug, { error: err }))
    }

    return { html, head, errorHtml, chunks }
  }

  const docProps = await loadGetInitialProps(Document, { ...ctx, renderPage })
  // While developing, we should not cache any assets.
  // So, we use a different buildId for each page load.
  // With that we can ensure, we have unique URL for assets per every page load.
  // So, it'll prevent issues like this: https://git.io/vHLtb
  const devBuildId = Date.now()

  if (res.finished) return

  if (!Document.prototype || !Document.prototype.isReactComponent) throw new Error('_document.js is not exporting a React element')
  const doc = createElement(Document, {
    __NEXT_DATA__: {
      props,
      pathname,
      query,
      buildId: dev ? devBuildId : buildId,
      buildStats,
      assetPrefix,
      nextExport,
      err: (err) ? serializeError(dev, err) : null
    },
    dev,
    dir,
    staticMarkup,
    ...docProps
  })

  return '<!DOCTYPE html>' + renderToStaticMarkup(doc)
}

export async function renderScript (req, res, page, opts) {
  try {
    const dist = getConfig(opts.dir).distDir
    const path = join(opts.dir, dist, 'bundles', 'pages', page)
    const realPath = await resolvePath(path)
    await serveStatic(req, res, realPath)
  } catch (err) {
    if (err.code === 'ENOENT') {
      renderScriptError(req, res, page, err, {}, opts)
      return
    }

    throw err
  }
}

export async function renderScriptError (req, res, page, error, customFields, opts) {
  // Asks CDNs and others to not to cache the errored page
  res.setHeader('Cache-Control', 'no-store, must-revalidate')
  // prevent XSS attacks by filtering the page before printing it.
  page = xssFilters.uriInSingleQuotedAttr(page)

  if (error.code === 'ENOENT') {
    res.setHeader('Content-Type', 'text/javascript')
    res.end(`
      window.__NEXT_REGISTER_PAGE('${page}', function() {
        var error = new Error('Page does not exist: ${page}')
        error.statusCode = 404

        return { error: error }
      })
    `)
    return
  }

  res.setHeader('Content-Type', 'text/javascript')
  const errorJson = {
    ...errorToJSON(error),
    ...customFields
  }

  res.end(`
    window.__NEXT_REGISTER_PAGE('${page}', function() {
      var error = ${JSON.stringify(errorJson)}
      return { error: error }
    })
  `)
}

export function sendHTML (req, res, html, method, { dev }) {
  if (res.finished) return
  const etag = generateETag(html)

  if (fresh(req.headers, { etag })) {
    res.statusCode = 304
    res.end()
    return
  }

  if (dev) {
    // In dev, we should not cache pages for any reason.
    // That's why we do this.
    res.setHeader('Cache-Control', 'no-store, must-revalidate')
  }

  res.setHeader('ETag', etag)
  res.setHeader('Content-Type', 'text/html')
  res.setHeader('Content-Length', Buffer.byteLength(html))
  res.end(method === 'HEAD' ? null : html)
}

export function sendJSON (res, obj, method) {
  if (res.finished) return

  const json = JSON.stringify(obj)
  res.setHeader('Content-Type', 'application/json')
  res.setHeader('Content-Length', Buffer.byteLength(json))
  res.end(method === 'HEAD' ? null : json)
}

function errorToJSON (err) {
  const { name, message, stack } = err
  const json = { name, message, stack }

  if (err.module) {
    // rawRequest contains the filename of the module which has the error.
    const { rawRequest } = err.module
    json.module = { rawRequest }
  }

  return json
}

function serializeError (dev, err) {
  if (dev) {
    return errorToJSON(err)
  }

  return { message: '500 - Internal Server Error.' }
}

export function serveStatic (req, res, path) {
  return new Promise((resolve, reject) => {
    send(req, path)
    .on('directory', () => {
      // We don't allow directories to be read.
      const err = new Error('No directory access')
      err.code = 'ENOENT'
      reject(err)
    })
    .on('error', reject)
    .pipe(res)
    .on('finish', resolve)
  })
}

async function ensurePage (page, { dir, hotReloader }) {
  if (!hotReloader) return
  if (page === '_error' || page === '_document') return

  await hotReloader.ensurePage(page)
}

function loadChunks ({ dev, dir, dist, availableChunks }) {
  const flushedChunks = flushChunks()
  const validChunks = []

  for (var chunk of flushedChunks) {
    const filename = join(dir, dist, 'chunks', chunk)
    const exists = dev ? existsSync(filename) : availableChunks[chunk]
    if (exists) {
      validChunks.push(chunk)
    }
  }

  return validChunks
}
<|endoftext|>


<commit_before>'use strict';

var gulp = require('gulp'),
    postcss = require('gulp-postcss'),
    autoprefixer = require('autoprefixer');

function postcssTask(options){
  gulp.task('postcss', function () {
    var processors = [
      autoprefixer({
        browsers: [
          'Android',
          'ChromeAndroid',
          'FirefoxAndroid',
          'Opera Mini',
          'Chrome',
          'Firefox',
          'Explorer',
          'Edge',
          'iOS',
          'Opera',
          'Safari',
          'ExplorerMobile'
        ]
      })
    ];

    return gulp.src(options.workingDir + '/*.css')
      .pipe(postcss(processors))
      .pipe(gulp.dest(options.workingDir));
  });
}

module.exports = postcssTask;<commit_msg>Simplify postcss task #12, #11<commit_after>'use strict';

var gulp = require('gulp'),
    postcss = require('gulp-postcss'),
    autoprefixer = require('autoprefixer');

function postcssTask(options){
  gulp.task('postcss', ['dupe'], function () {
    var processors = [
      autoprefixer({
        browsers: ['last 5 versions']
      })
    ];

    return gulp.src(options.workingDir + '/**/*.css')
      .pipe(postcss(processors))
      .pipe(gulp.dest(options.workingDir));
  });
}

module.exports = postcssTask;<|endoftext|>


<commit_before>// var context = require.context('./app/scripts/tests', true, /Test\.(js|jsx)$/);
var context = require.context('./app/scripts/tests', true, /BlockTest\.jsx/);

beforeEach(() => {
  sandbox = sinon.sandbox.create()
  requests = []
  xhr = sinon.useFakeXMLHttpRequest()
  xhr.onCreate = function (req) { requests.push(req); }
})

afterEach(() => {
  xhr.restore()
  sandbox.restore()
})

context.keys().forEach(context);
<commit_msg>Apply linter fixed in webpack.test.config.js<commit_after>const context = require.context('./app/scripts/tests', true, /Test\.(js|jsx)$/)

beforeEach(() => {
  sandbox = sinon.sandbox.create()
  requests = []
  xhr = sinon.useFakeXMLHttpRequest()
  xhr.onCreate = function(req) { requests.push(req) }
})

afterEach(() => {
  xhr.restore()
  sandbox.restore()
})

context.keys().forEach(context)
<|endoftext|>


<commit_before>// an idle game featuring automata and fun

/* TODO:
   - pause button
   - "offline" mode catchup is different?
   - fractional respect via Math.random()
   - show buttons / upgrades as they're available
   - different places for button vs upgrades
   - upgrades:
     - per archetype: increase life, increase ducats, increase rate, % increase to children cumulative
     - overall: increate rate, add new game features (capital letters, new letters, symbols, fancy backgrounds (day/night), fertility bonus (w/ fancy highlight for good squares), ...)

*/
var w = 80
var h = 40
var grid = []
var ducats = 1
var paused = false
var last_ms = 0
var last_max = 1
var upgrades = {}
var base_rate = 1000
var base_life = 3
var archetypes = {}
var base_ducats = 1
var render_rate = 100
var current_symbol = 'a'
var el_grid, el_ducats, el_buttons, el_upgrades

function tick() {
  // TODO: poke the cells in arbitrary order
  for(var i=0; i < w*h; i++) {
    var cell = grid[i]
    if(!cell.handler) continue
    run(cell, 'go')
    if(cell.life <= 0) {
      run(cell, 'post')
      spawn(cell, " ")
    }
  }
}

function tock(n) {
  // do n ticks
  for(var i=0; i < n; i++) {
    tick()
  }

  // update last tock
  last_ms = Date.now()
}

function time_to_tock(ms) {
  // when was our last tock?
  var diff = ms - last_ms

  // how many ticks should we do?
  // TODO: this doesn't work for ticks < 1
  var ticks = fancy_round(diff / base_rate)

  // call tock
  tock(ticks)
}

function loop() {
  if(paused) return
  var time = Date.now()         // get current time
  time_to_tock(time)
  render()
}

function looper() {
  loop()
  // window.requestAnimationFrame(looper)
  window.setTimeout(looper, Math.max(base_rate, render_rate))
}

function set_type(cell, type) {
  cell.char = type
  cell.handler = archetypes[type]
}

function add_nabes(cell, index) {
  cell.nabes.push(get_torus_cell(index, 0, -1))
  cell.nabes.push(get_torus_cell(index, 0,  1))
  cell.nabes.push(get_torus_cell(index, -1, 0))
  cell.nabes.push(get_torus_cell(index,  1, 0))
}

function add_diags(cell, index) {
  cell.diags.push(get_torus_cell(index, 1,  -1))
  cell.diags.push(get_torus_cell(index, 1,   1))
  cell.diags.push(get_torus_cell(index, -1,  1))
  cell.diags.push(get_torus_cell(index, -1, -1))
}

function get_torus_cell(index, dx, dy) {
  if(dx == -1 && index%w == 0)
    dx = w-1
  if(dx ==  1 && index%w == w-1)
    dx = 1-w

  if(dy == -1 && index < w)
    dy = h-1
  if(dy ==  1 && index > w*(h-1))
    dy = 1-h

  return grid[index + dx + dy*w]
}

function build_archetypes() {
  // a-z basic countdown (?)
  charloop('a', 'z', function(char) {
    // create the basic archetype
    archetypes[char] = clone(archetype_archetype)
    archetypes[char].price = Math.pow(4, char.charCodeAt() - 97)

    // set 'life' for cell
    // THINK: different types should have different amounts of life
    addhand(char, 'init', function(cell) {
      cell.life = fancy_round(base_life)
    })
  })

  // a adds $ each tick and on revert
  addhand('a', 'go', function(cell) {
    ducats += fancy_round(base_ducats)
    cell.life -= 1
  })

  addhand('a', 'post', function(cell) {
    ducats += fancy_round(base_ducats)
  })

  // b-z spawn N times then revert
  charloop('b', 'z', function(char) {
    addhand(char, 'go', function(cell) {
      var free = find_free_nabe(cell)
      if(!free) return
      cell.life -= 1
      spawn(free, String.fromCharCode(char.charCodeAt() - 1))
    })
  })
}

function build_upgrades() {
  upgrades =
    { "go_faster": { price: 10
                   , effect() { base_rate /= 2 }}
    , "more_fast": { price: 1000
                   , effect() { base_rate /= 2 }}
    , "even_fast": { price: 100000
                   , effect() { base_rate /= 2 }}
    , "good_fast": { price: 1000000
                   , effect() { base_rate /= 2 }}
    , "keen_fast": { price: 1000000
                   , effect() { base_rate /= 2 }}
    , "more_life": { price: 10000
                   , effect() { base_life *= 1.3 }}
    , "even_life": { price: 1000000
                   , effect() { base_life *= 1.3 }}
    , "good_life": { price: 100000000
                   , effect() { base_life *= 1.3 }}
    , "more_gold": { price: 10000
                   , effect() { base_ducats *= 1.3 }}
    }
}

function find_free_nabe(cell) {
  // TODO: check in arbitrary order
  var nabe
  var dims = [[0, 1], [0, -1], [1, 0], [-1, 0]]
  // var str  = '012302130132'
  // var offset = rand(8)
  // var indices = str.substr(offset, 4).split('')

  for(var i=0; i < 4; i++) {
    var index = rand(3)
    nabe = get_torus_cell(cell.index, dims[index][0], dims[index][1])
    if(nabe && nabe.char == " ") return nabe
  }
  // nabe = get_torus_cell(cell.index, 0,  1)
  // if(nabe && nabe.char == " ") return nabe
  // nabe = get_torus_cell(cell.index, 0, -1)
  // if(nabe && nabe.char == " ") return nabe
  // nabe = get_torus_cell(cell.index, 1,  0)
  // if(nabe && nabe.char == " ") return nabe
  // nabe = get_torus_cell(cell.index, -1, 0)
  // if(nabe && nabe.char == " ") return nabe
  return false
}

function run(cell, handler) {
  if(!cell.handler) return
  var hands = cell.handler[handler]
  var len = hands.length
  for(var i=0; i<len; i++)
    hands[i](cell)
}

function spawn(cell, char) {
  cell.char = char
  cell.handler = archetypes[char]
  run(cell, 'init')
}

function new_cell(i) {
  return { char: " "
         , handler: false
         , life: 0
         , index: i
         , nabes: []
         , diags: []
         // , skip: 0
         }
}

var archetype_archetype = { init: []
                          , pre:  []
                          , go:   []
                          , post: []
                          , price: 1
                          }

function addhand(char, handle, fun) {
  archetypes[char][handle].push(fun)
}

// init

function init() {
  // set up grid
  for(var i=0; i < w*h; i++) {
    grid[i] = new_cell(i)
  }

  // set up stats
  last_ms = Date.now()
  var el = document.getElementById.bind(document)
  el_grid = el('grid')
  el_ducats = el('ducats')
  el_buttons = el('buttons')
  el_upgrades = el('upgrades')

  // set up nabes and diags
  for(var i=0; i < w*h; i++) {
    add_nabes(grid[i], i)
    add_diags(grid[i], i)
  }

  // event bindings
  el_buttons.addEventListener('click', function(ev) {
    current_symbol = ev.target.id
    remove_class('#buttons span', 'selected')
    el(current_symbol).classList.add('selected')
  })

  el_upgrades.addEventListener('click', function(ev) {
    var upgrade = upgrades[ev.target.id]
    if(buy_upgrade(upgrade))
      el(ev.target.id).style.display = 'none'
  })

  el_grid.addEventListener('click', function(ev) {
    var index = click_to_index(ev)
    set_symbol(index, current_symbol)
  })

  build_archetypes()
  build_upgrades()
  looper()
}

function remove_class(query, classname) {
  var q = document.querySelectorAll(query)
  ;[].slice.call(q).forEach(function(el) {el.classList.remove(classname)})
}

function buy_upgrade(upgrade) {
  var price = upgrade.price
  if(price > ducats) {
    log("you can't afford that upgrade!")
    return false
  }

  upgrade.effect()
  return true
}

// buttons

function click_to_index(ev) {
  var sel = window.getSelection()
  // console.log(selection.focusNode.data[selection.focusOffset]);
  var index = sel.focusOffset
  return Math.max(0, index - 1 - Math.floor(index / w)) // remove newlines
}

function set_symbol(index, char) {
  // check ducats
  var price = archetypes[char].price
  if(price > ducats) {
    log("you can't afford that!")
    return
  }

  // THINK: check current symbol is lesser?

  spawn(grid[index], char)
  ducats -= price
}

// renderer

function render() {
  render_cells()
  render_stats()
  render_price_stuff()
}

function render_cells() {
  el_grid.textContent = grid_to_string()
}

function render_stats() {
  el_ducats.textContent = ducats
}

function render_price_stuff() {
  if(ducats <= last_max) return

  var new_button  = get_new_buttons (last_max, ducats)
  var new_upgrade = get_new_upgrades(last_max, ducats)

  last_max = ducats
  if(!new_button && !new_upgrade) return

  // TODO: innerHTML is kind of a drag
  el_buttons .innerHTML = new_button  + el_buttons .innerHTML
  el_upgrades.innerHTML = new_upgrade + el_upgrades.innerHTML
}

function grid_to_string() {
  var str = ""
  for(var i=0; i < w*h; i++) {
    if(i%w == 0) str += "\n"
    str += grid[i].char
  }
  return str
}

function get_new_buttons(low, high) {
  var str = ''
  for(var char in archetypes) {
    var price = archetypes[char].price
    if(low < price && price <= high)
      str = str_to_button(char) + str
  }
  return str
}

function get_new_upgrades(low, high) {
  var str = ''
  for(var name in upgrades) {
    var price = upgrades[name].price
    if(low < price && price <= high)
      str = str_to_button(name) + str
  }
  return str
}

function str_to_button(str) {
  return '' + str + ''
}

// helpers

function charloop(from, last, fun) {
  var from_ord = from.charCodeAt()
  var last_ord = last.charCodeAt()
  var args = [].slice.call(arguments, 3)

  for(var i=from_ord; i <= last_ord; i++)
    fun(String.fromCharCode(i), args)
}

function fancy_round(x) {
  return (x|0) + (Math.random() < x%1)
}

function rand(n) {
  return Math.floor(Math.random() * (n + 1))
}

function noop() {}

function clone(obj) {
  try {
    return JSON.parse(JSON.stringify(obj))
  } catch(e) {
    log(e)
    return {}
  }
}

function log(x) {
  console.log(x)
}

init()
<commit_msg>better comments<commit_after>// an idle game featuring automata and fun

/* TODO:
   - pause button
   - "offline" mode catchup is different?
   - fractional respect via Math.random()
   - show buttons / upgrades as they're available
   - different places for button vs upgrades
   - upgrades:
     - per archetype: increase life, increase ducats, increase rate, % increase to children cumulative
     - overall: increate rate, add new game features (capital letters, new letters, symbols, fancy backgrounds (day/night), fertility bonus (w/ fancy highlight for good squares), ...)

*/
var w = 80
var h = 40
var grid = []
var ducats = 1
var paused = false
var last_ms = 0
var last_max = 1
var upgrades = {}
var base_rate = 1000
var base_life = 3
var archetypes = {}
var base_ducats = 1
var render_rate = 100
var current_symbol = 'a'
var el_grid, el_ducats, el_buttons, el_upgrades

function tick() {
  // TODO: poke the cells in arbitrary order
  for(var i=0; i < w*h; i++) {
    var cell = grid[i]
    if(!cell.handler) continue
    run(cell, 'go')
    if(cell.life <= 0) {
      run(cell, 'post')
      spawn(cell, " ")
    }
  }
}

function tock(n) {
  // do n ticks
  for(var i=0; i < n; i++) {
    tick()
  }

  // update last tock
  last_ms = Date.now()
}

function time_to_tock(ms) {
  // when was our last tock?
  var diff = ms - last_ms

  // how many ticks should we do?
  // TODO: this doesn't work for ticks < 1
  var ticks = fancy_round(diff / base_rate)

  // call tock
  tock(ticks)
}

function loop() {
  if(paused) return
  var time = Date.now()         // get current time
  time_to_tock(time)
  render()
}

function looper() {
  loop()
  // window.requestAnimationFrame(looper)
  window.setTimeout(looper, Math.max(base_rate, render_rate))
}

function set_type(cell, type) {
  cell.char = type
  cell.handler = archetypes[type]
}

function add_nabes(cell, index) {
  cell.nabes.push(get_torus_cell(index, 0, -1))
  cell.nabes.push(get_torus_cell(index, 0,  1))
  cell.nabes.push(get_torus_cell(index, -1, 0))
  cell.nabes.push(get_torus_cell(index,  1, 0))
}

function add_diags(cell, index) {
  cell.diags.push(get_torus_cell(index, 1,  -1))
  cell.diags.push(get_torus_cell(index, 1,   1))
  cell.diags.push(get_torus_cell(index, -1,  1))
  cell.diags.push(get_torus_cell(index, -1, -1))
}

function get_torus_cell(index, dx, dy) {
  if(dx == -1 && index%w == 0)
    dx = w-1
  if(dx ==  1 && index%w == w-1)
    dx = 1-w

  if(dy == -1 && index < w)
    dy = h-1
  if(dy ==  1 && index > w*(h-1))
    dy = 1-h

  return grid[index + dx + dy*w]
}

function build_archetypes() {
  // a-z basic countdown (?)
  charloop('a', 'z', function(char) {
    // create the basic archetype
    archetypes[char] = clone(archetype_archetype)
    archetypes[char].price = Math.pow(4, char.charCodeAt() - 97)

    // set 'life' for cell
    // THINK: different types should have different amounts of life
    addhand(char, 'init', function(cell) {
      cell.life = fancy_round(base_life)
    })
  })

  // a adds $ each tick and on revert
  addhand('a', 'go', function(cell) {
    ducats += fancy_round(base_ducats)
    cell.life -= 1
  })

  addhand('a', 'post', function(cell) {
    ducats += fancy_round(base_ducats)
  })

  // b-z spawn N times then revert
  charloop('b', 'z', function(char) {
    addhand(char, 'go', function(cell) {
      var free = find_free_nabe(cell)
      if(!free) return
      cell.life -= 1
      spawn(free, String.fromCharCode(char.charCodeAt() - 1))
    })
  })
}

function build_upgrades() {
  upgrades =
    { "go_faster": { price: 10
                   , effect() { base_rate /= 2 }}
    , "more_fast": { price: 1000
                   , effect() { base_rate /= 2 }}
    , "even_fast": { price: 100000
                   , effect() { base_rate /= 2 }}
    , "good_fast": { price: 1000000
                   , effect() { base_rate /= 2 }}
    , "keen_fast": { price: 1000000
                   , effect() { base_rate /= 2 }}
    , "more_life": { price: 10000
                   , effect() { base_life *= 1.3 }}
    , "even_life": { price: 1000000
                   , effect() { base_life *= 1.3 }}
    , "good_life": { price: 100000000
                   , effect() { base_life *= 1.3 }}
    , "more_gold": { price: 10000
                   , effect() { base_ducats *= 1.3 }}
    }
}

function find_free_nabe(cell) {
  // TODO: check in arbitrary order
  var nabe
  var dims = [[0, 1], [0, -1], [1, 0], [-1, 0]]
  // var str  = '012302130132'
  // var offset = rand(8)
  // var indices = str.substr(offset, 4).split('')

  for(var i=0; i < 4; i++) {
    var index = rand(3)
    nabe = get_torus_cell(cell.index, dims[index][0], dims[index][1])
    if(nabe && nabe.char == " ") return nabe
  }
  // nabe = get_torus_cell(cell.index, 0,  1)
  // if(nabe && nabe.char == " ") return nabe
  // nabe = get_torus_cell(cell.index, 0, -1)
  // if(nabe && nabe.char == " ") return nabe
  // nabe = get_torus_cell(cell.index, 1,  0)
  // if(nabe && nabe.char == " ") return nabe
  // nabe = get_torus_cell(cell.index, -1, 0)
  // if(nabe && nabe.char == " ") return nabe
  return false
}

function run(cell, handler) {
  if(!cell.handler) return
  var hands = cell.handler[handler]
  var len = hands.length
  for(var i=0; i<len; i++)
    hands[i](cell)
}

function spawn(cell, char) {
  cell.char = char
  cell.handler = archetypes[char]
  run(cell, 'init')
}

function new_cell(i) {
  return { char: " "
         , handler: false
         , life: 0
         , index: i
         , nabes: []
         , diags: []
         // , skip: 0
         }
}

var archetype_archetype = { init: []
                          , pre:  []
                          , go:   []
                          , post: []
                          , price: 1
                          }

function addhand(char, handle, fun) {
  archetypes[char][handle].push(fun)
}

// init

function init() {
  // set up grid
  for(var i=0; i < w*h; i++) {
    grid[i] = new_cell(i)
  }

  // set up stats
  last_ms = Date.now()
  var el = document.getElementById.bind(document)
  el_grid = el('grid')
  el_ducats = el('ducats')
  el_buttons = el('buttons')
  el_upgrades = el('upgrades')

  // set up nabes and diags
  for(var i=0; i < w*h; i++) {
    add_nabes(grid[i], i)
    add_diags(grid[i], i)
  }

  // event bindings
  el_buttons.addEventListener('click', function(ev) {
    current_symbol = ev.target.id
    remove_class('#buttons span', 'selected')
    el(current_symbol).classList.add('selected')
  })

  el_upgrades.addEventListener('click', function(ev) {
    var upgrade = upgrades[ev.target.id]
    if(buy_upgrade(upgrade))
      el(ev.target.id).style.display = 'none'
  })

  el_grid.addEventListener('click', function(ev) {
    var index = click_to_index(ev)
    set_symbol(index, current_symbol)
  })

  build_archetypes()
  build_upgrades()
  looper()
}

function remove_class(query, classname) {
  var q = document.querySelectorAll(query)
  ;[].slice.call(q).forEach(function(el) {el.classList.remove(classname)})
}

function buy_upgrade(upgrade) {
  var price = upgrade.price
  if(price > ducats) {
    log("you can't afford that upgrade!")
    return false
  }

  upgrade.effect()
  return true
}

// buttons

function click_to_index(ev) {
  var sel = window.getSelection()
  // console.log(selection.focusNode.data[selection.focusOffset]);
  var index = sel.focusOffset
  return Math.max(0, index - 1 - Math.floor(index / w)) // remove newlines
}

function set_symbol(index, char) {
  // check ducats
  var price = archetypes[char].price
  if(price > ducats) {
    log("you can't afford that!")
    return
  }

  // THINK: check current symbol is lesser?

  spawn(grid[index], char)
  ducats -= price
}

// renderer

function render() {
  render_cells()
  render_stats()
  render_price_stuff()
}

function render_cells() {
  el_grid.textContent = grid_to_string()
}

function render_stats() {
  el_ducats.textContent = ducats
}

function render_price_stuff() {
  if(ducats <= last_max) return

  var new_button  = get_new_buttons (last_max, ducats)
  var new_upgrade = get_new_upgrades(last_max, ducats)

  last_max = ducats
  if(!new_button && !new_upgrade) return

  // TODO: innerHTML is kind of a drag
  el_buttons .innerHTML = new_button  + el_buttons .innerHTML
  el_upgrades.innerHTML = new_upgrade + el_upgrades.innerHTML
}

function grid_to_string() {
  var str = ""
  for(var i=0; i < w*h; i++) {
    if(i%w == 0) str += "\n"
    str += grid[i].char
  }
  return str
}

function get_new_buttons(low, high) {
  var str = ''
  for(var char in archetypes) {
    var price = archetypes[char].price
    if(low < price && price <= high)
      str = str_to_button(char) + str
  }
  return str
}

function get_new_upgrades(low, high) {
  var str = ''
  for(var name in upgrades) {
    var price = upgrades[name].price
    if(low < price && price <= high)
      str = str_to_button(name) + str
  }
  return str
}

function str_to_button(str) {
  return '' + str + ''
}

// helpers

function charloop(from, last, fun) {
  var from_ord = from.charCodeAt()
  var last_ord = last.charCodeAt()
  var args = [].slice.call(arguments, 3)

  for(var i=from_ord; i <= last_ord; i++)
    fun(String.fromCharCode(i), args)
}

function fancy_round(x) {
  // probabilistically rounds to nearby integer
  return (x|0) + (Math.random() < x%1)
}

function rand(n) {
  return Math.floor(Math.random() * (n + 1))
}

function noop() {}

function clone(obj) {
  try {
    return JSON.parse(JSON.stringify(obj))
  } catch(e) {
    log(e)
    return {}
  }
}

function log(x) {
  console.log(x)
}

init()
<|endoftext|>


<commit_before>Router.route('/contacts/download', function() {
  var user = '';
  log.info(this.request.cookies.meteor_login_token);
  if(this.request.cookies.meteor_login_token)
      user = Meteor.users.findOne({"services.resume.loginTokens.hashedToken": Accounts._hashLoginToken(this.request.cookies.meteor_login_token)});
  if (user && Houston._user_is_admin(user._id)) {
    log.info(user);
    var data = [];
    var stateSupport;
    var contact;
    Orders.find().forEach(function(order) {
      contact = order.getContact();
      if (contact) {
        stateSupport = contact.stateSupport.toString();
      } else {
        stateSupport = '';
      }
      data.push({
        name: order.name,
        phone: order.phone,
        course: order.courseName,
        stateSupport: stateSupport
      });
    });
    this.response.writeHead(200, {
      'Content-type': 'text/csv',
      'Content-Disposition': "attachment; filename=contacts.csv"
    });
    this.response.end(CSV.unparse(data));
  }
}, {where: 'server'});

Router.route('/orders/download', function() {
  var user = '';
  log.info(this.request.cookies.meteor_login_token);
  if(this.request.cookies.meteor_login_token)
      user = Meteor.users.findOne({"services.resume.loginTokens.hashedToken": Accounts._hashLoginToken(this.request.cookies.meteor_login_token)});
  if (user && Houston._user_is_admin(user._id)) {
    var pipeline = [ {$group: { _id: "$courseName", count:{$sum:1}}}];
    var data = Orders.aggregate(pipeline);
    this.response.writeHead(200, {
      'Content-type': 'text/csv',
      'Content-Disposition': "attachment; filename=orders.csv"
    });
    this.response.end(CSV.unparse(data));
  }
}, {where: 'server'});
<commit_msg>make sure the orders are from last year<commit_after>Router.route('/contacts/download', function() {
  var user = '';
  log.info(this.request.cookies.meteor_login_token);
  if(this.request.cookies.meteor_login_token)
      user = Meteor.users.findOne({"services.resume.loginTokens.hashedToken": Accounts._hashLoginToken(this.request.cookies.meteor_login_token)});
  if (user && Houston._user_is_admin(user._id)) {
    log.info(user);
    var data = [];
    var stateSupport;
    var contact;
    Orders.find().forEach(function(order) {
      contact = order.getContact();
      if (contact) {
        stateSupport = contact.stateSupport.toString();
      } else {
        stateSupport = '';
      }
      data.push({
        name: order.name,
        phone: order.phone,
        course: order.courseName,
        stateSupport: stateSupport
      });
    });
    this.response.writeHead(200, {
      'Content-type': 'text/csv',
      'Content-Disposition': "attachment; filename=contacts.csv"
    });
    this.response.end(CSV.unparse(data));
  }
}, {where: 'server'});

Router.route('/orders/download', function() {
  var user = '';
  log.info(this.request.cookies.meteor_login_token);
  if(this.request.cookies.meteor_login_token)
      user = Meteor.users.findOne({"services.resume.loginTokens.hashedToken": Accounts._hashLoginToken(this.request.cookies.meteor_login_token)});
  if (user && Houston._user_is_admin(user._id)) {
    var startYear = moment().startOf('year').toDate();
    var startLastYear = moment().subtract(1, 'year').startOf('year').toDate();
    var pipeline = [
      { $createdAt: { $lt: startYear, $gt: startLastYear } },
      { $group: { _id: "$courseName", count:{ $sum:1 } } }
    ];
    var data = Orders.aggregate(pipeline);
    this.response.writeHead(200, {
      'Content-type': 'text/csv',
      'Content-Disposition': "attachment; filename=orders.csv"
    });
    this.response.end(CSV.unparse(data));
  }
}, {where: 'server'});
<|endoftext|>


<commit_before>const webpack = require('webpack');
const Merge = require('webpack-merge');
const SentryWebpackPlugin = require('@sentry/webpack-plugin');

const CommonConfig = require('./config.common.js');
const { buildDir } = require('./paths');

const config = Merge(CommonConfig, {
  mode: 'production',

  devtool: 'source-map',

  plugins: [new webpack.HashedModuleIdsPlugin()],
});

/*
 Releasing requires the following env variables:
 - SENTRY_AUTH_TOKEN
 - SENTRY_ORG
 - SENTRY_PROJECT

 No need to have these variables in your local environment
 because we'll only ever want to do releases during a prod build.

 Given that, it's also important to have ENABLE_SENTRY_RELEASE=False
 set in your local environment.
*/

if (process.env.ENABLE_SENTRY_RELEASE.toLowerCase() === 'true') {
  config.plugins.push(
    new SentryWebpackPlugin({
      release: Date.now().toString(),
      include: buildDir,
    })
  );
}

module.exports = config;
<commit_msg>more descriptive sentry release name<commit_after>const webpack = require('webpack');
const Merge = require('webpack-merge');
const SentryWebpackPlugin = require('@sentry/webpack-plugin');

const CommonConfig = require('./config.common.js');
const { buildDir } = require('./paths');

const config = Merge(CommonConfig, {
  mode: 'production',

  devtool: 'source-map',

  plugins: [new webpack.HashedModuleIdsPlugin()],
});

/*
 Releasing requires the following env variables:
 - SENTRY_AUTH_TOKEN
 - SENTRY_ORG
 - SENTRY_PROJECT

 No need to have these variables in your local environment
 because we'll only ever want to do releases during a prod build.

 Given that, it's also important to have ENABLE_SENTRY_RELEASE=False
 set in your local environment.
*/

if (process.env.ENABLE_SENTRY_RELEASE.toLowerCase() === 'true') {
  config.plugins.push(
    new SentryWebpackPlugin({
      release: `${new Date().toString()}-${process.env.SENTRY_ENVIRONMENT}`,
      include: buildDir,
    })
  );
}

module.exports = config;
<|endoftext|>