<commit_before>#!/usr/bin/env node
var spawn = require('child_process').spawn;
var vimrl = require('vimrl');
var net = require('net');

var config = require(process.env.HOME + "/.jsiiConfig.js");

var nicks = {};

var chanNumber, server, chan;

var findChannel = function(searchString) {
    // exact match for server:chan format
    if(searchString.indexOf(':') !== -1) {
        // first try looking for a match in config
        for(var i = 0; i < config.favoriteChannels.length; i++) {
            if(searchString === (config.favoriteChannels[i].server + ':' +
                                config.favoriteChannels[i].chan)) {
                return {
                    chanNumber: i,
                    server: config.favoriteChannels[i].server,
                    chan: config.favoriteChannels[i].chan
                }
            }
        }
        // otherwise assume the user knows better, use provided names
        return {
            server: searchString.split(':')[0],
            chan: searchString.split(':')[1]
        }
    }

    // search for substring in shortName
    for(var i = 0; i < config.favoriteChannels.length; i++) {
        if(config.favoriteChannels[i].shortName.match(new RegExp(searchString))) {
            return {
                chanNumber: i,
                server: config.favoriteChannels[i].server,
                chan: config.favoriteChannels[i].chan
            }
        }
    }
};

/* find server & channels names from first argument */
// no args: pick first favorite chan
if(!process.argv[2]) {
    server = config.favoriteChannels[0].server;
    chan = config.favoriteChannels[0].chan;
    chanNumber = 0;
// by channel number
} else if (!isNaN(process.argv[2])) {
    chanNumber = parseInt(process.argv[2]);
    server = config.favoriteChannels[chanNumber].server;
    chan = config.favoriteChannels[chanNumber].chan;
// else search
} else {
    var results = findChannel(process.argv[2]);

    if(results) {
        server = results.server;
        chan = results.chan;
        chanNumber = results.chanNumber;
    } else {
        console.log("No results with given search terms!");
        process.exit(1);
    }
}

// for prompt
var getChanName = function(server, chan, chanNumber) {
    if(!isNaN(chanNumber)) {
        return config.favoriteChannels[chanNumber].shortName;
    } else {
        return server + ':' + chan;
    }
};

var sendMsg = function(msg) {
    socket.write(JSON.stringify(msg) + '\n');
};

// reset cursor to lower left corner
var cursorReset = function() {
    process.stdout.write('\033[' + process.stdout.rows + ';0f');
};
// clear line where cursor currently is
var clearLine = function() {
    process.stdout.write('\033[K');
};

// prints line to lower left corner of terminal
var printLine = function(msg) {
    var i;

    var hilight = false;
    var action = false;
    var separator = config.nickSeparator;

    // move cursor to lower left
    cursorReset();
    // clear current line
    clearLine();

    var nickColor = 0;
    var textColor = 15;

    // support irc ACTION messages
    if(msg.cmd === 'action') {
        msg.message = msg.nick + ' ' + msg.message.substring(8);
        msg.nick = '*';
        nickColor = config.actionColor;
        textColor = config.actionColor;
    } else if (msg.cmd === 'join') {
        separator = '';
        msg.message = config.joinMsg;
        textColor = config.joinColor;
    } else if (msg.cmd === 'part' || msg.cmd === 'quit') {
        separator = '';
        msg.message = config.partMsg;
        textColor = config.partColor;
    } else if (msg.cmd === 'nicklist') {
        msg.nick = '*';
        msg.message = 'Names: ' + msg.nicks.join(', ');
    } else if(msg.message.match(config.hilight_re)) {
        textColor = config.hilightColor;
    }

    if (msg.nick) {
        if(msg.nick === config.myNick) {
            nickColor = config.myNickColor;
            textColor = config.myNickColor;
        } else {
            // nick color, avoids dark colors
            for(i = 0; i < msg.nick.length; i++) {
                nickColor += msg.nick.charCodeAt(i);
            }
            nickColor = Math.pow(nickColor, 2) + nickColor * 2;
            nickColor = nickColor % 255;
            switch(nickColor) {
                case 18: case 22: case 23: case 24:
                    nickColor += 3; break;
                case 52: case 53: case 54: case 55: case 56: case 57: case 88: case 89:
                    nickColor += 6; break;
                case 232: case 233: case 234: case 235: case 236: case 237: case 238: case 239:
                    nickColor += 8; break;
                case 0: case 8: case 19: case 22:
                    nickColor++; break;
            }
        }
    }

    // limit nicklen
    msg.nick = msg.nick.substr(0, config.maxNickLen);
    // align nicks and print
    process.stdout.write(Array(config.maxNickLen - msg.nick.length + 1).join(' '));
    process.stdout.write('\033[38;5;' + nickColor + 'm' + msg.nick + // set nick color + nick
                         '\033[38;5;' + config.separatorColor + 'm' + separator + // set separator color + separator
                         '\033[000m'); // reset colors

    for (i = 0; i < config.findAndReplace.length; i++) {
        msg.message = msg.message.replace(config.findAndReplace[i][0],
                                          config.findAndReplace[i][1]);
    }

    var availWidth = process.stdout.columns - config.maxNickLen - separator.length;

    var wrappedChars = 0;
    i = 0;

    // terminal too small? don't print anything
    if(availWidth <= 5)
        return;

    while(i * availWidth - wrappedChars < msg.message.length) {
        var start = i * availWidth - wrappedChars;
        var curLine = msg.message.substr(start, availWidth);
        // remove leading space on next line
        curLine.replace(/^\s+/, '');
        // line wrap at word boundary only if there is whitespace on this line
        if(start + availWidth < msg.message.length && curLine.lastIndexOf(' ') !== -1) {
            curLine = curLine.slice(0, curLine.lastIndexOf(' '));
            // remove whitespace
            wrappedChars--;
        }

        wrappedChars += availWidth - curLine.length;

        // empty space on line wrap
        if (i > 0)
            process.stdout.write(Array(process.stdout.columns - availWidth + 1).join(' '));

        process.stdout.write('\033[38;5;' + textColor + 'm' + curLine + // set text color + text
                             '\033[000m' + '\n'); // reset colors + newline
        i++;
    }
};

// redraw screen
var redraw = function() {
    process.stdout.write('\u001B[2J\u001B[0;0f'); // clear terminal
    sendMsg({
        cmd: 'backlog',
        chan: chan,
        server: server
    });
    readline.redraw();
};

process.stdin.setRawMode(true);

// handle keyboard events
process.stdin.on('readable', function() {
    var input;
    while (null !== (input = process.stdin.read())) {
        // 'ctrl-c' = quit
        if(input == '\u0003') process.exit(0);

        var keyHex = input.toString('hex');

        // previous channel (alt + h)
        if(keyHex === '1b68') {
            if(isNaN(chanNumber)) chanNumber = 0;
            else chanNumber--;

            if(chanNumber < 0)
                chanNumber = config.favoriteChannels.length - 1;

            server = config.favoriteChannels[chanNumber].server;
            chan = config.favoriteChannels[chanNumber].chan;
            nicks = {};

            readline.changePrompt(config.getPrompt(
                        getChanName(server, chan, chanNumber), chanNumber));
            redraw();
        }
        // next channel (alt + l)
        else if(keyHex === '1b6c') {
            if(isNaN(chanNumber)) chanNumber = 0;
            else chanNumber++;

            if(chanNumber >= config.favoriteChannels.length)
                chanNumber = 0;

            server = config.favoriteChannels[chanNumber].server;
            chan = config.favoriteChannels[chanNumber].chan;
            nicks = {};

            readline.changePrompt(config.getPrompt(
                        getChanName(server, chan, chanNumber), chanNumber));
            redraw();
        }
        // jump to this channel (alt + 1-9)
        else if(keyHex.substring(0, 3) === '1b3' && keyHex.substring(3) !== '0') {
            chanNumber = parseInt(keyHex.substring(3));

            server = config.favoriteChannels[chanNumber].server;
            chan = config.favoriteChannels[chanNumber].chan;
            nicks = {};

            readline.changePrompt(config.getPrompt(
                        getChanName(server, chan, chanNumber), chanNumber));
            redraw();
        } else {
            readline.handleInput(input);
        }

        // DEBUG: uncomment this line to find the keycodes
        //console.log(key.toString('hex'));
    }
});

/*
// nickname completion from nicknames in file buffer
// TODO: get nicks from irc server?
completer: function(line) {
}
var word = line.substring(line.lastIndexOf(" ") + 1);
var hits = completions.filter(function(c) { return c.indexOf(word) == 0 })
for(var i = hits.length - 1; i >= 0; i--) {
    if(hits[i] === '!' || hits[i] === '***') { // skip status/info nicknames
        hits.splice(i, 1);
    } else if (word === line) { // typing at beginning of line? append colon
        hits[i] += ': ';
    } else { // else append comma
        hits[i] += ', ';
    }
}
return [hits, word]
*/

// watch file for changes
var socket = net.connect({port: config.port, host: config.addr});
var buffer = "";

socket.on('data', function(data) {
    buffer += data.toString('utf8');

    var lastNL = buffer.lastIndexOf('\n');
    if(lastNL !== -1) {
        var recvdLines = buffer.substr(0, lastNL).split('\n');
        buffer = buffer.substr(lastNL + 1);

        for(var i = 0; i < recvdLines.length; i++) {
            var msg = JSON.parse(recvdLines[i]);

            if(msg.cmd === 'quit') {
                if(nicks[msg.nick]) {
                    printLine(msg);
                    readline.redraw();

                    delete(nicks[msg.nick]);
                }
            }

            // is the message on the active channel?
            if(msg.server + ':' + msg.chan === server + ':' + chan) {
                // store nicklist
                if(msg.cmd === 'nicklist') {
                    for(var j = 0; j < msg.nicks.length; j++) {
                        nicks[msg.nicks[j]] = true;
                    }
                } else {
                    printLine(msg);
                    readline.redraw();

                    if(msg.cmd === 'join') {
                        nicks[msg.nick] = true;
                    } else if(msg.cmd === 'part') {
                        delete(nicks[msg.nick]);
                    }
                }
            }
        }
    }
});

// handle terminal resize
process.stdout.on('resize', function() {
    redraw();
});

var prompt = config.getPrompt(getChanName(server, chan, chanNumber), chanNumber);

// parse some select commands from input line
readline = vimrl(prompt, function(line) {
    if(line === '/bl' || line.substring(0, 4) === '/bl ') { // request backlog
        line = "/privmsg *backlog " + chan + ' ' + line.substring(4);
    } else if(line.substring(0, 4) === '/me ') { // irc ACTION message
        line = "\001ACTION " + line.substring(4);
    } else if(line === '/names') { // request nick list
        printLine({
            cmd: 'nicklist',
            nicks: Object.keys(nicks)
        });
        return;
    } else if (line === '/ul') { // list urls in buffer
        var current = 0;
        var splitFile = backlog.split('\n');

        var url;
        for (var i = splitFile.length - 1; i >= 0; i--) {
            // TODO: put this mess into a function
            // remove timestamps
            splitFile[i] = splitFile[i].replace(/^([^ ]+ ){2}/, '');
            var words = splitFile[i].split(' ');
            // nick is first whitespace separated word after timestamp
            var nick = words[0].substring(1, words[0].length - 1);
            // ignore select bots
            if(config.url_ignore_nicks.indexOf(nick) !== -1)
                continue;

            for (var j = words.length - 1; j >= 0; j--) {
                var res = config.url_re.exec(words[j]);
                if(res) {
                    url = res[0];
                    printLine({
                        nick: '***',
                        msg: 'URL ' + current + ': ' + url
                    });
                    readline.redraw();
                    current++;
                }
            }
        }

        return;
    } else if (line === '/u' || line.substring(0, 3) === '/u ') { // open url
        var skip = parseInt(line.substring(3)) | 0;
        var splitFile = backlog.split('\n');

        var url;
        for (var i = splitFile.length - 1; i >= 0; i--) {
            // TODO: put this mess into a function
            // remove timestamps
            splitFile[i] = splitFile[i].replace(/^([^ ]+ ){2}/, '');
            var words = splitFile[i].split(' ');
            // nick is first whitespace separated word after timestamp
            var nick = words[0].substring(1, words[0].length - 1);
            // ignore select bots
            if(config.url_ignore_nicks.indexOf(nick) !== -1)
                continue;

            for (var j = words.length - 1; j >= 0; j--) {
                var res = config.url_re.exec(words[j]);
                if(res) {
                    if(skip > 0) {
                        skip--;
                        continue;
                    }
                    url = res[0];
                    break;
                }
            }

            if(url)
                break;
        }

        if(url) {
            var child = spawn('chromium', [url], {
                detached: true,
                stdio: [ 'ignore', 'ignore' , 'ignore' ]
            });
            child.unref();
            printLine({
                nick: '***',
                msg: 'URL ' + (parseInt(line.substring(3)) | 0) +  " opened: " + url
            });
            readline.redraw();
        } else {
            printLine({
                nick: '***',
                msg: 'No URL found'
            });
            readline.redraw();
        }

        return;
    }

    // send input line to jsiid
    var msg = {
        cmd: 'message',
        chan: chan,
        server: server,
        message: line,
        nick: config.myNick
    };
    sendMsg(msg);

    // print non command messages
    if(msg.message[0] !== '/') {
        msg.nick = config.myNick;
        printLine(msg);
    }
});

readline.gotoInsertMode();

redraw();
<commit_msg>completions support<commit_after>#!/usr/bin/env node
var spawn = require('child_process').spawn;
var vimrl = require('vimrl');
var net = require('net');

var config = require(process.env.HOME + "/.jsiiConfig.js");

var nicks = {};

var chanNumber, server, chan;

var updateCompletions = function() {
    var nicksArray = Object.keys(nicks);
    for(var i = 0; i < nicksArray.length; i++) {
        if(nicksArray[i][0] === '@' || nicksArray[i][0] === '+') {
            nicksArray[i] = nicksArray[i].substr(1);
        }
    }

    readline.setCompletions(nicksArray);
};

var findChannel = function(searchString) {
    // exact match for server:chan format
    if(searchString.indexOf(':') !== -1) {
        // first try looking for a match in config
        for(var i = 0; i < config.favoriteChannels.length; i++) {
            if(searchString === (config.favoriteChannels[i].server + ':' +
                                config.favoriteChannels[i].chan)) {
                return {
                    chanNumber: i,
                    server: config.favoriteChannels[i].server,
                    chan: config.favoriteChannels[i].chan
                }
            }
        }
        // otherwise assume the user knows better, use provided names
        return {
            server: searchString.split(':')[0],
            chan: searchString.split(':')[1]
        }
    }

    // search for substring in shortName
    for(var i = 0; i < config.favoriteChannels.length; i++) {
        if(config.favoriteChannels[i].shortName.match(new RegExp(searchString))) {
            return {
                chanNumber: i,
                server: config.favoriteChannels[i].server,
                chan: config.favoriteChannels[i].chan
            }
        }
    }
};

/* find server & channels names from first argument */
// no args: pick first favorite chan
if(!process.argv[2]) {
    server = config.favoriteChannels[0].server;
    chan = config.favoriteChannels[0].chan;
    chanNumber = 0;
// by channel number
} else if (!isNaN(process.argv[2])) {
    chanNumber = parseInt(process.argv[2]);
    server = config.favoriteChannels[chanNumber].server;
    chan = config.favoriteChannels[chanNumber].chan;
// else search
} else {
    var results = findChannel(process.argv[2]);

    if(results) {
        server = results.server;
        chan = results.chan;
        chanNumber = results.chanNumber;
    } else {
        console.log("No results with given search terms!");
        process.exit(1);
    }
}

// for prompt
var getChanName = function(server, chan, chanNumber) {
    if(!isNaN(chanNumber)) {
        return config.favoriteChannels[chanNumber].shortName;
    } else {
        return server + ':' + chan;
    }
};

var sendMsg = function(msg) {
    socket.write(JSON.stringify(msg) + '\n');
};

// reset cursor to lower left corner
var cursorReset = function() {
    process.stdout.write('\033[' + process.stdout.rows + ';0f');
};
// clear line where cursor currently is
var clearLine = function() {
    process.stdout.write('\033[K');
};

// prints line to lower left corner of terminal
var printLine = function(msg) {
    var i;

    var hilight = false;
    var action = false;
    var separator = config.nickSeparator;

    // move cursor to lower left
    cursorReset();
    // clear current line
    clearLine();

    var nickColor = 0;
    var textColor = 15;

    // support irc ACTION messages
    if(msg.cmd === 'action') {
        msg.message = msg.nick + ' ' + msg.message.substring(8);
        msg.nick = '*';
        nickColor = config.actionColor;
        textColor = config.actionColor;
    } else if (msg.cmd === 'join') {
        separator = '';
        msg.message = config.joinMsg;
        textColor = config.joinColor;
    } else if (msg.cmd === 'part' || msg.cmd === 'quit') {
        separator = '';
        msg.message = config.partMsg;
        textColor = config.partColor;
    } else if (msg.cmd === 'nicklist') {
        msg.nick = '*';
        msg.message = 'Names: ' + msg.nicks.join(', ');
    } else if(msg.message.match(config.hilight_re)) {
        textColor = config.hilightColor;
    }

    if (msg.nick) {
        if(msg.nick === config.myNick) {
            nickColor = config.myNickColor;
            textColor = config.myNickColor;
        } else {
            // nick color, avoids dark colors
            for(i = 0; i < msg.nick.length; i++) {
                nickColor += msg.nick.charCodeAt(i);
            }
            nickColor = Math.pow(nickColor, 2) + nickColor * 2;
            nickColor = nickColor % 255;
            switch(nickColor) {
                case 18: case 22: case 23: case 24:
                    nickColor += 3; break;
                case 52: case 53: case 54: case 55: case 56: case 57: case 88: case 89:
                    nickColor += 6; break;
                case 232: case 233: case 234: case 235: case 236: case 237: case 238: case 239:
                    nickColor += 8; break;
                case 0: case 8: case 19: case 22:
                    nickColor++; break;
            }
        }
    }

    // limit nicklen
    msg.nick = msg.nick.substr(0, config.maxNickLen);
    // align nicks and print
    process.stdout.write(Array(config.maxNickLen - msg.nick.length + 1).join(' '));
    process.stdout.write('\033[38;5;' + nickColor + 'm' + msg.nick + // set nick color + nick
                         '\033[38;5;' + config.separatorColor + 'm' + separator + // set separator color + separator
                         '\033[000m'); // reset colors

    for (i = 0; i < config.findAndReplace.length; i++) {
        msg.message = msg.message.replace(config.findAndReplace[i][0],
                                          config.findAndReplace[i][1]);
    }

    var availWidth = process.stdout.columns - config.maxNickLen - separator.length;

    var wrappedChars = 0;
    i = 0;

    // terminal too small? don't print anything
    if(availWidth <= 5)
        return;

    while(i * availWidth - wrappedChars < msg.message.length) {
        var start = i * availWidth - wrappedChars;
        var curLine = msg.message.substr(start, availWidth);
        // remove leading space on next line
        curLine.replace(/^\s+/, '');
        // line wrap at word boundary only if there is whitespace on this line
        if(start + availWidth < msg.message.length && curLine.lastIndexOf(' ') !== -1) {
            curLine = curLine.slice(0, curLine.lastIndexOf(' '));
            // remove whitespace
            wrappedChars--;
        }

        wrappedChars += availWidth - curLine.length;

        // empty space on line wrap
        if (i > 0)
            process.stdout.write(Array(process.stdout.columns - availWidth + 1).join(' '));

        process.stdout.write('\033[38;5;' + textColor + 'm' + curLine + // set text color + text
                             '\033[000m' + '\n'); // reset colors + newline
        i++;
    }
};

// redraw screen
var redraw = function() {
    process.stdout.write('\u001B[2J\u001B[0;0f'); // clear terminal
    sendMsg({
        cmd: 'backlog',
        chan: chan,
        server: server
    });
    readline.redraw();
};

process.stdin.setRawMode(true);

// handle keyboard events
process.stdin.on('readable', function() {
    var input;
    while (null !== (input = process.stdin.read())) {
        // 'ctrl-c' = quit
        if(input == '\u0003') process.exit(0);

        var keyHex = input.toString('hex');

        // previous channel (alt + h)
        if(keyHex === '1b68') {
            if(isNaN(chanNumber)) chanNumber = 0;
            else chanNumber--;

            if(chanNumber < 0)
                chanNumber = config.favoriteChannels.length - 1;

            server = config.favoriteChannels[chanNumber].server;
            chan = config.favoriteChannels[chanNumber].chan;
            nicks = {};

            readline.changePrompt(config.getPrompt(
                        getChanName(server, chan, chanNumber), chanNumber));
            redraw();
        }
        // next channel (alt + l)
        else if(keyHex === '1b6c') {
            if(isNaN(chanNumber)) chanNumber = 0;
            else chanNumber++;

            if(chanNumber >= config.favoriteChannels.length)
                chanNumber = 0;

            server = config.favoriteChannels[chanNumber].server;
            chan = config.favoriteChannels[chanNumber].chan;
            nicks = {};

            readline.changePrompt(config.getPrompt(
                        getChanName(server, chan, chanNumber), chanNumber));
            redraw();
        }
        // jump to this channel (alt + 1-9)
        else if(keyHex.substring(0, 3) === '1b3' && keyHex.substring(3) !== '0') {
            chanNumber = parseInt(keyHex.substring(3));

            server = config.favoriteChannels[chanNumber].server;
            chan = config.favoriteChannels[chanNumber].chan;
            nicks = {};

            readline.changePrompt(config.getPrompt(
                        getChanName(server, chan, chanNumber), chanNumber));
            redraw();
        } else {
            readline.handleInput(input);
        }

        // DEBUG: uncomment this line to find the keycodes
        //console.log(key.toString('hex'));
    }
});

/*
// nickname completion from nicknames in file buffer
// TODO: get nicks from irc server?
completer: function(line) {
}
var word = line.substring(line.lastIndexOf(" ") + 1);
var hits = completions.filter(function(c) { return c.indexOf(word) == 0 })
for(var i = hits.length - 1; i >= 0; i--) {
    if(hits[i] === '!' || hits[i] === '***') { // skip status/info nicknames
        hits.splice(i, 1);
    } else if (word === line) { // typing at beginning of line? append colon
        hits[i] += ': ';
    } else { // else append comma
        hits[i] += ', ';
    }
}
return [hits, word]
*/

// watch file for changes
var socket = net.connect({port: config.port, host: config.addr});
var buffer = "";

socket.on('data', function(data) {
    buffer += data.toString('utf8');

    var lastNL = buffer.lastIndexOf('\n');
    if(lastNL !== -1) {
        var recvdLines = buffer.substr(0, lastNL).split('\n');
        buffer = buffer.substr(lastNL + 1);

        for(var i = 0; i < recvdLines.length; i++) {
            var msg = JSON.parse(recvdLines[i]);

            if(msg.cmd === 'quit') {
                if(nicks[msg.nick]) {
                    printLine(msg);
                    readline.redraw();

                    delete(nicks[msg.nick]);
                    updateCompletions();
                }
            }

            // is the message on the active channel?
            if(msg.server + ':' + msg.chan === server + ':' + chan) {
                // store nicklist
                if(msg.cmd === 'nicklist') {
                    for(var j = 0; j < msg.nicks.length; j++) {
                        nicks[msg.nicks[j]] = true;
                    }
                    updateCompletions();
                } else {
                    printLine(msg);
                    readline.redraw();

                    if(msg.cmd === 'join') {
                        nicks[msg.nick] = true;
                        updateCompletions();
                    } else if(msg.cmd === 'part') {
                        delete(nicks[msg.nick]);
                        updateCompletions();
                    }
                }
            }
        }
    }
});

// handle terminal resize
process.stdout.on('resize', function() {
    redraw();
});

var prompt = config.getPrompt(getChanName(server, chan, chanNumber), chanNumber);

// parse some select commands from input line
readline = vimrl(prompt, function(line) {
    if(line === '/bl' || line.substring(0, 4) === '/bl ') { // request backlog
        line = "/privmsg *backlog " + chan + ' ' + line.substring(4);
    } else if(line.substring(0, 4) === '/me ') { // irc ACTION message
        line = "\001ACTION " + line.substring(4);
    } else if(line === '/names') { // request nick list
        printLine({
            cmd: 'nicklist',
            nicks: Object.keys(nicks)
        });
        return;
    } else if (line === '/ul') { // list urls in buffer
        var current = 0;
        var splitFile = backlog.split('\n');

        var url;
        for (var i = splitFile.length - 1; i >= 0; i--) {
            // TODO: put this mess into a function
            // remove timestamps
            splitFile[i] = splitFile[i].replace(/^([^ ]+ ){2}/, '');
            var words = splitFile[i].split(' ');
            // nick is first whitespace separated word after timestamp
            var nick = words[0].substring(1, words[0].length - 1);
            // ignore select bots
            if(config.url_ignore_nicks.indexOf(nick) !== -1)
                continue;

            for (var j = words.length - 1; j >= 0; j--) {
                var res = config.url_re.exec(words[j]);
                if(res) {
                    url = res[0];
                    printLine({
                        nick: '***',
                        msg: 'URL ' + current + ': ' + url
                    });
                    readline.redraw();
                    current++;
                }
            }
        }

        return;
    } else if (line === '/u' || line.substring(0, 3) === '/u ') { // open url
        var skip = parseInt(line.substring(3)) | 0;
        var splitFile = backlog.split('\n');

        var url;
        for (var i = splitFile.length - 1; i >= 0; i--) {
            // TODO: put this mess into a function
            // remove timestamps
            splitFile[i] = splitFile[i].replace(/^([^ ]+ ){2}/, '');
            var words = splitFile[i].split(' ');
            // nick is first whitespace separated word after timestamp
            var nick = words[0].substring(1, words[0].length - 1);
            // ignore select bots
            if(config.url_ignore_nicks.indexOf(nick) !== -1)
                continue;

            for (var j = words.length - 1; j >= 0; j--) {
                var res = config.url_re.exec(words[j]);
                if(res) {
                    if(skip > 0) {
                        skip--;
                        continue;
                    }
                    url = res[0];
                    break;
                }
            }

            if(url)
                break;
        }

        if(url) {
            var child = spawn('chromium', [url], {
                detached: true,
                stdio: [ 'ignore', 'ignore' , 'ignore' ]
            });
            child.unref();
            printLine({
                nick: '***',
                msg: 'URL ' + (parseInt(line.substring(3)) | 0) +  " opened: " + url
            });
            readline.redraw();
        } else {
            printLine({
                nick: '***',
                msg: 'No URL found'
            });
            readline.redraw();
        }

        return;
    }

    // send input line to jsiid
    var msg = {
        cmd: 'message',
        chan: chan,
        server: server,
        message: line,
        nick: config.myNick
    };
    sendMsg(msg);

    // print non command messages
    if(msg.message[0] !== '/') {
        msg.nick = config.myNick;
        printLine(msg);
    }
});

readline.gotoInsertMode();

redraw();
<|endoftext|>


<commit_before>/**
 * JSON polyfill / shim
 *
 * (c) copyright 2011-2012 Brian Cavalier and John Hann
 *
 * poly is part of the cujo.js family of libraries (http://cujojs.com/)
 *
 * Licensed under the MIT License at:
 * 		http://www.opensource.org/licenses/mit-license.php
 *
 * TODO: document that JSON module is always downloaded at run-time unless
 * dev explicitly mentions it in build instructions
 */
define(['./support/json2'], function (JSON) {
	return JSON;
});
<commit_msg>remove coment about silly shenanigans around loading json<commit_after>/**
 * JSON polyfill / shim
 *
 * (c) copyright 2011-2012 Brian Cavalier and John Hann
 *
 * poly is part of the cujo.js family of libraries (http://cujojs.com/)
 *
 * Licensed under the MIT License at:
 * 		http://www.opensource.org/licenses/mit-license.php
 *
 */
define(['./support/json2'], function (JSON) {
	return JSON;
});
<|endoftext|>


<commit_before>/*
    --------------- jSQL ---------------
    a SQL like database using javascript
    website: http://jsql.us
    licence: MIT Licence
    version: 0.3.0 dev

    description: using jSQL to process the data easily.
*/

(function() {
    var slice            = Array.prototype.slice,
        hasOwnProperty   = Object.prototype.hasOwnProperty;

    var nativeForEach      = Array.prototype.forEach,
        nativeIsArray      = Array.isArray,
        nativeKeys         = Object.keys;

    var jSQL, _jSQL, _jsql, _DB = {}, _DBIndexMap = {};
    var jSQL_KEY_NAME = 'jSQL_Key';
    var utils = {};

    if(typeof(this.jSQL) !== 'undefined') {
        _jSQL = this.jSQL;
    }

    if(typeof(this.jsql) !== 'undefined') {
        _jsql = this.jsql;
    }

    jSQL = function() {
        this.init.apply(this, arguments);
    };

    jSQL.prototype = {
        version: '0.3.0-dev',

        init: function() {
            this._jSQL = _jSQL;
            this._jsql = _jsql;
            this._DB = _DB;
            this._currentDB = null;
            this._buffer = null;
            this._DBIndexMap = _DBIndexMap;
            this.utils = utils;
        },

        create: function(dbname, db) {
            var indexList;

            if(this._DB.hasOwnProperty(dbname)) {
                throw('DB Already Exist.');
            }

            if(utils.isArray(db)) {
                indexList = utils.listSlice(arguments, '2:');
                utils.appendKey(db, indexList);
                _DBIndexMap[dbname] = utils.arrayToObject(db);
            }

            if(utils.isPlainObject(db)) {
                _DBIndexMap[dbname] = utils.clone(db);
                db = utils.objectToArray(db);
            }

            this._DB[dbname] = db;
            return this;
        },

        use: function(dbname) {
            if(!this._DB.hasOwnProperty(dbname)) {
                throw('Database Not Exist.');
            }

            this._currentDB = this._DB[dbname];
            this._currentDBName = dbname;
            this.rebase();
            return this;
        },

        drop: function(dbname) {
            if(this._DB.hasOwnProperty(dbname)) {
                delete this._DB[dbname];
            }
        },

        dbs: function() {
            return utils.keys(this._DB);
        },

        db: function() {
            return this._currentDBName;
        },

        select: function(key) {
            if(!this._currentDB) {
                throw('Please Select Database First.');
            }

            this._buffer = this._currentDB; //reset the _buffer

            if(key === '*') {
                return this;
            }

            this.where(function(data) {
                return typeof(utils.deep(data, key)) !== 'undefined';
            });

            return this;
        },

        count: function() {
            var result;
            result = this._buffer.length;
            this.rebase();
            return result;
        },

        total: function(scope) {
            var rs = 0, _tmp;

            for(var _key in this._currentDB) {
                if(this._currentDB.hasOwnProperty(_key)) {
                    _tmp = scope === '*' ?
                        this._currentDB[_key] :
                        typeof(scope) === 'function' ?
                            scope.call(this, this._currentDB[_key], _key) === true ? true : undefined :
                            utils.deep(this._currentDB[_key], scope);

                    if(typeof(_tmp) !== 'undefined') {
                        rs++;
                    }
                }
            }

            return rs;
        },

        orderby: function(field, callback, order) {
            var _array = this._buffer;
            var _this = this;

            if(typeof(callback) !== 'function') {
                callback = [order, order = callback][0];
            }

            _array.sort(function(a, b) {
                a = utils.deep(a, field);
                b = utils.deep(b, field);
                if(callback) {
                    a = callback(a);
                    b = callback(b);
                }
                return  order && order.toLowerCase() === 'asc' ? a - b : b - a;
            });

            this._buffer = _array;
            return this;
        },

        where: function(fn) {
            var _tmp = [], _swap;
            this._buffer = this._buffer || this._currentDB;
            fn = utils.parseFn(fn);

            for(var i in this._buffer) {
                if(this._buffer.hasOwnProperty(i)) {
                    if(typeof(fn) === 'function') {
                        _swap = fn.call(this, utils.clone(this._buffer[i]), i);
                    }

                    if(utils.isArray(fn)) {
                        _swap = false;

                        for(var f in fn) {
                            if(fn.hasOwnProperty(f)) {
                                if(fn[f].call(this, utils.clone(this._buffer[i]), i)) {
                                    _swap = true;
                                }
                            }
                        }
                    }

                    if(_swap) {
                        _tmp.push(utils.clone(this._buffer[i]));
                    }
                }
            }
            this._buffer = _tmp;
            return this;
        },

        iterate: function(fn) {
            var _swap;
            this._buffer = this._buffer || this._currentDB;

            for(var i in this._buffer) {
                if(this._buffer.hasOwnProperty(i)) {
                    _swap = fn.call(this, this._buffer[i]);

                    if(_swap) {
                        this.update(i, _swap);
                    }
                }
            }
            return this;
        },

        findAll: function() {
            var result;
            result = utils.clone(utils.arrayToObject(this._buffer));
            this.rebase();
            return result;
        },

        find: function(key) {
            var result;
            var _tmp = this._DBIndexMap[this._currentDBName];

            if(!key) {
                for(var i in _tmp) {
                    if(_tmp.hasOwnProperty(i)) {
                        if(key) {
                            break;
                        }

                        if(this._buffer.hasOwnProperty(i)) {
                            key = i;
                        }
                    }
                }
            }

            result = utils.clone(_tmp[key]);
            this.rebase();
            return result;
        },

        listAll: function() {
            var result;
            result = utils.clone(this._buffer);
            this.rebase();
            return result;
        },

        update: function(key, data) {
            if(!this._currentDB) {
                throw('Please Select Database First.');
            }

            if(this._currentDB.hasOwnProperty(key)) {
                this._currentDB[key] = data;
            }
        },

        insert: function() {
            //TODO
        },

        limit: function(start, end) {
            var _tmp = this._buffer;
            var limit;

            if(!end) {
                start = [0, end = start][0];
            }

            limit = start + ':' + (start + end);

            this._buffer = utils.listSlice(_tmp, limit);
            return this;
        },

        keys: function() {
            return utils.keys(this.findAll());
        },

        first: function(fn) {
            if(fn) {
                return this.where(fn).first();
            }

            return utils.listSlice(this._buffer, ':1');
        },

        last: function(fn) {
            if(fn) {
                return this.where(fn).last();
            }

            return utils.listSlice(this._buffer, '-1:');
        },

        rebase: function() {
            this.select('*');
            return this;
        },

        noConflict: function() {
            if(window.jSQL === jSQL) {
                window.jSQL = this._jSQL;
            }

            if(window.jsql === jsql) {
                window.jsql = this._jsql;
            }

            return this;
        }
    };

    utils = {
        deep: function(data, scope) {
            var _tmp = data, scope = scope.split('.');
            for(var i = 0; i < scope.length; i++) {
                _tmp = _tmp[scope[i]];
            }
            return _tmp;
        },

        isArray: nativeIsArray || function(obj) {
            return toString.call(obj) === '[object Array]';
        },

        isObject: function(obj) {
            return obj === Object(obj);
        },

        isPlainObject: function(obj) {
            return this.isObject(obj) && obj.constructor === Object;
        },

        clone: function (obj) {
            if(obj == null || typeof(obj) != 'object') {
                return obj;
            }

            var temp = new obj.constructor();
            for(var key in obj) {
                temp[key] = arguments.callee(obj[key]);
            }

            return temp;
        },

        objectToArray: function(object) {
            var array = [], object = this.clone(object);

            for(var i in object) {
                if(object.hasOwnProperty(i)) {
                    object[i][jSQL_KEY_NAME] = i;
                    array.push(object[i]);
                }
            }

            return array;
        },

        arrayToObject: function(array, key) {
            var object = {};

            for(var i = 0; i < array.length; i++) {
                object[array[i][key || jSQL_KEY_NAME]] = this.clone(array[i]);
                delete object[array[i][key || jSQL_KEY_NAME]][key || jSQL_KEY_NAME];
            };

            return object;
        },

        each: function(list, fn) {
            if(nativeForEach) {
                list.forEach(fn);
                return;
            }

            for(var i = 0; i < list.length; i++) {
                fn(list[i], i, list);
            }
        },

        keygen: function(object, indexList) {
            var that = this;
            var baseRef = [].slice.call(arguments, 1);
            var key = '';

            if(that.isArray(indexList)) {
                baseRef = indexList;
            }

            that.each(baseRef, function(o, i, r) {
                key += utils.deep(object, o);
            });

            return key;
        },

        listSlice: function(list, range) {
            var start, end;

            list = [].slice.call(list);
            range = range.split(':');
            start = range[0] || 0;
            end = range.length > 1 ? range[1] || list.length : list.length;
            return [].slice.call(list, start, end);
        },

        appendKey: function(list, indexList) {
            var that = this;
            that.each(list, function(o, i, r) {
                o[jSQL_KEY_NAME] = that.keygen(o, indexList) || i;
            });
        },

        keys: nativeKeys || (function() {
            var hasDontEnumBug = true,
                dontEnums = [
                    'toString',
                    'toLocaleString',
                    'valueOf',
                    'hasOwnProperty',
                    'isPrototypeOf',
                    'propertyIsEnumerable',
                    'constructor'
                ],
                dontEnumsLength = dontEnums.length;

            for (var key in {'toString': null}) {
                hasDontEnumBug = false;
            }

            return function keys(object) {
                if ((typeof object != 'object' && typeof object != 'function') || object === null) {
                    throw new TypeError('Object.keys called on a non-object');
                }

                var keys = [];
                for (var name in object) {
                    if (object.hasOwnProperty(name)) {
                        keys.push(name);
                    }
                }

                if (hasDontEnumBug) {
                    for (var i = 0, ii = dontEnumsLength; i < ii; i++) {
                        var dontEnum = dontEnums[i];
                        if (object.hasOwnProperty(dontEnum)) {
                            keys.push(dontEnum);
                        }
                    }
                }
                return keys;
            };
        })(),

        parseFn: function(fn) {
            if(typeof(fn) === 'string') {
                fn = new Function('data', 'with(data) { return ' + fn + '; }');
            }

            return fn;
        }
    };

    jSQL = new jSQL();

    typeof(module) !== 'undefined' && module.exports ? module.exports = jSQL : this.jsql = this.jSQL = jSQL;
})();
<commit_msg>update.<commit_after>/*
    --------------- jSQL ---------------
    a SQL like database using javascript
    website: http://jsql.us
    licence: MIT Licence
    version: 0.3.0 dev

    description: using jSQL to process the data easily.
*/

(function() {
    var slice            = Array.prototype.slice,
        hasOwnProperty   = Object.prototype.hasOwnProperty;

    var nativeForEach      = Array.prototype.forEach,
        nativeIsArray      = Array.isArray,
        nativeKeys         = Object.keys;

    var jSQL, _jSQL, _jsql, _DB = {}, _DBIndexMap = {}, _protected = {};
    var jSQL_KEY_NAME = 'jSQL_Key';
    var utils = {};

    if(typeof(this.jSQL) !== 'undefined') {
        _jSQL = this.jSQL;
    }

    if(typeof(this.jsql) !== 'undefined') {
        _jsql = this.jsql;
    }

    jSQL = function() {
        this.init.apply(this, arguments);
    };

    jSQL.prototype = {
        version: '0.3.0-dev',

        init: function() {
            this._jSQL = _jSQL;
            this._jsql = _jsql;
            this._DB = _DB;
            this._currentDB = null;
            this._buffer = null;
            this._DBIndexMap = _DBIndexMap;
            this._protected = _protected;
            this.utils = utils;
        },

        create: function(dbname, db) {
            var indexList;

            if(this._DB.hasOwnProperty(dbname)) {
                throw('DB Already Exist.');
            }

            if(utils.isArray(db)) {
                indexList = utils.listSlice(arguments, '2:');
                utils.appendKey(db, indexList);
                _DBIndexMap[dbname] = utils.arrayToObject(db);
            }

            if(utils.isPlainObject(db)) {
                _DBIndexMap[dbname] = utils.clone(db);
                db = utils.objectToArray(db);
            }

            this._DB[dbname] = db;
            return this;
        },

        use: function(dbname) {
            if(!this._DB.hasOwnProperty(dbname)) {
                throw('Database Not Exist.');
            }

            this._currentDB = this._DB[dbname];
            this._currentDBName = dbname;
            this.rebase();
            return this;
        },

        drop: function(dbname) {
            if(this._DB.hasOwnProperty(dbname)) {
                delete this._DB[dbname];
            }
        },

        dbs: function() {
            return utils.keys(this._DB);
        },

        db: function() {
            return this._currentDBName;
        },

        select: function(key) {
            if(!this._currentDB) {
                throw('Please Select Database First.');
            }

            this._buffer = this._currentDB; //reset the _buffer
            this._protected['field'] = key;

            if(key === '*') {
                return this;
            }

            this.where(function(data) {
                return typeof(utils.deep(data, key)) !== 'undefined';
            });

            return this;
        },

        count: function() {
            var result;
            result = this._buffer.length;
            this.rebase();
            return result;
        },

        total: function(scope) {
            var rs = 0, _tmp;

            for(var _key in this._currentDB) {
                if(this._currentDB.hasOwnProperty(_key)) {
                    _tmp = scope === '*' ?
                        this._currentDB[_key] :
                        typeof(scope) === 'function' ?
                            scope.call(this, this._currentDB[_key], _key) === true ? true : undefined :
                            utils.deep(this._currentDB[_key], scope);

                    if(typeof(_tmp) !== 'undefined') {
                        rs++;
                    }
                }
            }

            return rs;
        },

        orderby: function(field, callback, order) {
            var _array = this._buffer;
            var _this = this;

            if(typeof(callback) !== 'function') {
                callback = [order, order = callback][0];
            }

            _array.sort(function(a, b) {
                a = utils.deep(a, field);
                b = utils.deep(b, field);
                if(callback) {
                    a = callback(a);
                    b = callback(b);
                }
                return  order && order.toLowerCase() === 'asc' ? a - b : b - a;
            });

            this._buffer = _array;
            return this;
        },

        where: function(fn) {
            var _tmp = [], _swap;
            this._buffer = this._buffer || this._currentDB;
            fn = utils.parseFn(fn);

            for(var i in this._buffer) {
                if(this._buffer.hasOwnProperty(i)) {
                    if(typeof(fn) === 'function') {
                        _swap = fn.call(this, utils.clone(this._buffer[i]), i);
                    }

                    if(utils.isArray(fn)) {
                        _swap = false;

                        for(var f in fn) {
                            if(fn.hasOwnProperty(f)) {
                                if(fn[f].call(this, utils.clone(this._buffer[i]), i)) {
                                    _swap = true;
                                }
                            }
                        }
                    }

                    if(_swap) {
                        _tmp.push(utils.clone(this._buffer[i]));
                    }
                }
            }
            this._buffer = _tmp;
            return this;
        },

        iterate: function(fn) {
            var _swap;
            this._buffer = this._buffer || this._currentDB;

            for(var i in this._buffer) {
                if(this._buffer.hasOwnProperty(i)) {
                    _swap = fn.call(this, this._buffer[i]);

                    if(_swap) {
                        this.update(i, _swap);
                    }
                }
            }
            return this;
        },

        findAll: function() {
            var result;
            result = utils.clone(utils.arrayToObject(this._buffer));
            this.rebase();
            return result;
        },

        find: function(key) {
            var result;
            var _tmp = this._DBIndexMap[this._currentDBName];

            if(!key) {
                for(var i in _tmp) {
                    if(_tmp.hasOwnProperty(i)) {
                        if(key) {
                            break;
                        }

                        if(this._buffer.hasOwnProperty(i)) {
                            key = i;
                        }
                    }
                }
            }

            result = utils.clone(_tmp[key]);
            this.rebase();
            return result;
        },

        listAll: function() {
            var result;
            result = utils.clone(this._buffer);
            this.rebase();
            return result;
        },

        update: function(key, data) {
            if(!this._currentDB) {
                throw('Please Select Database First.');
            }

            if(this._currentDB.hasOwnProperty(key)) {
                this._currentDB[key] = data;
            }
        },

        insert: function() {
            //TODO
        },

        limit: function(start, end) {
            var _tmp = this._buffer;
            var limit;

            if(!end) {
                start = [0, end = start][0];
            }

            limit = start + ':' + (start + end);

            this._buffer = utils.listSlice(_tmp, limit);
            return this;
        },

        keys: function() {
            return utils.keys(this.findAll());
        },

        first: function(fn) {
            if(fn) {
                return this.where(fn).first();
            }

            return utils.listSlice(this._buffer, ':1');
        },

        last: function(fn) {
            if(fn) {
                return this.where(fn).last();
            }

            return utils.listSlice(this._buffer, '-1:');
        },

        rebase: function() {
            this._protected = {};
            this.select('*');
            return this;
        },

        noConflict: function() {
            if(window.jSQL === jSQL) {
                window.jSQL = this._jSQL;
            }

            if(window.jsql === jsql) {
                window.jsql = this._jsql;
            }

            return this;
        },

        _select: function(data, field) {

        }
    };

    utils = {
        deep: function(data, scope) {
            var _tmp = data, scope = scope.split('.');
            for(var i = 0; i < scope.length; i++) {
                _tmp = _tmp[scope[i]];
            }
            return _tmp;
        },

        isArray: nativeIsArray || function(obj) {
            return toString.call(obj) === '[object Array]';
        },

        isObject: function(obj) {
            return obj === Object(obj);
        },

        isPlainObject: function(obj) {
            return this.isObject(obj) && obj.constructor === Object;
        },

        clone: function (obj) {
            if(obj == null || typeof(obj) != 'object') {
                return obj;
            }

            var temp = new obj.constructor();
            for(var key in obj) {
                temp[key] = arguments.callee(obj[key]);
            }

            return temp;
        },

        objectToArray: function(object) {
            var array = [], object = this.clone(object);

            for(var i in object) {
                if(object.hasOwnProperty(i)) {
                    object[i][jSQL_KEY_NAME] = i;
                    array.push(object[i]);
                }
            }

            return array;
        },

        arrayToObject: function(array, key) {
            var object = {};

            for(var i = 0; i < array.length; i++) {
                object[array[i][key || jSQL_KEY_NAME]] = this.clone(array[i]);
                delete object[array[i][key || jSQL_KEY_NAME]][key || jSQL_KEY_NAME];
            };

            return object;
        },

        each: function(list, fn) {
            if(nativeForEach) {
                list.forEach(fn);
                return;
            }

            for(var i = 0; i < list.length; i++) {
                fn(list[i], i, list);
            }
        },

        keygen: function(object, indexList) {
            var that = this;
            var baseRef = [].slice.call(arguments, 1);
            var key = '';

            if(that.isArray(indexList)) {
                baseRef = indexList;
            }

            that.each(baseRef, function(o, i, r) {
                key += utils.deep(object, o);
            });

            return key;
        },

        listSlice: function(list, range) {
            var start, end;

            list = [].slice.call(list);
            range = range.split(':');
            start = range[0] || 0;
            end = range.length > 1 ? range[1] || list.length : list.length;
            return [].slice.call(list, start, end);
        },

        appendKey: function(list, indexList) {
            var that = this;
            that.each(list, function(o, i, r) {
                o[jSQL_KEY_NAME] = that.keygen(o, indexList) || i;
            });
        },

        keys: nativeKeys || (function() {
            var hasDontEnumBug = true,
                dontEnums = [
                    'toString',
                    'toLocaleString',
                    'valueOf',
                    'hasOwnProperty',
                    'isPrototypeOf',
                    'propertyIsEnumerable',
                    'constructor'
                ],
                dontEnumsLength = dontEnums.length;

            for (var key in {'toString': null}) {
                hasDontEnumBug = false;
            }

            return function keys(object) {
                if ((typeof object != 'object' && typeof object != 'function') || object === null) {
                    throw new TypeError('Object.keys called on a non-object');
                }

                var keys = [];
                for (var name in object) {
                    if (object.hasOwnProperty(name)) {
                        keys.push(name);
                    }
                }

                if (hasDontEnumBug) {
                    for (var i = 0, ii = dontEnumsLength; i < ii; i++) {
                        var dontEnum = dontEnums[i];
                        if (object.hasOwnProperty(dontEnum)) {
                            keys.push(dontEnum);
                        }
                    }
                }
                return keys;
            };
        })(),

        parseFn: function(fn) {
            if(typeof(fn) === 'string') {
                fn = new Function('data', 'with(data) { return ' + fn + '; }');
            }

            return fn;
        }
    };

    jSQL = new jSQL();

    typeof(module) !== 'undefined' && module.exports ? module.exports = jSQL : this.jsql = this.jSQL = jSQL;
})();
<|endoftext|>


<commit_before>/** @license MIT License (c) copyright B Cavalier & J Hann */

/**
 * wire/aop plugin
 * Provides AOP for components created via wire, including Decorators,
 * Introductions (mixins), and Pointcut-based Aspect Weaving.
 *
 * wire is part of the cujo.js family of libraries (http://cujojs.com/)
 *
 * Licensed under the MIT License at:
 * http://www.opensource.org/licenses/mit-license.php
 */
(function(define) {
define(['aop', 'when'], function(aop, when) {

    var obj, tos, isArray, adviceTypes, whenAll, chain, deferred, undef;

    obj = {};
    tos = Object.prototype.toString;

    isArray = Array.isArray || function(it) {
        return tos.call(it) == '[object Array]';
    };

    whenAll  = when.all;
    chain    = when.chain;
    deferred = when.defer;

	adviceTypes = ['before', 'around', 'after', 'afterReturning', 'afterThrowing'];

    //
    // Decoration
    //

    function applyDecorator(target, Decorator, args) {
        args = args ? [target].concat(args) : [target];

        Decorator.apply(null, args);
    }

    function doDecorate(target, decorator, args, wire) {
        function apply(Decorator) {
            return args
                ? when(wire(args), function (resolvedArgs) {
                    applyDecorator(target, Decorator, resolvedArgs);
                })
                : applyDecorator(target, Decorator);
        }

        return when(wire.resolveRef(decorator), apply);
    }

    function decorateFacet(resolver, facet, wire) {
        var target, options, promises;

        target = facet.target;
        options = facet.options;
        promises = [];

        for(var d in options) {
            promises.push(doDecorate(target, d, options[d], wire));
        }

        chain(whenAll(promises), resolver);
    }

    //
    // Introductions
    //

    function introduce(target, src) {
        var name, s;

        for(name in src) {
            s = src[name];
            if(!(name in target) || (target[name] !== s && (!(name in obj) || obj[name] !== s))) {
                target[name] = s;
            }
        }

        return target;
    }

    function doIntroduction(target, introduction, wire) {
        return when(wire.resolveRef(introduction), function(resolved) {
            return introduce(target, resolved);
        });
    }

    function introduceFacet(resolver, facet, wire) {
        var target, intros;

        target = facet.target;
        intros = facet.options;

        if(!isArray(intros)) intros = [intros];

        chain(when.reduce(intros, function(target, intro) {
            return doIntroduction(target, intro, wire);
        }, target), resolver);
    }

	//
	// Simple advice
	//

	function addSingleAdvice(addAdviceFunc, advices, target, advice, options, wire) {
		var source, sourceMethod, targetMethod, promise, promises;

		// First, determine the direction of the connection(s)
		// If ref is a method on target, connect it to another object's method, i.e. calling a method on target
		// causes a method on the other object to be called.
		// If ref is a reference to another object, connect that object's method to a method on target, i.e.
		// calling a method on the other object causes a method on target to be called.
		if(typeof target[advice] == 'function') {
			// Connecting from an event on the current component to a method on
			// another component.  Set source = wiring target
			source = target;
			sourceMethod = advice;

			if(typeof options == 'string') {
				target = options.split('.');
				// eventName: 'componentName.methodName'

				targetMethod = target[1];
				target = target[0];

				promise = when(wire.resolveRef(target), function(target) {
					advices.push(addAdviceFunc(source, sourceMethod, target, targetMethod));
				});
			} else {
				// eventName: {
				//   componentName: 'methodName'
				// }

				promises = [];
				for(advice in options) {

					targetMethod = options[advice];
					promise = when(wire.resolveRef(advice), function(target) {
						advices.push(addAdviceFunc(source, sourceMethod, target, targetMethod));
					});

					promises.push(promise);
				}

				promise = when.all(promises);

			}

		} else {
			if(typeof options == 'string') {
				// 'component.eventName': 'methodName'

				source = advice.split('.');
				sourceMethod = source[1];
				source = source[0];
				targetMethod = options;

				promise = when(wire.resolveRef(source), function(source) {
					console.log(source, sourceMethod, target, targetMethod);
					advices.push(addAdviceFunc(source, sourceMethod, target, targetMethod))
				});
			} else {
				// componentName: {
				//   eventName: 'methodName'
				// }

				source = advice;
				promise = when(wire.resolveRef(advice), function(source) {
					for(sourceMethod in options) {
						advices.push(addAdviceFunc(source, sourceMethod, target, options[sourceMethod]));
					}
				});

			}
		}

		return promise;
	}

	function makeSingleAdviceAdd(adviceType) {
		return function (source, sourceMethod, target, targetMethod) {
			return aop[adviceType](source, sourceMethod, function() {
				return target[targetMethod].apply(target, arguments);
			});
		};
	}

	function addAfterResolvingAdvice(source, sourceMethod, target, targetMethod) {
		return aop.afterReturning(source, sourceMethod, function(promise) {
			return when(promise, function(resolved) {
				return target[targetMethod].call(target, resolved);
			});
		});
	}

	function addAfterRejectingAdvice(source, sourceMethod, target, targetMethod) {
		return aop.afterReturning(source, sourceMethod, function(promise) {
			return when(promise, null, function(resolved) {
				return target[targetMethod].call(target, resolved);
			});
		});
	}

	function addAfterPromiseAdvice(source, sourceMethod, target, targetMethod) {
		function handlePromise(value) {
			return target[targetMethod].call(target, value);
		}

		return aop.afterReturning(source, sourceMethod, function(promise) {
			return when(promise, handlePromise, handlePromise);
		});
	}

	function makeAdviceFacet(addAdviceFunc, advices) {
		return function(resolver, facet, wire) {
			var advice, target, advicesToAdd, promises;

			target = facet.target;
			advicesToAdd = facet.options;
			promises = [];

			for(advice in advicesToAdd) {
				promises.push(addSingleAdvice(addAdviceFunc, advices,
					target, advice, advicesToAdd[advice], wire));
			}

			when.chain(when.all(promises), resolver);
		}
	}

    //
    // Aspect Weaving
    //

    function applyAspectCombined(target, aspect, wire, add) {
        return when(wire.resolveRef(aspect), function (aspect) {
            var pointcut = aspect.pointcut;

            if (pointcut) {
                add(target, pointcut, aspect);
            }

            return target;
        });
    }

    function applyAspectSeparate(target, aspect, wire, add) {
        var pointcut, advice;

        pointcut = aspect.pointcut;
        advice = aspect.advice;

        function applyAdvice(pointcut) {
            return when(wire.resolveRef(advice), function (aspect) {
                add(target, pointcut, aspect);
                return target;
            });
        }

        return typeof pointcut === 'string'
            ? when(wire.resolveRef(pointcut, applyAdvice))
            : applyAdvice(pointcut);
    }

    function weave(resolver, proxy, wire, options, add) {

        var target, path, aspects, applyAdvice;

        aspects = options.aspects;
        path = proxy.path;

        if (!aspects || path === undef) {
            resolver.resolve();
            return;
        }

        target = proxy.target;
        applyAdvice = applyAspectCombined;

        // Reduce will preserve order of aspects being applied
        chain(when.reduce(aspects, function(target, aspect) {
            var aspectPath;

            if (aspect.advice) {
                aspectPath = aspect.advice;
                applyAdvice = applyAspectSeparate;
            } else {
                aspectPath = aspect;
            }

            return typeof aspectPath === 'string' && aspectPath !== path
                ? applyAdvice(target, aspect, wire, add)
                : target;

        }, target), resolver);
    }

    return {
        /**
         * Creates wire/aop plugin instances.
         *
         * @param ready {Promise} promise that will be resolved when the context has been wired,
         *  rejected if there is an error during the wiring process, and will receive progress
         *  events for object creation, property setting, and initialization.
         * @param destroyed {Promise} promise that will be resolved when the context has been destroyed,
         *  rejected if there is an error while destroying the context, and will receive progress
         *  events for objects being destroyed.
         * @param options {Object}
         */
        wire$plugin: function(ready, destroyed, options) {

            // Track aspects so they can be removed when the context is destroyed
            var woven, plugin, i, len, adviceType;

			woven = [];

            // Remove all aspects that we added in this context
            when(destroyed, function() {
                for(var i = woven.length; i >= 0; --i) {
                    woven[i].remove();
                }
            });

            /**
             * Function to add an aspect and remember it in the current context
             * so that it can be removed when the context is destroyed.
             * @param target
             * @param pointcut
             * @param aspect
             */
            function add(target, pointcut, aspect) {
                woven.push(aop.add(target, pointcut, aspect));
            }

            function makeFacet(step, callback) {
                var facet = {};

                facet[step] = function(resolver, proxy, wire) {
                    callback(resolver, proxy, wire);
                };

                return facet;
            }

            // Plugin
            plugin = {
                facets: {
                    decorate:  makeFacet('configure', decorateFacet),
                    introduce: makeFacet('configure', introduceFacet),
					afterResolving: {
						create: makeAdviceFacet(addAfterResolvingAdvice, woven)
					},
					afterRejecting: {
						create: makeAdviceFacet(addAfterRejectingAdvice, woven)
					},
					afterPromise: {
						create: makeAdviceFacet(addAfterPromiseAdvice, woven)
					}
                },
                create: function(resolver, proxy, wire) {
                    weave(resolver, proxy, wire, options, add);
                }
            };

			// Add all regular single advice facets
			for(i = 0, len = adviceTypes.length; i<len; i++) {
				adviceType = adviceTypes[i];
				plugin.facets[adviceType] = {
					create: makeAdviceFacet(makeSingleAdviceAdd(adviceType), woven)
				};
			}

			return plugin;
        }
    };
});
})(typeof define == 'function'
	// use define for AMD if available
	? define
    : function(deps, factory) {
        module.exports = factory.apply(this, deps.map(require));
    }
);
<commit_msg>Fix silly bug in unweaving aspects when context destroyed<commit_after>/** @license MIT License (c) copyright B Cavalier & J Hann */

/**
 * wire/aop plugin
 * Provides AOP for components created via wire, including Decorators,
 * Introductions (mixins), and Pointcut-based Aspect Weaving.
 *
 * wire is part of the cujo.js family of libraries (http://cujojs.com/)
 *
 * Licensed under the MIT License at:
 * http://www.opensource.org/licenses/mit-license.php
 */
(function(define) {
define(['aop', 'when'], function(aop, when) {

    var obj, tos, isArray, adviceTypes, whenAll, chain, deferred, undef;

    obj = {};
    tos = Object.prototype.toString;

    isArray = Array.isArray || function(it) {
        return tos.call(it) == '[object Array]';
    };

    whenAll  = when.all;
    chain    = when.chain;
    deferred = when.defer;

	adviceTypes = ['before', 'around', 'after', 'afterReturning', 'afterThrowing'];

    //
    // Decoration
    //

    function applyDecorator(target, Decorator, args) {
        args = args ? [target].concat(args) : [target];

        Decorator.apply(null, args);
    }

    function doDecorate(target, decorator, args, wire) {
        function apply(Decorator) {
            return args
                ? when(wire(args), function (resolvedArgs) {
                    applyDecorator(target, Decorator, resolvedArgs);
                })
                : applyDecorator(target, Decorator);
        }

        return when(wire.resolveRef(decorator), apply);
    }

    function decorateFacet(resolver, facet, wire) {
        var target, options, promises;

        target = facet.target;
        options = facet.options;
        promises = [];

        for(var d in options) {
            promises.push(doDecorate(target, d, options[d], wire));
        }

        chain(whenAll(promises), resolver);
    }

    //
    // Introductions
    //

    function introduce(target, src) {
        var name, s;

        for(name in src) {
            s = src[name];
            if(!(name in target) || (target[name] !== s && (!(name in obj) || obj[name] !== s))) {
                target[name] = s;
            }
        }

        return target;
    }

    function doIntroduction(target, introduction, wire) {
        return when(wire.resolveRef(introduction), function(resolved) {
            return introduce(target, resolved);
        });
    }

    function introduceFacet(resolver, facet, wire) {
        var target, intros;

        target = facet.target;
        intros = facet.options;

        if(!isArray(intros)) intros = [intros];

        chain(when.reduce(intros, function(target, intro) {
            return doIntroduction(target, intro, wire);
        }, target), resolver);
    }

	//
	// Simple advice
	//

	function addSingleAdvice(addAdviceFunc, advices, target, advice, options, wire) {
		var source, sourceMethod, targetMethod, promise, promises;

		// First, determine the direction of the connection(s)
		// If ref is a method on target, connect it to another object's method, i.e. calling a method on target
		// causes a method on the other object to be called.
		// If ref is a reference to another object, connect that object's method to a method on target, i.e.
		// calling a method on the other object causes a method on target to be called.
		if(typeof target[advice] == 'function') {
			// Connecting from an event on the current component to a method on
			// another component.  Set source = wiring target
			source = target;
			sourceMethod = advice;

			if(typeof options == 'string') {
				target = options.split('.');
				// eventName: 'componentName.methodName'

				targetMethod = target[1];
				target = target[0];

				promise = when(wire.resolveRef(target), function(target) {
					advices.push(addAdviceFunc(source, sourceMethod, target, targetMethod));
				});
			} else {
				// eventName: {
				//   componentName: 'methodName'
				// }

				promises = [];
				for(advice in options) {

					targetMethod = options[advice];
					promise = when(wire.resolveRef(advice), function(target) {
						advices.push(addAdviceFunc(source, sourceMethod, target, targetMethod));
					});

					promises.push(promise);
				}

				promise = when.all(promises);

			}

		} else {
			if(typeof options == 'string') {
				// 'component.eventName': 'methodName'

				source = advice.split('.');
				sourceMethod = source[1];
				source = source[0];
				targetMethod = options;

				promise = when(wire.resolveRef(source), function(source) {
					console.log(source, sourceMethod, target, targetMethod);
					advices.push(addAdviceFunc(source, sourceMethod, target, targetMethod))
				});
			} else {
				// componentName: {
				//   eventName: 'methodName'
				// }

				source = advice;
				promise = when(wire.resolveRef(advice), function(source) {
					for(sourceMethod in options) {
						advices.push(addAdviceFunc(source, sourceMethod, target, options[sourceMethod]));
					}
				});

			}
		}

		return promise;
	}

	function makeSingleAdviceAdd(adviceType) {
		return function (source, sourceMethod, target, targetMethod) {
			return aop[adviceType](source, sourceMethod, function() {
				return target[targetMethod].apply(target, arguments);
			});
		};
	}

	function addAfterResolvingAdvice(source, sourceMethod, target, targetMethod) {
		return aop.afterReturning(source, sourceMethod, function(promise) {
			return when(promise, function(resolved) {
				return target[targetMethod].call(target, resolved);
			});
		});
	}

	function addAfterRejectingAdvice(source, sourceMethod, target, targetMethod) {
		return aop.afterReturning(source, sourceMethod, function(promise) {
			return when(promise, null, function(resolved) {
				return target[targetMethod].call(target, resolved);
			});
		});
	}

	function addAfterPromiseAdvice(source, sourceMethod, target, targetMethod) {
		function handlePromise(value) {
			return target[targetMethod].call(target, value);
		}

		return aop.afterReturning(source, sourceMethod, function(promise) {
			return when(promise, handlePromise, handlePromise);
		});
	}

	function makeAdviceFacet(addAdviceFunc, advices) {
		return function(resolver, facet, wire) {
			var advice, target, advicesToAdd, promises;

			target = facet.target;
			advicesToAdd = facet.options;
			promises = [];

			for(advice in advicesToAdd) {
				promises.push(addSingleAdvice(addAdviceFunc, advices,
					target, advice, advicesToAdd[advice], wire));
			}

			when.chain(when.all(promises), resolver);
		}
	}

    //
    // Aspect Weaving
    //

    function applyAspectCombined(target, aspect, wire, add) {
        return when(wire.resolveRef(aspect), function (aspect) {
            var pointcut = aspect.pointcut;

            if (pointcut) {
                add(target, pointcut, aspect);
            }

            return target;
        });
    }

    function applyAspectSeparate(target, aspect, wire, add) {
        var pointcut, advice;

        pointcut = aspect.pointcut;
        advice = aspect.advice;

        function applyAdvice(pointcut) {
            return when(wire.resolveRef(advice), function (aspect) {
                add(target, pointcut, aspect);
                return target;
            });
        }

        return typeof pointcut === 'string'
            ? when(wire.resolveRef(pointcut, applyAdvice))
            : applyAdvice(pointcut);
    }

    function weave(resolver, proxy, wire, options, add) {

        var target, path, aspects, applyAdvice;

        aspects = options.aspects;
        path = proxy.path;

        if (!aspects || path === undef) {
            resolver.resolve();
            return;
        }

        target = proxy.target;
        applyAdvice = applyAspectCombined;

        // Reduce will preserve order of aspects being applied
        chain(when.reduce(aspects, function(target, aspect) {
            var aspectPath;

            if (aspect.advice) {
                aspectPath = aspect.advice;
                applyAdvice = applyAspectSeparate;
            } else {
                aspectPath = aspect;
            }

            return typeof aspectPath === 'string' && aspectPath !== path
                ? applyAdvice(target, aspect, wire, add)
                : target;

        }, target), resolver);
    }

    return {
        /**
         * Creates wire/aop plugin instances.
         *
         * @param ready {Promise} promise that will be resolved when the context has been wired,
         *  rejected if there is an error during the wiring process, and will receive progress
         *  events for object creation, property setting, and initialization.
         * @param destroyed {Promise} promise that will be resolved when the context has been destroyed,
         *  rejected if there is an error while destroying the context, and will receive progress
         *  events for objects being destroyed.
         * @param options {Object}
         */
        wire$plugin: function(ready, destroyed, options) {

            // Track aspects so they can be removed when the context is destroyed
            var woven, plugin, i, len, adviceType;

			woven = [];

            // Remove all aspects that we added in this context
            when(destroyed, function() {
                for(var i = woven.length - 1; i >= 0; --i) {
                    woven[i].remove();
                }
            });

            /**
             * Function to add an aspect and remember it in the current context
             * so that it can be removed when the context is destroyed.
             * @param target
             * @param pointcut
             * @param aspect
             */
            function add(target, pointcut, aspect) {
                woven.push(aop.add(target, pointcut, aspect));
            }

            function makeFacet(step, callback) {
                var facet = {};

                facet[step] = function(resolver, proxy, wire) {
                    callback(resolver, proxy, wire);
                };

                return facet;
            }

            // Plugin
            plugin = {
                facets: {
                    decorate:  makeFacet('configure', decorateFacet),
                    introduce: makeFacet('configure', introduceFacet),
					afterResolving: {
						create: makeAdviceFacet(addAfterResolvingAdvice, woven)
					},
					afterRejecting: {
						create: makeAdviceFacet(addAfterRejectingAdvice, woven)
					},
					afterPromise: {
						create: makeAdviceFacet(addAfterPromiseAdvice, woven)
					}
                },
                create: function(resolver, proxy, wire) {
                    weave(resolver, proxy, wire, options, add);
                }
            };

			// Add all regular single advice facets
			for(i = 0, len = adviceTypes.length; i<len; i++) {
				adviceType = adviceTypes[i];
				plugin.facets[adviceType] = {
					create: makeAdviceFacet(makeSingleAdviceAdd(adviceType), woven)
				};
			}

			return plugin;
        }
    };
});
})(typeof define == 'function'
	// use define for AMD if available
	? define
    : function(deps, factory) {
        module.exports = factory.apply(this, deps.map(require));
    }
);
<|endoftext|>


<commit_before>// wuw server
"use strict";

// import packages (api)
var express = require("express");
var expressValidator = require('express-validator');
var bodyParser = require("body-parser");
var util = require('util');
var mongoose = require("mongoose");
var morgan = require("morgan");
var https = require('https');
var fs = require('fs');

// ssl
var ca; var file;
var files = ["ssl-ca.crt", "ssl-intermediate.crt"];
files.forEach(function(file) {
    if (file) {
        ca += fs.readFileSync("./" + file);
    }
});
var key = fs.readFileSync('./ssl-wuw.key');
var cert = fs.readFileSync('./ssl-wuw.crt');
// load passphrase from file
var pass = require("./ssl-pass");
var https_options = {
    ca: ca,
    key: key,
    cert: cert,
    passphrase: pass.passphrase
};

// create the express app & configure port
var app = express();
var port = process.env.PORT || 8088;

// api version & url
var apiVersion = 0;
var apiBaseUrl = "/api/v" + apiVersion;

// use morgan to log
app.use(morgan("dev"));

// configure body parser
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
app.use(expressValidator());

// allow cross origin resource sharing
app.use(function(req, res, next) {
    res.header("Access-Control-Allow-Origin", "*");
    res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
    next();
});

// connect to mongodb
mongoose.connect("mongodb://localhost:27017/wuw");

// create models from our schemas
var Lecture = require("./model_lecture");
var Deadline = require("./model_deadline");

// routes
var router = express.Router();

// middleware to use for all requests
router.use(function(req, res, next) {
    //console.log("Something is happening...");
    next();
});

// test route to make sure everything is working (GET /$apiBaseUrl)
router.get("/", function(req, res) {
    res.status(200).json({ message: "welcome to the wuw api v" + apiVersion });
    res.end();
});

// on routes that end in /lectures
router.route("/lectures")

    // get all lectures (GET /$apiBaseUrl/lectures)
    .get(function(req, res) {
        Lecture.find({}).sort({startTime: 1}).exec(function(err, lectures) {
            if (err) { res.status(500).send(err); }
            res.status(200).json(lectures);
            res.end();
        });
    });

// on routes that end in /lectures/:lecture_id
router.route("/lectures/:lecture_id")

    // get lecture with that id (GET /$apiBaseUrl/lectures/:lecture_id)
    .get(function(req, res) {
        Lecture.findById(req.params.lecture_id, function(err, lecture) {
            if (err) { res.status(500).send(err); }
            res.status(200).json(lecture);
            res.end();
        });
    });

// on routes that end in /deadlines
router.route("/deadlines")

    // get all deadlines (GET /$apiBaseUrl/deadlines)
    .get(function(req, res) {
        Deadline.find(function(err, deadlines) {
            if (err) { res.status(500).send(err); }
            res.status(200).json(deadlines);
            res.end();
        });
    })

    // create a deadline (POST /$apiBaseUrl/deadlines)
    .post(function(req, res) {
        // create instance of Deadline model
        console.log(req.body);
        var deadline = new Deadline();

        // check inputs
        req.assert("deadline", "deadline must be a valid date").isDate();
        req.assert("deadline", "deadline must not be empty").notEmpty();
        req.assert("info", "info must not be empty").notEmpty();
        //req.assert("lectureName", "lectureName must not be empty").notEmpty();
        //req.assert("group", "group must not be empty").notEmpty();
        //req.assert("uuid", "oh kiddie...").notEmpty();

        // if there are errors, send 400
        var errors = req.validationErrors(true);
        if (errors) {
            res.status(400).send("There have been validation errors: " + util.inspect(errors));
            return;
        }

        // set attributes
        deadline.deadline = req.body.deadline;
        deadline.info = req.body.info;
        deadline.shortLectureName = req.body.shortLectureName;
        deadline.group = req.body.group;
        deadline.createdBy = req.body.uuid;

        // save deadline in mongodb
        deadline.save(function(err, deadline) {
            if (err) { res.send(err); }
            res.status(200).json({ message: "Deadline created!", id: deadline.id });
        });
    });

// on routes that end in /deadlines/:deadline_id
router.route("/deadlines/:deadline_id")

    // get deadline with that id (GET /$apiBaseUrl/deadlines/:deadline_id)
    .get(function(req, res) {
        Deadline.findById(req.params.deadline_id, function(err, deadline) {
            if (err) { res.status(500).send(err); }
            res.status(200).json(deadline);
            res.end();
        });
    })

    // update deadline with this id
    .put(function(req, res) {
        Deadline.findById(req.params.deadline_id, function(err, deadline) {
            if (err) { res.status(500).send(err); }

            // set new attributes
            deadline.deadline = req.body.deadline;
            deadline.shortLectureName = req.body.shortLectureName;
            deadline.group = req.body.group;

            deadline.save(function(err) {
                if (err) { res.send(err); }
                res.status(200).json({ message: "Deadline updated!" });
            });

        });
    })

    // delete the deadline with this id
    .delete(function(req, res) {
        Deadline.remove({
            _id: req.params.deadline_id
        }, function(err) {
            if (err) { res.status(500).send(err); }
            res.status(200).json({ message: "Deadline successfully deleted" });
        });
    });

// on routes that end in /upcomingLectures
router.route("/upcomingLectures")

    // get upcoming lectures (GET /$apiBaseUrl/upcomingLectures)
    .get(function(req, res) {
        Lecture.find({"endTime": {"$gte": new Date()}}).sort({startTime: 1}).exec(function(err, lectures) {
            if (err) { res.status(500).send(err); }
            res.status(200).json(lectures);
            res.end();
        });
    });

// on routes that end in /rooms
router.route("/rooms")

    // get all rooms (GET /$apiBaseUrl/rooms)
    .get(function(req, res) {
        // querys for all rooms and aggregate to one doc
        Lecture.aggregate([ { $unwind: "$rooms" }, { $group: { _id: "rooms", rooms: { $addToSet: "$rooms" } } } ]).exec(function(err, rooms) {
            if (err) { res.status(500).send(err); }
            res.status(200).json(rooms[0].rooms);
            res.end();
        });
    });

// on routes that end in /freeRooms
router.route("/freeRooms")

    // get all (probably) free rooms (GET /$apiBaseUrl/freeRooms)
    .get(function(req, res) {

        // get all rooms (in db)
        Lecture.aggregate([ { $unwind: "$rooms" }, { $group: { _id: "rooms", rooms: { $addToSet: "$rooms" } } } ]).exec(function(err, rooms) {
            if (err) { res.status(500).send(err); }

            // get rooms currently in use
            Lecture.aggregate([ { $match: { startTime: { "$lte": new Date() }, endTime: { "$gte": new Date() } } }, { $unwind: "$rooms" }, { $group: { _id: "rooms", rooms: { $addToSet: "$rooms" } } } ]).exec(function(err, usedRooms) {
                if (err) { res.status(500).send(err); }

                // filter used rooms by comparing both arrays
                var freeRooms;
                if(usedRooms[0] && usedRooms[0].rooms) {
                    freeRooms = rooms[0].rooms.filter(function(e) {
                        return (usedRooms[0].rooms.indexOf(e) < 0);
                    });
                } else {
                    // all rooms free
                    freeRooms = rooms[0].rooms;
                }

                res.status(200).json(freeRooms);
                res.end();
            });
        });
    });

// on routes that end in /groups
router.route("/groups")

    // get all groups (GET /$apiBaseUrl/groups)
    .get(function(req, res) {
        // querys for all groups and aggregate to one doc
        Lecture.aggregate([ { $unwind: "$groups" }, { $group: { _id: "groups", groups: { $addToSet: "$groups" } } } ]).exec(function(err, groups) {
            if (err) { res.status(500).send(err); }
            res.status(200).json(groups[0].groups);
            res.end();
        });
    });

// register the router & the base url
app.use(apiBaseUrl, router);

// start the https server
var server = https.createServer(https_options, app).listen(port);
console.log("magic happens at https://localhost:" + port + apiBaseUrl);

var startApi = function() {
    if (!server) {
        server = app.listen(port);
    }
};

var stopApi = function() {
    server.close();
};

// export functions
module.exports = { startApi: startApi, stopApi: stopApi };
<commit_msg>remove unneeded ca/intermediate<commit_after>// wuw server
"use strict";

// import packages (api)
var express = require("express");
var expressValidator = require('express-validator');
var bodyParser = require("body-parser");
var util = require('util');
var mongoose = require("mongoose");
var morgan = require("morgan");
var https = require('https');
var fs = require('fs');

// ssl
var key = fs.readFileSync('./ssl-wuw.key');
var cert = fs.readFileSync('./ssl-wuw.crt');
// load passphrase from file
var pass = require("./ssl-pass");
var https_options = {
    key: key,
    cert: cert,
    passphrase: pass.passphrase
};

// create the express app & configure port
var app = express();
var port = process.env.PORT || 8088;

// api version & url
var apiVersion = 0;
var apiBaseUrl = "/api/v" + apiVersion;

// use morgan to log
app.use(morgan("dev"));

// configure body parser
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
app.use(expressValidator());

// allow cross origin resource sharing
app.use(function(req, res, next) {
    res.header("Access-Control-Allow-Origin", "*");
    res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
    next();
});

// connect to mongodb
mongoose.connect("mongodb://localhost:27017/wuw");

// create models from our schemas
var Lecture = require("./model_lecture");
var Deadline = require("./model_deadline");

// routes
var router = express.Router();

// middleware to use for all requests
router.use(function(req, res, next) {
    //console.log("Something is happening...");
    next();
});

// test route to make sure everything is working (GET /$apiBaseUrl)
router.get("/", function(req, res) {
    res.status(200).json({ message: "welcome to the wuw api v" + apiVersion });
    res.end();
});

// on routes that end in /lectures
router.route("/lectures")

    // get all lectures (GET /$apiBaseUrl/lectures)
    .get(function(req, res) {
        Lecture.find({}).sort({startTime: 1}).exec(function(err, lectures) {
            if (err) { res.status(500).send(err); }
            res.status(200).json(lectures);
            res.end();
        });
    });

// on routes that end in /lectures/:lecture_id
router.route("/lectures/:lecture_id")

    // get lecture with that id (GET /$apiBaseUrl/lectures/:lecture_id)
    .get(function(req, res) {
        Lecture.findById(req.params.lecture_id, function(err, lecture) {
            if (err) { res.status(500).send(err); }
            res.status(200).json(lecture);
            res.end();
        });
    });

// on routes that end in /deadlines
router.route("/deadlines")

    // get all deadlines (GET /$apiBaseUrl/deadlines)
    .get(function(req, res) {
        Deadline.find(function(err, deadlines) {
            if (err) { res.status(500).send(err); }
            res.status(200).json(deadlines);
            res.end();
        });
    })

    // create a deadline (POST /$apiBaseUrl/deadlines)
    .post(function(req, res) {
        // create instance of Deadline model
        console.log(req.body);
        var deadline = new Deadline();

        // check inputs
        req.assert("deadline", "deadline must be a valid date").isDate();
        req.assert("deadline", "deadline must not be empty").notEmpty();
        req.assert("info", "info must not be empty").notEmpty();
        //req.assert("lectureName", "lectureName must not be empty").notEmpty();
        //req.assert("group", "group must not be empty").notEmpty();
        //req.assert("uuid", "oh kiddie...").notEmpty();

        // if there are errors, send 400
        var errors = req.validationErrors(true);
        if (errors) {
            res.status(400).send("There have been validation errors: " + util.inspect(errors));
            return;
        }

        // set attributes
        deadline.deadline = req.body.deadline;
        deadline.info = req.body.info;
        deadline.shortLectureName = req.body.shortLectureName;
        deadline.group = req.body.group;
        deadline.createdBy = req.body.uuid;

        // save deadline in mongodb
        deadline.save(function(err, deadline) {
            if (err) { res.send(err); }
            res.status(200).json({ message: "Deadline created!", id: deadline.id });
        });
    });

// on routes that end in /deadlines/:deadline_id
router.route("/deadlines/:deadline_id")

    // get deadline with that id (GET /$apiBaseUrl/deadlines/:deadline_id)
    .get(function(req, res) {
        Deadline.findById(req.params.deadline_id, function(err, deadline) {
            if (err) { res.status(500).send(err); }
            res.status(200).json(deadline);
            res.end();
        });
    })

    // update deadline with this id
    .put(function(req, res) {
        Deadline.findById(req.params.deadline_id, function(err, deadline) {
            if (err) { res.status(500).send(err); }

            // set new attributes
            deadline.deadline = req.body.deadline;
            deadline.shortLectureName = req.body.shortLectureName;
            deadline.group = req.body.group;

            deadline.save(function(err) {
                if (err) { res.send(err); }
                res.status(200).json({ message: "Deadline updated!" });
            });

        });
    })

    // delete the deadline with this id
    .delete(function(req, res) {
        Deadline.remove({
            _id: req.params.deadline_id
        }, function(err) {
            if (err) { res.status(500).send(err); }
            res.status(200).json({ message: "Deadline successfully deleted" });
        });
    });

// on routes that end in /upcomingLectures
router.route("/upcomingLectures")

    // get upcoming lectures (GET /$apiBaseUrl/upcomingLectures)
    .get(function(req, res) {
        Lecture.find({"endTime": {"$gte": new Date()}}).sort({startTime: 1}).exec(function(err, lectures) {
            if (err) { res.status(500).send(err); }
            res.status(200).json(lectures);
            res.end();
        });
    });

// on routes that end in /rooms
router.route("/rooms")

    // get all rooms (GET /$apiBaseUrl/rooms)
    .get(function(req, res) {
        // querys for all rooms and aggregate to one doc
        Lecture.aggregate([ { $unwind: "$rooms" }, { $group: { _id: "rooms", rooms: { $addToSet: "$rooms" } } } ]).exec(function(err, rooms) {
            if (err) { res.status(500).send(err); }
            res.status(200).json(rooms[0].rooms);
            res.end();
        });
    });

// on routes that end in /freeRooms
router.route("/freeRooms")

    // get all (probably) free rooms (GET /$apiBaseUrl/freeRooms)
    .get(function(req, res) {

        // get all rooms (in db)
        Lecture.aggregate([ { $unwind: "$rooms" }, { $group: { _id: "rooms", rooms: { $addToSet: "$rooms" } } } ]).exec(function(err, rooms) {
            if (err) { res.status(500).send(err); }

            // get rooms currently in use
            Lecture.aggregate([ { $match: { startTime: { "$lte": new Date() }, endTime: { "$gte": new Date() } } }, { $unwind: "$rooms" }, { $group: { _id: "rooms", rooms: { $addToSet: "$rooms" } } } ]).exec(function(err, usedRooms) {
                if (err) { res.status(500).send(err); }

                // filter used rooms by comparing both arrays
                var freeRooms;
                if(usedRooms[0] && usedRooms[0].rooms) {
                    freeRooms = rooms[0].rooms.filter(function(e) {
                        return (usedRooms[0].rooms.indexOf(e) < 0);
                    });
                } else {
                    // all rooms free
                    freeRooms = rooms[0].rooms;
                }

                res.status(200).json(freeRooms);
                res.end();
            });
        });
    });

// on routes that end in /groups
router.route("/groups")

    // get all groups (GET /$apiBaseUrl/groups)
    .get(function(req, res) {
        // querys for all groups and aggregate to one doc
        Lecture.aggregate([ { $unwind: "$groups" }, { $group: { _id: "groups", groups: { $addToSet: "$groups" } } } ]).exec(function(err, groups) {
            if (err) { res.status(500).send(err); }
            res.status(200).json(groups[0].groups);
            res.end();
        });
    });

// register the router & the base url
app.use(apiBaseUrl, router);

// start the https server
var server = https.createServer(https_options, app).listen(port);
console.log("magic happens at https://localhost:" + port + apiBaseUrl);

var startApi = function() {
    if (!server) {
        server = app.listen(port);
    }
};

var stopApi = function() {
    server.close();
};

// export functions
module.exports = { startApi: startApi, stopApi: stopApi };
<|endoftext|>